窗口操作#

pandas 包含一组紧凑的 API,用于执行窗口操作 - 对值的滑动分区执行聚合的操作。 API 的功能与groupbyAPI 类似,使用必要的参数Series调用DataFrame窗口方法,然后调用聚合函数。

In [1]: s = pd.Series(range(5))

In [2]: s.rolling(window=2).sum()
Out[2]: 
0    NaN
1    1.0
2    3.0
3    5.0
4    7.0
dtype: float64

窗口是通过从当前观察回顾窗口的长度来组成的。上述结果可以通过以下窗口数据分区的总和得出:

In [3]: for window in s.rolling(window=2):
   ...:     print(window)
   ...: 
0    0
dtype: int64
0    0
1    1
dtype: int64
1    1
2    2
dtype: int64
2    2
3    3
dtype: int64
3    3
4    4
dtype: int64

概述#

pandas 支持 4 种类型的窗口操作:

  1. 滚动窗口:值上的通用固定或可变滑动窗口。

  2. 加权窗口:由库提供的加权非矩形窗口scipy.signal。

  3. 扩展窗口:值上的累积窗口。

  4. 指数加权窗口:值的累积和指数加权窗口。

概念

方法

返回对象

支持基于时间的窗口

支持链式分组

支持表格法

支持在线操作

滚动窗

rolling

pandas.typing.api.Rolling

是的

是的

是(从版本 1.3 开始)

不

加权窗口

rolling

pandas.typing.api.Window

不

不

不

不

扩展窗口

expanding

pandas.typing.api.Expanding

不

是的

是(从版本 1.3 开始)

不

指数加权窗口

ewm

pandas.typing.api.ExponentialMovingWindow

不

是(从版本 1.2 开始)

不

是(从版本 1.3 开始)

如上所述,某些操作支持根据时间偏移指定窗口:

In [4]: s = pd.Series(range(5), index=pd.date_range('2020-01-01', periods=5, freq='1D'))

In [5]: s.rolling(window='2D').sum()
Out[5]: 
2020-01-01    0.0
2020-01-02    1.0
2020-01-03    3.0
2020-01-04    5.0
2020-01-05    7.0
Freq: D, dtype: float64

此外,一些方法支持将groupby操作与窗口操作链接起来,该操作将首先按指定的键对数据进行分组,然后为每组执行窗口操作。

In [6]: df = pd.DataFrame({'A': ['a', 'b', 'a', 'b', 'a'], 'B': range(5)})

In [7]: df.groupby('A').expanding().sum()
Out[7]: 
       B
A       
a 0  0.0
  2  2.0
  4  6.0
b 1  1.0
  3  4.0

笔记

窗口操作当前仅支持数字数据(整数和浮点数)并且始终返回float64值。

警告

由于底层加窗算法累加总和,某些加窗聚合、mean、和方法可能会出现数值不精确的情况。当值大小不同时sumvarstd\(1/np.finfo(np.double).eps\)这会导致截断。必须注意的是,较大的值可能会对窗口产生影响,而窗口不包括这些值。卡汉求和用于计算滚动总和,以尽可能保持准确性。

1.3.0 版本中的新增功能。

某些窗口操作还支持构造函数中的选项,该选项一次method='table'对整个而不是单个列或行执行窗口操作。这可以为具有许多列或行(带有相应参数)DataFrame的系统提供有用的性能优势,或者在窗口操作期间利用其他列的能力。仅当在相应的方法调用中指定时才能使用该选项。DataFrameaxismethod='table'engine='numba'

例如,可以通过指定单独的权重列来计算加权平均值。apply()

In [8]: def weighted_mean(x):
   ...:     arr = np.ones((1, x.shape[1]))
   ...:     arr[:, :2] = (x[:, :2] * x[:, 2]).sum(axis=0) / x[:, 2].sum()
   ...:     return arr
   ...: 

In [9]: df = pd.DataFrame([[1, 2, 0.6], [2, 3, 0.4], [3, 4, 0.2], [4, 5, 0.7]])

In [10]: df.rolling(2, method="table", min_periods=0).apply(weighted_mean, raw=True, engine="numba")  # noqa: E501
Out[10]: 
          0         1    2
