在某些情况下,Pandas 0.25.3 的剪辑会在重新采样创建的块上崩溃

Tan*_*och 4 python debugging time-series pandas pandas-groupby

关于熊猫 0.25.3 中基于时间的 groupby 的结果,我有一个非常狭窄的问题。

我正在编写一个库来执行各种基于时间的聚合,并在非常特定的情况下遇到了可能是错误的问题。它不会发生在 pandas>=1 中,但如果可能的话,我希望仍然能够支持 pandas 0.25.3(它仍然与我们的用户相关)。

以下情况我缩小了范围,导致“pandas/core/internals/blocks.py in where”中异常捕获的无限循环,最终导致python崩溃,退出代码为 134 :

import pandas as pd

data = pd.DataFrame(
    data=[15.0, 0.0, 0.0, -10.0,  0.0],
    index=pd.to_datetime(
        [
            "2018-01-01 00:00:00.000000",
            "2018-01-01 00:25:00.000000",
            "2018-01-01 00:30:00.000000",
            "2018-01-01 00:31:00.000000",
            "2018-01-01 00:47:00.000000",
        ]
    )
)


def clip_low_at_0(x):
    return x.clip(lower=0).sum()


data.resample("30min").agg(clip_low_at_0)
Run Code Online (Sandbox Code Playgroud)

我的 Python 版本是 3.7.6,pandas 0.25.3。

作为旁注,它适用于迭代器:

for entry, group in data.resample("30min"):
    clip_low_at_0(x=group)
Run Code Online (Sandbox Code Playgroud)

并且data.groupby(pd.Grouper(freq="30min"))有完全相同的问题。

导致问题的组是这个(第二个):

                        0
2018-01-01 00:30:00   0.0
2018-01-01 00:31:00 -10.0
2018-01-01 00:47:00   0.0
Run Code Online (Sandbox Code Playgroud)

据我检查,这似乎发生了:

  • 只有特定数量的值,即 2 个值没问题,3 和 4 有问题,7 没有问题。
  • 只有某些值的组合。这个会触发它,有些不会,有些会。
  • 对于任何索引值,它似乎都没有关系。
  • 组的位置很重要

实际上,通过进一步挖掘,似乎导致错误的组生成的系列.clip()已损坏。尝试.copy()失败,其他一些方法(如序列化方法)以错误结尾。也许我没有正确使用 group 和 agg,但是还有另一种使用熊猫来计算这个的好方法吗?

困扰我的是它似乎在很多情况下都有效,而这个确切的情况在 pandas>=1.0.0 中有效。

如果它恰好是一个真正的错误,我当然会在问题中向 Pandas 的团队报告。(编辑:熊猫似乎不鼓励旧版本的错误报告)

编辑:为了澄清,我想:

1) 知道它是否真的是误用或 groupby/agg 方面的实际错误

2) 如果有比自己剪辑更好的解决方法 (x.loc[x < 0] = 0)。因为这个组将来可能会导致其他用于聚合的函数出现其他问题。如果可能的话,我想保持重采样方法不必自己处理索引,实际上我使用相同的结构进行了一些其他聚合。

ama*_*ain 5

我可以重现您的问题,但似乎仅限于使用clip(),不是吗?其他功能如round()etc 似乎运行良好。所以这对我来说绝对是 Pandas 的 bug。

作为一般解决方法,您可以在聚合函数中直接使用numpy函数而不是 Pandas 函数:

def clip_low_at_0(x):
    return numpy.clip(x, a_min=0, a_max=None).sum()
Run Code Online (Sandbox Code Playgroud)

尽管如此,为了完整起见:针对Pandas 的错误行为的特定解决方法(也是一个奇怪的解决方法clip()是明确设置上限。虽然我还没有理解为什么会这样,但实际上确实如此(至少在这里):

def clip_low_at_0(x):
    # using numpy's 'double' dtype max value here, but this could
    # be replaced with sys.maxsize or any other sensible constant
    maxval = numpy.finfo('d').max
    return x.clip(lower=0, upper=maxval).sum()
Run Code Online (Sandbox Code Playgroud)

最后(你已经知道了):不需要在聚合函数中裁剪值,你可以预先将它应用到整个 DataFrame:

data.clip(lower=0).resample("30min").sum()
Run Code Online (Sandbox Code Playgroud)