NumPy配列ndarrayの論理・ビット演算(AND, OR, XOR, NOT, シフト)

Posted: | Tags: Python, NumPy

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がブールboolnumpy.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()関数でも算出可能。

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.0Falseでそれ以外は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()などでは各要素がブールとして判定されてから処理されるのに対し、後述のように整数intnumpy.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が自動的に形状変換される。

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がブールboolnumpy.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()関数でも算出可能。

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())の注意点

符号あり整数の場合、~xbitwise_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が自動的に形状変換される。

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と整数intnumpy.ndarray&bitwise_andで処理すると、True1False0としてビット演算が行われる。結果は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]

関連カテゴリー

関連記事