Pandas 滚动应用使用多列

Sut*_*iro 12 python dataframe pandas rolling-computation

我正在尝试pandas.DataFrame.rolling.apply()在多列上使用滚动函数。Python 版本是 3.7,pandas 是 1.0.2。

import pandas as pd

#function to calculate
def masscenter(x):
    print(x); # for debug purposes
    return 0;

#simple DF creation routine
df = pd.DataFrame( [['02:59:47.000282', 87.60, 739],
                    ['03:00:01.042391', 87.51, 10],
                    ['03:00:01.630182', 87.51, 10],
                    ['03:00:01.635150', 88.00, 792],
                    ['03:00:01.914104', 88.00, 10]], 
                   columns=['stamp', 'price','nQty'])
df['stamp'] = pd.to_datetime(df2['stamp'], format='%H:%M:%S.%f')
df.set_index('stamp', inplace=True, drop=True)
Run Code Online (Sandbox Code Playgroud)

'stamp'是单调且唯一的,'price'是双精度'nQty'值且不包含 NaN,是整数且也不包含 NaN。

所以,我需要计算滚动的“质心”,即sum(price*nQty)/sum(nQty)

到目前为止我尝试过的:

df.apply(masscenter, axis = 1)
Run Code Online (Sandbox Code Playgroud)

masscenter 被单行调用 5 次,输出将类似于

price     87.6
nQty     739.0
Name: 1900-01-01 02:59:47.000282, dtype: float64
Run Code Online (Sandbox Code Playgroud)

这是 a 的理想输入masscenter,因为我可以轻松访问pricenQty使用x[0], x[1]. 但是,我坚持rolling.apply() 阅读文档 DataFrame.rolling()rolling.apply() 我认为使用'axis'inrolling()'raw'in applyone 可以实现类似的行为。天真的方法

rol = df.rolling(window=2)
rol.apply(masscenter)
Run Code Online (Sandbox Code Playgroud)

逐行打印(将行数增加到窗口大小)

stamp
1900-01-01 02:59:47.000282    87.60
1900-01-01 03:00:01.042391    87.51
dtype: float64
Run Code Online (Sandbox Code Playgroud)

然后

stamp
1900-01-01 02:59:47.000282    739.0
1900-01-01 03:00:01.042391     10.0
dtype: float64
Run Code Online (Sandbox Code Playgroud)

因此,列masscenter分别传递给(预期)。

可悲的是,在文档中几乎没有关于'axis'. 然而,下一个变体显然是

rol = df.rolling(window=2, axis = 1)
rol.apply(masscenter)
Run Code Online (Sandbox Code Playgroud)

从不跟注masscenter和加注ValueError in rol.apply(..)

> Length of passed values is 1, index implies 5
Run Code Online (Sandbox Code Playgroud)

我承认'axis'由于缺乏文档,我不确定参数及其工作方式。这是问题的第一部分: 这里发生了什么?如何正确使用“轴”?它是为什么而设计的?

当然,之前有答案,即:

How-to-apply-a-function-to-two-columns-of-pandas-
dataframe 它适用于整个 DataFrame,而不是 Rolling。

How-to-invoke-pandas-rolling-apply-with-parameters-from-multiple-column
答案建议编写我自己的滚动函数,但对我来说罪魁祸首与评论中所问的相同 :如果需要使用怎么办'1T'非统一时间戳的偏移窗口大小(例如)?
我不喜欢从头开始重新发明轮子的想法。此外,我想将熊猫用于所有内容,以防止从熊猫获得的套装与“自制卷”之间出现不一致。这个问题还有另一个答案,建议单独填充数据框并计算我需要的任何东西,但它不起作用:存储的数据的大小将是巨大的。这里提出了相同的想法:
Apply-rolling-function-on-pandas-dataframe-with-multiple-arguments

此处发布的另一个问答
Pandas-using-rolling-on-multiple-columns
它很好,最接近我的问题,但同样,不可能使用偏移窗口大小 ( window = '1T')。

在 pandas 1.0 出现之前询问了一些答案,鉴于文档可能会好得多,我希望现在可以同时滚动多个列。

问题的第二部分是: 是否有可能使用带有偏移窗口大小的 pandas 1.0.x 同时滚动多个列?

非常感谢。

小智 13

您可以使用numpy_ext模块中的rolling_apply函数:

import numpy as np
import pandas as pd
from numpy_ext import rolling_apply


def masscenter(price, nQty):
    return np.sum(price * nQty) / np.sum(nQty)


df = pd.DataFrame( [['02:59:47.000282', 87.60, 739],
                    ['03:00:01.042391', 87.51, 10],
                    ['03:00:01.630182', 87.51, 10],
                    ['03:00:01.635150', 88.00, 792],
                    ['03:00:01.914104', 88.00, 10]], 
                   columns=['stamp', 'price','nQty'])
df['stamp'] = pd.to_datetime(df['stamp'], format='%H:%M:%S.%f')
df.set_index('stamp', inplace=True, drop=True)

window = 2
df['y'] = rolling_apply(masscenter, window, df.price.values, df.nQty.values)
print(df)

                            price  nQty          y
stamp                                             
1900-01-01 02:59:47.000282  87.60   739        NaN
1900-01-01 03:00:01.042391  87.51    10  87.598798
1900-01-01 03:00:01.630182  87.51    10  87.510000
1900-01-01 03:00:01.635150  88.00   792  87.993890
1900-01-01 03:00:01.914104  88.00    10  88.000000
Run Code Online (Sandbox Code Playgroud)


adr*_*adr 12

这个怎么样:

def masscenter(ser):
    print(df.loc[ser.index])
    return 0

rol = df.price.rolling(window=2)
rol.apply(masscenter, raw=False)
Run Code Online (Sandbox Code Playgroud)

它使用滚动逻辑从任意列中获取子集。raw=False 选项为您提供这些子集的索引值(作为系列提供给您),然后您使用这些索引值从原始 DataFrame 中获取多列切片。

  • 然而,对于较大的数据集来说,这是一个非常昂贵的解决方案。 (6认同)
  • 对我有用,尽管 pandas 的开发人员应该以某种方式真正实现这一点...... (2认同)

Con*_*ngo 6

参考@saninstein 的出色回答。

从以下位置安装 numpy_ext: https: //pypi.org/project/numpy-ext/

import numpy as np
import pandas as pd
from numpy_ext import rolling_apply as rolling_apply_ext

def box_sum(a,b):
    return np.sum(a) + np.sum(b)

df = pd.DataFrame({"x": [1,2,3,4], "y": [1,2,3,4]})

window = 2
df["sum"] = rolling_apply_ext(box_sum, window , df.x.values, df.y.values)
Run Code Online (Sandbox Code Playgroud)

输出:

print(df.to_string(index=False))
 x  y  sum
 1  1  NaN
 2  2  6.0
 3  3 10.0
 4  4 14.0
Run Code Online (Sandbox Code Playgroud)

笔记

  • 滚动函数对时间序列友好。它默认总是向后查看,因此 6 是数组中当前值和过去值的总和。
  • 在上面的示例中,导入rolling_applyrolling_apply_ext这样它就不可能干扰对 Pandas 的任何现有调用rolling_apply(感谢 @LudoSchmidt 的评论)。

顺便说一句,我放弃了使用 Pandas 的尝试。它从根本上被破坏了:它处理单列聚合并且应用时几乎没有问题,但当试图让它与更多两列或更多列一起工作时,它是一个过于复杂的鲁布-戈德堡机器。

  • 不幸的是,“我放弃了使用 Pandas 的尝试。它从根本上坏了”。 (5认同)