0  1.000000  2.000000  1.0
1  1.800000  2.000000  1.0
2  3.333333  2.333333  1.0
3  1.555556  7.000000  1.0

1.3 版本中的新增功能。

一些开窗操作还支持online在构造开窗对象后返回一个新对象的方法,该新对象支持传入新对象DataFrame或Series对象以继续使用新值进行开窗计算(即在线计算)。

这个新窗口对象上的方法必须首先调用聚合方法来“启动”在线计算的初始状态。然后,可以在参数中传递新的DataFrame或对象以继续加窗计算。Seriesupdate

In [11]: df = pd.DataFrame([[1, 2, 0.6], [2, 3, 0.4], [3, 4, 0.2], [4, 5, 0.7]])

In [12]: df.ewm(0.5).mean()
Out[12]: 
          0         1         2
0  1.000000  2.000000  0.600000
1  1.750000  2.750000  0.450000
2  2.615385  3.615385  0.276923
3  3.550000  4.550000  0.562500
In [13]: online_ewm = df.head(2).ewm(0.5).online()

In [14]: online_ewm.mean()
Out[14]: 
      0     1     2
0  1.00  2.00  0.60
1  1.75  2.75  0.45

In [15]: online_ewm.mean(update=df.tail(1))
Out[15]: 
          0         1         2
3  3.307692  4.307692  0.623077

所有窗口操作都支持一个参数,该参数指示窗口必须具有的min_periods最小非值数量;np.nan否则,结果值为np.nan。 min_periods对于基于时间的窗口和window固定窗口,默认为 1

In [16]: s = pd.Series([np.nan, 1, 2, np.nan, np.nan, 3])

In [17]: s.rolling(window=3, min_periods=1).sum()
Out[17]: 
0    NaN
1    1.0
2    3.0
3    3.0
4    2.0
5    3.0
dtype: float64

In [18]: s.rolling(window=3, min_periods=2).sum()
Out[18]: 
0    NaN
1    NaN
2    3.0
3    3.0
4    NaN
5    NaN
dtype: float64

# Equivalent to min_periods=3
In [19]: s.rolling(window=3, min_periods=None).sum()
Out[19]: 
0   NaN
1   NaN
2   NaN
3   NaN
4   NaN
5   NaN
dtype: float64

此外,所有窗口操作都支持aggregate返回应用于窗口的多个聚合结果的方法。

In [20]: df = pd.DataFrame({"A": range(5), "B": range(10, 15)})

In [21]: df.expanding().agg(["sum", "mean", "std"])
Out[21]: 
      A                    B                
    sum mean       std   sum  mean       std
0   0.0  0.0       NaN  10.0  10.0       NaN
1   1.0  0.5  0.707107  21.0  10.5  0.707107
2   3.0  1.0  1.000000  33.0  11.0  1.000000
3   6.0  1.5  1.290994  46.0  11.5  1.290994
4  10.0  2.0  1.581139  60.0  12.0  1.581139

滚动窗口#

通用滚动窗口支持将窗口指定为固定数量的观测值或基于偏移量的可变数量的观测值。如果提供了基于时间的偏移,则相应的基于时间的索引必须是单调的。

In [22]: times = ['2020-01-01', '2020-01-03', '2020-01-04', '2020-01-05', '2020-01-29']

In [23]: s = pd.Series(range(5), index=pd.DatetimeIndex(times))

In [24]: s
Out[24]: 
2020-01-01    0
2020-01-03    1
2020-01-04    2
2020-01-05    3
2020-01-29    4
dtype: int64

# Window with 2 observations
In [25]: s.rolling(window=2).sum()
Out[25]: 
2020-01-01    NaN
2020-01-03    1.0
2020-01-04    3.0
2020-01-05    5.0
2020-01-29    7.0
dtype: float64

# Window with 2 days worth of observations
In [26]: s.rolling(window='2D').sum()
Out[26]: 
2020-01-01    0.0
2020-01-03    1.0
2020-01-04    3.0
2020-01-05    5.0
2020-01-29    4.0
dtype: float64

