note.nkmk.me

NumPy配列ndarrayの符号(正負)を取得・判定・置換

Posted: 2019-09-18 / Modified: 2019-12-11 / Tags: Python, NumPy

NumPy配列numpy.ndarrayの符号(正負、プラス・マイナス)に関する処理について、以下の内容を説明する。

  • NumPy配列ndarrayの符号を取得: np.sign()
    • 基本的な使い方
    • 負のゼロや無限大inf、欠損値nanの場合
    • 複素数の場合
  • NumPy配列ndarrayの符号を判定: np.signbit()
    • 基本的な使い方
    • 比較演算子で判定
    • 符号ごとに要素数をカウント
    • 負のゼロや無限大inf、欠損値nanの場合
    • 複素数の場合
  • NumPy配列ndarrayの符号を別の配列のものに置換: np.copysign()
    • 基本的な使い方
    • ブロードキャスト
    • 負のゼロや無限大inf、欠損値nanの場合
    • 複素数の場合

NumPyを使わずにsign()copysign()の処理を行いたい場合は以下の記事を参照。

スポンサーリンク

NumPy配列ndarrayの符号を取得: np.sign()

numpy.sign()関数でnumpy.ndarrayの符号を取得できる。

基本的な使い方

numpy.sign()の引数にnumpy.ndarrayを指定する。負の値は-1、正の値は100となるnumpy.ndarrayが返される。

import numpy as np

a = np.array([-100, -10, 0, 10, 100])
print(a)
# [-100  -10    0   10  100]

print(np.sign(a))
# [-1 -1  0  1  1]

print(type(np.sign(a)))
# <class 'numpy.ndarray'>

print(np.sign(a).dtype)
# int64

データ型dtypeは元のnumpy.ndarrayと同じ。浮動小数点数floatの場合はfloat

a_float = np.array([-1.23, 0.0, 1.23])
print(a_float)
# [-1.23  0.    1.23]

print(np.sign(a_float))
# [-1.  0.  1.]

print(np.sign(a_float).dtype)
# float64

スカラー値に対してはスカラー値を返す。この場合も元の型と同じ型となる。

print(np.sign(100))
# 1

print(type(np.sign(100)))
# <class 'numpy.int64'>

print(np.sign(-1.23))
# -1.0

print(type(np.sign(-1.23)))
# <class 'numpy.float64'>

負のゼロや無限大inf、欠損値nanの場合

浮動小数点数floatでは負のゼロ(= -0.0)を表現できる。整数intのゼロには正負はない。

numpy.sign()は正負いずれの0.0に対しても0.0を返す。

無限大infはその符号、欠損値nannanとなる。np.infnp.nanは浮動小数点数floatなので返り値もfloat

a_special = np.array([0.0, -0.0, np.inf, -np.inf, np.nan])
print(a_special)
# [  0.  -0.  inf -inf  nan]

print(np.sign(a_special))
# [ 0.  0.  1. -1. nan]

print(np.sign(a_special).dtype)
# float64

複素数の場合

複素数の場合、実部が0でなければ実部の符号、実部が0の場合は虚部の符号が返される。返り値はすべて虚部が0の複素数。0, nanも実部が0, nanで虚部が0の複素数となる。

a_complex = np.array([[10 + 10j, -10 + 10j], [10 - 10j, -10 - 10j], [10, -10], [10j, -10j], [0, np.nan], [0j, np.nan * 1j]])
print(a_complex)
# [[ 10.+10.j -10.+10.j]
#  [ 10.-10.j -10.-10.j]
#  [ 10. +0.j -10. +0.j]
#  [  0.+10.j  -0.-10.j]
#  [  0. +0.j  nan +0.j]
#  [  0. +0.j  nan+nanj]]

print(np.sign(a_complex))
# [[ 1.+0.j -1.+0.j]
#  [ 1.+0.j -1.+0.j]
#  [ 1.+0.j -1.+0.j]
#  [ 1.+0.j -1.+0.j]
#  [ 0.+0.j nan+0.j]
#  [ 0.+0.j nan+0.j]]

実部、虚部それぞれの符号を取得したい場合はreal, imag属性を使う。

print(a_complex.real)
# [[ 10. -10.]
#  [ 10. -10.]
#  [ 10. -10.]
#  [  0.  -0.]
#  [  0.  nan]
#  [  0.  nan]]

