NumPy配列ndarrayの論理・ビット演算(AND, OR, XOR, NOT, シフト)
NumPy配列ndarrayの要素ごとの論理演算(ブール演算)およびビット演算には&(AND, 論理積)、|(OR, 論理和)、^(XOR, 排他的論理和)、~(NOT, 否定・反転)、<<, >>(ビットシフト)の各演算子を用いる。
ここでは以下の内容について説明する。
and,or,notと&,|,~の違い- NumPy配列
ndarrayの要素ごとの論理演算(ブール演算)&,|,^,~演算子logical_and(),logical_or(),logical_xor(),logical_not()関数- ブロードキャスト
- 条件式を用いる場合の注意点
- NumPy配列
ndarrayの要素ごとのビット演算&,|,^,~,<<,>>演算子bitwise_and(),bitwise_or(),bitwise_xor(),bitwise_not(),invert()関数- 反転(
~,bitwise_not(),invert())の注意点 - ブロードキャスト
- 浮動小数点数
floatやブールboolの場合
and, or, notと&, |, ~の違い
Pythonでは、オブジェクトの論理演算(ブール演算)にはand, or, not演算子、整数のビット演算(二進数で表した各ビットに対する論理演算)などには&, |, ~演算子を使う。
NumPy配列ndarrayの要素ごとの処理には、いずれも&, |, ~演算子を使う。
以降のサンプルコードで示すように、データ型dtypeがブールboolであれば要素ごとの論理演算となり、整数intであれば要素ごとのビット演算となる。
要素数が1個でないNumPy配列ndarrayに対してand, or, not演算子を使うとエラーになるので注意。以降の説明でも簡単に触れるが、要素数が1個の場合など、より詳細は以下の記事を参照。
NumPy配列ndarrayの要素ごとの論理演算(ブール演算)
以下のnumpy.ndarrayを例とする。データ型dtypeはブールbool。
import numpy as np
print(np.__version__)
# 1.17.3
a_bool = np.array([True, True, False, False])
b_bool = np.array([True, False, True, False])
print(a_bool.dtype)
# bool
print(b_bool.dtype)
# bool
&, |, ^, ~演算子
&, |, ^, ~演算子で、要素ごとのAND(論理積)、OR(論理和)、XOR(排他的論理和)、NOT(否定)を算出できる。
print(a_bool & b_bool)
# [ True False False False]
print(a_bool | b_bool)
# [ True True True False]
print(a_bool ^ b_bool)
# [False True True False]
print(~a_bool)
# [False False True True]
結果もデータ型dtypeがブールboolのnumpy.ndarrayとなる。
print(type(a_bool & b_bool))
# <class 'numpy.ndarray'>
print((a_bool & b_bool).dtype)
# bool
要素数が1個でない場合、and, or, notはエラーとなる。
# print(a_bool and b_bool)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
詳細は以下の記事を参照。
logical_and(), logical_or(), logical_xor(), logical_not()関数
要素ごとのAND(論理積)、OR(論理和)、XOR(排他的論理和)、NOT(否定)はlogical_and(), logical_or(), logical_xor(), logical_not()関数でも算出可能。
- numpy.logical_and — NumPy v1.18 Manual
- numpy.logical_or — NumPy v1.18 Manual
- numpy.logical_xor — NumPy v1.18 Manual
- numpy.logical_not — NumPy v1.18 Manual
print(np.logical_and(a_bool, b_bool))
# [ True False False False]
print(np.logical_or(a_bool, b_bool))
# [ True True True False]
print(np.logical_xor(a_bool, b_bool))
# [False True True False]
print(np.logical_not(a_bool))
# [False False True True]
データ型dtypeがブールbool以外のnumpy.ndarrayは、まず各要素がブールとして判定されてから処理される。
例えば整数intや浮動小数点数floatでは、0および0.0がFalseでそれ以外はTrueと判定される。
c_int = np.arange(4)
print(c_int)
# [0 1 2 3]
print(np.logical_not(c_int))
# [ True False False False]
d_int = c_int + 4
print(d_int)
# [4 5 6 7]
print(np.logical_not(d_int))
# [False False False False]
logical_and()やlogical_or()などでは各要素がブールとして判定されてから処理されるのに対し、後述のように整数intのnumpy.ndarrayに対する&や|などはビット演算となるため、結果が異なる。要注意。
print(np.logical_and(c_int, d_int))
# [False True True True]
print(c_int & d_int)
# [0 1 2 3]
ブロードキャスト
+, -のような演算子と同様に、&, |, ^演算子やlogical_and(), logical_or(), logical_xor()でもブロードキャストが行われ、次元の異なるnumpy.ndarrayが自動的に形状変換される。
- 関連記事: NumPyのブロードキャスト(形状の自動変換)
a_bool_2d = np.array([[True, True, False, False], [False, False, True, True]])
print(a_bool_2d)
# [[ True True False False]
# [False False True True]]
print(a_bool_2d & b_bool)
# [[ True False False False]
# [False False True False]]
print(np.logical_and(a_bool_2d, a_bool))
# [[ True True False False]
# [False False False False]]
各要素とスカラー値を処理することも可能。
print(a_bool & True)
# [ True True False False]
print(np.logical_and(a_bool, True))
# [ True True False False]
条件式を用いる場合の注意点
numpy.ndarrayに対して<や==などの比較演算子を用いると、各要素がそれぞれ判定され、データ型dtypeがブールboolのnumpy.ndarrayが返される。
print(c_int)
# [0 1 2 3]
print(c_int < 2)
# [ True True False False]
print(c_int % 2 == 0)
# [ True False True False]
この結果に対して&, |, ^, ~演算子を使う場合、各条件式を括弧()で囲む必要がある。括弧が無いとエラーになるので注意。
print((c_int < 2) & (c_int % 2 == 0))
# [ True False False False]
# print(c_int < 2 & c_int % 2 == 0)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
これは、&, |, ^, ~演算子は比較演算子(<など)よりも優先順位が高いため。
括弧がないと以下のように処理されてしまう。
# print(c_int < (2 & (c_int % 2)) == 0)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
logical_and()などでは引数ごとに扱われるため、当然ながら特に括弧は必要ない。
print(np.logical_and(c_int < 2, c_int % 2 == 0))
# [ True False False False]
NumPy配列ndarrayの要素ごとのビット演算
以下のnumpy.ndarrayを例とする。データ型dtypeは整数int。
import numpy as np
print(np.__version__)
# 1.17.3
a_int = np.array([0, 1, 3]) # [0b00 0b01 0b11]
b_int = np.array([1, 0, 2]) # [0b01 0b00 0b10]
print(a_int.dtype)
# int64
print(b_int.dtype)
# int64
&, |, ^, ~, <<, >>演算子
&, |, ^, ~, <<, >>演算子で、要素ごとのビット単位のAND(論理積)、OR(論理和)、XOR(排他的論理和)、NOT(反転)、左シフト、右シフトを算出できる。
print(a_int & b_int)
# [0 0 2]
print(a_int | b_int)
# [1 1 3]
print(a_int ^ b_int)
# [1 1 1]
print(~a_int)
# [-1 -2 -4]
print(a_int << b_int)
# [ 0 1 12]
print(a_int >> b_int)
# [0 1 0]
反転の結果については後述。
bitwise_and(), bitwise_or(), bitwise_xor(), bitwise_not(), invert()
要素ごとのビット単位のAND(論理積)、OR(論理和)、XOR(排他的論理和)、NOT(反転)はbitwise_and(), bitwise_or(), bitwise_xor(), bitwise_not(), invert()関数でも算出可能。
- numpy.bitwise_and — NumPy v1.18 Manual
- numpy.bitwise_or — NumPy v1.18 Manual
- numpy.bitwise_xor — NumPy v1.18 Manual
- numpy.invert — NumPy v1.18 Manual
bitwise_not()はinvert()のエイリアスなのでどちらを使っても同じ。
print(np.bitwise_and(a_int, b_int))
# [0 0 2]
print(np.bitwise_or(a_int, b_int))
# [1 1 3]
print(np.bitwise_xor(a_int, b_int))
# [1 1 1]
print(np.bitwise_not(a_int))
# [-1 -2 -4]
print(np.invert(a_int))
# [-1 -2 -4]
反転(~, bitwise_not(), invert())の注意点
符号あり整数の場合、~xやbitwise_not(x), invert(x)は-(x + 1)となる値を返す。Pythonにおける~の挙動と同じく、負の値を表現するための2の補数形式を考慮した結果となる。
print(~a_int)
# [-1 -2 -4]
print(-(a_int + 1))
# [-1 -2 -4]
符号なし整数uintの場合、単純にビット反転した結果となる。データ型dtypeのビット数によって結果が異なるので注意。
a_uint8 = np.array([0, 1, 3], dtype=np.uint8)
print(~a_uint8)
# [255 254 252]
a_uint16 = np.array([0, 1, 3], dtype=np.uint16)
print(~a_uint16)
# [65535 65534 65532]
bitwise_not(), invert()の例は省略するが、~と同様。
ブロードキャスト
+, -のような演算子と同様に、&, |, ^, <<, >>演算子やbitwise_and(), bitwise_or(), bitwise_xor()でもブロードキャストが行われ、次元の異なるnumpy.ndarrayが自動的に形状変換される。
- 関連記事: NumPyのブロードキャスト(形状の自動変換)
c_int_2d = np.arange(6).reshape(2, 3)
print(c_int_2d)
# [[0 1 2]
# [3 4 5]]
print(c_int_2d & a_int)
# [[0 1 2]
# [0 0 1]]
print(np.bitwise_and(c_int_2d, a_int))
# [[0 1 2]
# [0 0 1]]
各要素とスカラー値を処理することも可能。
print(c_int_2d & 2)
# [[0 0 2]
# [2 0 0]]
print(np.bitwise_and(c_int_2d, 2))
# [[0 0 2]
# [2 0 0]]
浮動小数点数floatやブールboolの場合
ビット演算を行う演算子や関数は浮動小数点数floatには対応していないので、エラーとなる。
d_float = np.array([0, 1, 3], dtype=float)
# print(a_int & d_float)
# TypeError: ufunc 'bitwise_and' 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(~d_float)
# TypeError: ufunc 'invert' 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.bitwise_and(a_int, d_float))
# TypeError: ufunc 'bitwise_and' 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.bitwise_not(d_float))
# TypeError: ufunc 'invert' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
上述の通り、データ型dtypeがブールbool同士の&や|は要素ごとの論理演算となり、結果もboolとなる。
ブールboolと整数intのnumpy.ndarrayを&やbitwise_andで処理すると、Trueが1、Falseが0としてビット演算が行われる。結果はintとなる。
e_bool = np.array([True, False, True])
print(a_int & e_bool)
# [0 0 1]
print((a_int & e_bool).dtype)
# int64
print(np.bitwise_and(a_int, e_bool))
# [0 0 1]
print(np.bitwise_and(a_int, e_bool).dtype)
# int64
なお、bitwise_not(), invert()では、boolを渡すとboolが返される。
print(~e_bool)
# [False True False]
print(np.logical_not(e_bool))
# [False True False]
print(np.bitwise_not(e_bool))
# [False True False]
print(np.invert(e_bool))
# [False True False]