【问题标题】:Expressions with "== True" and "is True" give different results带有“== True”和“is True”的表达式给出不同的结果
【发布时间】:2016-08-17 23:30:25
【问题描述】:

我有以下MCVE

#!/usr/bin/env python3                                           

import pandas as pd

df = pd.DataFrame([True, False, True])

print("Whole DataFrame:")
print(df)

print("\nFiltered DataFrame:")
print(df[df[0] == True])

输出如下,符合我的预期:

Whole DataFrame:
     0
  0  True
  1  False
  2  True

Filtered DataFrame:
     0
  0  True
  2  True

好的,但是 PEP8 样式似乎是错误的,它说:E712 与 True 的比较应该是 if cond is Trueif cond。所以我把它改成了is True而不是== True,但现在它失败了,输出是:

Whole DataFrame:
    0
0   True
1  False
2   True

Filtered DataFrame:
0     True
1    False
2     True
Name: 0, dtype: bool

发生了什么事?

【问题讨论】:

  • “好的,但是 PEP8 样式似乎是错误的,它说:E712 与 True 的比较应该是 if cond is True or if cond.”。怎么回事? PEP8 实际上说“是:if greeting,否:if greeting == True,更糟糕的是if greeting is True”。
  • 在任何情况下df[df[0]] 不是更可取吗?
  • @IanS 可能是(不像 IMO 那样可读),但问题不在于那个 :^) “聪明人和聪明人有什么区别?-聪明人摆脱困境任何飞行颜色的麻烦,一个聪明的人都不会进入它。”

标签: python pandas pep8


【解决方案1】:

这里的问题是,在df[df[0] == True] 中,您没有将对象与True 进行比较

正如其他答案所说,==pandas 中被重载以生成Series,而不是像通常那样生成bool[] 也被重载,以解释 Series 并给出过滤结果。代码本质上等同于:

series = df[0].__eq__(True)
df.__getitem__(series)

所以,将== 留在这里,您并没有违反 PEP8。


基本上,pandas 赋予熟悉的语法不寻常的语义——这就是造成混乱的原因。

According to Stroustroup (sec.3.3.3),自发明以来,运算符重载就一直因此而引起麻烦(他不得不认真考虑是否将其包含到 C++ 中)。 Seeing even more abuse of it in C++,Gosling 在 Java 中跑到了另一个极端,完全禁止它,而事实证明这正是一个极端。

因此,现代语言和代码往往会出现运算符重载,但要密切注意不要过度使用它,并确保语义保持一致。

【讨论】:

  • 我认为他不建议使用它。使用== 非常好。
  • @ClaudiuCreanga PyCharm 很聪明,但没那么聪明 :-) 这就是为什么每个这样的检查器都可以选择告诉它:“闭嘴,我知道我在做什么。”
【解决方案2】:

在 python 中,is 测试一个对象是否与另一个相同。 ==pandas.Series 定义以按元素行事,is 不是。

因此,df[0] is True 比较 df[0]True 是否是同一个对象。结果是False,又等于0,所以你在做df[df[0] is True]时得到0

【讨论】:

  • 所以,这基本上是由于缺少过载而导致的错误?
  • 你不能超载is
【解决方案3】:

没有来自 linter 的抱怨但仍然合理的子设置语法的一种解决方法可能是:

s = pd.Series([True] * 10 + [False])

s.loc[s == True]  # bad comparison in Python's eyes
s.loc[s.isin([True])]  # valid comparison, not as ugly as s.__eq__(True)

两者也需要相同的时间。

此外,对于数据帧,可以使用query

df = pd.DataFrame([
        [True] * 10 + [False],
        list(range(11))],
    index=['T', 'N']).T
df.query("T == True")  # also okay

【讨论】:

    【解决方案4】:

    我认为在pandas 中比较只适用于==,结果是boolean Seriesis 的输出是 False。更多关于is的信息。

    print df[0] == True
    0     True
    1    False
    2     True
    Name: 0, dtype: bool
    
    print df[df[0]]
          0
    0  True
    2  True
    
    print df[df[0] == True]
          0
    0  True
    2  True
    
    print df[0] is True
    False
    
    print df[df[0] is True]
    0     True
    1    False
    2     True
    Name: 0, dtype: bool
    

    【讨论】:

    • 就这样完成了,因为== 可能会为自定义类重新定义而is 可能不会。这与 SQLAlchemy where 子句的情况相同 - OP 必须忽略警告或使用 noqa 注释禁用它。
    【解决方案5】:

    这是对 MaxNoe 答案的详细说明,因为包含 在 cmets 中。

    正如他所指出的,df[0] is True 的计算结果为 False,然后被强制 到0,它对应于一个列名。有趣的是 如果你运行

    >>>df = pd.DataFrame([True, False, True])
    >>>df[False]
    KeyError                                  Traceback (most recent call last)
    <ipython-input-21-62b48754461f> in <module>()
    ----> 1 df[False]
    
    >>>df[0]
    0     True
    1    False
    2     True
    Name: 0, dtype: bool
    >>>df[False]
    0     True
    1    False
    2     True
    Name: 0, dtype: bool
    

    起初这似乎有点令人困惑(至少对我而言),但与如何 pandas 使用缓存。如果你看看df[False] 是如何解决的,它 看起来像

      /home/matthew/anaconda/lib/python2.7/site-packages/pandas/core/frame.py(1975)__getitem__()
    -> return self._getitem_column(key)
      /home/matthew/anaconda/lib/python2.7/site-packages/pandas/core/frame.py(1999)_getitem_column()
    -> return self._get_item_cache(key)
    > /home/matthew/anaconda/lib/python2.7/site-packages/pandas/core/generic.py(1343)_get_item_cache()
    -> res = cache.get(item)
    

    由于cache 只是一个普通的python dict,在运行df[0] 之后cache 看起来像

    >>>cache
    {0: 0     True
    1    False
    2     True
    Name: 0, dtype: bool}
    

    所以当我们查找 False 时,python 会将其强制转换为 0。如果我们没有 已经使用df[0] 准备好缓存,然后resNone,它会触发一个 KeyErrorgeneric.py 的第 1345 行

    def _get_item_cache(self, item):
    1341            """Return the cached item, item represents a label indexer."""
    1342            cache = self._item_cache
    1343 ->         res = cache.get(item)
    1344            if res is None:
    1345                values = self._data.get(item)
    

    【讨论】:

    • 这与原来的问题已经没有什么关系了,但是很有趣……
    猜你喜欢
    • 1970-01-01
    • 2020-12-10
    • 1970-01-01
    • 2021-03-12
    • 2014-02-02
    • 2022-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多