web-dev-qa-db-ja.com

Pandasのブールインデックスの論理演算子

私はパンダでブールインデックスを使用しています。問題は、声明がなぜであるかです:

a[(a['some_column']==some_number) & (a['some_other_column']==some_other_number)]

正常に動作しますが

a[(a['some_column']==some_number) and (a['some_other_column']==some_other_number)]

エラーで終了しますか?

例:

a=pd.DataFrame({'x':[1,1],'y':[10,20]})

In: a[(a['x']==1)&(a['y']==10)]
Out:    x   y
     0  1  10

In: a[(a['x']==1) and (a['y']==10)]
Out: ValueError: The truth value of an array with more than one element is ambiguous.     Use a.any() or a.all()
96
user2988577

あなたが言う時

(a['x']==1) and (a['y']==10)

(a['x']==1)および(a['y']==10)をブール値に変換するようにPythonに暗黙的に要求しています。

NumPy配列(長さが1を超える)およびSeriesなどのPandasオブジェクトにはブール値がありません。つまり、

ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().

ブール値として使用される場合。それは、その TrueまたはFalseになるべきかどうか不明 であるためです。一部のユーザーは、Pythonリストのように、長さがゼロではない場合にTrueと想定する場合があります。他の人は、allの要素がTrueである場合にのみTrueにしたいかもしれません。他の人は、その要素のanyがTrueであればTrueにしたいかもしれません。

相反する期待が非常に多いため、NumPyとPandasの設計者は推測することを拒否し、代わりにValueErrorを発生させます。

代わりに、empty()all()またはany()メソッドを呼び出して、希望する動作を示すことにより、明示的にする必要があります。

ただし、この場合、ブール値の評価は不要であり、element-wiselogical-andが必要です。それが&二項演算子が実行することです:

(a['x']==1) & (a['y']==10)

ブール配列を返します。


ところで、 alexpmil notes のように、&には==よりも高い 演算子優先順位 があるため、括弧は必須です。括弧がなければ、a['x']==1 & a['y']==10a['x'] == (1 & a['y']) == 10として評価され、連鎖比較(a['x'] == (1 & a['y'])) and ((1 & a['y']) == 10)と同等になります。これは、Series and Seriesという形式の式です。 2つのシリーズでandを使用すると、上記と同じValueErrorが再びトリガーされます。そのため、括弧は必須です。

150
unutbu

TLDR; Pandasの論理演算子は&|~であり、括弧(...)は重要です!

Pythonのandor、およびnot論理演算子は、スカラーで動作するように設計されています。したがって、Pandasは、この機能のベクトル化(要素単位)バージョンを実現するために、ビット単位演算子をオーバーライドする必要があります。

python(exp1exp2の以下はブール値の結果に評価される式です)...

exp1 and exp2              # Logical AND
exp1 or exp2               # Logical OR
not exp1                   # Logical NOT

...に変換されます...

exp1 & exp2                # Element-wise logical AND
exp1 | exp2                # Element-wise logical OR
~exp1                      # Element-wise logical NOT

パンダ用。

論理演算の実行中にValueErrorを取得する場合、グループ化に括弧を使用する必要があります。

(exp1) op (exp2)

例えば、

(df['col1'] == x) & (df['col2'] == y) 

等々。


Boolean Indexing :一般的な操作は、論理条件を通じてブールマスクを計算してデータをフィルタリングすることです。Pandas提供three演算子:論理ANDの&、論理ORの|、および論理NOTの~

次の設定を検討してください。

np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (5, 3)), columns=list('ABC'))
df

   A  B  C
0  5  0  3
1  3  7  9
2  3  5  2
3  4  7  6
4  8  8  1

論理AND

上記のdfについて、A <5およびB> 5のすべての行を返したいとします。これは、各条件のマスクを個別に計算し、それらをAND演算することによって行われます。

ビットごとのオーバーロード&演算子
続行する前に、このドキュメントの特定の抜粋に注意してください。