有关所有支持的聚合函数,请参阅滚动窗口函数。

居中窗口#

默认情况下,标签设置在窗口的右边缘,但 center可以使用关键字将标签设置在中心。

In [27]: s = pd.Series(range(10))

In [28]: s.rolling(window=5).mean()
Out[28]: 
0    NaN
1    NaN
2    NaN
3    NaN
4    2.0
5    3.0
6    4.0
7    5.0
8    6.0
9    7.0
dtype: float64

In [29]: s.rolling(window=5, center=True).mean()
Out[29]: 
0    NaN
1    NaN
2    2.0
3    3.0
4    4.0
5    5.0
6    6.0
7    7.0
8    NaN
9    NaN
dtype: float64

这也可以应用于类似日期时间的索引。

1.3.0 版本中的新增功能。

In [30]: df = pd.DataFrame(
   ....:     {"A": [0, 1, 2, 3, 4]}, index=pd.date_range("2020", periods=5, freq="1D")
   ....: )
   ....: 

In [31]: df
Out[31]: 
            A
2020-01-01  0
2020-01-02  1
2020-01-03  2
2020-01-04  3
2020-01-05  4

In [32]: df.rolling("2D", center=False).mean()
Out[32]: 
              A
2020-01-01  0.0
2020-01-02  0.5
2020-01-03  1.5
2020-01-04  2.5
2020-01-05  3.5

In [33]: df.rolling("2D", center=True).mean()
Out[33]: 
              A
2020-01-01  0.5
2020-01-02  1.5
2020-01-03  2.5
2020-01-04  3.5
2020-01-05  4.0

滚动窗口端点#

可以使用以下参数指定滚动窗口计算中包含的间隔端点closed :

价值

行为

'right'

关闭右端点

'left'

关闭左端点

'both'

关闭两个端点

'neither'

开放端点

例如,在许多要求当前信息与过go信息之间不存在污染的问题中,打开正确的端点非常有用。这允许滚动窗口计算“截至该时间点”的统计数据,但不包括该时间点。

In [34]: df = pd.DataFrame(
   ....:     {"x": 1},
   ....:     index=[
   ....:         pd.Timestamp("20130101 09:00:01"),
   ....:         pd.Timestamp("20130101 09:00:02"),
   ....:         pd.Timestamp("20130101 09:00:03"),
   ....:         pd.Timestamp("20130101 09:00:04"),
   ....:         pd.Timestamp("20130101 09:00:06"),
   ....:     ],
   ....: )
   ....: 

In [35]: df["right"] = df.rolling("2s", closed="right").x.sum()  # default

In [36]: df["both"] = df.rolling("2s", closed="both").x.sum()

In [37]: df["left"] = df.rolling("2s", closed="left").x.sum()

In [38]: df["neither"] = df.rolling("2s", closed="neither").x.sum()

In [39]: df
Out[39]: 
                     x  right  both  left  neither
2013-01-01 09:00:01  1    1.0   1.0   NaN      NaN
2013-01-01 09:00:02  1    2.0   2.0   1.0      1.0
2013-01-01 09:00:03  1    2.0   3.0   2.0      1.0
2013-01-01 09:00:04  1    2.0   3.0   2.0      1.0
2013-01-01 09:00:06  1    1.0   2.0   1.0      NaN

自定义窗口滚动#

除了接受整数或偏移量作为window参数之外,rolling还接受BaseIndexer允许用户定义计算窗口边界的自定义方法的子类。子类BaseIndexer需要定义一个get_window_bounds返回两个数组元组的方法,第一个是窗口的起始索引,第二个是窗口的结束索引。此外,num_values、min_periods、center和将自动传递给,closed 并且定义的方法必须始终接受这些参数。stepget_window_bounds

例如,如果我们有以下内容DataFrame

In [40]: use_expanding = [True, False, True, False, True]

In [41]: use_expanding
Out[41]: [True, False, True, False, True]

In [42]: df = pd.DataFrame({"values": range(5)})

In [43]: df
Out[43]: 
   values
0       0
1       1
2       2
3       3
4       4

并且我们想要使用扩展窗口,否则use_expanding窗口True大小为 1,我们可以创建以下BaseIndexer子类:

