NumPy, pandasのValueError: The truth value ... is ambiguousの対処法

Modified: | Tags: Python, NumPy, pandas, エラー処理

NumPyやpandasで、numpy.ndarraypandas.DataFrameをif文の条件式で使ったりandorで演算したりすると、以下のようなエラーが出ることがある。

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

ValueError: The truth value of a Series is ambiguous.  
Use a.empty, a.bool(), a.item(), a.any() or a.all().

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

処理内容にもよるが、多くの場合、以下の2点に気を付ければよい。numpy.ndarrayでもpandas.DataFrame, pandas.Seriesでも考え方は同じ。

  1. and, or, notではなく&, |, ~を使う
    • and, or, notはオブジェクト自体の真偽を判定するから
    • ndarrayDataFrameの要素ごとの演算は&, |, ~
  2. 複数の条件式を組み合わせるときは、それぞれの条件式を括弧()で囲む
    • &, |は比較演算子(<など)より優先順位が高いから

本記事のサンプルコードのNumPyのバージョンは1.25.1、pandasのバージョンは2.0.3。バージョンが異なると挙動が異なる可能性があるので注意。

import numpy as np

print(np.__version__)
# 1.25.1
import pandas as pd

print(pd.__version__)
# 2.0.3

ValueError: The truth value ... is ambiguousの例

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

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でも同様にエラーが発生する。

ValueError: The truth value ... is ambiguousの原因

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

例えばリストの場合、空(要素数が0)だとFalse、それ以外はTrueとして処理される。

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

print(bool([]))
# False

print(not [0, 1, 2])
# False

print(not [])
# True

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

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

# 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()などを使って明確にする必要がある。

なお、エラーメッセージにあるように、要素数が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

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

print(a_bool.size)
# 3

print(a_bool.size == 0)
# False

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

要素ごとのブール演算・ビット演算は&, |, ~

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

データ型dtypeboolnumpy.ndarrayに対しては、&, |, ~, ^演算子は要素ごとの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において、&, |, ~は整数値に対するビット単位の演算。

データ型dtypeが整数intnumpy.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]

and, or, not&, |, ~は混同しがちだが、基本的には、Pythonにおいてand, or, notはオブジェクトそのものの真偽を判定して処理する。

一方、&|は、上述の整数値に対するビット単位の処理やnumpy.ndarrayの要素ごとの処理のほか、set型の集合演算にも使われる。

if A and B:のような形で使うことが多いのが英単語のandorで、それ以外の処理で使われるのが記号の&|とでも覚えておけばよいだろう。

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

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]]

上述のように、これらのnumpy.ndarrayの要素ごとのANDやORを算出するにはandorではなく&|を使うが、複数の条件式を&|で組み合わせる場合、各条件式を括弧()で囲まないと意図した結果とならない。

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

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

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

なお、numpy.ndarrayではないオブジェクトに対する比較演算では、多くの場合、True, Falseそのものを返す。

x = 10

print(x > 3)
# True

print(x % 2 == 1)
# False

True, Falseのブール演算(論理演算)にはandorを使う。andorは比較演算子(<など)より優先順位が低いため、このときは括弧がなくてもエラーにならない。もちろん括弧があっても良い。

print(x > 3 or x % 2 == 1)
# True

print((x > 3) or (x % 2 == 1))
# True

要素数1個以下の場合

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

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

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

a_single = np.array([0])
b_single = np.array([1])
c_single = np.array([2])

print(bool(a_single))
# False

print(bool(b_single))
# True

print(bool(c_single))
# True

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

print(b_single and c_single)
# [2]

print(c_single and b_single)
# [1]

print(b_single or c_single)
# [1]

print(c_single or b_single)
# [2]

&|では要素ごとのビット演算が行われる。

print(b_single & c_single)
# [0]

print(b_single | c_single)
# [3]

notではbool値の否定が返され、~では各要素に対して~の処理(符号付き整数の場合、~x-(x + 1))が行われる。

print(not a_single)
# True

print(not b_single)
# False

print(not c_single)
# False

print(~a_single)
# [-1]

print(~b_single)
# [-2]

print(~c_single)
# [-3]

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

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

print(bool(a_empty))
# False
# 
# /var/folders/rf/b7l8_vgj5mdgvghn_326rn_c0000gn/T/ipykernel_40648/256863747.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.
#   print(bool(a_empty))

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

pandas.DataFrameの場合

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

df = pd.DataFrame(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().

pandas.DataFrameall(), 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=None))
# False

空かどうかを判定するempty属性や、全要素数を返すsize属性もある。

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

print(df.empty)
# False

print(df.size)
# 12
df_empty = pd.DataFrame()
print(df_empty)
# Empty DataFrame
# Columns: []
# Index: []

print(df_empty.empty)
# True

print(df_empty.size)
# 0

pandas.Seriesの場合

pandas.Seriesnumpy.ndarraypandas.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

関連カテゴリー

関連記事