もう1つの一般的な操作は、ブールベクトルを使用してデータをフィルタリングすることです。演算子は、orの場合は|andの場合は&notの場合は~です。 これらは、デフォルトでPythonがdf.A > 2 & df.B < 3などの式をdf.A > (2 & df.B) < 3として評価するため、括弧を使用してグループ化する必要があります。順序は(df.A > 2) & (df.B < 3)です。

したがって、これを念頭に置いて、要素ごとの論理ANDは、ビットごとの演算子&を使用して実装できます。

df['A'] < 5

0    False
1     True
2     True
3     True
4    False
Name: A, dtype: bool

df['B'] > 5

0    False
1     True
2    False
3     True
4     True
Name: B, dtype: bool
(df['A'] < 5) & (df['B'] > 5)

0    False
1     True
2    False
3     True
4    False
dtype: bool

そして、その後のフィルタリング手順は単純に、

df[(df['A'] < 5) & (df['B'] > 5)]

   A  B  C
1  3  7  9
3  4  7  6

括弧は、条件演算子<および>よりも優先順位が高いビット単位演算子のデフォルトの優先順位をオーバーライドするために使用されます。 pythonドキュメントの Operator Precedence のセクションを参照してください。

括弧を使用しない場合、式は誤って評価されます。たとえば、次のような何かを誤って試みた場合

df['A'] < 5 & df['B'] > 5

次のように解析されます

df['A'] < (5 & df['B']) > 5

どちらになる

df['A'] < something_you_dont_want > 5

これは( チェーン演算子比較 )のpythonドキュメントを参照)、

(df['A'] < something_you_dont_want) and (something_you_dont_want > 5)

どちらになる

# Both operands are Series...
something_else_you_dont_want1 and something_else_you_dont_want2

どっちが

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

だから、その間違いをしないでください!1

括弧のグループ化の回避
修正は実際には非常に簡単です。ほとんどの演算子には、DataFrameに対応するバインドメソッドがあります。条件演算子ではなく関数を使用して個々のマスクを構築する場合、評価順序を指定するために括弧でグループ化する必要はなくなります。

df['A'].lt(5)

0     True
1     True
2     True
3     True
4    False
Name: A, dtype: bool

df['B'].gt(5)

0    False
1     True
2    False
3     True
4     True
Name: B, dtype: bool
df['A'].lt(5) & df['B'].gt(5)

0    False
1     True
2    False
3     True
4    False
dtype: bool

柔軟な比較のセクションを参照してください。 。要約すると、

╒════╤════════════╤════════════╕
│    │ Operator   │ Function   │
╞════╪════════════╪════════════╡
│  0 │ >          │ gt         │
├────┼────────────┼────────────┤
│  1 │ >=         │ ge         │
├────┼────────────┼────────────┤
│  2 │ <          │ lt         │
├────┼────────────┼────────────┤
│  3 │ <=         │ le         │
├────┼────────────┼────────────┤
│  4 │ ==         │ eq         │
├────┼────────────┼────────────┤
│  5 │ !=         │ ne         │
╘════╧════════════╧════════════╛

括弧を避けるための別のオプションは、 DataFrame.query (またはeval)を使用することです:

df.query('A < 5 and B > 5')

   A  B  C
1  3  7  9
3  4  7  6

私はを広範に持っています_。にqueryevalを文書化しています ダイナミック式評価pandasでpd.eval() を使用しています。

operator.and_
この操作を機能的な方法で実行できます。内部的にビット単位演算子に対応するSeries.__and__を呼び出します。

import operator 

operator.and_(df['A'] < 5, df['B'] > 5)
# Same as,
# (df['A'] < 5).__and__(df['B'] > 5) 

0    False
1     True
2    False
3     True
4    False
dtype: bool

df[operator.and_(df['A'] < 5, df['B'] > 5)]

   A  B  C
1  3  7  9
3  4  7  6

通常これは必要ありませんが、知っておくと便利です。

