rol = DataFrame.rolling(window, min_periods=None, center=False, win_type=None, on=None, axis=no_default, closed=None, step=None, method='single')
Pandasсодержит компактный интерфейс для выполнения оконных операций - операции, которая выполняет агрегацию по скользящему разделу значений. API оконных операций функционирует аналогично APIDataFrame.groupby(), вызывая метод оконного управления с необходимыми параметрами, а затем впоследствии вызывая функцию агрегирования.
window - размер скользящего окна. Если передано:
int - фиксированное количество наблюдений, используемых для каждого окна.timedelta или offset - период времени каждого окна. Каждое окно будет иметь переменный размер в зависимости от наблюдений, включенных в период времени. Это справедливо только для индексов типа datetime.BaseIndexer - границы окна основаны на методе .get_window_bounds(). Дополнительные ключевые аргументы, а именно min_ periods, center, close и step, будут переданы в .get_window_bounds().min_periods=None - минимальное количество наблюдений в окне (принимает int или None), необходимое для получения значения. В противном случае результатом будет np.nan.
min_ periods по умолчанию будет равно 1.center=False - если False, то установит метки окон как правый край индекса окна. Если True, то установит метки окон в центре индекса окна.
win_type=None - если None, то все баллы имеют одинаковый вес. Если это строка, то она должна быть действительная оконная функция scipy.signal.
Некоторые типы окон Scipy требуют передачи дополнительных аргументов в функцию агрегирования. Они должны соответствовать ключевым словам, указанным в сигнатуре методов оконных операций Scipy.
on=None - для DataFrame - метка столбца или уровень индекса, на котором рассчитывается скользящее окно, а не индекс DataFrame. Принимает строку - как метку индекса столбца. Если указать столбец как целочисленный индекс, то он игнорируется и исключается из результата, т.к. целочисленный индекс не используется для вычисления скользящего окна.
axis=no_default - ключевой аргумент axis устарел с версии 2.1.0. Теперь для использования axis=1 необходимо транспонировать DataFrame.T.
index, обрабатывает строки.columns, обрабатывает столбцы.Для Series аргумент не используется и по умолчанию равен 0.
closed=None - по умолчанию None («право»).
right, первая точка в окне исключается из расчетов.left, последняя точка в окне исключается из расчетов.both, ни одна точка в окне не будет исключена из вычислений.neither, первая и последняя точки в окне исключаются из вычислений.step=None - оценивает окно при каждом результате шага step, что эквивалентно срезу как [::step]. Шаг должен быть целым числом. Использование step, отличного от None или 1, приведет к получению результата, форма которого отличается от формы входных данных.
method='single' - выполняет операцию скользящего окна для одного столбца или строки (single) или для всего объекта (table). Этот аргумент реализуется только при указании engine='numba' в вызове метода.
typing.Window. В противном случае возвращается экземпляр typing.Rolling.DataFrame.rolling():Метод DataFrame.rolling() предоставляет расчеты скользящего окна.
Стандартные скользящие окна поддерживают указание окон как фиксированного числа наблюдений или переменного числа наблюдений на основе смещения offset. Если указано смещение по времени, то соответствующий индекс по времени должен быть монотонным.
>>> import pandas as pd >>> times = ['2020-01-01', '2020-01-03', '2020-01-04', '2020-01-05', '2020-01-29'] >>> s = pd.Series(range(5), index=pd.DatetimeIndex(times)) >>> s # 2020-01-01 0 # 2020-01-03 1 # 2020-01-04 2 # 2020-01-05 3 # 2020-01-29 4 # dtype: int64 # Окно с 2 наблюдениями >>> s.rolling(window=2).sum() # 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 # Окно наблюдений на 2 дня вперед >>> s.rolling(window='2D').sum() # 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
| Функция | Что делает |
Rolling.count([numeric_only]) | Вычисляет количество переходящих наблюдений, отличных от NaN |
Rolling.sum([numeric_only, engine, ...]) | Рассчитывает скользящую сумму. |
Rolling.mean([numeric_only, engine, ...]) | Рассчитывает скользящее среднее значение |
Rolling.median([numeric_only, engine, ...]) | Рассчитывает скользящую медиану. |
Rolling.var([ddof, numeric_only, engine, ...]) | Рассчитывает скользящую дисперсию |
Rolling.std([ddof, numeric_only, engine, ...]) | Рассчитывает скользящее стандартное отклонение. |
Rolling.min([numeric_only, engine, ...]) | Рассчитывает скользящий минимум |
Rolling.max([numeric_only, engine, ...]) | Рассчитывает скользящий максимум |
Rolling.corr([other, pairwise, ddof, ...]) | Рассчитывает скользящую корреляцию |
Rolling.cov([other, pairwise, ddof, ...]) | Рассчитывает ковариацию скользящей выборки |
Rolling.skew([numeric_only]) | Рассчитывает несмещенную асимметрию. |
Rolling.kurt([numeric_only]) | Рассчитывает определение эксцесса по методу Роллинга Фишера без предвзятости. |
Rolling.apply(func[, raw, engine, ...]) | Применяет пользовательскую функцию агрегирования |
Rolling.aggregate(func, *args, **kwargs) | Агрегирует, используя одну или несколько операций над указанной осью. |
Rolling.quantile(q[, interpolation, ...]) | Рассчитывает квантиль скользящего окна |
Rolling.sem([ddof, numeric_only]) | Рассчитывает стандартную ошибку среднего значения скользящего окна |
Rolling.rank([method, ascending, pct, ...]) | Рассчитывает ранг скользящего окна |
По умолчанию метки устанавливаются у правого края окна, но доступен ключевой аргумент center, поэтому метки можно установить по центру.
>>> import pandas as pd >>> s = pd.Series(range(10)) >>> s.rolling(window=5).mean() # 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 >>> s.rolling(window=5, center=True).mean() # 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.
df = pd.DataFrame( {"A": [0, 1, 2, 3, 4]}, index=pd.date_range("2024", periods=5, freq="1D") ) >>> df # A # 2024-01-01 0 # 2024-01-02 1 # 2024-01-03 2 # 2024-01-04 3 # 2024-01-05 4 >>> df.rolling("2D", center=False).mean() # A # 2024-01-01 0.0 # 2024-01-02 0.5 # 2024-01-03 1.5 # 2024-01-04 2.5 # 2024-01-05 3.5 >>> df.rolling("2D", center=True).mean() # A # 2024-01-01 0.5 # 2024-01-02 1.5 # 2024-01-03 2.5 # 2024-01-04 3.5 # 2024-01-05 4.0
Включение конечных точек интервала в вычисления скользящего окна может быть задано с помощью аргумента closed:
right, первая точка в окне исключается из расчетов.left, последняя точка в окне исключается из расчетов.both, ни одна точка в окне не будет исключена из вычислений.neither, первая и последняя точки в окне исключаются из вычислений.Например, наличие открытой нужной конечной точки полезно во многих задачах, которые требуют, чтобы текущая информация не переходила обратно в прошлую информацию. Это позволяет скользящему окну вычислять статистику "до этого момента времени", но не включая этот момент времени.
df = pd.DataFrame( {"x": 1}, index=[ pd.Timestamp("20240101 09:00:01"), pd.Timestamp("20240101 09:00:02"), pd.Timestamp("20240101 09:00:03"), pd.Timestamp("20240101 09:00:04"), pd.Timestamp("20240101 09:00:06"), ], ) >>> df["right"] = df.rolling("2s", closed="right").x.sum() # по умолчанию >>> df["both"] = df.rolling("2s", closed="both").x.sum() >>> df["left"] = df.rolling("2s", closed="left").x.sum() >>> df["neither"] = df.rolling("2s", closed="neither").x.sum() >>> df # x right both left neither # 2024-01-01 09:00:01 1 1.0 1.0 NaN NaN # 2024-01-01 09:00:02 1 2.0 2.0 1.0 1.0 # 2024-01-01 09:00:03 1 2.0 3.0 2.0 1.0 # 2024-01-01 09:00:04 1 2.0 3.0 2.0 1.0 # 2024-01-01 09:00:06 1 1.0 2.0 1.0 NaN
Помимо того, что аргумент window принимает целое число int или смещение offset, также может принимать подкласс BaseIndexer, который позволяет пользователю определить собственный метод для расчета границ окна. Подкласс BaseIndexer должен будет определить метод .get_window_bounds, который возвращает кортеж из двух массивов, первый из которых - начальные индексы окон, а второй - конечные индексы окон. Кроме того, num_values, min_ periods, center, close и step будут автоматически передаваться в get_window_bounds, и определенный пользователем метод должен всегда принимать эти аргументы.
Например, если есть следующий DataFrame:
>>> use_expanding = [True, False, True, False, True] >>> df = pd.DataFrame({"values": range(5)}) >>> df # values # 0 0 # 1 1 # 2 2 # 3 3 # 4 4
и нужно использовать расширяющееся окно, где use_expanding имеет значение True, в противном случае окно размером 1, то можно создать следующий подкласс BaseIndexer:
>>> from pandas.api.indexers import BaseIndexer >>> import numpy as np 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 >>> indexer = CustomIndexer(window_size=1, use_expanding=use_expanding) >>> df.rolling(indexer).sum() # values # 0 0.0 # 1 1.0 # 2 3.0 # 3 3.0 # 4 10.0
В примерах ниже можно отметить один подкласс VariableOffsetWindowIndexer, который позволяет выполнять операции скользящего окна по нефиксированному смещению, например BusinessDay.
>>> from pandas.api.indexers import VariableOffsetWindowIndexer >>> df = pd.DataFrame(range(10), index=pd.date_range("2024", periods=10)) >>> offset = pd.offsets.BDay(1) >>> indexer = VariableOffsetWindowIndexer(index=df.index, offset=offset) >>> df # 0 # 2024-01-01 0 # 2024-01-02 1 # 2024-01-03 2 # 2024-01-04 3 # 2024-01-05 4 # 2024-01-06 5 # 2024-01-07 6 # 2024-01-08 7 # 2024-01-09 8 # 2024-01-10 9 >>> df.rolling(indexer).sum() # 0 # 2024-01-01 0.0 # 2024-01-02 1.0 # 2024-01-03 2.0 # 2024-01-04 3.0 # 2024-01-05 4.0 # 2024-01-06 5.0 # 2024-01-07 11.0 # 2024-01-08 18.0 # 2024-01-09 8.0 # 2024-01-10 9.0
Для некоторых задач знание будущего доступно для анализа. Например, это происходит, когда каждая точка данных представляет собой полный временной ряд, считанный из эксперимента, и задача состоит в том, чтобы извлечь основные условия. В этих случаях может быть полезно выполнить прогнозные вычисления скользящего окна. Для этой цели доступен класс FixedForwardWindowIndexer. Этот подкласс BaseIndexer реализует закрытое скользящее окно фиксированной ширины. Его можно использовать его следующим образом:
>>> from pandas.api.indexers import FixedForwardWindowIndexer >>> indexer = FixedForwardWindowIndexer(window_size=2) >>> df.rolling(indexer, min_periods=1).sum() # 0 # 2024-01-01 1.0 # 2024-01-02 3.0 # 2024-01-03 5.0 # 2024-01-04 7.0 # 2024-01-05 9.0 # 2024-01-06 11.0 # 2024-01-07 13.0 # 2024-01-08 15.0 # 2024-01-09 17.0 # 2024-01-10 9.0
Добиться такого поведения также можно используя срез и применяя агрегацию скользящего окна, а затем переворачивая результат, как показано в примере ниже:
df = pd.DataFrame( data=[ [pd.Timestamp("2024-01-01 00:00:00"), 100], [pd.Timestamp("2024-01-01 00:00:01"), 101], [pd.Timestamp("2024-01-01 00:00:03"), 103], [pd.Timestamp("2024-01-01 00:00:04"), 111], ], columns=["time", "value"], ).set_index("time") >>> df # 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 >>> reversed_df = df[::-1].rolling("2s").sum()[::-1] >>> reversed_df # 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() для скользящего окна.Метод apply() принимает дополнительный аргумент func и выполняет общие вычисления скользящего окна. Аргумент func должен представлять собой одну функцию, которая создает одно значение из входных данных ndarray. Аргумент raw указывает, преобразуются ли окна в объекты Series(raw=False) или в объекты ndarray(raw=True).
def mad(x): return np.fabs(x - x.mean()).mean() >>> s = pd.Series(range(10)) >>> s.rolling(window=4).apply(mad, raw=True) # 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() может использовать Numba, если он установлен как необязательная зависимость. Агрегацию apply можно выполнить с помощью Numba, указав аргументы engine='numba' и engine_kwargs (для аргумента raw также должно быть установлено значение True).
Numba будет применяться потенциально в двух случаях:
func является стандартной функцией Python, движок выполнит JIT переданную функцию. func также может быть JIT-функцией.for, в котором метод .apply() применяется к каждому окну.Аргумент engine_kwargs - это словарь ключевых аргументов, которые будут переданы в декоратор numba.jit. Эти аргументы будут применяться как к переданной функции (если это стандартная функция Python), так и к циклу apply for для каждого окна.
Методы cov() и corr() могут вычислять статистику перемещения окна по двум Series или любой комбинации DataFrame/Series или DataFrame/DataFrame. Рассмотрим поведение в каждом случае:
Series: вычисляет статистику для пары.DataFrame/Series: вычисляет статистику для каждого столбца DataFrame с переданной серией, возвращая таким образом DataFrame.DataFrame/DataFrame: по умолчанию вычисляет статистику соответствия имен столбцов, возвращая DataFrame. Если передан аргумент pairwise=True, то вычисляет статистику для каждой пары столбцов, возвращая DataFrame с MultiIndex, значениями которого являются рассматриваемые даты.Например:
df = pd.DataFrame( np.random.randn(10, 4), index=pd.date_range("2020-01-01", periods=10), columns=["A", "B", "C", "D"], ) >>> df = df.cumsum() >>> df2 = df[:4] >>> df2.rolling(window=2).corr(df2["B"]) # 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 выдаст MultiIndex, индексом которого являются рассматриваемые даты. В случае одного аргумента DataFrame аргумент pairwise может быть даже опущен:
Примечание. Пропущенные значения игнорируются, и каждая запись вычисляется с использованием попарных полных наблюдений.
Предполагая, что недостающие данные отсутствуют случайным образом, это приводит к оценке ковариационной матрицы, которая является несмещенной. Однако для многих приложений эта оценка может оказаться неприемлемой, т.к. не гарантируется, что оцененная ковариационная матрица будет положительно полуопределенной. Это может привести к тому, что оценочные корреляции будут иметь абсолютные значения, превышающие единицу, и/или необратимую ковариационную матрицу.
covs = ( df[["B", "C", "D"]] .rolling(window=4) .cov(df[["A", "B", "C"]], pairwise=True) ) >>> covs # 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 # C NaN NaN NaN # 2020-01-03 A NaN NaN NaN # B NaN NaN NaN # C NaN NaN NaN # 2020-01-04 A 0.570713 -0.029821 0.382259 # B 2.987649 0.168730 1.743468 # C 0.168730 0.394460 0.140073 # 2020-01-05 A 0.401121 -0.105891 0.298607 # B 1.534023 -0.455098 0.932260 # C -0.455098 0.141586 -0.257665 # 2020-01-06 A 0.328475 -0.095758 -0.375328 # B 0.590550 -0.181626 -0.778261 # C -0.181626 0.063990 0.321139 # 2020-01-07 A 0.090902 -0.168251 -0.280190 # B 1.472003 0.082712 -1.158504 # C 0.082712 0.121967 0.081700 # 2020-01-08 A -0.211723 -0.278281 -0.096997 # B 1.085040 0.875290 -0.736516 # C 0.875290 1.261084 -0.226900 # 2020-01-09 A -0.243264 0.321226 0.203174 # B 0.326425 0.544196 0.084798 # C 0.544196 1.523514 0.308076 # 2020-01-10 A -0.181656 0.360408 0.278193 # B 0.068862 0.034841 -0.032263 # C 0.034841 0.767510 -0.066379
Аргумент win_type в .rolling() генерирует взвешенные окна, которые обычно используются при фильтрации и спектральной оценке. Аргумент win_type должен быть строкой, соответствующей оконной функции scipy.signal. Для использования этих окон должен быть установлен Scipy, а дополнительные аргументы, которые принимают оконные методы Scipy, должны быть указаны в функции агрегации.
>>> s = pd.Series(range(10)) >>> s.rolling(window=5).mean() # 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 >>> s.rolling(window=5, win_type="triang").mean() # 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 # Дополнительные аргументы `Scipy`, передаваемые в функцию агрегации >>> s.rolling(window=5, win_type="gaussian").mean(std=0.1) # 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
| Функция | Что делает |
Window.mean([numeric_only]) | Вычисляет среднее значение скользящего взвешенного окна |
Window.sum([numeric_only]) | Вычисляет сумму скользящего взвешенного окна |
Window.var([ddof, numeric_only]) | Вычисляет дисперсию скользящего взвешенного окна |
Window.std([ddof, numeric_only]) | Вычисляет стандартное отклонение скользящего взвешенного окна |
DataFrame.rolling()>>> import pandas as pd >>> import numpy as np >>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]}) >>> df # B # 0 0.0 # 1 1.0 # 2 2.0 # 3 NaN # 4 4.0
windowСкользящая сумма с длиной окна в 2 наблюдения.
>>> df.rolling(2).sum() # B # 0 NaN # 1 1.0 # 2 3.0 # 3 NaN # 4 NaN
Скользящая сумма с интервалом в 2 секунды.
df_time = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]}, index=[pd.Timestamp('20240101 09:00:00'), pd.Timestamp('20240101 09:00:02'), pd.Timestamp('20240101 09:00:03'), pd.Timestamp('20240101 09:00:05'), pd.Timestamp('20240101 09:00:06')]) >>> df_time # B # 2024-01-01 09:00:00 0.0 # 2024-01-01 09:00:02 1.0 # 2024-01-01 09:00:03 2.0 # 2024-01-01 09:00:05 NaN # 2024-01-01 09:00:06 4.0 >>> df_time.rolling('2s').sum() # B # 2024-01-01 09:00:00 0.0 # 2024-01-01 09:00:02 1.0 # 2024-01-01 09:00:03 3.0 # 2024-01-01 09:00:05 NaN # 2024-01-01 09:00:06 4.0
Скользящая сумма с окнами, ориентированными вперед, с 2 наблюдениями.
>>> indexer = pd.api.indexers.FixedForwardWindowIndexer(window_size=2) >>> df.rolling(window=indexer, min_periods=1).sum() # B # 0 1.0 # 1 3.0 # 2 2.0 # 3 4.0 # 4 4.0
min_periodsСкользящая сумма с длиной окна в 2 наблюдения, но для вычисления значения требуется минимум 1 наблюдение.
>>> df.rolling(2, min_periods=1).sum() # B # 0 0.0 # 1 1.0 # 2 3.0 # 3 2.0 # 4 4.0
centerСкользящая сумма с результатом, присвоенным центру индекса окна.
>>> df.rolling(3, min_periods=1, center=True).sum() # B # 0 1.0 # 1 3.0 # 2 3.0 # 3 6.0 # 4 4.0 >>> df.rolling(3, min_periods=1, center=False).sum() # B # 0 0.0 # 1 1.0 # 2 3.0 # 3 3.0 # 4 6.0
stepСкользящая сумма с длиной окна в 2 наблюдения, минимум 1 наблюдение для вычисления значения и шагом в 2.
>>> df.rolling(2, min_periods=1, step=2).sum() # B # 0 0.0 # 2 3.0 # 4 4.0
win_typeСкользящая сумма с длиной окна 2, используя тип окна Scipy 'gaussian'.
>>> df.rolling(2, win_type='gaussian').sum(std=3) # B # 0 NaN # 1 0.986207 # 2 2.958621 # 3 NaN # 4 NaN
onСкользящая сумма с окном продолжительностью 2 дня
df = pd.DataFrame({ 'A': [pd.to_datetime('2024-01-01'), pd.to_datetime('2024-01-01'), pd.to_datetime('2024-01-02'),], 'B': [1, 2, 3], }, index=pd.date_range('2020', periods=3)) >>> df # A B # 2020-01-01 2024-01-01 1 # 2020-01-02 2024-01-01 2 # 2020-01-03 2024-01-02 3 >>> df.rolling('2D', on='A').sum() # A B # 2020-01-01 2024-01-01 1.0 # 2020-01-02 2024-01-01 3.0 # 2020-01-03 2024-01-02 6.0