使用 pandas 对数据进行移动计算
假设有 10 天的销售额数据,我们想每三天求一次总和,比如第五天的总和就是第三天 第四天 第五天的销售额之和,这个时候该怎么做呢?
Series 对象有一个 rolling 方法,专门用来做移动计算,我们来看一下。
import pandas as pd
amount = pd.Series(
[100, 90, 110, 150, 110, 130, 80, 90, 100, 150])
print(amount.rolling(3).sum())
"""
0 NaN # NaN NaN 100
1 NaN # NaN 100 90
2 300.0 # 100 90 110
3 350.0 # 90 110 150
4 370.0 # 110 150 110
5 390.0 # 150 110 130
6 320.0 # 110 130 80
7 300.0 # 130 80 90
8 270.0 # 80 90 100
9 340.0 # 90 100 150
dtype: float64
"""
结果和我们想要的是一样的,amount.rolling(3) 相当于创建了一个长度为 3 的窗口,窗口从上到下依次滑动,我们画一张图:
amount.rolling(3) 就做了类似于图中的事情,然后在其基础上调用 sum,会将每个窗口里面的元素加起来,就得到上面代码输出的结果。另外窗口的大小可以任意,这里我们以 3 为例。
除了sum,还可以求平均值、求方差等等,可以进行很多的操作,有兴趣可以自己尝试一下。当然我们也可以自定义函数:
import pandas as pd
import numpy as np
amount = pd.Series(
[100, 90, 110, 150, 110, 130, 80, 90, 100, 150])
print(
# 调用 agg 方法,传递一个函数
# 参数 x 就是每个窗口里面的元素组成的 Series 对象
amount.rolling(3).agg(lambda x: np.sum(x) * 2)
)
"""
0 NaN # (NaN NaN 100) * 2
1 NaN # (NaN 100 90) * 2
2 600.0 # (100 90 110) * 2
3 700.0 # (90 110 150) * 2
4 740.0 # (110 150 110) * 2
5 780.0 # (150 110 130) * 2
6 640.0 # (110 130 80) * 2
7 600.0 # (130 80 90) * 2
8 540.0 # (80 90 100) * 2
9 680.0 # (90 100 150) * 2
dtype: float64
"""
agg 里面的函数的逻辑可以任意,但返回的必须是一个数值。
此外我们注意到,开始的两个元素为 NaN,这是因为 rolling(3) 表示从当前位置往上筛选,总共筛选 3 个元素,图上已经画的很清晰了。但如果我们希望元素不够的时候有多少算多少,该怎么办呢?比如:第一个窗口里面的元素之和就是第一个元素,第二个窗口里面的元素之和是第一个元素加上第二个元素。
import pandas as pd
amount = pd.Series(
[100, 90, 110, 150, 110, 130, 80, 90, 100, 150])
print(
# min_periods 表示窗口的最小观测值
amount.rolling(3, min_periods=1).sum()
)
"""
0 100.0
1 190.0
2 300.0
3 350.0
4 370.0
5 390.0
6 320.0
7 300.0
8 270.0
9 340.0
dtype: float64
"""
添加一个 min_periods 参数即可实现,这个参数表示窗口的最小观测值,即:窗口里面元素的最小数量,默认它和窗口的长度相等。我们窗口长度为 3,但指定了 min_periods 为 1,表示元素不够也没关系,只要有一个就行。
因此元素不够的话,有几个就算几个。如果我们指定 min_periods 为 2 的话,那么会是什么结果呢?显然第一个是 NaN,第二个还是 190.0,因为窗口里面的元素个数至少为 2。
import pandas as pd
amount = pd.Series(
[100, 90, 110, 150, 110, 130, 80, 90, 100, 150])
print(
# 窗口的最小观测值为 2
amount.rolling(3, min_periods=2).sum()
)
"""
0 NaN
1 190.0
2 300.0
3 350.0
4 370.0
5 390.0
6 320.0
7 300.0
8 270.0
9 340.0
dtype: float64
"""