Pandas 滑动窗口 .rolling().apply() 详细教程与高级特性介绍

Pandas 滑动窗口 .rolling().apply() 详细教程与高级特性介绍

1. 基本概念

.rolling().apply() 是pandas中用于在滚动窗口上应用自定义函数的方法。它由两部分组成:

  • .rolling(): 创建一个滚动窗口对象
  • .apply(): 在滚动窗口上应用一个函数

2. 基本语法

df.rolling(window).apply(func)
  • window: 滚动窗口的大小
  • func: 要应用的函数

3. 参数详解

.rolling() 的主要参数:

  • window: 窗口大小(必需)
  • min_periods: 窗口中所需的最小观察次数(默认为window)
  • center: 是否将标签放在窗口的中心(默认为False)

.apply() 的主要参数:

  • func: 要应用的函数(必需)
  • raw: 如果为True,传递原始ndarray而不是Series(默认为False)
  • engine: 计算引擎(‘cython’或’numba’,默认为None)

4. 使用示例

让我们通过一些示例来深入了解 .rolling().apply() 的使用:

示例1:计算移动平均

import pandas as pd
import numpy as np

# 创建一个示例DataFrame
df = pd.DataFrame({'A': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]})

# 计算3天移动平均
df['3D_MA'] = df['A'].rolling(window=3).apply(lambda x: np.mean(x))

print(df)

输出:

    A  3D_MA
0   1    NaN
1   2    NaN
2   3    2.0
3   4    3.0
4   5    4.0
5   6    5.0
6   7    6.0
7   8    7.0
8   9    8.0
9  10    9.0

示例2:自定义函数 - 计算窗口内的最大值与最小值之差

def max_min_diff(x):
    return x.max() - x.min()

df['3D_Range'] = df['A'].rolling(window=3).apply(max_min_diff)

print(df)

输出:

    A  3D_MA  3D_Range
0   1    NaN       NaN
1   2    NaN       NaN
2   3    2.0       2.0
3   4    3.0       2.0
4   5    4.0       2.0
5   6    5.0       2.0
6   7    6.0       2.0
7   8    7.0       2.0
8   9    8.0       2.0
9  10    9.0       2.0

5. 高级用法

使用不同的窗口类型

除了固定大小的窗口,pandas还支持其他类型的窗口:

# 指数加权移动平均
df['EWMA'] = df['A'].ewm(span=3).mean()

# 扩展窗口(累积)
df['Cumulative_Mean'] = df['A'].expanding().mean()

处理时间序列数据

对于时间序列数据,可以使用基于时间的窗口:

import pandas as pd

# 创建一个时间序列DataFrame
dates = pd.date_range('20230101', periods=10)
df = pd.DataFrame({'Date': dates, 'Value': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]})
df.set_index('Date', inplace=True)

# 使用7天滚动窗口
df['7D_MA'] = df['Value'].rolling('7D').mean()

print(df)

6. 性能考虑

  • 对于简单操作(如均值、和等),使用内置方法(如 .mean().sum() 等)比 .apply() 更快。
  • 对于复杂操作,考虑使用 numba 引擎来提高性能。

7. 注意事项

  • 滚动窗口操作可能会产生 NaN 值,特别是在数据开始的地方。
  • 确保自定义函数能够处理 NaN 值,以避免意外错误。
  • 大型数据集上的滚动窗口操作可能会很慢,需要考虑性能优化。

8. 窗口大小不足时的行为

当窗口大小不足时,.apply(func)中的func函数是否被调用取决于min_periods参数的设置:

  1. 如果没有指定min_periods,默认值等于窗口大小。在这种情况下,当窗口中的有效数据少于窗口大小时,func不会被调用,结果将是NaN

  2. 如果指定了min_periods,只要窗口中的有效数据数量达到或超过min_periodsfunc就会被调用。

让我们通过一个例子来说明这一点:

import pandas as pd
import numpy as np

def custom_func(x):
    print(f"Function called with data: {x}")
    return x.mean()

# 创建一个示例DataFrame
df = pd.DataFrame({'A': [1, 2, 3, 4, 5]})

print("Case 1: Without min_periods")
df['Rolling_1'] = df['A'].rolling(window=3).apply(custom_func)

