pandas:索引数据框时的多个条件 - 意外行为
- 2025-01-20 09:07:00
- admin 原创
- 113
问题描述:
我正在根据两列的值过滤数据框中的行。
由于某种原因,OR 运算符的行为与我期望 AND 运算符的行为一样,反之亦然。
我的测试代码:
df = pd.DataFrame({'a': range(5), 'b': range(5) })
# let's insert some -1 values
df['a'][1] = -1
df['b'][1] = -1
df['a'][3] = -1
df['b'][4] = -1
df1 = df[(df.a != -1) & (df.b != -1)]
df2 = df[(df.a != -1) | (df.b != -1)]
print(pd.concat([df, df1, df2], axis=1,
keys = [ 'original df', 'using AND (&)', 'using OR (|)',]))
结果如下:
original df using AND (&) using OR (|)
a b a b a b
0 0 0 0 0 0 0
1 -1 -1 NaN NaN NaN NaN
2 2 2 2 2 2 2
3 -1 3 NaN NaN -1 3
4 4 -1 NaN NaN 4 -1
[5 rows x 6 columns]
如您所见,AND
运算符会删除其中至少有一个值等于的每一行-1
。另一方面,OR
运算符要求两个值都等于才能-1
删除它们。我期望的结果恰恰相反。有人能解释这种行为吗?
我正在使用 pandas 0.13.1。
解决方案 1:
如您所见,AND 运算符会删除其中至少有一个值等于 -1 的每一行。另一方面,OR 运算符要求两个值都等于 -1 才能删除它们。
没错。请记住,您是在根据要保留的内容编写条件,而不是根据要删除的内容编写条件。对于df1
:
df1 = df[(df.a != -1) & (df.b != -1)]
df.a
您的意思是“保留不是 -1 且不是 -1的行df.b
”,这与删除至少有一个值为 -1 的每一行相同。
为了df2
:
df2 = df[(df.a != -1) | (df.b != -1)]
df.a
您的意思是“保留或不为 -1的行df.b
”,这与删除两个值都是 -1 的行相同。
PS:使用 和 进行链式访问df['a'][1] = -1
可能会给您带来麻烦。最好养成使用.loc
和 的习惯.iloc
。
解决方案 2:
回答晚了,但您也可以使用query(),例如:
df_filtered = df.query('a == 4 & b != 2')
解决方案 3:
这里有一点数理逻辑理论:
“NOT a AND NOT b”与“NOT (a OR b)”相同,因此:
“a NOT -1 AND b NOT -1” 等价于 “NOT (a is -1 OR b is -1)” ,后者是“(a is -1 OR b is -1)”的反义词(补语)。
因此,如果您想要完全相反的结果,df1 和 df2 应该如下所示:
df1 = df[(df.a != -1) & (df.b != -1)]
df2 = df[(df.a == -1) | (df.b == -1)]
解决方案 4:
使用括号
如果您因为条件在逻辑上正确但过滤操作却没有给出正确答案而来到此页面,那么首先要检查的是您是否使用括号分隔条件。
例如,如果你想过滤掉列'a'
和值'b'
不等于 -1 的行,那么编写以下代码
df[df['a'] != -1 & df['b'] != -1] # <--- forgot parenthesis
会产生完全出乎意料的输出,仅仅是因为&
/比/等|
比较运算符具有更高的优先级。您可以通过括号分别评估每个条件来获得正确的输出:!=
`==`
df[(df['a'] != -1) & (df['b'] != -1)] # <--- used parentheses
NB @Pedro 的答案使用query()
消除了这种需要,因为在评估的数值表达式中query
,比较运算符实际上是在and
/or
等之前进行评估的。
写出正确的逻辑表达式
根据德摩根定律,(i)并集的否定等于否定的交集,(ii)交集的否定等于否定的并集,即
A AND B <=> not A OR not B
A OR B <=> not A AND not B
如果目的是
删除至少有一个值等于 -1 的每一行
您可以使用运算符来识别要保留AND
的行,也可以使用运算符来识别要删除的行。OR
# select rows where both a and b values are not equal to -1
df2_0 = df[df['a'].ne(-1) & df['b'].ne(-1)]
# index of rows where at least one of a or b equals -1
idx = df.index[df.eval('a == -1 or b == -1')]
# drop `idx` rows
df2_1 = df.drop(idx)
df2_0.equals(df2_1) # True
另一方面,如果目标是
删除两个值都等于 -1 的每一行
你做的恰恰相反;要么使用运算符来识别要保留的OR
行,要么使用运算符来识别要删除的行。AND
解决方案 5:
您可以尝试以下操作:
df1 = df[(df['a'] != -1) & (df['b'] != -1)]
扫码咨询,免费领取项目管理大礼包!