窗口操作#
Pandas包含一组紧凑的API,用于执行窗口操作--在值的滑动分区上执行聚合的操作。该API的功能类似于 groupby
API中的 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种类型的窗口操作:
滚动窗口:通用的固定或可变滑动窗口。
加权窗口:加权的非矩形窗口,由
scipy.signal
类库。扩展窗口:对值进行累计窗口。
指数加权窗口:对值进行累加和指数加权的窗口。
概念 |
方法 |
返回的对象 |
支持基于时间的窗口 |
支持链式分组方式 |
支持表法 |
支持在线操作 |
---|---|---|---|---|---|---|
滚动窗 |
|
|
是 |
是 |
是(从1.3版开始) |
不是的 |
加权窗口 |
|
|
不是的 |
不是的 |
不是的 |
不是的 |
展开窗口 |
|
|
不是的 |
是 |
是(从1.3版开始) |
不是的 |
指数加权窗 |
|
|
不是的 |
是(从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
, sum
, var
and std
methods may suffer from numerical imprecision due to the underlying windowing algorithms accumulating sums. When values differ with magnitude \(1/np.finfo(np.double).eps\) this results in truncation. It must be noted, that large values may have an impact on windows, which do not include these values. Kahan summation 用于计算滚动总和,以尽可能保持准确性。
1.3.0 新版功能.
一些窗口化操作还支持 method='table'
选项,该选项对整个 DataFrame
而不是一次只有一列或一行。这可以为用户提供有用的性能优势 DataFrame
具有许多列或行(具有对应的 axis
参数)或在窗口操作期间使用其他列的能力。这个 method='table'
选项仅在以下情况下才能使用 engine='numba'
在相应的方法调用中指定。
例如,一个 weighted mean 计算可以用以下公式计算 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
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/compat/_optional.py:139, in import_optional_dependency(name, extra, errors, min_version)
138 try:
--> 139 module = importlib.import_module(name)
140 except ImportError:
File /usr/lib/python3.10/importlib/__init__.py:126, in import_module(name, package)
125 level += 1
--> 126 return _bootstrap._gcd_import(name[level:], package, level)
File <frozen importlib._bootstrap>:1050, in _gcd_import(name, package, level)
File <frozen importlib._bootstrap>:1027, in _find_and_load(name, import_)
File <frozen importlib._bootstrap>:1004, in _find_and_load_unlocked(name, import_)
ModuleNotFoundError: No module named 'numba'
During handling of the above exception, another exception occurred:
ImportError Traceback (most recent call last)
Input In [10], in <cell line: 1>()
----> 1 df.rolling(2, method="table", min_periods=0).apply(weighted_mean, raw=True, engine="numba") # noqa:E501
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/core/window/rolling.py:1893, in Rolling.apply(self, func, raw, engine, engine_kwargs, args, kwargs)
1872 @doc(
1873 template_header,
1874 create_section_header("Parameters"),
(...)
1891 kwargs: dict[str, Any] | None = None,
1892 ):
-> 1893 return super().apply(
1894 func,
1895 raw=raw,
1896 engine=engine,
1897 engine_kwargs=engine_kwargs,
1898 args=args,
1899 kwargs=kwargs,
1900 )
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/core/window/rolling.py:1345, in RollingAndExpandingMixin.apply(self, func, raw, engine, engine_kwargs, args, kwargs)
1341 apply_func = generate_numba_apply_func(
1342 func, **get_jit_arguments(engine_kwargs, kwargs)
1343 )
1344 else:
-> 1345 apply_func = generate_numba_table_func(
1346 func, **get_jit_arguments(engine_kwargs, kwargs)
1347 )
1348 elif engine in ("cython", None):
1349 if engine_kwargs is not None:
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/core/window/numba_.py:209, in generate_numba_table_func(func, nopython, nogil, parallel)
177 @functools.lru_cache(maxsize=None)
178 def generate_numba_table_func(
179 func: Callable[..., np.ndarray],
(...)
182 parallel: bool,
183 ):
184 """
185 Generate a numba jitted function to apply window calculations table-wise.
186
(...)
207 Numba function
208 """
--> 209 numba_func = jit_user_function(func, nopython, nogil, parallel)
210 if TYPE_CHECKING:
211 import numba
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/core/util/numba_.py:91, in jit_user_function(func, nopython, nogil, parallel)
89 import numba
90 else:
---> 91 numba = import_optional_dependency("numba")
93 if numba.extending.is_jitted(func):
94 # Don't jit a user passed jitted function
95 numba_func = func
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/compat/_optional.py:142, in import_optional_dependency(name, extra, errors, min_version)
140 except ImportError:
141 if errors == "raise":
--> 142 raise ImportError(msg)
143 else:
144 return None
ImportError: Missing optional dependency 'numba'. Use pip or conda to install numba.
1.3 新版功能.
某些窗口化操作还支持 online
在构造窗口对象后,该窗口对象返回支持传入新的 DataFrame
或 Series
对象继续使用新值进行窗口化计算(即联机计算)。
这个新窗口对象上的方法必须首先调用Aggregation方法来“准备”在线计算的初始状态。然后,新的 DataFrame
或 Series
对象可以在 update
参数继续窗口计算。
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()
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/compat/_optional.py:139, in import_optional_dependency(name, extra, errors, min_version)
138 try:
--> 139 module = importlib.import_module(name)
140 except ImportError:
File /usr/lib/python3.10/importlib/__init__.py:126, in import_module(name, package)
125 level += 1
--> 126 return _bootstrap._gcd_import(name[level:], package, level)
File <frozen importlib._bootstrap>:1050, in _gcd_import(name, package, level)
File <frozen importlib._bootstrap>:1027, in _find_and_load(name, import_)
File <frozen importlib._bootstrap>:1004, in _find_and_load_unlocked(name, import_)
ModuleNotFoundError: No module named 'numba'
During handling of the above exception, another exception occurred:
ImportError Traceback (most recent call last)
Input In [14], in <cell line: 1>()
----> 1 online_ewm.mean()
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/core/window/ewm.py:1013, in OnlineExponentialMovingWindow.mean(self, update, update_times, *args, **kwargs)
1011 result_kwargs["name"] = self._selected_obj.name
1012 np_array = self._selected_obj.astype(np.float64).to_numpy()
-> 1013 ewma_func = generate_online_numba_ewma_func(
1014 **get_jit_arguments(self.engine_kwargs)
1015 )
1016 result = self._mean.run_ewm(
1017 np_array if is_frame else np_array[:, np.newaxis],
1018 update_deltas,
1019 self.min_periods,
1020 ewma_func,
1021 )
1022 if not is_frame:
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/core/window/online.py:33, in generate_online_numba_ewma_func(nopython, nogil, parallel)
31 import numba
32 else:
---> 33 numba = import_optional_dependency("numba")
35 @numba.jit(nopython=nopython, nogil=nogil, parallel=parallel)
36 def online_ewma(
37 values: np.ndarray,
(...)
44 ignore_na: bool,
45 ):
46 """
47 Compute online exponentially weighted mean per column over 2D values.
48
49 Takes the first observation as is, then computes the subsequent
50 exponentially weighted mean accounting minimum periods.
51 """
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/compat/_optional.py:142, in import_optional_dependency(name, extra, errors, min_version)
140 except ImportError:
141 if errors == "raise":
--> 142 raise ImportError(msg)
143 else:
144 return None
ImportError: Missing optional dependency 'numba'. Use pip or conda to install numba.
In [15]: online_ewm.mean(update=df.tail(1))
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [15], in <cell line: 1>()
----> 1 online_ewm.mean(update=df.tail(1))
File /usr/local/lib/python3.10/dist-packages/pandas-1.5.0.dev0+697.gf9762d8f52-py3.10-linux-x86_64.egg/pandas/core/window/ewm.py:993, in OnlineExponentialMovingWindow.mean(self, update, update_times, *args, **kwargs)
991 if update is not None:
992 if self._mean.last_ewm is None:
--> 993 raise ValueError(
994 "Must call mean with update=None first before passing update"
995 )
996 result_from = 1
997 result_kwargs["index"] = update.index
ValueError: Must call mean with update=None first before passing update
所有窗口化操作都支持 min_periods
参数,该参数指示窗口必须具有的非`np.nan``值的最小数量;否则,结果值为 np.nan
。 min_periods
对于基于时间的窗口和 window
适用于固定窗
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([np.sum, np.mean, np.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
这也适用于类似DATETIME的索引。
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
参数:
价值 |
行为 |
---|---|
|
关闭右端点 |
|
关闭左端点 |
|
关闭两个端点 |
|
开放端点 |
例如,在许多需要从当前信息返回到过去信息的问题中,打开正确的端点是很有用的。这允许滚动窗口计算“到该时间点”的统计数据,但不包括该时间点。
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
自定义滚窗#
1.0 新版功能.
除了接受整数或偏移量作为 window
论点, rolling
还接受一个 BaseIndexer
子类,允许用户定义用于计算窗口边界的自定义方法。这个 BaseIndexer
子类将需要定义一个 get_window_bounds
方法,该方法返回由两个数组组成的元组,第一个是窗口的开始索引,第二个是窗口的结束索引。另外, num_values
, min_periods
, center
, closed
,并将自动传递给 get_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 [2]: from pandas.api.indexers import BaseIndexer
In [3]: class CustomIndexer(BaseIndexer):
...: def get_window_bounds(self, num_values, min_periods, center, closed):
...: 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 [4]: indexer = CustomIndexer(window_size=1, use_expanding=use_expanding)
In [5]: df.rolling(indexer).sum()
Out[5]:
values
0 0.0
1 1.0
2 3.0
3 3.0
4 10.0
您可以查看其他示例 BaseIndexer
subclasses here
1.1 新版功能.
这些示例中值得注意的一个子类是 VariableOffsetWindowIndexer
,它允许在非固定偏移量上滚动操作,如 BusinessDay
。
In [44]: from pandas.api.indexers import VariableOffsetWindowIndexer
In [45]: df = pd.DataFrame(range(10), index=pd.date_range("2020", periods=10))
In [46]: offset = pd.offsets.BDay(1)
In [47]: indexer = VariableOffsetWindowIndexer(index=df.index, offset=offset)
In [48]: df
Out[48]:
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 [49]: df.rolling(indexer).sum()
Out[49]:
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 [50]: from pandas.api.indexers import FixedForwardWindowIndexer
In [51]: indexer = FixedForwardWindowIndexer(window_size=2)
In [52]: df.rolling(indexer, min_periods=1).sum()
Out[52]:
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 [53]: 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 [54]: df
Out[54]:
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 [55]: reversed_df = df[::-1].rolling("2s").sum()[::-1]
In [56]: reversed_df
Out[56]:
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 [57]: def mad(x):
....: return np.fabs(x - x.mean()).mean()
....:
In [58]: s = pd.Series(range(10))
In [59]: s.rolling(window=4).apply(mad, raw=True)
Out[59]:
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
Numba引擎#
1.0 新版功能.
另外, apply()
可以利用 Numba 如果作为可选依赖项安装,则。可以使用Numba执行应用聚合,方法是指定 engine='numba'
和 engine_kwargs
论据 (raw
还必须设置为 True
)。看见 enhancing performance with Numba 以了解参数的一般用法和性能注意事项。
Numba可能会在两个例程中应用:
如果
func
is a standard Python function, the engine will JIT 传递的函数。func
也可以是JITed函数,在这种情况下,引擎不会再次JIT该函数。引擎将JIT for循环,其中将Apply函数应用到每个窗口。
这个 engine_kwargs
argument is a dictionary of keyword arguments that will be passed into the numba.jit decorator 。这些关键字参数将应用于 both 传递的函数(如果是标准的Python函数)和每个窗口上的应用for循环。
1.3.0 新版功能.
mean
, median
, max
, min
,以及 sum
还支持 engine
和 engine_kwargs
争论。
二进制窗函数#
cov()
and corr()
can compute moving window statistics about two Series
or any combination of DataFrame
/Series
或 DataFrame
/DataFrame
。以下是每种情况下的行为:
二
Series
:计算配对的统计量。DataFrame
/Series
:使用传递的Series计算DataFrame的每一列的统计信息,从而返回DataFrame。DataFrame
/DataFrame
:默认情况下,计算匹配列名的统计信息,返回DataFrame。如果关键字参数pairwise=True
传递,然后计算每对列的统计信息,并返回DataFrame
使用一个MultiIndex
其值是有问题的日期(请参见 the next section )。
例如:
In [60]: df = pd.DataFrame(
....: np.random.randn(10, 4),
....: index=pd.date_range("2020-01-01", periods=10),
....: columns=["A", "B", "C", "D"],
....: )
....:
In [61]: df = df.cumsum()
In [62]: df2 = df[:4]
In [63]: df2.rolling(window=2).corr(df2["B"])
Out[63]:
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
输入将产生多索引的 DataFrame
谁的 index
都是有问题的日期。在单个DataFrame参数的情况下, pairwise
甚至可以省略参数:
备注
遗漏的值被忽略,并且使用成对的完整观测来计算每个条目。
假设丢失的数据是随机丢失的,这导致对协方差矩阵的估计是无偏的。然而,对于许多应用,这种估计可能是不可接受的,因为估计的协方差矩阵不能保证是半正定的。这可能导致估计的相关性具有大于1的绝对值,和/或不可逆的协方差矩阵。看见 Estimation of covariance matrices 了解更多详细信息。
In [64]: covs = (
....: df[["B", "C", "D"]]
....: .rolling(window=4)
....: .cov(df[["A", "B", "C"]], pairwise=True)
....: )
....:
In [65]: covs
Out[65]:
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
argument in .rolling
generates a weighted windows that are commonly used in filtering and spectral estimation. win_type
must be string that corresponds to a scipy.signal window function 。必须安装Scipy才能使用这些窗口,并且必须在聚合函数中指定Scipy窗口方法采用的补充参数。
In [66]: s = pd.Series(range(10))
In [67]: s.rolling(window=5).mean()
Out[67]:
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 [68]: s.rolling(window=5, win_type="triang").mean()
Out[68]:
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 [69]: s.rolling(window=5, win_type="gaussian").mean(std=0.1)
Out[69]:
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 [70]: df = pd.DataFrame(range(5))
In [71]: df.rolling(window=len(df), min_periods=1).mean()
Out[71]:
0
0 0.0
1 0.5
2 1.0
3 1.5
4 2.0
In [72]: df.expanding(min_periods=1).mean()
Out[72]:
0
0 0.0
1 0.5
2 1.0
3 1.5
4 2.0
有关所有支持的聚合函数,请参见 扩展窗口函数 。
指数加权窗#
指数加权窗口类似于扩展窗口,但每个先前点相对于当前点被指数向下加权。
一般而言,加权移动平均数的计算公式为
哪里 \(x_t\) 是输入, \(y_t\) 就是结果,而 \(w_i\) 就是重量。
有关所有支持的聚合函数,请参见 指数加权窗函数 。
EW函数支持指数权重的两种变体。默认情况下, adjust=True
,使用权重 \(w_i = (1 - \alpha)^i\) 这给了我们
什么时候 adjust=False
则移动平均值的计算方式为
这相当于使用权重
备注
这些方程式有时用以下形式写成 \(\alpha' = 1 - \alpha\) ,例如
出现上述两种变体之间的差异是因为我们所处理的级数具有有限的历史。考虑一系列无限的历史, adjust=True
:
注意到分母是一个几何级数,初始项等于1,比率为 \(1 - \alpha\) 我们有
它的表达方式与 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\) 直接来说,通常更容易想到的是 span , 质心(COM) 或 half-life 一个EW时刻:
必须准确地指定以下其中之一 span , 质心 , half-life 和 Alpha 致EW职能部门:
Span 与通常所说的“N日EW移动均线”相对应.
质心 有更多的物理解释,可以从跨度的角度来考虑: \(c = (s - 1) / 2\) 。
Half-life 是指数权重减少到一半的时间段。
Alpha 直接指定平滑系数。
1.1.0 新版功能.
您还可以指定 halflife
以时间增量可转换单位表示,用于指定观测值衰减到其值的一半所需的时间量,同时还指定 times
。
In [73]: df = pd.DataFrame({"B": [0, 1, 2, np.nan, 4]})
In [74]: df
Out[74]:
B
0 0.0
1 1.0
2 2.0
3 NaN
4 4.0
In [75]: times = ["2020-01-01", "2020-01-03", "2020-01-10", "2020-01-15", "2020-01-17"]
In [76]: df.ewm(halflife="4 days", times=pd.DatetimeIndex(times)).mean()
Out[76]:
B
0 0.000000
1 0.585786
2 1.523889
3 1.523889
4 3.233686
以下公式用于计算具有时间输入向量的指数加权平均值:
ExponentialMovingWindow还具有 ignore_na
参数,该参数确定中间空值如何影响权重的计算。什么时候 ignore_na=False
(默认),权重是基于绝对位置计算的,因此中间空值会影响结果。什么时候 ignore_na=True
,则通过忽略中间空值来计算权重。例如,假设 adjust=True
,如果 ignore_na=False
的加权平均值 3, NaN, 5
将被计算为
鉴于如果 ignore_na=True
,加权平均数的计算公式为
这个 var()
, std()
,以及 cov()
函数有一个 bias
参数,指定结果应包含有偏统计信息还是无偏统计信息。例如,如果 bias=True
, ewmvar(x)
的计算公式为 ewmvar(x) = ewma(x**2) - ewma(x)**2
;鉴于如果 bias=False
(默认设置),有偏方差统计数据按去偏系数进行缩放
(适用于 \(w_i = 1\) ,这将减少到通常的水平。 \(N / (N - 1)\) 系数,带 \(N = t + 1\) 。)看见 Weighted Sample Variance 有关更多细节,请访问维基百科。