print("\nCase 2: With min_periods=1")
df['Rolling_2'] = df['A'].rolling(window=3, min_periods=1).apply(custom_func)

print("\nFinal DataFrame:")
print(df)

输出:

Case 1: Without min_periods
Function called with data: [1. 2. 3.]
Function called with data: [2. 3. 4.]
Function called with data: [3. 4. 5.]

Case 2: With min_periods=1
Function called with data: [1.]
Function called with data: [1. 2.]
Function called with data: [1. 2. 3.]
Function called with data: [2. 3. 4.]
Function called with data: [3. 4. 5.]

Final DataFrame:
   A  Rolling_1  Rolling_2
0  1        NaN   1.000000
1  2        NaN   1.500000
2  3   2.000000   2.000000
3  4   3.000000   3.000000
4  5   4.000000   4.000000

从这个例子中,我们可以观察到:

  1. 在没有指定min_periods的情况下(Case 1),custom_func只在窗口大小达到3时才被调用。

  2. 当指定min_periods=1时(Case 2),即使窗口中只有一个或两个值,custom_func也会被调用。

  3. 结果显示,Case 1的前两行是NaN,而Case 2从第一行开始就有值。

这个行为对于理解和使用.rolling().apply()非常重要,尤其是在处理数据集开始部分或存在缺失值的情况下。通过适当设置min_periods,您可以控制函数在数据不足时的行为。

9. step 参数对 .apply() 的影响

rolling()函数中的step参数允许你控制滚动窗口的步进大小。当使用step参数时,apply()的行为会发生以下变化:

  1. 窗口移动频率step参数决定了窗口在每次操作后向前移动的行数。默认情况下,step=1,意味着窗口每次向前移动一行。

  2. 输出频率apply()函数将只在每个步进点上被调用,而不是对每一行都调用。这会减少输出的行数。

  3. 性能影响:使用更大的step值可以显著提高处理大数据集时的性能,因为它减少了apply()函数被调用的次数。

让我们通过一个例子来说明step参数的作用:

import pandas as pd
import numpy as np

def custom_func(x):
    print(f"Function called with data: {x}")
    return x.mean()

# 创建一个示例DataFrame
df = pd.DataFrame({'A': range(1, 11)})

print("Case 1: Without step parameter")
df['Rolling_1'] = df['A'].rolling(window=3).apply(custom_func)

print("\nCase 2: With step=2")
df['Rolling_2'] = df['A'].rolling(window=3, step=2).apply(custom_func)

print("\nFinal DataFrame:")
print(df)

输出:

Case 1: Without step parameter
Function called with data: [1. 2. 3.]
Function called with data: [2. 3. 4.]
Function called with data: [3. 4. 5.]
Function called with data: [4. 5. 6.]
Function called with data: [5. 6. 7.]
Function called with data: [6. 7. 8.]
Function called with data: [7. 8. 9.]
Function called with data: [8. 9. 10.]

Case 2: With step=2
Function called with data: [1. 2. 3.]
Function called with data: [3. 4. 5.]
Function called with data: [5. 6. 7.]
Function called with data: [7. 8. 9.]

Final DataFrame:
    A  Rolling_1  Rolling_2
0   1        NaN        NaN
1   2        NaN        NaN
2   3   2.000000   2.000000
3   4   3.000000        NaN
4   5   4.000000   4.000000
5   6   5.000000        NaN
6   7   6.000000   6.000000
7   8   7.000000        NaN
8   9   8.000000   8.000000
9  10   9.000000        NaN

从这个例子中,我们可以观察到:

  1. 在没有step参数的情况下(Case 1),custom_func在每一行都被调用(从第3行开始)。

  2. 当使用step=2时(Case 2),custom_func只在每隔一行被调用。这导致输出结果只在每隔一行有值,其他行为NaN

  3. 使用step参数显著减少了函数调用的次数(从8次减少到4次)。

  4. 在输出结果列中,两个有具体值的元素之间NaN的数量为step-1

注意事项

  • 使用step > 1时,输出将包含更多的NaN值。在处理这些结果时需要特别注意。
  • step参数对于大数据集的性能优化特别有用,但可能会影响结果的精度或频率。
  • 当使用step参数时,确保你的分析不会因为跳过中间值而产生偏差。
  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值