API · Pandas

Pandas DataFrame.rolling()函数

DataFrame rolling()函数为数学运算提供了一个滚动的窗口。

本页内容

Python Pandas DataFrame.rolling() 函数为数学运算提供了一个滚动窗口。单独调用 rolling() 不会计算任何东西:它返回一个定义移动窗口的 Rolling 对象,然后 sum()、mean() 或 std() 等聚合方法将每个窗口归约为一行结果。由于窗口通常在当前行结束,因此结果保留与原始 DataFrame 相同的索引,这使得 rolling() 成为构建滚动总和、移动平均值和其他运行统计量的标准方法。

pandas.DataFrame.rolling() 的语法

DataFrame.rolling(
    window, min_periods=None, center=False, win_type=None, on=None,
    closed=None, step=None, method='single'
)

参数

window 它是整数、偏移量或 BaseIndexer 子类类型的参数。它指定移动窗口的大小:固定数量的观测值,或者在使用偏移量时指定一个时间跨度,例如 '3D'。
min_periods 它是一个整数参数。它指定窗口中非 NaN 观测值的最小数量;观测值更少的窗口返回 NaN。如果不设置,它的默认值对于固定窗口等于窗口大小,对于基于时间的窗口等于 1。
center 它是一个布尔参数。当它为 True 时,每个结果都标注在其窗口的中心,而不是右边缘。
win_type 它是一个字符串参数。它指定加权窗口类型,例如 'triang',使窗口内的观测值以不同的权重参与计算。大多数加权窗口类型需要 SciPy。
on 它是一个字符串参数。它指定用于计算滚动窗口的列名,而不是索引;当日期存储在某一列中时,它与基于时间的窗口一起使用。
closed 它是一个字符串参数。它指定窗口区间的哪一侧是闭合的。它有四个选项:right、left、both 或 neither。默认值为 right。
step 它是一个整数参数。窗口仅在每 step 行处求值一次,因此结果只包含这些行。

返回

它返回一个 Rolling 窗口对象。对其调用聚合方法会返回一个与输入具有相同索引的 DataFrame。

上面的签名是当前签名。旧教程中出现的 axis 参数已不存在,因为滚动窗口始终沿着索引移动;传入它会引发 TypeError,如下面的错误部分所示。

示例:窗口大小为 2 的固定窗口滚动总和

最常见的形式是使用按行数计数的整数窗口。在此示例中,rolling(2).sum() 将每一行与其正上方的一行相加。

import pandas as pd

dataframe = pd.DataFrame({
    'Attendance': {0: 60, 1: 100, 2: 80, 3: 78, 4: 95},
    'Obtained Marks': {0: 90, 1: 75, 2: 82, 3: 64, 4: 45},
})
print("The Original DataFrame is:")
print(dataframe)
print("The Rolling Sum With a Window of 2 is:")
print(dataframe.rolling(2).sum())

输出:

The Original DataFrame is:
   Attendance  Obtained Marks
0          60              90
1         100              75
2          80              82
3          78              64
4          95              45
The Rolling Sum With a Window of 2 is:
   Attendance  Obtained Marks
0         NaN             NaN
1       160.0           165.0
2       180.0           157.0
3       158.0           146.0
4       173.0           109.0

第 0 行上方没有行,因此它的窗口只包含一个观测值——少于默认 min_periods 所需的两个——所以结果是 NaN。从第 1 行开始,每个值都是当前行与前一行的总和:60 + 100 = 160、100 + 80 = 180,依此类推。整数列由于第一个 NaN 变成了浮点数。

示例:滚动平均值和 min_periods

相同的窗口适用于任何聚合。在这里,rolling(3).mean() 对当前行及其上方的两行求平均值。使用默认的 min_periods 时,前两行无法填满三行窗口,保持为 NaN。设置 min_periods=1 告诉 pandas 使用开头处可用的任意数量的观测值,因此结果从第一行开始就被定义。

import pandas as pd

dataframe = pd.DataFrame({
    'Attendance': {0: 60, 1: 100, 2: 80, 3: 78, 4: 95},
    'Obtained Marks': {0: 90, 1: 75, 2: 82, 3: 64, 4: 45},
})
print("The Rolling Mean With a Window of 3 is:")
print(dataframe.rolling(3).mean())
print("The Rolling Mean With a Window of 3 and min_periods=1 is:")
print(dataframe.rolling(3, min_periods=1).mean())

输出:

The Rolling Mean With a Window of 3 is:
   Attendance  Obtained Marks
0         NaN             NaN
1         NaN             NaN
2   80.000000       82.333333
3   86.000000       73.666667
4   84.333333       63.666667
The Rolling Mean With a Window of 3 and min_periods=1 is:
   Attendance  Obtained Marks
0   60.000000       90.000000
1   80.000000       82.500000
2   80.000000       82.333333
3   86.000000       73.666667
4   84.333333       63.666667

两个输出从索引 2 开始一致。区别在于开头:min_periods=1 还为第一行报告 60.00(仅该值本身),为第二行报告 80.00(前两个值的平均值)。请注意,min_periods 计算的是非 NaN 的观测值,这也使其成为控制数据内部缺口的主要手段,如最后一个示例所示。

示例:基于时间窗口的滚动总和

window 也可以是时间偏移量,例如 '3D'。此时窗口覆盖的是一段时间跨度,而不是固定数量的行,因此无论记录的间隔多么不规则,只有落在该跨度内的记录才会参与计算。当日期位于某一列而不是索引中时,使用 on 指定该列。下面的销售数据没有 2026-01-04 和 2026-01-07 的记录。

import pandas as pd

