note.nkmk.me

NumPy, pandasのValueError: ...one element is ambiguousの対処法

Date: 2019-09-15 / tags: Python, NumPy, pandas, エラー

NumPyやpandasで、オブジェクトをif文の条件式として使ったりandorで処理しようとしたりすると、以下のようなエラーが出ることがある。

ValueError: The truth value of an array with more than one element is ambiguous.

このエラーの原因と対処法について以下の内容を説明する。

  • 原因
  • 対処法1: all(), any(), size
  • 対処法2: and, or, notではなく&, |, ~
    • 要素ごとのビット演算は&, |, ~
    • 複数の条件式では括弧が必要
  • 要素数1個以下の場合
  • pandas.DataFrame, pandas.Seriesの場合

処理内容にもよるが、多くの場合、以下の2点に気をつければよい。

  • and, or, notではなく&, |, ~を使う
  • 複数の条件式を組み合わせるときは、それぞれの条件式を括弧()で囲む

以下の内容のNumPyのバージョンは1.16.4、pandasのバージョンは0.25.1。バージョンが異なると挙動が異なる可能性があるので注意。

スポンサーリンク

ValueError: The truth value of an array with more than one element is ambiguous.

以下のように、numpy.ndarrayをif文の条件式にそのまま使ったり、and, or, notで演算しようとするとエラーが発生する。

import numpy as np

a_bool = np.array([True, True, True])
b_bool = np.array([True, False, False])

# if a_bool:
#     pass
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

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

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

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

後述のように、pandas.DataFrame, pandas.Seriesでも同様。

原因

Pythonにおいて、if文の条件式やand, or, notの演算などでは、オブジェクトや式がbool値(True, False)として評価される。

例えばリストの場合、空だとFalse、そうでないとTrueとして処理される。

print(bool([0, 1, 2]))
# True

print(bool([]))
# False

print(not [])
# True

numpy.ndarrayに対してbool値を評価しようとするとエラーとなるようになっている。

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

ambiguous(曖昧)という文言の通り、何に対してTrue, Falseを判定したいのか(要素全体なのか、要素ごとなのか)が曖昧だというエラー。以降で説明するように、all()andなどを使って明確に指定しなければならない。

なお、エラーメッセージにあるように、要素数が1個以下の場合はエラーにならない。後述。

対処法1: all(), any(), size

オブジェクトそのものに対してTrue,Falseを判定したい場合、エラーメッセージにあるようにall(), any()を使う。

all()はすべての要素がTrueだとTrueany()は少なくとも1つの要素がTrueだとTrueを返す。

a_bool = np.array([True, True, True])
b_bool = np.array([True, False, False])
print(a_bool.all())
# True

print(a_bool.any())
# True

print(b_bool.all())
# False

print(b_bool.any())
# True

デフォルトは要素全体に対する処理だが、引数axisで行ごとや列ごとなどの処理が可能。以下の例はall()だがany()も同じ。

a_bool_2d = np.array([[True, True, True], [True, False, False]])
print(a_bool_2d)
# [[ True  True  True]
#  [ True False False]]

print(a_bool_2d.all())
# False

print(a_bool_2d.all(axis=0))
# [ True False False]

print(a_bool_2d.all(axis=1))
# [ True False]

print(type(a_bool_2d.all(axis=0)))
# <class 'numpy.ndarray'>

多次元配列で引数axisNone(デフォルト)以外に指定した場合はnumpy.ndarrayが返されるので、当然ながら、それをそのままif文の条件式として使ったりするとエラーになる。

そのほか、size属性で全要素数を取得可能。numpy.ndarrayが空かどうかなどを判定できる。

print(a_bool.size)
# 3

print(a_bool.size == 0)
# False

対処法2: and, or, notではなく&, |, ~

要素ごとのビット演算は&, |, ~

要素ごとのAND, OR, NORなどの演算を意図している場合、and, or, notではなく&, |, ~を使う。XOR^もある。

a_bool = np.array([True, True, True])
b_bool = np.array([True, False, False])
print(a_bool & b_bool)
# [ True False False]

print(a_bool | b_bool)
# [ True  True  True]

print(~b_bool)
# [False  True  True]

print(a_bool ^ b_bool)
# [False  True  True]

Pythonにおいて&, |, ~は整数値に対するビットごとの演算。

これまでの例はbool値を要素とするnumpy.ndarrayだったが、整数intを要素とするnumpy.ndarrayに対しては要素ごとのビット演算となる。

a_int = np.array([0, 1, 3])  # [0b00 0b01 0b11]
b_int = np.array([1, 0, 2])  # [0b01 0b00 0b10]

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 | 2)
# [2 3 3]

<<, >>によるビットシフトもできる。これも要素ごとやスカラー値との処理ができる。

print(a_int << b_int)
# [ 0  1 12]

print(a_int << 2)
# [ 0  4 12]

なお、上記関連記事にも書いたように、Pythonでは~x-(x+1)となる値を返す。単純なビット反転ではない。

True1とみなされ、以下のように~True-2となる。

print(~True)
# -2

bool値を要素とするnumpy.ndarrayに対してはそのようなことはなく、~に対してはTrueFalseが反転する。特に気をつける必要はない。

print(~a_bool)
# [False False False]

print(~b_bool)
# [False  True  True]

上の例のように整数のnumpy.ndarrayに対する~~x-(x+1)の処理となる。

ビット演算は整数値intのみが対象。浮動小数点数floatだとエラーになる。

a_float = np.array([0.1, 0.2, 0.3])

# print(a_float & a_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''

複数の条件式では括弧が必要

numpy.ndarrayに対して比較演算などを行うと、各要素がbool値のnumpy.ndarrayが返される。

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

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

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

