Дневные данные слишком подробны: 365 чисел не окинуть взглядом, и в них много случайных колебаний. Обычно их укрупняют — смотрят выручку по неделям или месяцам. Это та же группировка, только группы — отрезки времени. Для неё есть отдельный метод, который знает календарь.
import pandas as pd
orders = pd.read_csv("data/shop_orders.csv", parse_dates=["date"])
orders["revenue"] = orders["price"] * orders["quantity"]
daily = orders.groupby("date")["revenue"].sum()
calendar = pd.date_range("2025-01-01", "2025-12-31")
sales = daily.reindex(calendar, fill_value=0)
order_count = orders.groupby("date")["order_id"].nunique().reindex(calendar, fill_value=0).rename("orders")
print(sales.head(3))
order_count.head(3)2025-01-01 9060 2025-01-02 10450 2025-01-03 18620 Freq: D, Name: revenue, dtype: int64
2025-01-01 3 2025-01-02 4 2025-01-03 5 Freq: D, Name: orders, dtype: int64
Примеры в уроке — на числе заказов, упражнения — на выручке. У ряда заказов имя orders (метод rename
у Series меняет имя ряда): без него в выводе стояло бы Name: order_id — имя столбца, из которого ряд посчитан.
resample
Метод resample («пересчитать частоту») работает как groupby: сначала делит ряд на периоды, потом к каждому
применяется итог. Период задают строкой-обозначением:
order_count.resample("ME").sum().head(4)2025-01-31 143 2025-02-28 138 2025-03-31 134 2025-04-30 133 Freq: ME, Name: orders, dtype: int64
Строка Freq: ME внизу — частота получившегося ряда.
"ME" значит «month end» — конец месяца: каждое значение подписано последним днём своего месяца.
Если удобнее подписывать началом, есть "MS" («month start»):
order_count.resample("MS").sum().head(4)2025-01-01 143 2025-02-01 138 2025-03-01 134 2025-04-01 133 Freq: MS, Name: orders, dtype: int64
Суммы одинаковые — отличается только метка.
Частые обозначения:
| Обозначение | Период |
|---|---|
"D" |
день |
"W" |
неделя |
"ME", "MS" |
месяц (метка — конец или начало) |
"QE", "QS" |
квартал |
"YE", "YS" |
год |
В старых учебниках и ответах в интернете месяц обозначен одной буквой "M". В pandas 3 это ошибка:
order_count.resample("M").sum()ValueError: Invalid frequency: M. Failed to parse with error message: ValueError("'M' is no longer supported for offsets. Please use 'ME' instead.")Метка периода — обычная дата. У отдельной даты части достают без приставки .dt: .month — номер месяца,
.quarter — номер квартала.
quarter = order_count.resample("QE").sum().index[1]
print(quarter)
print(quarter.month, quarter.quarter)2025-06-30 00:00:00 6 2
Сохраните в monthly выручку по месяцам. В best_month сохраните номер месяца с наибольшей выручкой,
в worst_month — с наименьшей.
sales.resample("ME").sum().monthly.idxmax() вернёт дату — метку месяца; её номер — атрибут .month.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
monthly = ...
best_month = ...
worst_month = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
monthly = sales.resample("ME").sum()
best_month = monthly.idxmax().month
worst_month = monthly.idxmin().monthНедели и их края
weekly_orders = order_count.resample("W").sum()
print(len(weekly_orders))
weekly_orders.head(3)53
2025-01-05 18 2025-01-12 39 2025-01-19 40 Freq: W-SUN, Name: orders, dtype: int64
Недель получилось 53, и подписаны они воскресеньями: "W" считает неделю с понедельника по воскресенье
и ставит метку на её конец. Об этом и говорит Freq: W-SUN — «недели, которые кончаются в воскресенье»
(Sunday). Но год не обязан начинаться в понедельник. Сколько дней попало в первую, вторую
и последнюю неделю, покажет count:
print(order_count.resample("W").count().iloc[[0, 1, -1]])2025-01-05 5 2025-01-12 7 2026-01-04 3 Name: orders, dtype: int64
1 января 2025 года — среда, поэтому в первой неделе пять дней. Последняя неделя заканчивается уже в 2026 году, и от неё в наших данных три дня.
two_weeks = pd.Series(1, index=pd.date_range("2025-03-03", "2025-03-16"))
print(two_weeks.resample("W").sum().tolist())3 марта 2025 года — понедельник, 16 марта — воскресенье. Что выведет этот код?
Сохраните в weekly выручку по неделям. В full_weeks — только полные недели: без первой и последней.
В best_week_end сохраните дату окончания недели с наибольшей выручкой, в week_mean — среднюю выручку
полной недели.
weekly.iloc[1:-1].full_weeks.idxmax() и full_weeks.mean().Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
weekly = ...
full_weeks = ...
best_week_end = ...
week_mean = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
weekly = sales.resample("W").sum()
full_weeks = weekly.iloc[1:-1]
best_week_end = full_weeks.idxmax()
week_mean = full_weeks.mean()Не только сумма и не только Series
После resample доступны те же итоги, что после groupby: sum, mean, max, min, count, agg.
А если пересчитывать таблицу, столбец выбирают так же — в скобках:
weather = pd.read_csv("data/weather.csv", parse_dates=["date"])
moscow = weather[weather["city"] == "Москва"].set_index("date")
moscow.resample("ME")["temp_max"].mean().round(1).head(3)date 2025-01-31 -3.8 2025-02-28 -4.7 2025-03-31 1.6 Freq: ME, Name: temp_max, dtype: float64
Выбор итога зависит от смысла: выручку и осадки складывают, температуру усредняют.
Сохраните в climate таблицу по месяцам для Москвы: temp — средний temp_max, rain — сумма precip_mm;
округление до одного знака. В rainiest сохраните номер самого дождливого месяца.
moscow.resample("ME").agg(temp=("temp_max", "mean"), rain=("precip_mm", "sum")).climate["rain"].idxmax().month.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
climate = ...
rainiest = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
climate = moscow.resample("ME").agg(temp=("temp_max", "mean"), rain=("precip_mm", "sum")).round(1)
rainiest = climate["rain"].idxmax().monthКварталы и пустые периоды
order_count.resample("QE").sum()2025-03-31 415 2025-06-30 401 2025-09-30 302 2025-12-31 458 Freq: QE-DEC, Name: orders, dtype: int64
Freq: QE-DEC — кварталы года, который кончается в декабре (December), то есть обычного календарного.
У resample есть важное отличие от groupby: он создаёт все периоды от начала до конца ряда — даже те,
в которые не попало ни одного значения. Возьмём исходный ряд daily, где дней без продаж просто нет:
print(daily.resample("D").sum().loc["2025-07-01":"2025-07-03"])
print(daily.resample("D").mean().loc["2025-07-01":"2025-07-03"])date 2025-07-01 5370 2025-07-02 0 2025-07-03 3110 Freq: D, Name: revenue, dtype: int64 date 2025-07-01 5370.0 2025-07-02 NaN 2025-07-03 3110.0 Freq: D, Name: revenue, dtype: float64
resample("D") вернул в ряд пропавший день. Сумма пустого периода — ноль, а среднее — NaN: усреднять
нечего. Так resample("D").sum() восстанавливает пропущенные дни внутри ряда — от первой его даты до последней.
Дни до первой и после последней даты он не достроит; для этого нужен reindex с полным календарём.
Сохраните в quarterly выручку по кварталам, в quarter_share — долю каждого квартала в выручке года
(три знака). В weak_quarter сохраните номер самого слабого квартала.
sales.resample("QE").sum(); доли — делением на сумму..quarter — номер квартала: quarter_share.idxmin().quarter.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
quarterly = ...
quarter_share = ...
weak_quarter = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
quarterly = sales.resample("QE").sum()
quarter_share = (quarterly / quarterly.sum()).round(3)
weak_quarter = quarter_share.idxmin().quarterИтоги
ряд.resample("частота").sum()— итог по периодам; нужен индекс из дат."W"— неделя,"ME"/"MS"— месяц,"QE"— квартал;"M"в pandas 3 не работает.- Сумму — для того, что накапливается (выручка, осадки); среднее — для уровня (температура, цена).
- Периоды по краям могут быть неполными — не сравнивайте их с полными.
- Пустые периоды
resampleсоздаёт сам: сумма — ноль, среднее —NaN.