Pandas中布尔索引的逻辑运算符

use*_*577 119 python filtering boolean dataframe pandas

我在Pandas中使用布尔索引.问题是为什么声明:

a[(a['some_column']==some_number) & (a['some_other_column']==some_other_number)]
Run Code Online (Sandbox Code Playgroud)

工作正常,而

a[(a['some_column']==some_number) and (a['some_other_column']==some_other_number)]
Run Code Online (Sandbox Code Playgroud)

存在错误?

例:

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()
Run Code Online (Sandbox Code Playgroud)

unu*_*tbu 176

当你说

(a['x']==1) and (a['y']==10)
Run Code Online (Sandbox Code Playgroud)

你是隐问Python的转换(a['x']==1)(a['y']==10)布尔值.

NumPy数组(长度大于1)和Pandas对象(如Series)没有布尔值 - 换句话说,它们会引发

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

当用作布尔值时.那是因为它不清楚它应该是真还是假.如果某些用户长度非零,则可能会认为它们是True,如Python列表.其他人可能希望它只有在其所有元素都是真的时才是真的.如果其中任何元素为True,其他人可能希望它为True.

因为有太多相互矛盾的期望,NumPy和Pandas的设计师拒绝猜测,而是提出了一个ValueError.

相反,您必须明确,通过调用empty(),all()any()方法来指示您想要的行为.

但是,在这种情况下,看起来你不想要布尔评估,你想要元素逻辑和.这就是&二元运算符执行的操作:

(a['x']==1) & (a['y']==10)
Run Code Online (Sandbox Code Playgroud)

返回一个布尔数组.