print(np.sign(a_complex.real))
# [[ 1. -1.]
#  [ 1. -1.]
#  [ 1. -1.]
#  [ 0.  0.]
#  [ 0. nan]
#  [ 0. nan]]

print(a_complex.imag)
# [[ 10.  10.]
#  [-10. -10.]
#  [  0.   0.]
#  [ 10. -10.]
#  [  0.   0.]
#  [  0.  nan]]

print(np.sign(a_complex.imag))
# [[ 1.  1.]
#  [-1. -1.]
#  [ 0.  0.]
#  [ 1. -1.]
#  [ 0.  0.]
#  [ 0. nan]]

NumPy配列ndarrayの符号を判定: np.signbit()

numpy.signbit()関数でnumpy.ndarrayの符号を判定できる。名前の通り、符号ビット(sign bit)をブール値で返す。負の値がTrue0および正の値がFalseとなる。

後述のように、比較演算子を使っても同様の処理が可能。

基本的な使い方

numpy.signbit()の引数にnumpy.ndarrayを指定する。負の値がTrue0および正の値がFalseとなるnumpy.ndarrayが返される。

a = np.array([-100, -10, 0, 10, 100])
print(a)
# [-100  -10    0   10  100]

print(np.signbit(a))
# [ True  True False False False]

print(type(np.signbit(a)))
# <class 'numpy.ndarray'>

print(np.signbit(a).dtype)
# bool

スカラー値に対してはスカラー値を返す。

print(np.signbit(-100))
# True

比較演算子で判定

同様の処理は比較演算子を使ってもできる。

print(a == 0)
# [False False  True False False]

print(a > 0)
# [False False False  True  True]

print(a >= 0)
# [False False  True  True  True]

print(a < 0)
# [ True  True False False False]

print(a <= 0)
# [ True  True  True False False]

符号ごとに要素数をカウント

numpy.count_nonzero()関数の引数にboolを要素とするnumpy.ndarrayを指定するとTrueの数をカウントできる。sum()でもよい。

したがって、numpy.signbit()の結果をnumpy.count_nonzero()の引数に指定すると、True、すなわち、負の値の数をカウントできる。

print(np.count_nonzero(np.signbit(a)))
# 2

~bool値の各要素の否定も可能。False、すなわち、0および正の値の数をカウントできる。

print(~np.signbit(a))
# [False False  True  True  True]

print(np.count_nonzero(~np.signbit(a)))
# 3

比較演算子を使っても同様の処理が可能。

print(np.count_nonzero(a == 0))
# 1

print(np.count_nonzero(a < 0))
# 2

print(np.count_nonzero(a > 0))
# 2

負のゼロや無限大inf、欠損値nanの場合

numpy.signbit()では、浮動小数点数floatのゼロおよび無限大infはその符号を元に判定され、欠損値nanFalseとなる。

a_special = np.array([0.0, -0.0, np.inf, -np.inf, np.nan])
print(a_special)
# [  0.  -0.  inf -inf  nan]

print(np.signbit(a_special))
# [False  True False  True False]

0との比較演算の結果は以下の通り。

print(a_special == 0)
# [ True  True False False False]

print(a_special < 0)
# [False False False  True False]
# 
# /usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in less
#   """Entry point for launching an IPython kernel.

print(a_special > 0)
# [False False  True False False]
# 
# /usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in greater
#   """Entry point for launching an IPython kernel.

正負いずれの00と等価とみなされる。また、欠損値nanはすべての比較演算に対してFalseとなる。

環境によっては、上の例のように、要素数が2個以上で欠損値nanを含むnumpy.ndarrayの比較演算に対して警告が出る場合がある。エラーではないので処理は中断されない。

複素数の場合

複素数を要素とするnumpy.ndarraynumpy.signbit()の対象外。エラーとなる。

a_complex = np.array([3 + 4j, -3 - 4j])
print(a_complex)
# [ 3.+4.j -3.-4.j]

# print(np.signbit(a_complex))
# TypeError: ufunc 'signbit' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

np.abs()で絶対値、real, imag属性で実部・虚部を取得できる。それらに対する処理はもちろん可能。

print(np.abs(a_complex))
# [5. 5.]

print(a_complex.real)
# [ 3. -3.]

print(a_complex.imag)
# [ 4. -4.]

print(np.signbit(a_complex.real))
# [False  True]

print(a_complex.real < 0)
# [False  True]

NumPy配列ndarrayの符号を別の配列のものに置換: np.copysign()

