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


152

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

6
これは、配列/シリーズのすべての要素を別の配列と比較するときに、numpy配列とpandasシリーズが論理ではなくビット単位の演算子を使用するためです。したがって、この状況で論理演算子を使用しても意味がありません。:関連参照stackoverflow.com/questions/8632033/...
EdChum

9
Pythonでand != &andPython の演算子はオーバーライドできませんが、&演算子(__and__)はオーバーライドできます。したがって、&numpyとpandasでの使用の選択。
Steven Rumbalski、2014年

回答:


208

あなたが言う時

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

あなたは暗黙的にPythonに変換(a['x']==1)(a['y']==10)てブール値に変換するように求めています。

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であると想定する場合があります。他の人は、その要素がすべて Trueである場合にのみ、Trueになることを望みます。他の人は、その要素のいずれかがTrueの場合 Trueにしたいと思うかもしれません。

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

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

ただし、この場合は、ブール値の評価ではなく、要素ごとの論理ANDが必要なようです。これは、&二項演算子が実行するものです。

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

ブール配列を返します。


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


3
numpy配列は、長さが1の場合にこのプロパティを持ちます。パンダの開発者だけが(頑固に)推測を拒否します:p
アンディヘイデン

4
「&」には「and」と同じ曖昧な曲線がありませんか?「&」となると、突然、すべてのユーザーが要素単位である必要があることにすべてのユーザーが同意しますが、「and」を見ると、期待が異なります。
Indominus

16
@Indominus:Python言語自体では、式x and ybool(x)and の評価をトリガーする必要がありbool(y)ます。Pythonは「最初に評価しxます。xがfalseの場合、その値が返されます。それ以外の場合yは評価され、結果の値が返されます。」だから、構文は、x and y要素-wised論理だけ以来のために使用することはできないxか、y返すことができます。対照的に、x & yトリガーx.__and__(y)__and__メソッドは、好きなものを返すように定義できます。
unutbu

2
注意が重要:==句を囲む括弧は必須です。a['x']==1 & a['y']==10質問と同じエラーを返します。
Alex P. Miller

1
「|」とは何ですか?
Euler_Salter 2018年

62

TLDR; パンダにおける論理演算子はあり&|かつ~、括弧は(...)重要です!

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

だから、pythonで、次の(exp1およびexp2boolean型の結果を評価する式です)...

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) 

等々。


ブールインデックス:一般的な操作は、論理条件を介してブールマスクを計算し、データをフィルター処理することです。Pandasには、論理AND、論理OR、論理NOTの 3つの演算子があります。&|~

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

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つの一般的な操作は、ブールベクトルを使用してデータをフィルター処理することです。演算子は、for 、|for or&for です。デフォルトではPythonはas などの式を評価しますが、必要な評価順序はであるため、これらは括弧を使用してグループ化する必要がありますand~notdf.A > 2 & df.B < 3df.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のドキュメントの演算子の優先順位のセクションを参照してください。

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

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

私は広範囲に文書化queryevalpd.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_andufunc(Universal Functions)であり、ほとんどのufuncにはreduceメソッドがあります。これはlogical_and、ANDするマスクが複数ある場合に一般化するのが簡単であることを意味します。たとえば、ANDマスクm1m2and m3を使用するには&、次のようにする必要があります。

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

論理否定

以下のようなマスクが与えられたとします

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
これは派手なバリアントです。

np.logical_not(mask)

0    False
1    False
2     True
dtype: bool

注意は、np.logical_andの代わりに使用することができるnp.bitwise_andlogical_orbitwise_or、とlogical_notinvert


@ OR、あなたが使用して提唱要素ごとのブールのためTLDRでcs95、|に相当し、numpy.bitwise_or代わりに、numpy.logical_or。理由を伺ってもよろしいでしょうか?numpy.logical_orこのタスク用に特別に設計されていませんか?要素のペアごとにビット単位で実行する負担を追加するのはなぜですか?
flow2k

@ flow2k関連テキストを引用していただけますか?あなたの言っていることが見つかりません。FWIW私は、logical_ *が演算子と機能的に同等であることを維持しています。
cs95

@ cs95私は回答の最初の行を参照しています:「TLDR;パンダの論理演算子は&、|、〜」です。
flow2k

@ flow2kこれは文字通りドキュメントにあります:「もう1つの一般的な操作はブールベクタを使用してデータをフィルタリングすることです。演算子は次のとおりです:| for、&forおよびand〜for not。
cs95

@ cs95、そう、私はこのセクションを読んだだけで|、要素ごとのブール演算に使用します。しかし、私にとって、そのドキュメントはより「チュートリアル」であり、対照的に、これらのAPI参照は真実の情報源に近いと感じています:numpy.bitwise_orおよびnumpy.logical_or-したがって、私はここで説明します。
flow2k

4

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

それはあなたは、Pythonのいずれかを使用することはできませんことを認識することが重要だ論理演算子andorまたはnot上)、pandas.Seriesまたはpandas.DataFrameSを(同様に、あなたが上でそれらを使用することはできませんnumpy.array複数の要素で秒)。これらを使用できない理由は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サブクラス:

したがって、基本的には、次のものを使用する必要があります(パンダデータフレームであるdf1と想定df2)。

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

ブールのビットごとの関数とビットごとの演算子

ただし、ブールNumPy配列、pandas Series、またはpandas DataFramesがある場合は、要素単位のビット単位関数を使用することもできます(ブール値の場合、論理関数と区別できません-または少なくともそうでなければなりません)。

  • ビット単位のand:np.bitwise_andまたは&演算子
  • ビットごとのor: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のアレイのために動作しません、シリーズのパンダ、パンダとのデータフレーム。他のものは、これらのデータ構造(およびプレーンPythonオブジェクト)を処理し、要素ごとに処理します。ただし、プレーンなPython boolのビット単位の反転には注意してください。ブール値はこのコンテキストでは整数として解釈されるためです(たとえば、~Falsereturn -1~Truereturns -2)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.