顺便说一句,作为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 Series.使用and两个系列将再次触发与ValueError上述相同.这就是为什么括号是强制性的.

  • @Indominus:[Python语言本身需要](https://docs.python.org/2/reference/expressions.html#boolean-operations)表达式'x和y`触发`bool(x)的评估`和`bool(y)`.Python"首先计算`x`;如果`x`为false,则返回其值;否则,计算`y`并返回结果值." 所以语法`x和y`不能用于元素化的逻辑 - 因为只能返回`x`或`y`.相反,`x&y`触发`x .__和__(y)`,并且`__and__`方法可以定义为返回我们喜欢的任何东西. (14认同)
  • numpy数组确实具有此属性*if*它们是长度为1.只有大熊猫(顽固地)拒绝猜测:p (3认同)
  • 在此讨论:https://groups.google.com/forum/#!topic/pydata/XzSHSLlTSZ8 (2认同)
  • “&”是否不具有与“ and”相同的模糊曲线?当涉及到“&”时,突然之间,所有用户都同意它应该是元素化的,而当他们看到“和”时,他们的期望却有所不同? (2认同)
  • 重要的是要注意:`==` 子句周围的括号是*必需的*。`a['x']==1 & a['y']==10` 返回与问题相同的错误。 (2认同)

cs9*_*s95 35

TLDR;

Python的&,|~逻辑运算符的设计与标量的工作.因此,Pandas必须做得更好并覆盖按位运算符以实现此功能的矢量化(逐元素)版本.

所以下面的python ......

exp1 and exp2              # Logical AND
exp1 or exp2               # Logical OR
not exp1                   # Logical NOT
Run Code Online (Sandbox Code Playgroud)

......将翻译成,

exp1 & exp2                # Element-wise logical AND
exp1 | exp2                # Element-wise logical OR
~exp1                      # Element-wise logical NOT
Run Code Online (Sandbox Code Playgroud)

对于熊猫,等等.

如果在执行逻辑操作的过程中得到a (...),则需要使用括号进行分组:

(exp1) op (exp2)
Run Code Online (Sandbox Code Playgroud)

例如,

(df['col1'] == x) & (df['col2'] == y) 
Run Code Online (Sandbox Code Playgroud)

等等.


布尔索引

常见的操作是通过逻辑条件计算布尔掩码以过滤数据.Pandas提供三个运算符:and逻辑AND,or逻辑OR和not逻辑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
Run Code Online (Sandbox Code Playgroud)

逻辑和

对于exp1上面,假设您要返回A <5和B> 5的所有行.这可以通过分别计算每个条件的掩码并对它们进行AND运算来完成.

重载按位exp2运算符
在继续之前,请注意文档的这个特定摘录,其中说明了这一点

另一种常见操作是使用布尔向量来过滤数据.运营商是:ValueErrorfor &,|for ~dffor &.必须使用括号对这些进行分组,因为默认情况下,Python将评估表达式,例如|as or,而期望的评估顺序是&.

因此,考虑到这一点,可以使用按位运算符实现元素明智的逻辑AND 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
Run Code Online (Sandbox Code Playgroud)

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

0    False
1     True
2    False
3     True
4    False
dtype: bool
Run Code Online (Sandbox Code Playgroud)

后续的过滤步骤很简单,

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

   A  B  C
1  3  7  9
3  4  7  6
Run Code Online (Sandbox Code Playgroud)

括号用于覆盖按位运算符的默认优先顺序,其优先级高于条件运算符~not.请参阅python文档中的Operator Precedence部分.

如果不使用括号,则表达式计算不正确.例如,如果你不小心尝试了诸如此类的东西

df['A'] < 5 & df['B'] > 5
Run Code Online (Sandbox Code Playgroud)

它被解析为

df['A'] < (5 & df['B']) > 5
Run Code Online (Sandbox Code Playgroud)

哪个变成,

df['A'] < something_you_dont_want > 5
Run Code Online (Sandbox Code Playgroud)

哪个成为(参见链接运算符比较的python文档),

(df['A'] < something_you_dont_want) and (something_you_dont_want > 5)
Run Code Online (Sandbox Code Playgroud)

哪个变成,

# Both operands are Series...
something_else_you_dont_want1 and something_else_you_dont_want2
Run Code Online (Sandbox Code Playgroud)

哪个投掷

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

所以,不要犯那个错误!1

避免括号分组
修复实际上非常简单.大多数运算符都有相应的DataFrames绑定方法.如果使用函数而不是条件运算符构建单个掩码,则不再需要按parens分组来指定评估顺序:

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
Run Code Online (Sandbox Code Playgroud)

df['A'].lt(5) & df['B'].gt(5)

0    False
1     True
2    False
3     True
4    False
dtype: bool
Run Code Online (Sandbox Code Playgroud)

请参阅灵活比较部分..总而言之,我们有

????????????????????????????????
?    ? Operator   ? Function   ?
????????????????????????????????
?  0 ? >          ? gt         ?
????????????????????????????????
?  1 ? >=         ? ge         ?
????????????????????????????????
?  2 ? <          ? lt         ?
????????????????????????????????
?  3 ? <=         ? le         ?
????????????????????????????????
?  4 ? ==         ? eq         ?
????????????????????????????????
?  5 ? !=         ? ne         ?
????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

避免使用括号的另一个选择是使用df.A > 2 & df.B < 3(或df.A > (2 & df.B) < 3):

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

   A  B  C
1  3  7  9
3  4  7  6
Run Code Online (Sandbox Code Playgroud)

我已经广泛地记录(df.A > 2) & (df.B < 3)&使用pd.eval动态表达评价大熊猫() .

<
允许您以功能方式执行此操作.内部调用>对应于按位运算符.

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
Run Code Online (Sandbox Code Playgroud)

你通常不需要这个,但知道它是有用的.

推广:( DataFrame.queryeval)
另一种选择是使用 query,也不需要括号分组:

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
Run Code Online (Sandbox Code Playgroud)

eval是一个ufunc(通用函数),大多数ufunc都有一个operator.and_方法.这意味着Series.__and__如果你有多个AND的掩码,则更容易概括.例如,要和口罩np.logical_and,并logical_and.reducenp.logical_andnp.logical_and,你就必须做

m1 & m2 & m3
Run Code Online (Sandbox Code Playgroud)

但是,更简单的选择是

np.logical_and.reduce([m1, m2, m3])
Run Code Online (Sandbox Code Playgroud)

这很强大,因为它允许您使用更复杂的逻辑构建此类(例如,在列表理解中动态生成掩码并添加所有这些掩码):

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
Run Code Online (Sandbox Code Playgroud)

1 - 我知道我在这一点上很难受,但请耐心等待.这是一个非常,非常常见的初学者的错误,必须非常有详尽的解释.


逻辑或

对于reduce上述情况,假设您要返回A == 3或B == 7的所有行.

按位重载 logical_and

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
Run Code Online (Sandbox Code Playgroud)

(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
Run Code Online (Sandbox Code Playgroud)

如果您还没有,请阅读上面的逻辑和部分,所有注意事项都适用于此处.

或者,可以使用指定此操作

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

   A  B  C
1  3  7  9
2  3  5  2
3  4  7  6
Run Code Online (Sandbox Code Playgroud)

m1
m2引擎盖下的 电话.

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
Run Code Online (Sandbox Code Playgroud)

m3
对于两个条件,请使用&:

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
Run Code Online (Sandbox Code Playgroud)

对于多个面具,请使用df:

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
Run Code Online (Sandbox Code Playgroud)

逻辑不

给出一个面具,如

mask = pd.Series([True, True, False])
Run Code Online (Sandbox Code Playgroud)

如果您需要反转每个布尔值(以便最终结果是|),那么您可以使用以下任何方法.

按位 operator.or_

~mask

0    False
1    False
2     True
dtype: bool
Run Code Online (Sandbox Code Playgroud)

同样,表达式需要括起来.

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

0     True
1    False
2    False
3     True
4     True
Name: A, dtype: bool
Run Code Online (Sandbox Code Playgroud)

内部调用

mask.__invert__()

0    False
1    False
2     True
dtype: bool
Run Code Online (Sandbox Code Playgroud)

但是不要直接使用它.

Series.__or__
内部呼唤np.logical_or系列赛.

operator.inv(mask)

0    False
1    False
2     True
dtype: bool
Run Code Online (Sandbox Code Playgroud)

logical_or
这是numpy变种.

np.logical_not(mask)

0    False
1    False
2     True
dtype: bool
Run Code Online (Sandbox Code Playgroud)

注意,logical_or.reduce可以替换[False, False, True],~使用operator.inv__invert__使用np.logical_not.


MSe*_*ert 5

Pandas 中布尔索引的逻辑运算符

重要的是要意识到您不能在or s上使用任何 Python逻辑运算符and, oror not)(同样,您不能在具有多个元素的 s 上使用它们)。你不能使用它们的原因是因为它们隐式调用了抛出异常的操作数,因为这些数据结构决定了数组的布尔值是不明确的:pandas.Seriespandas.DataFramenumpy.arraybool

>>> 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().
Run Code Online (Sandbox Code Playgroud)

在回答“系列的真值不明确。使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()”的回答中确实更广泛地涵盖了这一点+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)
Run Code Online (Sandbox Code Playgroud)

布尔值的按位函数和按位运算符

但是,如果您有布尔值 NumPy 数组、pandas Series 或 Pandas DataFrames,您也​​可以使用逐元素按位函数(对于布尔值,它们 - 或者至少应该 - 与逻辑函数无法区分):

通常使用运算符。但是,当与比较运算符结合使用时,必须记住将比较括在括号中,因为按位运算符的优先级高于比较运算符

(df1 < 10) | (df2 > 10)  # instead of the wrong df1 < 10 | df2 > 10
Run Code Online (Sandbox Code Playgroud)

这可能很烦人,因为 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)
Run Code Online (Sandbox Code Playgroud)

由于 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])
Run Code Online (Sandbox Code Playgroud)

汇总表

>>> 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().
Run Code Online (Sandbox Code Playgroud)

其中的逻辑运算符不适合与NumPy阵列工作,熊猫系列,和熊猫DataFrames。其他人处理这些数据结构(和普通的 Python 对象)并按元素工作。但是要小心纯 Python bools上的按位反转,因为 bool 将在此上下文中被解释为整数(例如~False返回-1~True返回-2)。