複数の条件式を&|で組み合わせる場合、各条件式を括弧()で囲まないと意図した結果とならない。

これは、orandは比較演算子<などより優先順位が低いが、&|は比較演算子<などより優先順位が高いため。以下の例で、前者は後者のように処理される。

# print(a > 3 & a % 2 == 0)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

# print(a > (3 & (a % 2)) == 0)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

各条件式を括弧()で囲めばOK。

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

要素数1個以下の場合

エラーメッセージにmore than one elementとあるように、要素数が1個以下の場合はエラーにならない。

ValueError: The truth value of an array with more than one element is ambiguous.

要素数が1個の場合、その要素の値に対してbool値が評価される。例えば整数intを要素とする場合、0だとFalseでそれ以外はTrue

a_one = np.array([0])
b_one = np.array([1])
c_one = np.array([2])

print(bool(a_one))
# False

print(bool(b_one))
# True

print(bool(c_one))
# True

and, or, notはオブジェクトに対するブール演算で、&, |, ~などは要素ごとのビット演算となる、という基本の通りに処理される。

and, orではPythonの通常の文法に則って、いずれかのオブジェクトが返される。True, Falseが返されるわけではないので注意。詳細は以下の記事を参照。

print(b_one and c_one)
# [2]

print(c_one and b_one)
# [1]

&|では要素ごとのビット演算。

print(b_one & c_one)
# [0]

print(b_one | c_one)
# [3]

notではbool値の反転が返され、~では各要素に対して~の処理(整数intの場合、~x-(x+1))が行われる。

print(not c_one)
# False

print(~c_one)
# [-3]

要素数0個の場合、エラーにはならないが警告(DeprecationWarning)が発生する。

a_empty = np.array([])
print(a_empty)
# []

print(bool(a_empty))
# False
# 
# /usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:1: DeprecationWarning: The truth value of an empty array is ambiguous. Returning False, but in future this will result in an error. Use `array.size > 0` to check that an array is not empty.
#   """Entry point for launching an IPython kernel.

将来的にはエラーになると書いてある(上の例はバージョン1.16.4)ので、メッセージにあるようにsizeを使って判定したほうがよいだろう。

pandas.DataFrame, pandas.Seriesの場合

pandas.DataFrameも考え方はnumpy.ndarrayと同様。要素ごとに処理する場合は&|を使い、複数条件の場合はそれぞれを括弧()で囲む。

import pandas as pd

df = pd.DataFrame(pd.np.arange(12).reshape(3, 4), columns=['a', 'b', 'c', 'd'], index=['x', 'y', 'z'])
print(df)
#    a  b   c   d
# x  0  1   2   3
# y  4  5   6   7
# z  8  9  10  11

print((df > 3) & (df % 2 == 0))
#        a      b      c      d
# x  False  False  False  False
# y   True  False   True  False
# z   True  False   True  False

&|ではなくand, orを使ったり、括弧()を省略したりするとエラー。

# print((df > 3) and (df % 2 == 0))
# ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

# print(df > 3 & df % 2 == 0)
# ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

スカラー値とのビット演算も可能。

print(df & 7)
#    a  b  c  d
# x  0  1  2  3
# y  4  5  6  7
# z  0  1  2  3

print(df | 1)
#    a  b   c   d
# x  1  1   3   3
# y  5  5   7   7
# z  9  9  11  11

ビットシフト<<, >>は使えない。

# print(df << 1)
# TypeError: unsupported operand type(s) for <<: 'DataFrame' and 'int'

# print(df << df)
# TypeError: unsupported operand type(s) for <<: 'DataFrame' and 'DataFrame'

all(), any()メソッドも用意されているが、numpy.ndarrayと異なりデフォルトがaxis=0(列ごと)なので注意。全体を対象とする場合はaxis=Noneとする。

print(df > 3)
#        a      b      c      d
# x  False  False  False  False
# y   True   True   True   True
# z   True   True   True   True

print((df > 3).all())
# a    False
# b    False
# c    False
# d    False
# dtype: bool

print((df > 3).all(axis=1))
# x    False
# y     True
# z     True
# dtype: bool

print((df > 3).all(axis=None))
# False

空かどうかを判定するempty属性がある。size属性もある。

print(df.empty)
# False

df_empty = pd.DataFrame()
print(df_empty.empty)
# True

print(df.size)
# 12

print(df_empty.size)
# 0

pandas.Seriesでも同様。

条件に応じて行を選択する際などに利用する。numpy.ndarray, pandas.DataFrameと同じく、&, |, ~および括弧()を使う。

df = pd.read_csv('data/src/sample_pandas_normal.csv')
print(df)
#       name  age state  point
# 0    Alice   24    NY     64
# 1      Bob   42    CA     92
# 2  Charlie   18    CA     70
# 3     Dave   68    TX     70
# 4    Ellen   24    CA     88
# 5    Frank   30    NY     57
print(df['age'] < 35)
# 0     True
# 1    False
# 2     True
# 3    False
# 4     True
# 5     True
# Name: age, dtype: bool

print(~(df['state'] == 'NY'))
# 0    False
# 1     True
# 2     True
# 3     True
# 4     True
# 5    False
# Name: state, dtype: bool

print((df['age'] < 35) & ~(df['state'] == 'NY'))
# 0    False
# 1    False
# 2     True
# 3    False
# 4     True
# 5    False
# dtype: bool

df_and = df[(df['age'] < 35) & ~(df['state'] == 'NY')]
print(df_and)
#       name  age state  point
# 2  Charlie   18    CA     70
# 4    Ellen   24    CA     88
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事