gpt4 book ai didi

python-3.x - bool pandas.Series 之间的操作对称性不等,索引不等

转载 作者:行者123 更新时间:2023-12-04 21:05:17 25 4
gpt4 key购买 nike

pandas的隐式索引匹配用于不同 DataFrame 之间的操作/Series很棒,而且在大多数情况下,它只是有效。

但是,我偶然发现了一个无法按预期工作的示例:

import pandas as pd # 0.21.0
import numpy as np # 1.13.3
x = pd.Series([True, False, True, True], index = range(4))
y = pd.Series([False, True, True, False], index = [2,4,3,5])

# logical AND: this works, symmetric as it should be
pd.concat([x, y, x & y, y & x], keys = ['x', 'y', 'x&y', 'y&x'], axis = 1)
# x y x&y y&x
# 0 True NaN False False
# 1 False NaN False False
# 2 True False False False
# 3 True True True True
# 4 NaN True False False
# 5 NaN False False False

# but logical OR is not symmetric anymore (same for XOR: x^y vs. y^x)
pd.concat([x, y, x | y, y | x], keys = ['x', 'y', 'x|y', 'y|x'], axis = 1)
# x y x|y y|x
# 0 True NaN True False <-- INCONSISTENT!
# 1 False NaN False False
# 2 True False True True
# 3 True True True True
# 4 NaN True False True <-- INCONSISTENT!
# 5 NaN False False False

研究了一下,我发现了两点似乎相关:
  • bool(np.nan)等于 True ,参见https://stackoverflow.com/a/15686477/2965879
  • |已解析为 np.bitwise_or , 而不是 np.logical_or ,参见https://stackoverflow.com/a/37132854/2965879

  • 但最终,关键似乎是 Pandas 确实从 nan 类型转换。至 False在某一点。从上面看,这似乎是在调用 np.bitwise_or 后发生的。 ,而我认为这应该发生在之前?

    特别是,使用 np.logical_or没有帮助,因为它错过了 pandas 的索引对齐。有,而且,我不想要 np.nan or False等于 True . (换句话说,答案 https://stackoverflow.com/a/37132854/2965879 没有帮助。)

    我认为如果提供这个美妙的语法糖,它应该尽可能保持一致*,所以|应该是对称的 .当总是对称的东西突然不再存在时,真的很难调试(就像我发生的那样)。

    所以最后,问题是:是否有任何可行的解决方法(例如重载某些东西)来挽救 x|y == y|x ,理想情况下,(粗略地说) nan | True == True == True | nannan | False == False == False | nan ?

    *即使德摩根定律不管怎样 - ~(x&y)不能完全匹配 ~y|~x因为 NaN 仅在索引对齐时出现(因此不受先前否定的影响)。

    最佳答案

    在pandas中做了一些探索之后,我发现有一个函数叫pandas.core.ops._bool_method_SERIES这是为 Series 对象包装 bool 运算符的几个工厂函数之一。

    >>> f = pandas.Series.__or__
    >>> f #the actual function you call when you do x|y
    <function _bool_method_SERIES.<locals>.wrapper at 0x107436bf8>
    >>> f.__closure__[0].cell_contents
    #it holds a reference to the other function defined in this factory na_op
    <function _bool_method_SERIES.<locals>.na_op at 0x107436b70>
    >>> f.__closure__[0].cell_contents.__closure__[0].cell_contents
    #and na_op has a reference to the built-in function or_
    <built-in function or_>

    这意味着我们理论上可以定义我们自己的方法来执行逻辑或正确的逻辑,首先让我们看看它实际会做什么(请记住,如果操作无法执行,运算符函数预计会引发 TypeError)
    def test_logical_or(a,b):
    print("**** calling logical_or with ****")
    print(type(a), a)
    print(type(b), b)
    print("******")
    raise TypeError("my_logical_or isn't implemented")

    #make the wrapper method
    wrapper = pd.core.ops._bool_method_SERIES(test_logical_or, None,None)
    pd.Series.logical_or = wrapper #insert method


    x = pd.Series([True, False, True, True], index = range(4))
    y = pd.Series([False, True, True, False], index = [2,4,3,5])

    z = x.logical_or(y) #lets try it out!

    print(x,y,z, sep="\n")

    当它运行时(至少对于 Pandas vs 0.19.1)
    **** calling logical_or with ****
    <class 'numpy.ndarray'> [True False True True nan nan]
    <class 'numpy.ndarray'> [False False False True True False]
    ******
    **** calling logical_or with ****
    <class 'bool'> True
    <class 'bool'> False
    ******
    Traceback (most recent call last):
    ...

    所以看起来它试图用两个 numpy 数组调用我们的方法,无论出于何种原因,第二个数组都有 nan值已替换为 False但不是第一个,这可能是我们的对称性破裂的原因。然后当失败时它再次尝试我会假设元素明智。

    因此,作为让这个工作的最低限度,你可以明确地检查两个参数是否都是 numpy 数组,尝试转换所有 nan第一个到 False的条目然后 return np.logical_or(a,b) .我将假设如果有任何其他情况,我们只会引发错误。
    def my_logical_or(a,b):
    if isinstance(a, np.ndarray) and isinstance(b, np.ndarray):
    a[np.isnan(a.astype(float))] = False
    b[np.isnan(b.astype(float))] = False
    return np.logical_or(a,b)
    else:
    raise TypeError("custom logical or is only implemented for numpy arrays")

    wrapper = pd.core.ops._bool_method_SERIES(my_logical_or, None,None)
    pd.Series.logical_or = wrapper


    x = pd.Series([True, False, True, True], index = range(4))
    y = pd.Series([False, True, True, False], index = [2,4,3,5])

    z = pd.concat([x, y, x.logical_or(y), y.logical_or(x)], keys = ['x', 'y', 'x|y', 'y|x'], axis = 1)
    print(z)
    # x y x|y y|x
    # 0 True NaN True True
    # 1 False NaN False False <-- same!
    # 2 True False True True
    # 3 True True True True
    # 4 NaN True True True <-- same!
    # 5 NaN False False False

    所以这可能是您的解决方法,我不建议修改 Series.__or__因为我们不知道还有谁会使用它,并且不想破坏任何期望默认行为的代码。

    或者,我们可以在 pandas.core.ops line 943 处修改源代码填写 NaN self 的值为 False(或 0)在同一个 way it does with other ,所以我们会改变这一行:
        return filler(self._constructor(na_op(self.values, other.values),
    index=self.index, name=name))

    使用 filler(self).values而不是 self.values :
        return filler(self._constructor(na_op(filler(self).values, other.values),
    index=self.index, name=name))

    这也解决了 or 的问题和 xor不是对称的,但是,我不建议这样做,因为它可能会破坏其他代码,我个人对 Pandas 没有足够的经验来确定这在不同情况下会发生什么变化。

    关于python-3.x - bool pandas.Series 之间的操作对称性不等,索引不等,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47659400/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com