Коротко
rolling(n)— окно изnпоследних строк; дальше агрегация:.mean(),.sum(),.max(),.agg([...]). Первыеn − 1значений —NaN.min_periods— сколько значений достаточно для расчёта;center=True— окно вокруг точки, а не до неё.rolling("7D")при датах в индексе — окно по времени: учитываются строки за последние 7 дней, даже если дней с данными меньше.expanding()— окно от начала до текущей строки: максимум или среднее «на сегодняшний день».ewm(span=…)— экспоненциальное сглаживание: недавние значения весят больше.
Синтаксис
s.rolling(window, min_periods=None, center=False).mean()
s.rolling("7D").sum() # окно по времени
s.expanding(min_periods=1).max()
s.ewm(span=None, alpha=None, adjust=True).mean()
df.groupby("key")["col"].rolling(3).mean()Параметры
ПараметрЧто делаетПо умолчанию
windowразмер окна: число строк или длительность вроде "7D", "12h"обязательныйmin_periodsминимум значений в окне для результатаразмер окнаcenterокно с центром в текущей строкеFalsespan / alphaв ewm: скорость забывания; alpha = 2 / (span + 1)обязательныйadjustв ewm: False — рекуррентная формула s_t = α·x_t + (1 − α)·s_{t−1}TrueПримеры
days = pd.date_range("2024-03-01", periods=8, freq="D")
visits = pd.Series([120, 135, 128, 150, 162, 90, 85, 140], index=days, name="visits")visits.rolling(3).mean().round(1) # среднее за 3 дня; первые 2 — NaNВывод
2024-03-01 NaN 2024-03-02 NaN 2024-03-03 127.7 2024-03-04 137.7 2024-03-05 146.7 2024-03-06 134.0 2024-03-07 112.3 2024-03-08 105.0 Freq: D, Name: visits, dtype: float64
visits.rolling(3, min_periods=1).mean().round(1) # считать, даже если данных меньше окнаВывод
2024-03-01 120.0 2024-03-02 127.5 2024-03-03 127.7 2024-03-04 137.7 2024-03-05 146.7 2024-03-06 134.0 2024-03-07 112.3 2024-03-08 105.0 Freq: D, Name: visits, dtype: float64
visits.rolling(3, center=True).mean().round(1) # окно вокруг дня, а не до негоВывод
2024-03-01 NaN 2024-03-02 127.7 2024-03-03 137.7 2024-03-04 146.7 2024-03-05 134.0 2024-03-06 112.3 2024-03-07 105.0 2024-03-08 NaN Freq: D, Name: visits, dtype: float64
gaps = visits.drop(visits.index[[2, 3]]) # два дня без данных
gaps.rolling("3D").sum() # окно по времени, а не по числу строкВывод
2024-03-01 120.0 2024-03-02 255.0 2024-03-05 162.0 2024-03-06 252.0 2024-03-07 337.0 2024-03-08 315.0 Name: visits, dtype: float64
pd.DataFrame({"visits": visits, "max_so_far": visits.expanding().max(), "mean_so_far": visits.expanding().mean().round(1)})Вывод
visits max_so_far mean_so_far 2024-03-01 120 120.0 120.0 2024-03-02 135 135.0 127.5 2024-03-03 128 135.0 127.7 2024-03-04 150 150.0 133.2 2024-03-05 162 162.0 139.0 2024-03-06 90 162.0 130.8 2024-03-07 85 162.0 124.3 2024-03-08 140 162.0 126.2
visits.ewm(span=3, adjust=False).mean().round(1) # недавние дни весят большеВывод
2024-03-01 120.0 2024-03-02 127.5 2024-03-03 127.8 2024-03-04 138.9 2024-03-05 150.4 2024-03-06 120.2 2024-03-07 102.6 2024-03-08 121.3 Freq: D, Name: visits, dtype: float64
visits.rolling(3).agg(["min", "max"])Вывод
min max 2024-03-01 NaN NaN 2024-03-02 NaN NaN 2024-03-03 120.0 135.0 2024-03-04 128.0 150.0 2024-03-05 128.0 162.0 2024-03-06 90.0 162.0 2024-03-07 85.0 162.0 2024-03-08 85.0 140.0
Подводные камни
Окна через границы групп
Если в одной таблице несколько рядов, простое rolling смешает их. Группируйте перед окном — результат получит MultiIndex (группа, исходная метка).
sales = pd.DataFrame({
"shop": ["A", "A", "A", "B", "B", "B"],
"cups": [10, 20, 30, 5, 15, 25],
})
sales.groupby("shop")["cups"].rolling(2).mean() # окна не переходят через границу группВывод
shop
A 0 NaN
1 15.0
2 25.0
B 3 NaN
4 10.0
5 20.0
Name: cups, dtype: float64Окно по строкам при пропусках в датах
rolling(3) берёт 3 строки, даже если между ними неделя. Для «последних 3 дней» нужен rolling("3D") и даты в индексе, как в примере выше.