note.nkmk.me

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

Date: 2019-09-18 / tags: Python, NumPy

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

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

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の場合

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

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

print(np.sign(a_special))
# [-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.sign()関数でnumpy.ndarrayの符号を判定できる。負の値を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()でもよい。

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

~bool値の各要素の否定も可能。

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))
# 3

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

無限大infはその符号を元に判定され、欠損値nanFalseとなる。

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

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

なお、欠損値nanはすべての比較演算に対してFalseとなる。

print(np.nan == 0)
# False

print(np.nan < 0)
# False

print(np.nan > 0)
# False

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

print(a_special < 0)
# [ True False 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  True 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.

複素数の場合

複素数を要素とする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

上の例の結果を見ると0-0.0になっている。後述のように、基本的には気にしなくても大丈夫だが、割り算を行う場合などは注意。

スカラー値でも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に対する処理の注意点

上の例でもあったように、numpy.copysign()の処理において0の符号が負になると-0.0が生成される。

zero_pos = np.copysign(0, 1)
print(zero_pos)
# 0.0

zero_nega = np.copysign(0, -1)
print(zero_nega)
# -0.0

値としては0と等価。

print(zero_pos == zero_nega == 0)
# True

上述のnumpy.sign()でも0として扱われる。

print(np.sign([zero_pos, zero_nega]))
# [0. 0.]

割り算を行う場合、結果の無限大infの符号が異なる。

print(1 / zero_pos)
# inf
# 
# /usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: divide by zero encountered in double_scalars
#   """Entry point for launching an IPython kernel.

print(1 / zero_nega)
# -inf
# 
# /usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: divide by zero encountered in double_scalars
#   """Entry point for launching an IPython kernel.

そのほかの処理でも結果が異なることがあるかもしれないので要注意。

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

無限大infは符号を持つのでそのほかの値と同じように扱われる。

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

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

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

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

第二引数にnanがある場合は第一引数の対応する要素は正となる。

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

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

複素数の場合

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''
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事