note.nkmk.me

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

Posted: 2019-09-15 / Modified: 2020-06-14 / 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ではなく&, |, ~を使う
    • 理由: and, or, notはオブジェクト自体をTrue, Falseで判定するから
      • ndarrayDataFrameの要素ごとの演算は&, |, ~
  • 複数の条件式を組み合わせるときは、それぞれの条件式を括弧()で囲む
    • 理由: &, |は比較演算子(<など)より優先順位が高いから

numpy.ndarrayでもpandas.DataFrame, pandas.Seriesでも考え方は同じ。

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

スポンサーリンク

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

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

import numpy as np

print(np.__version__)
# 1.17.3

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)として評価される。

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

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

print(bool([]))
# False

print(not [0, 1, 2])
# 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()などを使って明確にしなければならない。

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

データ型dtypeがブールboolnumpy.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]

numpy.ndarrayに対する&, |, ~, ^演算子についての詳細は以下の記事を参照。ビットシフト演算子(<<, >>)などもある。

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

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

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

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

numpy.ndarrayに対して比較演算などを行うと、各要素がbool値(データ型dtypebool)の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()

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

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値が評価される。例えば整数intを要素とする場合、0だとFalseでそれ以外は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

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

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
# 
# /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.17.3)ので、メッセージにあるように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
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事