numpy.copysign()で、あるnumpy.ndarrayの符号を別のnumpy.ndarrayのものに置き換えることができる。

基本的な使い方

第一引数、第二引数にそれぞれnumpy.ndarrayを指定する。

第一引数の符号が第二引数の符号に置き換えられる。引数のデータ型によらず、返り値のデータ型は常に浮動小数点数float

a = np.arange(12).reshape(3, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

b = np.arange(-5, 7).reshape(3, 4)
print(b)
# [[-5 -4 -3 -2]
#  [-1  0  1  2]
#  [ 3  4  5  6]]

a_copysign = np.copysign(a, b)
print(a_copysign)
# [[-0. -1. -2. -3.]
#  [-4.  5.  6.  7.]
#  [ 8.  9. 10. 11.]]

print(a_copysign.dtype)
# float64

スカラー値でもOK。この場合もfloatが返される。

print(np.copysign(10, -5))
# -10.0

print(type(np.copysign(10, -5)))
# <class 'numpy.float64'>

ブロードキャスト

形状shapeが異なるnumpy.ndarray同士の演算では、可能な場合はブロードキャストによって形状が揃えられる。

print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

b_small = np.array([-100, -100, 100, 100])
print(b_small)
# [-100 -100  100  100]

print(a + b_small)
# [[-100  -99  102  103]
#  [ -96  -95  106  107]
#  [ -92  -91  110  111]]

numpy.copysign()においてもブロードキャストが行われる。

print(np.copysign(a, b_small))
# [[-0. -1.  2.  3.]
#  [-4. -5.  6.  7.]
#  [-8. -9. 10. 11.]]

ブロードキャストできない場合はエラーとなる。

b_mismatch = np.array([-100, -100, 100])
print(b_mismatch)
# [-100 -100  100]

# print(np.copysign(a, b_mismatch))
# ValueError: operands could not be broadcast together with shapes (3,4) (3,) 

numpy.copysign()の第二引数にはスカラー値も指定できる。

print(np.copysign(b, -10))
# [[-5. -4. -3. -2.]
#  [-1. -0. -1. -2.]
#  [-3. -4. -5. -6.]]

第二引数にスカラー値を指定した場合は全てに要素がその符号に揃えられるが、絶対値を返すnp.abs()を使って同じように要素全体の符号を揃えることもできる。numpy.copysign()の返り値はfloatだが、np.abs()を使うとそれぞれのデータ型に応じた型となる。

print(np.abs(b) * -1)
# [[-5 -4 -3 -2]
#  [-1  0 -1 -2]
#  [-3 -4 -5 -6]]

print(np.abs(b) * -1.0)
# [[-5. -4. -3. -2.]
#  [-1. -0. -1. -2.]
#  [-3. -4. -5. -6.]]

負の0や無限大inf、欠損値nanの場合

浮動小数点数floatのゼロや無限大infは符号を持つのでそのほかの値と同じように扱われる。

第一引数がnanである場合は第二引数によらずnanのまま。

a_special = np.array([0.0, -0.0, np.inf, -np.inf, np.nan])
print(a_special)
# [  0.  -0.  inf -inf  nan]

print(np.copysign(a_special, 1))
# [ 0.  0. inf inf nan]

print(np.copysign(a_special, -1))
# [ -0.  -0. -inf -inf  nan]

第二引数がnanである場合は正となる。

print(np.copysign([10, 10, 10, 10, 10], a_special))
# [ 10. -10.  10. -10.  10.]

print(np.copysign([-10, -10, -10, -10, -10], a_special))
# [ 10. -10.  10. -10.  10.]

第二引数に整数int0を指定した場合は正として扱われる。

print(np.copysign(10, 0))
# 10.0

numpy.copysign()は常に浮動小数点数floatを返すので、第一引数が整数の0でも第二引数に応じて正負いずれかのゼロ(0.0または-0.0)となる。

print(np.copysign(0, 10))
# 0.0

print(np.copysign(0, -10))
# -0.0

複素数の場合

numpy.copysign()は複素数には対応していない。第一引数に指定しても第二引数に指定してもエラーとなる。

a_complex = np.array([10 + 10j, -10 + 10j])
print(a_complex)
# [ 10.+10.j -10.+10.j]

# print(np.copysign(a_complex, 1))
# TypeError: ufunc 'copysign' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

# print(np.copysign([1, 1], a_complex))
# TypeError: ufunc 'copysign' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事