sales = pd.DataFrame({
    'Date': pd.to_datetime(['2026-01-01', '2026-01-02', '2026-01-03',
                            '2026-01-05', '2026-01-06', '2026-01-08']),
    'Sales': [120, 80, 150, 90, 200, 60],
})
print(sales)
print("The Rolling Sum Over the Previous 3 Days is:")
print(sales.rolling('3D', on='Date').sum())

输出:

        Date  Sales
0 2026-01-01    120
1 2026-01-02     80
2 2026-01-03    150
3 2026-01-05     90
4 2026-01-06    200
5 2026-01-08     60
The Rolling Sum Over the Previous 3 Days is:
        Date  Sales
0 2026-01-01  120.0
1 2026-01-02  200.0
2 2026-01-03  350.0
3 2026-01-05  240.0
4 2026-01-06  290.0
5 2026-01-08  260.0

对于 2026-01-05,三天窗口回溯到 2026-01-02 之前,因此它只包含 2026-01-03 和 2026-01-05 的销售额:150 + 90 = 240。缺失的 2026-01-04 不产生任何贡献,也不会像多出一行那样移动固定窗口的跨度。请注意,第一行已经有了结果:基于时间的窗口默认使用 min_periods=1,这与固定窗口的默认值正好相反。

示例:使用 center 居中标签并使用 closed 排除当前行

两个参数会改变结果所属的位置。使用 center=True 时,聚合值被放置在其窗口的中间行而不是最后一行,当移动平均值应与其平滑的数据对齐时,这正是您想要的效果;第一行和最后一行失去了它们的结果,因为没有完整的窗口以它们为中心。closed 控制包含区间的哪一端:使用 closed='left' 时,一行的窗口覆盖其之前的行,而该行本身被排除在其自身的统计之外。

import pandas as pd

temps = pd.DataFrame({'Temperature': [10, 12, 14, 16, 18, 20]})
print("The Centered Rolling Mean With a Window of 3 is:")
print(temps.rolling(3, center=True).mean())
print("The Rolling Sum With a Window of 3 and closed='left' is:")
print(temps.rolling(3, closed='left').sum())

输出:

The Centered Rolling Mean With a Window of 3 is:
   Temperature
0          NaN
1         12.0
2         14.0
3         16.0
4         18.0
5          NaN
The Rolling Sum With a Window of 3 and closed='left' is:
   Temperature
0          NaN
1          NaN
2          NaN
3         36.0
4         42.0
5         48.0

索引 1 处的居中平均值为 12.0——即 10、12 和 14 的平均值——并且它位于该窗口的中间行。closed='left' 的总和显示出熟悉的 36、42、48 模式向下移动了一行:在索引 3 处,窗口包含第 0 至 2 行(10 + 12 + 14 = 36),因为第 3 行本身被排除了。

常见错误和边界情况

三种行为最常让用户感到意外,每一种行为都可以在一个小示例中看到。

窗口内的缺失值

由于 min_periods 计算的是非 NaN 的观测值,单个缺失值会清空包含它的每个固定窗口,以及紧随其后的窗口。降低 min_periods 可使聚合使用确实存在的值。

import pandas as pd

scores = pd.DataFrame({'Score': [50.0, None, 70.0, 90.0]})
print(scores)
print("The Rolling Sum With a Window of 2 is:")
print(scores.rolling(2).sum())
print("The Rolling Sum With a Window of 2 and min_periods=1 is:")
print(scores.rolling(2, min_periods=1).sum())

输出:

   Score
0   50.0
1    NaN
2   70.0
3   90.0
The Rolling Sum With a Window of 2 is:
   Score
0    NaN
1    NaN
2    NaN
3  160.0
The Rolling Sum With a Window of 2 and min_periods=1 is:
   Score
0   50.0
1   50.0
2   70.0
3  160.0

在默认设置下,只有最后一个总和 70 + 90 保留下来。使用 min_periods=1 时,索引 2 处的窗口包含一个有效值,因此其总和为 70:缺失的条目被跳过而不是传播。这两种行为都不会修复数据——请根据部分窗口对您的统计量是否有意义来进行选择。

无效参数引发 ValueError

rolling() 在计算任何内容之前验证其参数。负窗口立即失败:

import pandas as pd

temps = pd.DataFrame({'Temperature': [10, 12, 14, 16, 18, 20]})
print(temps.rolling(-1).sum())

输出:

ValueError: window must be an integer 0 or greater

未知的 closed 值以相同的方式失败,错误信息为 ValueError: closed must be 'right', 'left', 'both' or 'neither',而诸如 2.5 的分数窗口则以与负窗口相同的消息被拒绝。请注意,window=0 是被接受的,但却是退化的:每个窗口都是空的,因此 sum() 对每一行都返回 0.0。

非数字列引发 DataError

聚合只接受数字数据。对仍包含文本列的 DataFrame 应用滚动会引发错误:

import pandas as pd

mixed = pd.DataFrame({'Name': ['A', 'B', 'C'], 'Value': [1, 2, 3]})
print(mixed.rolling(2).sum())

输出:

pandas.errors.DataError: Cannot aggregate non-numeric type: str

在滚动之前选择数字列——例如 mixed[['Value']].rolling(2).sum()——或者先删除标签列。

需要记住的事项

  • 单独的 rolling() 返回一个 Rolling 对象;在调用聚合方法之前不会计算任何内容。结果始终带有输入的索引,因此可以直接将其赋值为一个新列。
  • 一旦输出中出现 NaN,整数列就会变成浮点数,因为 NaN 没有整数表示形式。
  • 对于分组计算,在 groupby 对象上调用 rolling(),例如 df.groupby('key').rolling(2).sum():这样窗口会在每个组重新开始,而不是跨越组的边界。
  • 通过 win_type 的加权窗口适用于信号类数据;对于普通的业务数据,上例中的未加权聚合是正确的默认选择。