note.nkmk.me

NumPy配列ndarrayを要素ごとに比較(比較演算子、np.allcloseなど)

Date: 2019-12-22 / tags: Python, NumPy

2つのNumPy配列ndarrayを要素ごとに比較するには、>==などの比較演算子を使う。真偽値bool型(True, False)を要素とするndarrayが返される。ndarray同士だけでなくndarrayとスカラー値との比較も可能。

また、すべての要素が等しいか判定するnp.array_equal(), np.array_equiv()、それぞれの要素またはすべての要素が近いか判定するnp.isclose(), np.allclose()といった関数も提供されている。

ここでは、以下の内容について説明する。

  • 比較演算子による配列ndarrayの比較
    • ブール値の配列ndarrayが返される
    • np.count_nonzero(), np.all(), np.any()と組み合わせ
    • 欠損値NaNとの比較: np.isnan()
    • 複数条件や複数配列の場合: &, |
  • すべての要素が等しいか判定: np.array_equal(), np.array_equiv()
  • それぞれの要素が近いか判定: np.isclose()
  • すべての要素が近いか判定: np.allclose()
スポンサーリンク

比較演算子による配列ndarrayの比較

ブール値の配列ndarrayが返される

以下の2つのndarrayを例とする。

import numpy as np

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

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

比較演算子でそのまま比較すると、真偽値bool型(True, False)を要素とするndarrayが返される。

a_compare = a < b
print(a_compare)
# [[False  True  True  True]
#  [False False  True  True]
#  [False False False False]]

print(type(a_compare))
# <class 'numpy.ndarray'>

print(a_compare.dtype)
# bool

そのほかの比較演算子でも同様。

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

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

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

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

print(a != b)
# [[False  True  True  True]
#  [ True  True  True  True]
#  [ True  True  True False]]

値としての比較なので、データ型dtypeが異なっていても問題ない。

b_float = b.astype(float)
print(b_float)
# [[ 0.  3.  6.  9.]
#  [ 1.  4.  7. 10.]
#  [ 2.  5.  8. 11.]]

print(b_float.dtype)
# float64

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

なお、浮動小数点数float==で比較する場合は誤差に注意。ある程度の差を許容して比較するには後述のnp.isclose()を使う。

次元数が異なっていても可能であればブロードキャストされる。

b_1d = np.arange(4, 8)
print(b_1d)
# [4 5 6 7]

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

スカラー値と比較すると、その値とすべての要素がそれぞれ比較される。

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

演算結果と比較することも可能。例えば整数の要素が偶数かどうかを判定する例は以下のように書ける。

print(a % 2)
# [[0 1 0 1]
#  [0 1 0 1]
#  [0 1 0 1]]

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

np.count_nonzero(), np.all(), np.any()と組み合わせ

bool型の配列ndarrayTrueの数はnp.count_nonzero()でカウントできる。

print(np.count_nonzero(a < 6))
# 6

すべてTrueか、または、少なくとも一つはTrueを含むかといった判定にはnp.all(), np.any()を使う。いずれも引数axisを使うことで行ごとや列ごとに処理できる。

print(np.all(a < 6))
# False

print(np.all(a < 6, axis=1))
# [ True False False]

print(np.any(a < 6))
# True

print(np.any(a < 6, axis=1))
# [ True  True False]

比較演算子とnp.count_nonzero(), np.all(), np.any()との組み合わせについての詳細は以下の記事を参照。

欠損値NaNとの比較: np.isnan()

データが欠落したCSVファイルを読み込んだ場合などに欠損値NaNが発生する。

欠損値NaN同士を比較してもFalseが返されるので、欠損値NaNの存在確認などにはnp.isnan()を使う必要がある。

a_nan = np.array([0, 1, np.nan])
print(a_nan)
# [ 0.  1. nan]

print(a_nan == np.nan)
# [False False False]

print(np.isnan(a_nan))
# [False False  True]

大小比較の場合もNaNとの比較はFalseとなるので注意。

