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

Даты

Превратить текст в даты: pd.to_datetime и parse_dates; достать месяц, день и день недели через .dt; сравнить и вычесть даты; формат ДД.ММ.ГГГГ и выгрузка с датами в разных форматах.

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

Дата в файле — это текст. Пока она остаётся текстом, с ней почти ничего нельзя сделать: ни взять месяц, ни узнать день недели, ни посчитать, сколько дней прошло. Сначала её нужно превратить в настоящую дату.

Из текста в дату

[ ]Даты заказов — пока текст
import pandas as pd

orders = pd.read_csv("data/shop_orders.csv")
orders["date"].head(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
0    2025-01-01
1    2025-01-01
2    2025-01-01
Name: date, dtype: str

Тип str. Функция pd.to_datetime превращает текст в даты:

[ ]Настоящие даты
orders["date"] = pd.to_datetime(orders["date"])
orders["date"].head(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
0   2025-01-01
1   2025-01-01
2   2025-01-01
Name: date, dtype: datetime64[us]

Выглядит так же, но тип теперь datetime64 — дата и время. Буквы в скобках (us) — точность хранения, на неё можно не смотреть.

Части даты: .dt

Как у текста есть приставка .str, у дат есть .dt. Через неё достают части даты:

[ ]Год, месяц, день
print(orders["date"].dt.year.head(3).tolist())
print(orders["date"].dt.month.tail(3).tolist())
print(orders["date"].dt.day.tail(3).tolist())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
[2025, 2025, 2025]
[12, 12, 12]
[31, 31, 31]
[ ]Упражнение 1Месяц покупки решено

Добавьте в orders столбец month — номер месяца покупки. Сохраните в december строки декабря.

# добавьте в orders столбец month
december = ...

День недели возвращает .dt.dayofweek — числом, считая с нуля: понедельник — 0, воскресенье — 6.

[ ]1 января 2025 года — среда
orders["weekday"] = orders["date"].dt.dayofweek
orders[["date", "weekday"]].head(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
date weekday
0 2025-01-01 2
1 2025-01-01 2
2 2025-01-01 2

Названия дней pandas по умолчанию выдаёт по-английски (.dt.day_name()), поэтому русские подписи проще получить словарём и методом map:

[ ]Покупки по дням недели
names = {0: "пн", 1: "вт", 2: "ср", 3: "чт", 4: "пт", 5: "сб", 6: "вс"}
orders["weekday"].map(names).value_counts()
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
weekday
вс    425
сб    387
вт    339
пт    333
пн    325
чт    320
ср    319
Name: count, dtype: int64
[ ]Упражнение 2Выходные решено

Сохраните в is_weekend маску покупок, сделанных в субботу или воскресенье, а в weekend_share — долю таких покупок.

is_weekend = ...
weekend_share = ...

На два выходных дня из семи (29 % недели) приходится треть покупок — в выходные покупают чуть активнее.

Пояснение с ответом появится, когда решите упражнение «Выходные».

Сравнение и разность

Даты сравниваются между собой и со строкой-датой в формате ГГГГ-ММ-ДД:

[ ]Покупки с 25 декабря и границы периода
print((orders["date"] >= "2025-12-25").sum())
print(orders["date"].min(), orders["date"].max())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
70
2025-01-01 00:00:00 2025-12-31 00:00:00

Отдельное значение даты называется Timestamp («метка времени»); время 00:00:00 — полночь, потому что в наших данных времени нет.

Даты можно вычитать. Разность — особый тип, длительность (Timedelta); число дней в ней — атрибут days:

[ ]От первой покупки до последней
span = orders["date"].max() - orders["date"].min()
print(span)
print(span.days)
print(pd.to_datetime("2025-12-31") - orders["date"].max())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
364 days 00:00:00
364
0 days 00:00:00

В третьей строке одна дата получена прямо из строки: pd.to_datetime("2025-12-31") работает и с отдельным значением. Получилось 0 days: последняя покупка в таблице — как раз 31 декабря.

Разность столбца дат и одной даты — столбец длительностей. Число дней из него достаёт .dt.days:

[ ]Сколько дней прошло с начала года
since_start = orders["date"] - orders["date"].min()
print(since_start.tail(2))
print(since_start.dt.days.tail(2).tolist())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
2446   364 days
2447   364 days
Name: date, dtype: timedelta64[us]
[364, 364]

Превратить столбцы в даты можно прямо при чтении файла — параметром parse_dates:

[ ]Даты сразу при загрузке
fresh = pd.read_csv("data/shop_orders.csv", parse_dates=["date"])
fresh.dtypes.head(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
order_id                int64
date           datetime64[us]
customer_id               str
dtype: object

Другой порядок: день.месяц.год

Формат 2025-01-31 pandas понимает сам. Но в российских выгрузках дата чаще выглядит как 31.01.2025. Для таких случаев формат указывают явно — параметром format:

[ ]Дата обновления в прайсе поставщика
supplier = pd.read_csv("data/supplier_prices.csv")
print(supplier["updated"].head(3).tolist())
pd.to_datetime(supplier["updated"], format="%d.%m.%Y").head(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
['15.01.2026', '13.01.2026', '12.01.2026']
0   2026-01-15
1   2026-01-13
2   2026-01-12
Name: updated, dtype: datetime64[us]

В строке формата %d — день, %m — месяц, %Y — год из четырёх цифр; остальные символы (точки, дефисы, косые черты) пишутся как есть.

Что будет, если формат не указать? pandas определит его сам — по первому значению столбца. Если день там не больше 12, он примет его за месяц, по-американски, — и так разберёт весь столбец.

[ ]5 и 6 января стали 1 мая и 1 июня
guessed = pd.to_datetime(pd.Series(["05.01.2026", "06.01.2026"]))
print(guessed.dt.month.tolist())
print(guessed.dt.day.tolist())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
[5, 6]
[1, 1]
Вопрос

Какой месяц получится: pd.to_datetime(pd.Series(["03.02.2026"]), format="%d.%m.%Y").dt.month?

[ ]Упражнение 3Свежесть прайса решено

Сохраните в updated столбец updated таблицы supplier как даты. В last_update — самую позднюю дату обновления. В age_days — сколько дней прошло от обновления каждой строки до 1 февраля 2026 года (числами).

updated = ...
last_update = ...
age_days = ...

Несколько форматов в одном столбце

Журнал возвратов «Кофейной лавки» вели разные люди — и даты записывали кто как:

[ ]Даты возвратов
returns = pd.read_csv("data/returns_raw.csv")
returns["return_date"].head(6).tolist()
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
['17.01.2025', '2025-01-19', '21.01.2025', '2025-01-15', '24.01.2025', '15.01.2025']

Три формата вперемешку: 2025-01-19, 17.01.2025 и 15/03/2025. Прочитать такой столбец одним вызовом не получится — pandas определяет формат по первому значению и спотыкается о второе (а заодно предупреждает, что формат с днём впереди он угадал):

[ ]Формат не подошёлошибка: ValueError
pd.to_datetime(returns["return_date"])
Ошибкасохранённый · нажмите «Выполнить», чтобы получить свой
UserWarning: Parsing dates in %d.%m.%Y format when dayfirst=False (the default) was specified. Pass `dayfirst=True` or specify a format to silence this warning.
ValueError: time data "2025-01-19" doesn't match format "%d.%m.%Y". You might want to try:
    - passing `format` if your strings have a consistent format;
    - passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
    - passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.

Надёжный способ — разобрать каждый формат отдельно. С errors="coerce" значения другого формата становятся пропусками. У дат пропуск обозначается NaT (not a time):

[ ]Только формат ГГГГ-ММ-ДД
iso = pd.to_datetime(returns["return_date"], format="%Y-%m-%d", errors="coerce")
print(iso.head(4))
print(iso.notna().sum(), "из", len(iso))
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
0          NaT
1   2025-01-19
2          NaT
3   2025-01-15
Name: return_date, dtype: datetime64[us]
91 из 163

Распознана 91 дата из 163. Остальные разберём так же, а потом соберём три результата в один: fillna принимает не только одно значение, но и другой Series — и берёт из него значения для пропусков.

[ ]Упражнение 4Собрать даты решено

Сохраните в dotted даты формата ДД.ММ.ГГГГ, в slashed — формата ДД/ММ/ГГГГ (остальные значения — пропуски). Добавьте в returns столбец date: даты из iso, а где там пропуск — из dotted или slashed. В n_unparsed сохраните число оставшихся пропусков в date.

dotted = ...
slashed = ...
# добавьте в returns столбец date
n_unparsed = ...

У to_datetime есть режим format="mixed", который угадывает формат каждого значения отдельно. Он соблазнительно короток — и ошибается молча:

[ ]format="mixed" против разбора по форматам
mixed = pd.to_datetime(returns["return_date"], format="mixed", dayfirst=True)
wrong = mixed != returns["date"]
print(wrong.sum())
print(returns.loc[wrong, "return_date"].head(3).tolist())
print(mixed[wrong].head(3).tolist())
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
28
['2025-02-04', '2025-02-01', '2025-06-11']
[Timestamp('2025-04-02 00:00:00'), Timestamp('2025-01-02 00:00:00'), Timestamp('2025-11-06 00:00:00')]
Вывод появится, когда решите упражнение «Собрать даты», — иначе он подскажет ответ. Ячейку можно и выполнить.

28 дат из 163 прочитаны неверно: с dayfirst=True («сначала день») дата 2025-02-04 превратилась во 2 апреля. Без dayfirst пострадали бы даты с точками и с косой чертой. Когда форматы известны, разбирайте каждый явно — и проверяйте, что пропусков не осталось.

Пояснение с ответом появится, когда решите упражнение «Собрать даты».

Итоги

  • pd.to_datetime(столбец) или parse_dates=[...] при чтении — текст в даты.
  • .dt.year, .dt.month, .dt.day, .dt.dayofweek (понедельник — 0) — части даты.
  • Даты сравниваются со строкой "ГГГГ-ММ-ДД"; разность дат — длительность, число дней — .dt.days.
  • Формат «день.месяц.год» указывайте явно: format="%d.%m.%Y", иначе день и месяц могут поменяться местами.
  • Несколько форматов — разобрать каждый с errors="coerce" и собрать через fillna.

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

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