一般化: np.logical_and (およびlogical_and.reduce
別の方法として、np.logical_andを使用する方法があります。これも、括弧のグループ化を必要としません。

np.logical_and(df['A'] < 5, df['B'] > 5)

0    False
1     True
2    False
3     True
4    False
Name: A, dtype: bool

df[np.logical_and(df['A'] < 5, df['B'] > 5)]

   A  B  C
1  3  7  9
3  4  7  6

np.logical_andfunc(Universal Functions) であり、ほとんどのufuncには reduce メソッドがあります。これは、ANDに複数のマスクがある場合、logical_andで一般化するのが簡単であることを意味します。たとえば、m1m2m3&と組み合わせて、あなたがしなければならない

m1 & m2 & m3

ただし、より簡単なオプションは

np.logical_and.reduce([m1, m2, m3])

これは、より複雑なロジック(たとえば、リスト内包表記でマスクを動的に生成し、それらすべてを追加する)を使用して構築できるため、強力です。

import operator

cols = ['A', 'B']
ops = [np.less, np.greater]
values = [5, 5]

m = np.logical_and.reduce([op(df[c], v) for op, c, v in Zip(ops, cols, values)])
m 
# array([False,  True, False,  True, False])

df[m]
   A  B  C
1  3  7  9
3  4  7  6

1-私はこの点について心配していることは知っていますが、どうか我慢してください。これは非常に非常に一般的な初心者の間違いであり、非常に徹底的に説明する必要があります。


論理OR

上記のdfについて、A == 3またはB == 7であるすべての行を返したいとします。

ビットごとのオーバーロード|

df['A'] == 3

0    False
1     True
2     True
3    False
4    False
Name: A, dtype: bool

df['B'] == 7

0    False
1     True
2    False
3     True
4    False
Name: B, dtype: bool
(df['A'] == 3) | (df['B'] == 7)

0    False
1     True
2     True
3     True
4    False
dtype: bool

df[(df['A'] == 3) | (df['B'] == 7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

まだお持ちでない場合は、上記の論理ANDのセクションもお読みください。すべての警告がここに適用されます。

または、この操作は次のように指定できます。

df[df['A'].eq(3) | df['B'].eq(7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

operator.or_
内部でSeries.__or__を呼び出します。

operator.or_(df['A'] == 3, df['B'] == 7)
# Same as,
# (df['A'] == 3).__or__(df['B'] == 7)

0    False
1     True
2     True
3     True
4    False
dtype: bool

df[operator.or_(df['A'] == 3, df['B'] == 7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

np.logical_or
2つの条件では、logical_orを使用します。

np.logical_or(df['A'] == 3, df['B'] == 7)

0    False
1     True
2     True
3     True
4    False
Name: A, dtype: bool

df[np.logical_or(df['A'] == 3, df['B'] == 7)]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

複数のマスクの場合、logical_or.reduceを使用します。

np.logical_or.reduce([df['A'] == 3, df['B'] == 7])
# array([False,  True,  True,  True, False])

df[np.logical_or.reduce([df['A'] == 3, df['B'] == 7])]

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6

論理的NOT

次のようなマスクを指定します

mask = pd.Series([True, True, False])

すべてのブール値を反転する必要がある場合(最終結果が[False, False, True]になるように)、以下のいずれかの方法を使用できます。

ビット単位~

~mask

0    False
1    False
2     True
dtype: bool

繰り返しますが、式は括弧で囲む必要があります。

~(df['A'] == 3)

0     True
1    False
2    False
3     True
4     True
Name: A, dtype: bool

これは内部的に呼び出します

mask.__invert__()

0    False
1    False
2     True
dtype: bool

ただし、直接使用しないでください。

operator.inv
シリーズで__invert__を内部的に呼び出します。

operator.inv(mask)

0    False
1    False
2     True
dtype: bool

np.logical_not
これはnumpyの亜種です。

np.logical_not(mask)

0    False
1    False
2     True
dtype: bool

np.logical_andは、np.bitwise_andlogical_orbitwise_orlogical_notinvertに置き換えることができます。

14
cs95

パンダのブールインデックスの論理演算子

pandas.Seriesまたはpandas.DataFramesでPython論理演算子andorまたはnot)のいずれも使用できないことを認識することが重要です(同様に使用できません)複数の要素を持つnumpy.arrays)。これらを使用できない理由は、これらのデータ構造が配列のブール値が曖昧であると判断したため、例外で例外をスローするオペランドでboolを暗黙的に呼び出すためです。

>>> import numpy as np
>>> import pandas as pd
>>> arr = np.array([1,2,3])
>>> s = pd.Series([1,2,3])
>>> df = pd.DataFrame([1,2,3])
>>> bool(arr)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> bool(s)
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> bool(df)
ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

私はこれをより広範囲にカバーしました 「シリーズの真理値はあいまいです。a.empty、a.bool()、a.item()、a.any()またはa.allを使用してください。 () "Q + A

NumPys論理関数

ただし、 NumPy は、これらの演算子と同等の要素単位の操作を、numpy.arraypandas.Seriespandas.DataFrame、またはその他の(準拠する)numpy.arrayサブクラスで使用できる関数として提供します。

したがって、本質的には、df1df2がpandas DataFramesであると仮定して使用する必要があります。

np.logical_and(df1, df2)
np.logical_or(df1, df2)
np.logical_not(df1)
np.logical_xor(df1, df2)

ブール値のビット単位関数とビット単位演算子

ただし、ブールNumPy配列、pandasシリーズ、またはpandas DataFramesがある場合は、 要素ごとのビット単位関数 (ブール値の場合は-または少なくとも論理関数と見分けがつかない):

  • ビット単位および: np.bitwise_and または&演算子
  • ビット単位または: np.bitwise_or または|演算子
  • ビット単位ではない: np.invert (またはエイリアスnp.bitwise_not)または~演算子
  • ビット単位のxor: np.bitwise_xor または^演算子

通常、演算子が使用されます。ただし、比較演算子と組み合わせた場合、ビット単位演算子には 比較演算子よりも高い優先順位 があるため、比較を括弧で囲む必要があります。

(df1 < 10) | (df2 > 10)  # instead of the wrong df1 < 10 | df2 > 10

Python論理演算子は比較演算子より優先順位が低いため、通常はa < 10 and b > 10(ここでabは単純な整数です)を記述し、括弧を必要としないため、これはいらいらするかもしれません。

論理演算とビット演算の違い(非ブール演算)

ビット演算と論理演算は、ブール値のNumPy配列(およびブール値のシリーズとデータフレーム)でのみ同等であることを強調することは本当に重要です。これらにブール値が含まれていない場合、操作によって異なる結果が得られます。 NumPy配列を使用した例を含めますが、結果はpandasデータ構造についても同様です。

>>> import numpy as np
>>> a1 = np.array([0, 0, 1, 1])
>>> a2 = np.array([0, 1, 0, 1])

>>> np.logical_and(a1, a2)
array([False, False, False,  True])
>>> np.bitwise_and(a1, a2)
array([0, 0, 0, 1], dtype=int32)

また、NumPy(および同様にパンダ)はブール( ブールまたは「マスク」インデックス配列 )と整数( インデックス配列 )インデックスに対して異なることを行うため、インデックス作成の結果も異なる:

>>> a3 = np.array([1, 2, 3, 4])

>>> a3[np.logical_and(a1, a2)]
array([4])
>>> a3[np.bitwise_and(a1, a2)]
array([1, 1, 1, 2])

要約表

Logical operator | NumPy logical function | NumPy bitwise function | Bitwise operator
-------------------------------------------------------------------------------------
       and       |  np.logical_and        | np.bitwise_and         |        &
-------------------------------------------------------------------------------------
       or        |  np.logical_or         | np.bitwise_or          |        |
-------------------------------------------------------------------------------------
                 |  np.logical_xor        | np.bitwise_xor         |        ^
-------------------------------------------------------------------------------------
       not       |  np.logical_not        | np.invert              |        ~

NumPy配列、pandas Series、およびpandas DataFramesでは論理演算子は機能しません。その他は、これらのデータ構造(およびプレーンPythonオブジェクト)で機能し、要素ごとに機能します。ただし、プレーンPython boolsのビット単位の反転には注意してください。ブールはこのコンテキストでは整数として解釈されます(たとえば、~False-1を返し、~True-2を返します)。

2
MSeifert