Нашли ошибку?
Скачать чистый
урок 1 из 4≈ 20 минупражнения: 0 из 3

Даты как индекс

Временной ряд — значения, привязанные к датам. Делаем даты индексом и выбираем день, месяц или период строкой: loc["2025-03"]. Что делать с днями, которых нет в данных, — pd.date_range и reindex.

Python запустится при первом выполнении

Продажи по дням, температура по дням, курс валюты по дням — всё это временные ряды: значения, у каждого из которых есть момент времени. Главный вопрос к таким данным — «что было в таком-то периоде». Отвечать на него проще всего, когда даты стоят в индексе.

Даты в индексе

Возьмём погоду в Москве и сделаем дату индексом:

[ ]Дата — метка строки
import pandas as pd

weather = pd.read_csv("data/weather.csv", parse_dates=["date"])
moscow = weather[weather["city"] == "Москва"].set_index("date").drop(columns=["city"])
moscow.head(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
temp_min temp_max precip_mm wind_ms
date
2025-01-01 -9.9 -3.2 0.0 1.9
2025-01-02 -9.7 -3.2 0.0 2.3
2025-01-03 -9.0 -3.7 0.0 5.2

Индекс из дат — особый тип, DatetimeIndex:

[ ]Тип индекса и границы ряда
print(type(moscow.index))
print(moscow.index.min(), moscow.index.max())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
<class 'pandas.DatetimeIndex'>
2025-01-01 00:00:00 2025-12-31 00:00:00

С ним loc понимает даты, записанные строкой. Один день:

[ ]9 мая
moscow.loc["2025-05-09"]
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
temp_min     10.7
temp_max     15.3
precip_mm    10.2
wind_ms       3.3
Name: 2025-05-09 00:00:00, dtype: float64

Часть даты вместо целой

Если указать не полную дату, а только год и месяц, loc вернёт все дни этого месяца:

[ ]Весь май одной строкой
moscow.loc["2025-05"].head(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
temp_min temp_max precip_mm wind_ms
date
2025-05-01 8.9 13.3 0.0 2.2
2025-05-02 12.0 16.3 14.7 5.1
2025-05-03 6.8 12.5 0.0 2.2
[ ]31 день и средняя температура мая
print(len(moscow.loc["2025-05"]))
print(moscow.loc["2025-05", "temp_max"].mean())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
31
15.545161290322582

Сравните с двумя сравнениями дат через & — запись loc["2025-05"] короче и читается как обычная речь. Так же работает год: loc["2025"].

Вопрос

Сколько строк вернёт moscow.loc["2025-02"]?

[ ]Упражнение 1Март решено

Сохраните в march мартовские дни таблицы moscow. В march_max — наибольшее значение temp_max за март, в march_warmest — дату этого дня.

march = ...
march_max = ...
march_warmest = ...

idxmax вернул дату — ещё одно удобство дат в индексе: «когда был максимум» получается сразу.

Срезы по датам

Период задают срезом. Как у любого среза по меткам, обе границы входят:

[ ]Три последних дня года
moscow.loc["2025-12-29":"2025-12-31"]
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
temp_min temp_max precip_mm wind_ms
date
2025-12-29 -10.9 -6.4 0.0 0.8
2025-12-30 -10.1 -4.8 4.1 2.4
2025-12-31 -7.1 -2.0 0.0 3.0

Границами могут быть и месяцы: от начала первого до конца последнего.

[ ]Весна: март — май
spring = moscow.loc["2025-03":"2025-05"]
print(len(spring))
print(spring.index.min(), spring.index.max())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
92
2025-03-01 00:00:00 2025-05-31 00:00:00
[ ]Упражнение 2Лето и майские праздники решено

Сохраните в summer дни с июня по август, в summer_mean — средний temp_max за лето. В holidays сохраните temp_max с 1 по 11 мая включительно, в holidays_mean — его среднее.

summer = ...
summer_mean = ...
holidays = ...
holidays_mean = ...

Части даты у индекса

У столбца дат части доставали через .dt. У индекса из дат приставка не нужна — атрибуты есть у него самого:

[ ]Месяц, день недели и число выходных в году
print(moscow.index.month[:3].tolist())
print(moscow.index.dayofweek[:3].tolist())
print((moscow.index.dayofweek >= 5).sum())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
[1, 1, 1]
[2, 3, 4]
104

Дни, которых нет

Соберём ряд выручки магазина по дням:

[ ]Выручка по дням
orders = pd.read_csv("data/shop_orders.csv", parse_dates=["date"])
orders["revenue"] = orders["price"] * orders["quantity"]
daily = orders.groupby("date")["revenue"].sum()
print(len(daily))
daily.head(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
358
date
2025-01-01     9060
2025-01-02    10450
2025-01-03    18620
Name: revenue, dtype: int64

Дней в году 365, а в ряду — 358. В семь дней покупок не было, и в таблице продаж этих дат просто нет. Группировка не может создать строку для даты, которая не встретилась:

[ ]2 июля продаж не былоошибка: KeyError
daily.loc["2025-07-02"]
Ошибкасохранённый · нажмите «Выполнить», чтобы получить свой
KeyError: '2025-07-02'

Для временного ряда это проблема. Среднее по 358 дням завышает «выручку в день»; неделя из шести дней сравнивается с неделей из семи; на графике пропавший день незаметен. Нужен ряд с полным календарём.

Функция pd.date_range создаёт набор дат от начала до конца — по дню на каждую:

[ ]Пять дней подряд
pd.date_range("2025-01-01", "2025-01-05")
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
DatetimeIndex(['2025-01-01', '2025-01-02', '2025-01-03', '2025-01-04',
               '2025-01-05'],
              dtype='datetime64[us]', freq='D')

freq='D' в конце значит «шаг — один день» (day).

А reindex из прошлого модуля приводит ряд к этому набору: для дат, которых не было, ставит указанное значение.

[ ]Упражнение 3Полный календарь решено

Сохраните в calendar все дни 2025 года. Приведите daily к этому календарю и сохраните в sales: в дни без продаж — ноль. В empty_days сохраните число дней без продаж, в december — выручку декабря.

calendar = ...
sales = ...
empty_days = ...
december = ...

Индекс должен быть отсортирован

Срезы по датам рассчитаны на то, что даты идут по порядку. Если строки перемешаны, срез не сработает:

[ ]Срез по неотсортированным датамошибка: KeyError
shuffled = moscow.sort_values("temp_max")
shuffled.loc["2025-03-01":"2025-03-05"]
Ошибкасохранённый · нажмите «Выполнить», чтобы получить свой
KeyError: 'Value based partial slicing on non-monotonic DatetimeIndexes with non-existing keys is not allowed.'

Итоги

  • set_index("date") — даты в индексе; дальше loc понимает даты строкой.
  • loc["2025-03"] — весь месяц, loc["2025"] — весь год, loc["2025-06":"2025-08"] — период, обе границы входят.
  • У индекса из дат есть index.month, index.dayofweek и другие части — без .dt.
  • В данных может не быть строк для «пустых» дней: pd.date_range и reindex восстанавливают календарь.
  • Срезы по датам требуют отсортированного индекса.

Подробнее в справочнике

Нашли ошибку?