In [44]: from pandas.api.indexers import BaseIndexer

In [45]: class CustomIndexer(BaseIndexer):
   ....:      def get_window_bounds(self, num_values, min_periods, center, closed, step):
   ....:          start = np.empty(num_values, dtype=np.int64)
   ....:          end = np.empty(num_values, dtype=np.int64)
   ....:          for i in range(num_values):
   ....:              if self.use_expanding[i]:
   ....:                  start[i] = 0
   ....:                  end[i] = i + 1
   ....:              else:
   ....:                  start[i] = i
   ....:                  end[i] = i + self.window_size
   ....:          return start, end
   ....: 

In [46]: indexer = CustomIndexer(window_size=1, use_expanding=use_expanding)

In [47]: df.rolling(indexer).sum()
Out[47]: 
   values
0     0.0
1     1.0
2     3.0
3     3.0
4    10.0

您可以在此处查看BaseIndexer子类的其他示例

这些示例中值得注意的一个子类是VariableOffsetWindowIndexer允许在非固定偏移量上进行滚动操作,例如BusinessDay.

In [48]: from pandas.api.indexers import VariableOffsetWindowIndexer

In [49]: df = pd.DataFrame(range(10), index=pd.date_range("2020", periods=10))

In [50]: offset = pd.offsets.BDay(1)

In [51]: indexer = VariableOffsetWindowIndexer(index=df.index, offset=offset)

In [52]: df
Out[52]: 
            0
2020-01-01  0
2020-01-02  1
2020-01-03  2
2020-01-04  3
2020-01-05  4
2020-01-06  5
2020-01-07  6
2020-01-08  7
2020-01-09  8
2020-01-10  9

In [53]: df.rolling(indexer).sum()
Out[53]: 
               0
2020-01-01   0.0
2020-01-02   1.0
2020-01-03   2.0
2020-01-04   3.0
2020-01-05   7.0
2020-01-06  12.0
2020-01-07   6.0
2020-01-08   7.0
2020-01-09   8.0
2020-01-10   9.0

对于某些问题,未来的知识可供分析。例如,当每个数据点都是从实验中读取的完整时间序列,并且任务是提取基础条件时,就会发生这种情况。在这些情况下,执行前瞻性滚动窗口计算可能会很有用。 FixedForwardWindowIndexer类可用于此目的。该BaseIndexer子类实现了一个封闭的定宽前视滚动窗口,我们可以如下使用它:

In [54]: from pandas.api.indexers import FixedForwardWindowIndexer

In [55]: indexer = FixedForwardWindowIndexer(window_size=2)

In [56]: df.rolling(indexer, min_periods=1).sum()
Out[56]: 
               0
2020-01-01   1.0
2020-01-02   3.0
2020-01-03   5.0
2020-01-04   7.0
2020-01-05   9.0
2020-01-06  11.0
2020-01-07  13.0
2020-01-08  15.0
2020-01-09  17.0
2020-01-10   9.0

我们还可以通过使用切片、应用滚动聚合,然后翻转结果来实现此目的,如下例所示:

In [57]: df = pd.DataFrame(
   ....:     data=[
   ....:         [pd.Timestamp("2018-01-01 00:00:00"), 100],
   ....:         [pd.Timestamp("2018-01-01 00:00:01"), 101],
   ....:         [pd.Timestamp("2018-01-01 00:00:03"), 103],
   ....:         [pd.Timestamp("2018-01-01 00:00:04"), 111],
   ....:     ],
   ....:     columns=["time", "value"],
   ....: ).set_index("time")
   ....: 

In [58]: df
Out[58]: 
                     value
time                      
2018-01-01 00:00:00    100
2018-01-01 00:00:01    101
2018-01-01 00:00:03    103
2018-01-01 00:00:04    111

In [59]: reversed_df = df[::-1].rolling("2s").sum()[::-1]

In [60]: reversed_df
Out[60]: 
                     value
time                      
2018-01-01 00:00:00  201.0
2018-01-01 00:00:01  101.0
2018-01-01 00:00:03  214.0
2018-01-01 00:00:04  111.0

