重采样 .agg/.apply 行为

3
这个问题涉及重新采样的.agg/.apply与groupby的.agg/.apply不同。


以下是一个示例数据框:
df = pd.DataFrame({'A':range(0,100),'B':range(0,200,2)},index=pd.date_range('1/1/2022',periods=100,freq='D'))

输出:

             A    B
2022-01-01   0    0
2022-01-02   1    2
2022-01-03   2    4
2022-01-04   3    6
2022-01-05   4    8
...         ..  ...
2022-04-06  95  190
2022-04-07  96  192
2022-04-08  97  194
2022-04-09  98  196
2022-04-10  99  198

我的问题是,在下面的apply函数中,x代表什么。有时它会像一个系列一样运作,而其他时候它会像一个df一样运作。通过调用type(x),它返回df。然而,下面的代码返回一个错误,说"No axis named 1 for object type Series"
df.resample('M').apply(lambda x: x.sum(axis=1))

但是这并不是这样的。对于一个序列来说没有堆栈,因此这会意味着 x 代表了一个 df

df.resample('M').apply(lambda x: x.stack())

此外,当您运行df.resample('M').apply(lambda x: print(type(x)))时,输出结果是系列数据,但df.resample('M').apply(lambda x: type(x))的输出结果是数据框类型。
因此,我的主要问题是,在resample中传递给应用程序的是series还是dataframe

我一直觉得这方面的文档不够清晰/完整。在我的情况下(pandas 1.5.2),对于每个列,无论是agg还是apply,都会为每个“组”传递一个Series。 - Chrysophylaxs
1
这是一个很好的观点,我没有运行1.5.2版本,但刚刚升级了,发现了同样的问题。我在我的问题中添加了更多内容,展示了调用print(type(x))type(x)之间的关系。 - rhug123
1
非常有趣,我无法解释那种行为,毫无头绪 :) - Chrysophylaxs
1个回答

3

这是一个非常好的问题,我认为我没有正确的答案。

  1. resample 一个时间序列会返回一个 DatetimeIndexResampler 实例。
  2. applyaggregate 函数的别名。

现在请查看源代码

    @doc(
        _shared_docs["aggregate"],
        see_also=_agg_see_also_doc,
        examples=_agg_examples_doc,
        klass="DataFrame",
        axis="",
    )
    def aggregate(self, func=None, *args, **kwargs):


        result = ResamplerWindowApply(self, func, args=args, kwargs=kwargs).agg()
        if result is None:
            how = func
            result = self._groupby_and_aggregate(how, *args, **kwargs)


        result = self._apply_loffset(result)
        return result


    agg = aggregate
    apply = aggregate

我理解的是:如果ResamplerWindowApply出现问题,aggregate函数会有一个回退机制,重新评估带有_groupby_and_aggregate的函数。
最后一个的文档字符串是:
"""
Re-evaluate the obj with a groupby aggregation.
"""

让我们使用命名函数进行调试:

import inspect

def f(x):
    print(inspect.stack()[2].function)
    print(f'begin: {type(x)}')
    x.stack()
    print(f'end: {type(x)}')
    return x.sum(axis=1)

df.resample('M').apply(f)

输出:

_aggregate_series_pure_python
begin: <class 'pandas.core.series.Series'>  # something goes wrong
_python_apply_general  # the caller has changed
begin: <class 'pandas.core.frame.DataFrame'>  # now x is a DataFrame
end: <class 'pandas.core.frame.DataFrame'>
_python_apply_general
begin: <class 'pandas.core.frame.DataFrame'>
end: <class 'pandas.core.frame.DataFrame'>
_python_apply_general
begin: <class 'pandas.core.frame.DataFrame'>
end: <class 'pandas.core.frame.DataFrame'>
_python_apply_general
begin: <class 'pandas.core.frame.DataFrame'>
end: <class 'pandas.core.frame.DataFrame'>

在使用 Series 失败后,aggregate 会使用 DataFrame 调用该函数。不幸的是,这种行为没有被记录在文档中。


这非常有趣... 你觉得我应该在 GitHub 上提出这个问题吗? - rhug123
我不这么认为。这个行为很可能是开发者真正想要做的 :-) - Corralien
1
@rhug123。我使用inspect模块更新了我的答案,以获取调用者函数的信息。 - Corralien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接