TLDR;Pandas中的逻辑运算符是&,|和~,括号(…)很重要!
Python的and、or和not逻辑运算符被设计用来处理标量。因此Pandas必须做得更好,重写按位操作符以实现该功能的向量化(按元素)版本。
所以在python中(exp1和exp2是表达式,计算结果为布尔值)…
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提供了三个操作符:&用于逻辑与,|用于逻辑或,~用于逻辑非。
考虑以下设置:
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
逻辑和
对于上面的df,假设你想返回A < 5和B > 5的所有行。这是通过分别计算每个条件的掩码,并对它们进行and运算来完成的。
重载位&操作符
在继续之前,请注意这个文档的特别摘录,其中说明
另一个常用操作是使用布尔向量来过滤
数据。操作符为:|表示或,&表示与,~表示非。这些
必须使用括号分组,因为默认Python将
计算一个表达式,比如df。A > 2 & df。B < 3,即df。A > (2 &
df. b) < 3,而期望求值顺序为(df. b)。A > 2) & (df。B <
3)。
因此,考虑到这一点,按元素逻辑与可以用按位操作符&实现:
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绑定方法。如果单个掩码是使用函数而不是条件操作符构建的,您将不再需要通过paren分组来指定求值顺序:
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。查询(或eval):
df.query('A < 5 and B > 5')
A B C
1 3 7 9
3 4 7 6
我使用pd.eval()在pandas的动态表达式求值中广泛记录了查询和eval。
operator.and_
允许您以功能性的方式执行此操作。内部调用系列。__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_and是一个ufunc(通用函数),大多数ufuncs都有一个reduce方法。这意味着如果对AND有多个掩码,则使用logical_and更容易泛化。例如,用&来AND遮罩m1和m2和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 -我知道我在这一点上喋喋不休,但请耐心听我说。这是初学者经常犯的错误,必须解释清楚。
逻辑或
对于上面的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
如果你还没有,也请阅读上面的逻辑与部分,所有的警告都适用于这里。
或者,可以使用
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_
电话系列。解析:在引擎盖下。
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
对于两种情况,使用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
这是numpy变量。
np.logical_not(mask)
0 False
1 False
2 True
dtype: bool
注意,np。Logical_and可以代替np。Bitwise_and, logical_or with bitwise_or, logical_not with invert。