滚动应用#

该apply()函数需要一个额外的func参数并执行通用滚动计算。参数func应该是从 ndarray 输入生成单个值的单个函数。raw指定窗口是转换为Series对象 ( raw=False) 还是 ndarray 对象 ( raw=True)。

In [61]: def mad(x):
   ....:     return np.fabs(x - x.mean()).mean()
   ....: 

In [62]: s = pd.Series(range(10))

In [63]: s.rolling(window=4).apply(mad, raw=True)
Out[63]: 
0    NaN
1    NaN
2    NaN
3    1.0
4    1.0
5    1.0
6    1.0
7    1.0
8    1.0
9    1.0
dtype: float64

努巴引擎#

此外, 如果作为可选依赖项安装,则apply()可以利用Numbaengine='numba' 。可以使用 Numba 通过指定和参数来执行应用聚合 engine_kwargs(raw也必须设置为True)。有关参数的一般用法和性能注意事项,请参阅使用 Numba 增强性能。

Numba 将应用于两个潜在的例程中:

  1. 如果func是标准Python函数,引擎将JIT传递的函数。func也可以是 JIT 函数,在这种情况下,引擎将不会再次 JIT 该函数。

  2. 引擎将 JIT for 循环,其中 apply 函数应用于每个窗口。

该engine_kwargs参数是一个关键字参数的字典,它将被传递到 numba.jit 装饰器中。这些关键字参数将应用于传递的函数(如果是标准 Python 函数)和每个窗口上的 apply for 循环。

1.3.0 版本中的新增功能。

mean、median、max、min、 也sum支持engine和engine_kwargs论点。

二值窗函数#

cov()并且可以计算关于/或 /中的两个或任意组合的corr()移动窗口统计数据。以下是每种情况下的行为:SeriesDataFrameSeriesDataFrameDataFrame

  • 二Series:计算配对的统计数据。

  • DataFrame/ Series:使用传递的 Series 计算 DataFrame 每一列的统计信息,从而返回一个 DataFrame。

  • DataFrame/ DataFrame:默认情况下计算匹配列名的统计信息,返回一个 DataFrame。如果传递关键字参数pairwise=True,则计算每对列的统计信息,返回 a DataFrame, MultiIndex其值是相关日期(请参阅下一节)。

例如:

In [64]: df = pd.DataFrame(
   ....:     np.random.randn(10, 4),
   ....:     index=pd.date_range("2020-01-01", periods=10),
   ....:     columns=["A", "B", "C", "D"],
   ....: )
   ....: 

In [65]: df = df.cumsum()

In [66]: df2 = df[:4]

In [67]: df2.rolling(window=2).corr(df2["B"])
Out[67]: 
              A    B    C    D
2020-01-01  NaN  NaN  NaN  NaN
2020-01-02 -1.0  1.0 -1.0  1.0
2020-01-03  1.0  1.0  1.0 -1.0
2020-01-04 -1.0  1.0  1.0 -1.0

计算滚动成对协方差和相关性#

在金融数据分析和其他领域,计算时间序列集合的协方差和相关矩阵是很常见的。通常人们还对移动窗口协方差和相关矩阵感兴趣。这可以通过传递pairwise关键字参数来完成,在输入的情况下 DataFrame将产生一个 MultiIndexed DataFrame,其index是相关日期。对于单个 DataFrame 参数,pairwise甚至可以省略该参数:

笔记

缺失值将被忽略,并且每个条目均使用成对完整观测值进行计算。

假设缺失数据是随机缺失的,这会导致对协方差矩阵的估计是无偏的。然而,对于许多应用来说,这种估计可能是不可接受的,因为估计的协方差矩阵不能保证是半正定的。这可能导致估计的相关性具有大于一的绝对值和/或不可逆的协方差矩阵。 有关更多详细信息,请参阅协方差矩阵的估计。

In [68]: covs = (
   ....:     df[["B", "C", "D"]]
   ....:     .rolling(window=4)
   ....:     .cov(df[["A", "B", "C"]], pairwise=True)
   ....: )
   ....: 

In [69]: covs
Out[69]: 
                     B         C         D
