Дата в файле — это текст. Пока она остаётся текстом, с ней почти ничего нельзя сделать: ни взять месяц, ни узнать день недели, ни посчитать, сколько дней прошло. Сначала её нужно превратить в настоящую дату.
Из текста в дату
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]
Добавьте в orders столбец month — номер месяца покупки. Сохраните в december строки декабря.
orders["date"].dt.month — числа от 1 до 12.orders["month"] == 12.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
# добавьте в orders столбец month
december = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
orders["month"] = orders["date"].dt.month
december = orders[orders["month"] == 12]День недели возвращает .dt.dayofweek — числом, считая с нуля: понедельник — 0, воскресенье — 6.
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
Сохраните в is_weekend маску покупок, сделанных в субботу или воскресенье, а в weekend_share — долю
таких покупок.
orders["weekday"] >= 5.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
is_weekend = ...
weekend_share = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
is_weekend = orders["weekday"] >= 5
weekend_share = is_weekend.mean()На два выходных дня из семи (29 % недели) приходится треть покупок — в выходные покупают чуть активнее.
Сравнение и разность
Даты сравниваются между собой и со строкой-датой в формате ГГГГ-ММ-ДД:
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, он примет его за месяц, по-американски, — и так разберёт весь столбец.
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?
%d — день, вторым %m — месяц. Значит, 03 — день, 02 — месяц: третье февраля.Сохраните в updated столбец updated таблицы supplier как даты. В last_update — самую позднюю дату
обновления. В age_days — сколько дней прошло от обновления каждой строки до 1 февраля 2026 года (числами).
pd.to_datetime(supplier["updated"], format="%d.%m.%Y"); самая поздняя дата — max().pd.to_datetime("2026-02-01"). Из неё вычтите updated и возьмите .dt.days.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
updated = ...
last_update = ...
age_days = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
updated = pd.to_datetime(supplier["updated"], format="%d.%m.%Y")
last_update = updated.max()
age_days = (pd.to_datetime("2026-02-01") - updated).dt.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 определяет формат по первому значению и спотыкается о второе (а заодно предупреждает,
что формат с днём впереди он угадал):
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 — и берёт из него значения для пропусков.
Сохраните в dotted даты формата ДД.ММ.ГГГГ, в slashed — формата ДД/ММ/ГГГГ (остальные значения —
пропуски). Добавьте в returns столбец date: даты из iso, а где там пропуск — из dotted или slashed.
В n_unparsed сохраните число оставшихся пропусков в date.
iso выше, но с format="%d.%m.%Y" и format="%d/%m/%Y".iso.fillna(dotted).fillna(slashed).Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
dotted = ...
slashed = ...
# добавьте в returns столбец date
n_unparsed = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
dotted = pd.to_datetime(returns["return_date"], format="%d.%m.%Y", errors="coerce")
slashed = pd.to_datetime(returns["return_date"], format="%d/%m/%Y", errors="coerce")
returns["date"] = iso.fillna(dotted).fillna(slashed)
n_unparsed = returns["date"].isna().sum()У to_datetime есть режим 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.