如何快速在Pandas DataFrame行窗口迭代并处理二维窗口数据?
问题描述
我有一个如下所示的时间序列DataFrame:
datetime gesture left-5-x ...30 columns omitted 2022-09-27 19:54:54.396680 gesture0255 533 2022-09-27 19:54:54.403298 gesture0255 534 2022-09-27 19:54:54.408938 gesture0255 535 2022-09-27 19:54:54.413995 gesture0255 523 2022-09-27 19:54:54.418666 gesture0255 522 ... 95 000 rows omitted
我想要创建新列df['cross_correlation'],该列的值由连续多行数据计算得出,即第i行的cross_correlation依赖于i-10到i+10行的数据。
我可以使用df.iterrow()实现,但这不符合Pandas的惯用写法。是否存在类似如下的函数:
df.window(-10, +10).apply(lambda rows: calculate_cross_correlation(rows)
或类似功能的方法?
补充说明
感谢@chris指出可以使用df.rolling(),但我遇到了如下问题:
以下是我要在移动窗口上应用的简化版函数,实际函数要求输入为形状(window_size, num_columns)的完整二维窗口数据,我添加了断言来验证这一点:
def sum_over_2d(x): assert len(x.shape) == 2, f'shape of input is {x.shape} and not of length 2' return x.sum()
当我使用.rolling结合.apply时:
df.rolling(window=10, center=True).apply( sum_over_2d )
触发了断言错误:
AssertionError: shape of input is (10,) and not of length 2
打印断言前的输入x,得到的是数据集某一列的一维数据:
0 533.0 1 534.0 2 535.0 3 523.0 4 522.0 5 526.0 6 510.0 7 509.0 8 502.0 9 496.0 dtype: float64
我期望输入x是DataFrame或二维numpy数组。
解决方案
Pandas的rolling()默认会按列单独处理数据,所以传给apply()的是一维数组,这就是触发断言错误的原因。要拿到完整的二维窗口数据,试试下面两种方法:
方法1:给apply加raw=False参数
设置raw=False后,apply会把窗口内的所有数据以DataFrame的形式传入函数,正好满足你需要的(window_size, num_columns)形状。另外注意,你要的是i-10到i+10的窗口,窗口大小得设为21(10+1+10),不是之前的10:
def sum_over_2d(x): # x现在是窗口内的完整DataFrame assert len(x.shape) == 2, f'shape of input is {x.shape} and not of length 2' return x.sum().sum() # 先按列求和,再把列的结果加总 df['cross_correlation'] = df.rolling(window=21, center=True).apply(sum_over_2d, raw=False)
方法2:用numpy滑动窗口工具直接生成二维窗口数据
如果你的计算逻辑比较复杂,用numpy的滑动窗口工具效率更高,适合处理大数量级的数据(比如你的95000行):
import numpy as np # 先提取所有数值列,排除datetime、gesture这类非数值列 numeric_data = df.select_dtypes(include=[np.number]).values window_size = 21 # 生成滑动窗口数组,形状是(有效行数, window_size, 列数) windows = np.lib.stride_tricks.sliding_window_view(numeric_data, window_shape=(window_size, numeric_data.shape[1])) windows = windows.squeeze(axis=1) # 去掉多余的维度 # 批量跑你的计算函数 results = np.array([calculate_cross_correlation(window) for window in windows]) # 把结果填回原DataFrame,首尾各10行(window_size//2)没法计算,用NaN填充 df['cross_correlation'] = np.concatenate([ [np.nan]*10, results, [np.nan]*10 ])
内容的提问来源于stack exchange,提问作者beyarkay
相关产品推荐
相关产品推荐