2020-01-01 A       NaN       NaN       NaN
           B       NaN       NaN       NaN
           C       NaN       NaN       NaN
2020-01-02 A       NaN       NaN       NaN
           B       NaN       NaN       NaN
...                ...       ...       ...
2020-01-09 B  0.342006  0.230190  0.052849
           C  0.230190  1.575251  0.082901
2020-01-10 A -0.333945  0.006871 -0.655514
           B  0.649711  0.430860  0.469271
           C  0.430860  0.829721  0.055300

[30 rows x 3 columns]

加权窗口#

win_type中的参数生成.rolling加权窗口,常用于滤波和频谱估计。win_type必须是对应于scipy.signal 窗口函数的字符串。必须安装 Scipy 才能使用这些窗口,并且必须在聚合函数中指定 Scipy 窗口方法采用的补充参数。

In [70]: s = pd.Series(range(10))

In [71]: s.rolling(window=5).mean()
Out[71]: 
0    NaN
1    NaN
2    NaN
3    NaN
4    2.0
5    3.0
6    4.0
7    5.0
8    6.0
9    7.0
dtype: float64

In [72]: s.rolling(window=5, win_type="triang").mean()
Out[72]: 
0    NaN
1    NaN
2    NaN
3    NaN
4    2.0
5    3.0
6    4.0
7    5.0
8    6.0
9    7.0
dtype: float64

# Supplementary Scipy arguments passed in the aggregation function
In [73]: s.rolling(window=5, win_type="gaussian").mean(std=0.1)
Out[73]: 
0    NaN
1    NaN
2    NaN
3    NaN
4    2.0
5    3.0
6    4.0
7    5.0
8    6.0
9    7.0
dtype: float64

有关所有支持的聚合函数,请参阅加权窗口函数。

扩展窗口#

扩展窗口会生成包含截至该时间点的所有可用数据的聚合统计值。由于这些计算是滚动统计的特殊情况,因此它们在 pandas 中实现,因此以下两个调用是等效的:

In [74]: df = pd.DataFrame(range(5))

In [75]: df.rolling(window=len(df), min_periods=1).mean()
Out[75]: 
     0
0  0.0
1  0.5
2  1.0
3  1.5
4  2.0

In [76]: df.expanding(min_periods=1).mean()
Out[76]: 
     0
0  0.0
1  0.5
2  1.0
3  1.5
4  2.0

有关所有支持的聚合函数,请参阅扩展窗口函数。

指数加权窗口#

指数加权窗口类似于扩展窗口,但每个先前点相对于当前点按指数加权。

一般来说,加权移动平均线的计算方式为

\[y_t = \frac{\sum_{i=0}^t w_i x_{t-i}}{\sum_{i=0}^t w_i},\]

在哪里\(x_t\)是输入,\(y_t\)是结果和\(w_i\) 是权重。

有关所有支持的聚合函数,请参阅指数加权窗口函数。

EW 函数支持指数权重的两种变体。默认值adjust=True,使用权重\(w_i = (1 - \alpha)^i\) 这使

\[y_t = \frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ... + (1 - \alpha)^t x_{0}}{1 + (1 - \alpha) + (1 - \alpha)^2 + ... + (1 - \alpha)^t}\]

当adjust=False指定时,移动平均线计算如下

\[\begin{split}y_0 &= x_0 \\ y_t &= (1 - \alpha) y_{t-1} + \alpha x_t,\end{split}\]

这相当于使用权重

\[\begin{split}w_i = \begin{cases} \alpha (1 - \alpha)^i & \text{if } i < t \\ (1 - \alpha)^i & \text{if } i = t. \end{cases}\end{split}\]

笔记

这些方程有时可以写成\(\alpha' = 1 - \alpha\),例如

\[y_t = \alpha' y_{t-1} + (1 - \alpha') x_t.\]

上述两种变体之间的差异是因为我们正在处理具有有限历史的系列。考虑一系列无限的历史,其中adjust=True:

\[y_t = \frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ...} {1 + (1 - \alpha) + (1 - \alpha)^2 + ...}\]

