简介:
给定一个数据框,我认为以下内容是正确的:
df[(condition_1) | (condition_2)] <=> df[(condition_2) | (condition_1)]
如
df[(df.col1==1) | (df.col1==2)] <=> df[(df.col1==2) | (df.col1==1)]
问题:
但事实证明它在以下情况下失败,其中涉及 NaN
这可能是它失败的原因:
df = pd.DataFrame([[np.nan, "abc", 2], ["abc", 2, 3], [np.nan, 5,6], [8,9,10]], columns=["A", "B", "C"])
df
A B C
0 NaN abc 2
1 abc 2 3
2 NaN 5 6
3 8 9 10
以下按预期工作:
df[(df.A.isnull()) | (df.A.str.startswith("a"))]
A B C
0 NaN abc 2
1 abc 2 3
2 NaN 5 6
但是如果我交换元素,我会得到不同的结果:
df[(df.A.str.startswith("a")) | (df.A.isnull())]
A B C
1 abc 2 3
我认为问题来自于这种情况:
df.A.str.startswith("a")
0 NaN
1 True
2 NaN
3 NaN
Name: A, dtype: object
我用 NaN
而不是 False
的地方。
问题:
- 这种行为是预期的吗?这是一个错误吗?因为如果没有预料到这种行为,它可能会导致潜在的数据丢失。
- 为什么它会这样(以不可交换的方式)?
更多详情:
更准确地说,让我们C1 = (df.A.str.startswith("a"))
和C2 = (df.A.isnull())
:
与:
C1 C2
NaN True
True False
NaN True
NaN False
我们有:
C1 | C2
0 False
1 True
2 False
3 False
Name: A, dtype: bool
这里不计算 C2,NaN 变为 False。
在这里:
C2 | C1
0 True
1 True
2 True
3 False
Name: A, dtype: bool
NaN 为 False(它返回所有带 &
的 False)但两个条件都被评估。
显然:C1 | C2 != C2 | C1
只要保留交换性,我就不会介意 NaN
产生奇怪的结果,但这里有一个条件未计算。
实际上输入中的 NaN 不是问题,因为您在 B
列上有同样的问题:
(df.B.str.startswith("a")) | (df.B==2) != (df.B==2) | (df.B.str.startswith("a"))
这是因为在其他对象上应用 str
方法会返回 NaN
*,如果先计算它会阻止计算第二个条件。所以主要问题仍然存在。
*(可以使用 str.startswith("a", na=False)
来选择,正如@ayhan 所注意到的)
最佳答案
经过一些研究,我相当确定这是 pandas
中的一个错误。我无法在他们的代码中找到具体原因,但我的结论是,要么完全禁止您进行比较,要么在评估 |
表达式时存在错误。您可以使用一个非常简单的示例重现该问题,即:
import numpy as np
import pandas as pd
a = pd.Series(np.nan)
b = pd.Series(True)
print( a | b ) # Gives False
print( b | a ) # Gives True
第二个结果显然是正确的。我只能猜测第一个失败的原因,因为我不了解 pandas
代码库。因此,如果我弄错了,请纠正我,或者如果您觉得这还不够回答,请告诉我。
通常,np.nan
在整个 python 中都被视为 True
,您可以轻松检查:
import numpy as np
if np.nan:
print("I am True")
这在 numpy
甚至 pandas
中也是有效的,正如您可以看到的那样:
import numpy as np
import pandas as pd
if np.all(np.array([np.nan])):
print("I am True in numpy")
if pd.Series(np.nan).astype("bool").bool():
print("and in pandas")
或者通过简单地执行 pd.Series([np.nan]).astype("bool")
。
到目前为止一切都是一致的。当您使用包含 NaN
的 Series
执行 |
时,现在会出现问题。还有很多其他人有类似的问题,例如这个 question或者那个blog post (不过,这是针对旧版本的)。没有人对这个问题给出满意的答案。链接问题的唯一答案实际上没有给出充分的理由,因为 |
的行为方式甚至与包含相同信息的 numpy
数组的行为方式不同。对于 numpy,np.array(np.nan) | np.array(True)
和 np.array(np.nan) | np.array(1.0)
实际上给出了一个 TypeError
,因为 np.bitwise_or
无法处理 float 。
由于行为不一致且缺少任何相关文档,我只能断定这是一个错误。作为解决方法,您可以回退到@ayhan 提出的解决方案并使用 na
参数(如果您需要的所有函数都存在该参数)。您还可以在要比较的 Series
/Dataframe
上使用 .astype("bool")
。但是请注意,这会将 NaN
转换为 True
,因为这是通常的 python
约定(参见 answer 例如)。如果你想避免这种情况,你可以使用 .fillna(False).astype("bool")
,我找到了 here .通常,应该提交一份关于 pandas 的错误报告,因为这种行为显然是不一致的!
关于python - Pandas 列选择 : non commutative bitwise OR when selecting on str and NaN,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39000907/