print(a_nan > 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.

なお、要素数1個(NaNだけ)の場合やNaN単体を比較した場合は警告は発生しないが、要素数2個以上の配列にNaNが含まれていると上の例のように警告が発生する(多数の要素の中にNaNが紛れているのを知らせるため?)。

a_nan_only = np.array([np.nan])
print(a_nan_only)
# [nan]

print(a_nan_only > 0)
# [False]

print(np.nan > 0)
# False

NaNNaNが同じ位置にあるときにTrueとしたい場合は後述のnp.isclose()を使う。

複数条件や複数配列の場合: &, |

Pythonでは以下のように条件式をつなげて書ける。

x = 6

print(4 < x < 8)
# True

ndarrayにはこのような書き方はできない。複数条件を指定したい場合はそれぞれの条件式を別々に書き、&(かつ)や|(または)を使う。

# print(4 < a < 8)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

print((a > 4) & (a < 8))
# [[False False False False]
#  [False  True  True  True]
#  [False False False False]]

andorを使ったり、括弧を省略するとエラーになるので注意。

# print((a > 4) and (a < 8))
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

# print(a > 4 & a < 8)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

このエラーについての詳細は以下の記事を参照。

3つ以上のndarrayを比較する際も同様。Pythonの文法上はまとめて比較可能。

x = 6
y = 6
z = 6

print(x == y == z)
# True

これもndarrayではダメ。それぞれの条件式と&|をつかう。

c = np.zeros((3, 4), int)
print(c)
# [[0 0 0 0]
#  [0 0 0 0]
#  [0 0 0 0]]

# print(a == b == c)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

print((a == b) & (b == c))
# [[ True False False False]
#  [False False False False]
#  [False False False False]]

すべての要素が等しいか判定: np.array_equal(), np.array_equiv()

2つのndarrayのすべての要素が等しいかどうかは、上述のように==np.all()を使って判定できる。

a = np.arange(3)
print(a)
# [0 1 2]

b = np.arange(3)
print(b)
# [0 1 2]

c = np.arange(1, 4)
print(c)
# [1 2 3]

print(np.all(a == b))
# True

print(np.all(a == c))
# False

np.array_equal(), np.array_equiv()という関数を使う方法もある。

print(np.array_equal(a, b))
# True

print(np.array_equal(a, c))
# False

print(np.array_equiv(a, b))
# True

print(np.array_equiv(a, c))
# False

==と同じく値としての比較なので、データ型dtypeが異なっていても問題ない。

b_f = np.arange(3, dtype=float)
print(b_f)
# [0. 1. 2.]

print(np.array_equal(a, b_f))
# True

print(np.array_equiv(a, b_f))
# True

np.array_equal()は形状shapeが不一致だとFalseを返すのに対し、np.array_equiv()はスカラー値との比較やブロードキャストを伴う比較が可能。

ones = np.ones(3)
print(ones)
# [1. 1. 1.]

print(np.array_equal(ones, 1))
# False

print(np.array_equiv(ones, 1))
# True

a_2d = np.array([[0, 1, 2], [0, 1, 2], [0, 1, 2]])
print(a_2d)
# [[0 1 2]
#  [0 1 2]
#  [0 1 2]]

print(np.array_equal(a_2d, b))
# False

print(np.array_equiv(a_2d, b))
# True

欠損値NaN同士の比較はFalseとなるので、NaNが含まれていると同じ位置にあってもFalseとなる。

a_nan = np.array([np.nan, 1, 2])
print(a_nan)
# [nan  1.  2.]

b_nan = np.array([np.nan, 1, 2])
print(b_nan)
# [nan  1.  2.]

print(np.array_equal(a_nan, b_nan))
# False

print(np.array_equiv(a_nan, b_nan))
# False

print(np.all(a_nan == b_nan))
# False

同じ位置にNaNがあるときはすべての要素が等しいと判定したい場合は、NaNを他の値に置換するか、最後に説明するnp.allclose()を使う。

それぞれの要素が近いか判定: np.isclose()

浮動小数点数floatはコンピュータの内部では2進数で表現されているため、10進数の小数と厳密に同じ値を表現できない。

例えば0.1 + 0.1 + 0.10.3と等しくない。

print(0.1 + 0.1 + 0.1)
# 0.30000000000000004

a = np.array([0.3, 0.1 + 0.1 + 0.1])
print(a)
# [0.3 0.3]

b = np.array([0.3, 0.3])
print(b)
# [0.3 0.3]

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

なお、上の例ではprint()の出力がすべて0.3と表示されているが、これはデフォルトが小数点以下8桁までの表示であるため。np.set_printoptions()で設定を変更できる。

np.set_printoptions(precision=18)

print(a)
# [0.3                 0.30000000000000004]

np.isclose()を使うと、ある程度の誤差を許容してそれぞれの要素の値が近いかを判定できる。スカラー値との比較や、ここでは例を省略するがブロードキャストを伴う比較も可能。

print(np.isclose(a, b))
# [ True  True]

print(np.isclose(a, 0.3))
# [ True  True]

スカラー値同士の比較はndarrayではなくスカラー値を返す。

print(np.isclose(0.1 + 0.1 + 0.1, 0.3))
# True

np.isclose()は引数rtol, qtolを指定可能(デフォルトはrtol=1e-05, atol=1e-08)。その2つの値を用いて以下の計算式で判定される。absoluteは絶対値。

absolute(a - b) <= (atol + rtol * absolute(b)) numpy.isclose — NumPy v1.17 Manual

標準ライブラリのmathモジュールにもmath.isclose()という関数があるが、判定の計算式が異なるので注意。

例えば、単純に差分の絶対値が1以内のときにTrueとしたい場合は以下のように設定する。

print(np.isclose(100, 101))
# False

print(np.isclose(100, 101, rtol=0, atol=1))
# True

また、np.isclose()では引数equal_nanを指定可能。equal_nan=Trueとすると、欠損値NaN同士の比較がTrueとなる。

print(np.isclose(np.nan, np.nan))
# False

print(np.isclose(np.nan, np.nan, equal_nan=True))
# True

TrueとなるのはあくまでもNaN同士で、他の値とNaNの比較はequal_nan=TrueでもFalse`。

print(np.isclose(np.nan, 100, equal_nan=True))
# False

NaNを含むndarrayの例。

a_nan = np.array([np.nan, 1, 2])
print(a_nan)
# [nan  1.  2.]

b_nan = np.array([np.nan, np.nan, 2])
print(b_nan)
# [nan nan  2.]

print(np.isclose(a_nan, b_nan))
# [False False  True]

print(np.isclose(a_nan, b_nan, equal_nan=True))
# [ True False  True]

すべての要素が近いか判定: np.allclose()

np.allclose()は2つのndarrayのすべての要素が近いかを判定する。

a = np.array([0.3, 0.1 + 0.1 + 0.1])
print(a)
# [0.3 0.3]

b = np.array([0.3, 0.3])
print(b)
# [0.3 0.3]

c = np.array([0.2, 0.3])
print(c)
# [0.2 0.3]

print(np.allclose(a, b))
# True

print(np.allclose(a, c))
# False

引数はnp.isclose()と同じで、判定のための計算式も同じ。内部ではnp.isclose()の結果をnp.all()で判定しているだけ。

引数rtol, qtolを指定する例。

a_100 = np.array([99, 100, 101])
print(a_100)
# [ 99 100 101]

print(np.allclose(a_100, 100))
# False

print(np.allclose(a_100, 100, rtol=0, atol=1))
# True

上の例のようなスカラー値との比較や、ここでは例を省略するがブロードキャストを伴う比較も可能。

引数equal_nanを指定する例。

a_nan = np.array([np.nan, 1, 2])
print(a_nan)
# [nan  1.  2.]

b_nan = np.array([np.nan, 1, 2])
print(b_nan)
# [nan  1.  2.]

print(np.allclose(a_nan, b_nan))
# False

print(np.allclose(a_nan, b_nan, equal_nan=True))
# True
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事