请注意,分母是一个几何级数,其初始项等于 1,比率为\(1 - \alpha\)我们有

\[\begin{split}y_t &= \frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ...} {\frac{1}{1 - (1 - \alpha)}}\\ &= [x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ...] \alpha \\ &= \alpha x_t + [(1-\alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ...]\alpha \\ &= \alpha x_t + (1 - \alpha)[x_{t-1} + (1 - \alpha) x_{t-2} + ...]\alpha\\ &= \alpha x_t + (1 - \alpha) y_{t-1}\end{split}\]

这与上面的表达式相同adjust=False,因此显示了无限级数的两个变体的等价性。当 时adjust=False,我们有\(y_0 = x_0\)和 \(y_t = \alpha x_t + (1 - \alpha) y_{t-1}\)。因此,有一个假设\(x_0\)不是一个普通值,而是截至该点的无限级数的指数加权矩。

必须有一个\(0 < \alpha \leq 1\),虽然可以通过 \(\alpha\)直接地,通常更容易考虑 EW 时刻的跨度、质心 (com)或半衰期:

\[\begin{split}\alpha = \begin{cases} \frac{2}{s + 1}, & \text{for span}\ s \geq 1\\ \frac{1}{1 + c}, & \text{for center of mass}\ c \geq 0\\ 1 - \exp^{\frac{\log 0.5}{h}}, & \text{for half-life}\ h > 0 \end{cases}\end{split}\]

必须为 EW 函数精确指定跨度、质心、半衰期 和alpha之一:

  • 跨度对应于通常所说的“N 日 EW 移动平均线”。

  • 质心有更多的物理解释,可以用跨度来思考:\(c = (s - 1) / 2\)。

  • 半衰期是指数重量减少到一半的时间段。

  • Alpha直接指定平滑因子。

您还可以指定halflifetimedelta 可转换单位,以指定在指定 的序列时观测值衰减到其值一半所需的时间量times。

In [77]: df = pd.DataFrame({"B": [0, 1, 2, np.nan, 4]})

In [78]: df
Out[78]: 
     B
0  0.0
1  1.0
2  2.0
3  NaN
4  4.0

In [79]: times = ["2020-01-01", "2020-01-03", "2020-01-10", "2020-01-15", "2020-01-17"]

In [80]: df.ewm(halflife="4 days", times=pd.DatetimeIndex(times)).mean()
Out[80]: 
          B
0  0.000000
1  0.585786
2  1.523889
3  1.523889
4  3.233686

以下公式用于计算输入时间向量的指数加权平均值:

\[y_t = \frac{\sum_{i=0}^t 0.5^\frac{t_{t} - t_{i}}{\lambda} x_{t-i}}{\sum_{i=0}^t 0.5^\frac{t_{t} - t_{i}}{\lambda}},\]

ExponentialMovingWindow 还有一个ignore_na参数,它确定中间空值如何影响权重的计算。当ignore_na=False(默认)时,权重是根据绝对位置计算的,因此中间空值会影响结果。当 时ignore_na=True,通过忽略中间空值来计算权重。例如,假设adjust=True,如果ignore_na=False,则 的加权平均值将计算为3, NaN, 5

\[\frac{(1-\alpha)^2 \cdot 3 + 1 \cdot 5}{(1-\alpha)^2 + 1}.\]

而如果ignore_na=True,加权平均值将计算为

\[\frac{(1-\alpha) \cdot 3 + 1 \cdot 5}{(1-\alpha) + 1}.\]

var()、std()和函数cov()有一个bias参数,指定结果是否应包含有偏差或无偏差的统计数据。例如,如果bias=True,ewmvar(x)计算为 ;而如果(默认),有偏方差统计量按go偏因子缩放ewmvar(x) = ewma(x**2) - ewma(x)**2bias=False

\[\frac{\left(\sum_{i=0}^t w_i\right)^2}{\left(\sum_{i=0}^t w_i\right)^2 - \sum_{i=0}^t w_i^2}.\]

(为了\(w_i = 1\),这减少到通常的\(N / (N - 1)\)因子,与\(N = t + 1\).) 有关更多详细信息,请参阅维基百科上的加权样本方差。