Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 18 из 20 Начальный 22 мин 130 XP

Даты в Pandas: to_datetime

Столбец дат приезжает из выгрузки строками — pd.to_datetime делает его настоящими датами, после чего работают dt.year, dt.month, фильтры по периоду и сортировка по времени.

Редакция Питоники

Восемнадцать уроков курса работали с одной датой за раз. Реальные данные — это столбцы: тысяча заказов с датами из выгрузки, таблица визитов, журнал платежей. Для столбцов в курсе есть отдельный инструмент — pandas, и у его дат своё правило номер один: пока не сказано обратное, дата в таблице — это строка. Урок опирается на strptime и коды формата из уроков 4–5, но в pandas всё делается одним вызовом.

Если pandas ещё не знаком — не страшно: здесь нужны четыре приёма, и каждый запускается прямо на этой странице. Пишете import pandas as pd — библиотека ставится в песочницу автоматически при первом импорте.

выгрузка, в которой даты — строки
import pandas as pd

df = pd.DataFrame({
    'order': [104, 101, 107, 102],
    'date': ['2026-10-12', '2026-09-28', '2026-10-05', '2026-10-01'],
    'total': [3200, 4990, 12500, 8800],
})
print(df)
print(type(df['date'][0]).__name__)
Вывод
   order        date  total
0    104  2026-10-12   3200
1    101  2026-09-28   4990
2    107  2026-10-05  12500
3    102  2026-10-01   8800
str

Четыре заказа, даты не по порядку. В последней строке — проверка: type(df['date'][0]).__name__ печатает str, то есть первый элемент столбца — обычная Python-строка. В выгрузке из CSV было бы то же самое. Строки с датами отсортировались бы по алфавиту, из них нельзя вычесть неделю и не спросить у них месяц по-человечески. Чинится одним вызовом:

to_datetime делает столбец датами
import pandas as pd

df = pd.DataFrame({
    'order': [104, 101, 107, 102],
    'date': ['2026-10-12', '2026-09-28', '2026-10-05', '2026-10-01'],
    'total': [3200, 4990, 12500, 8800],
})
df['date'] = pd.to_datetime(df['date'])
print(df)
print(type(df['date'][0]).__name__)
Вывод
   order       date  total
0    104 2026-10-12   3200
1    101 2026-09-28   4990
2    107 2026-10-05  12500
3    102 2026-10-01   8800
Timestamp

Тот же print(df), но первый элемент столбца теперь Timestamp — класс pandas, прямой наследник знакомого datetime из первого урока. Столбец целиком перешёл на тип datetime64: печатаются даты в том же международном виде, зато внутри это настоящие моменты времени. Дома тип столбца видно через df.dtypes — там появится строка вида datetime64[ns] или datetime64[us]; буквы в скобках — единица хранения, разные у версий pandas, на приёмы этого урока они не влияют.

dt.year и dt.month: части дат из столбца

У одиночного datetime месяц доставляло поле .month из урока про классы дат. У столбца вместо поля — dt-аксессуар: df['date'].dt.month — сразу месяц каждой строки:

месяц заказа отдельным столбцом
import pandas as pd

df = pd.DataFrame({
    'order': [104, 101, 107, 102],
    'date': ['2026-10-12', '2026-09-28', '2026-10-05', '2026-10-01'],
    'total': [3200, 4990, 12500, 8800],
})
df['date'] = pd.to_datetime(df['date'])
df['month'] = df['date'].dt.month
print(df[['order', 'month']])
Вывод
   order  month
0    104     10
1    101      9
2    107     10
3    102     10

dt работает и с .year, и с .day, и со всем набором полей datetime. Новый столбец month готов к группировке: выручка по месяцам — это df.groupby('month'), знакомое по курсу pandas.

Фильтр по периоду

Зачем всё это — самый частый ответ: выбрать заказы за период. Фильтр по дате в pandas выглядит как сравнение с текстом, но работает как сравнение с датой:

заказы после 1 октября
import pandas as pd

df = pd.DataFrame({
    'order': [104, 101, 107, 102],
    'date': ['2026-10-12', '2026-09-28', '2026-10-05', '2026-10-01'],
    'total': [3200, 4990, 12500, 8800],
})
df['date'] = pd.to_datetime(df['date'])

recent = df[df['date'] > '2026-10-01']
print(recent)
Вывод
   order       date  total
0    104 2026-10-12   3200
2    107 2026-10-05  12500

Справа строка '2026-10-01', слева — столбец Timestamp, и pandas приводит строку к дате сам: из четырёх заказов остались два, строго после первого октября, а сам 1 октября в выборку не попал — сравнение строгое. Диапазон задаётся двумя условиями через &: df[(df['date'] >= '2026-10-01') & (df['date'] <= '2026-10-31')].

Сортировка по дате

Последний штрих — вернуть таблице хронологию. sort_values по столбцу дат упорядочивает строки по времени, а не по алфавиту:

хронология восстановлена
import pandas as pd

df = pd.DataFrame({
    'order': [104, 101, 107, 102],
    'date': ['2026-10-12', '2026-09-28', '2026-10-05', '2026-10-01'],
    'total': [3200, 4990, 12500, 8800],
})
df['date'] = pd.to_datetime(df['date'])

df = df.sort_values('date')
print(df)
Вывод
   order       date  total
1    101 2026-09-28   4990
3    102 2026-10-01   8800
2    107 2026-10-05  12500
0    104 2026-10-12   3200

Строки выстроились с 28 сентября по 12 октября. Обратите внимание на левый столбец: метки 1, 3, 2, 0 — это исходные индексы строк, они едут вместе со строками. Вернуть аккуратную нумерацию можно цепочкой .reset_index(drop=True) — так делают почти всегда, если индекс не несёт смысла.

Что дальше

Таблица — не единственный источник дат-строк. Второй по частоте — логи: каждая строка начинается с момента в формате «2026-10-21 09:15:03», и его предстоит разобрать, отсортировать и отфильтровать по интервалу. Это следующий урок — даты в логах, последний перед финальным проектом.

Фильтр по дате в pandas выглядит как сравнение с текстом, но работает как сравнение с датой.

Грязный столбец: что делать с битыми строками

Выгрузки редко бывают чистыми. Где-то в середине тысячи строк обязательно найдётся пустая ячейка, где-то — дата в чужом формате, а где-то — опечатка. Поведение pandas на такое по умолчанию неприятное: он не пропустит строку молча, а уронит всё вычисление. Для одиночной строки это честно, для столбца — дорого.

мягкий разбор: битые строки в NaT
import pandas as pd

df = pd.DataFrame({'d': ['2026-10-21', 'битая', '2026-11-02']})
df['d'] = pd.to_datetime(df['d'], errors='coerce')

print(df['d'].isna().sum())
print(df['d'].notna().sum())
Вывод
1
2

Аргумент errors='coerce' меняет стратегию: вместо исключения нераспознанная строка превращается в NaT — пустое значение даты. Обратите внимание на асимметрию: isna() отвечает про исходную строку и даёт 1, а notna() — про распознанные даты и даёт 2. Одна битая строка из трёх не исчезает бесследно: она превращается в NaT, который можно посчитать, отфильтровать и показать в отчёте.

NaT в dt-аксессорах
import pandas as pd

df = pd.DataFrame({'d': ['2026-10-21', 'битая', '2026-11-02']})
df['d'] = pd.to_datetime(df['d'], errors='coerce')

print(df['d'].dt.month.tolist())
Вывод
[10.0, nan, 11.0]

Вот тонкость, на которой спотыкаются почти все: dt-аксессор работает и по NaT, но вместо числа для пустой ячейки выдаёт nan. Тип столбца меняется на float, и целые месяцы превращаются в 10.0, а пропуск виден как nan. Это не поломка — это честное отражение того, что в столбце есть дырка. Сравнивать такие значения с обычными числами надо осторожно: nan не равно ничему, даже самому себе.

Формат, который pandas не угадает

ISO-строки pandas разбирает сам, и это спасает в девяти случаях из десяти. Но русская выгрузка с датами вида «21.10.2026» — это уже не ISO, и тут угадывать нечего: тот же набор цифр «04.10.2026» может значить и 4 октября, и 10 апреля. Модуль datetime в уроке 5 требует явный шаблон, и pandas ведёт себя так же.

русский формат через format=
import pandas as pd

df = pd.DataFrame({'d': ['21.10.2026', '01.11.2026']})
df['d'] = pd.to_datetime(df['d'], format='%d.%m.%Y')

print(df['d'].dt.strftime('%d.%m.%Y').tolist())
Вывод
['21.10.2026', '01.11.2026']

С явным format='%d.%m.%Y' порядок дней и месяцев задаётся раз и навсегда, и две даты разбираются так, как их записал человек. Тот же столбец без format pandas читает по своим правилам — и на «04.10.2026» даст 10 апреля, потому что по умолчанию в нём американский порядок месяца и дня. Ни ошибки, ни предупреждения: просто другая дата.

dayfirst: одна опция вместо риска
import pandas as pd

s = pd.to_datetime(['04.10.2026'], dayfirst=True)

print(s[0].month, s[0].day)
Вывод
10 4

Аргумент dayfirst=True переключает приоритет: первым читается день, вторым месяц. Это спасательный круг для европейских выгрузок, где формат не указан, но все даты написаны в одном стиле. Но мы его не используем: dayfirst — это предположение о данных, а format='%d.%m.%Y' — это утверждение. Первое может внезапно перестать работать, второе выскажет себя сразу на первой неоднозначной дате.

От unix-времени к датам

В API и базах даты часто приходят числами — тем самым unix-временем из урока 13. У pandas для этого есть аргумент unit: он говорит, в каких единицах лежат числа, и делает то же, что деление на 1000, но сразу и правильно.

to_datetime с unit=
import pandas as pd

df = pd.DataFrame({'ts': [1792593045, 1792582200]})
df['when'] = pd.to_datetime(df['ts'], unit='s')

print(df['when'].dt.strftime('%d.%m.%Y %H:%M:%S').tolist())
Вывод
['21.10.2026 14:30:45', '21.10.2026 11:30:00']

Единицы бывают разные: unit='s' — секунды, unit='ms' — миллисекунды, unit='D' — дни. Ошибка в выборе единицы не выглядит как ошибка: миллисекунды, прочитанные как секунды, дают дату в 1970 году с номерами тысяч. Мы всегда сверяем результат с ожидаемым диапазоном: если столбец дат вдруг оказался в 1970-м, первое, что стоит проверить, — единицы измерения.

Группировка: месяцы вместо дней

Столбец дат — это готовый признак для группировки. Чтобы узнать выручку по месяцам, не нужно ни циклов, ни словарей: достаточно добавить столбец с номером месяца и позвать groupby.

выручка по месяцам
import pandas as pd

df = pd.DataFrame({
    'd': pd.to_datetime(['2026-09-28', '2026-10-12', '2026-10-20']),
    'v': [10, 20, 30],
})
df['m'] = df['d'].dt.month

print(df.groupby('m')['v'].sum().to_dict())
Вывод
{9: 10, 10: 50}

Сентябрь дал 10, октябрь — 50: две продажи в октябре сложились вместе. Ключ группировки — номер месяца, целое число, поэтому результат приходит словарём и печатается без лишних танцев с индексами. Заметьте: если в данных есть даты из разных годов, группировка по одному месяцу их смешает — тогда нужен ключ из года и месяца, а это уже отдельная тема урока про временные ряды.

ЗадачаВызовЧто получится
Месяц каждой строкиdf['d'].dt.month10, 9, 10 — целые числа
День недели (0 — пн)df['d'].dt.weekday2, 5 — как в datetime
Имя дня неделиdf['d'].dt.day_name()Wednesday, Saturday — по-английски
Номер кварталаdf['d'].dt.quarter4, 1
Неделя года по ISOdf['d'].dt.isocalendar().week43, 1
Первый день месяцаdf['d'].dt.is_month_startTrue, False
Дата без времениdf['d'].dt.normalize()полночь той же даты
Сумма по месяцамdf.groupby(df['d'].dt.month)['v'].sum()словарь {месяц: сумма}

Восемь строк, которые закрывают почти весь бытовой анализ по датам. Заметим две особенности. Первая: day_name() даёт английские названия — Wednesday, Saturday, — потому что локаль pandas по умолчанию не переключается; русские дни недели придётся приклеивать словарём, как в уроке про русские даты. Вторая: isocalendar().week считает недели по стандарту ISO, где первая неделя года та, что содержит 4 января, — из-за этого номер недели в начале года меньше, чем кажется интуитивно.

границы периода через between
import pandas as pd

df = pd.DataFrame({'d': pd.to_datetime(['2026-10-01', '2026-10-21', '2026-11-02'])})

m = df[df['d'].between('2026-10-01', '2026-10-31')]
print(len(m))
Вывод
2

between() удобнее цепочки из двух сравнений с &: он же и короче, и понятнее. Границы включаются в диапазон с обеих сторон, а задавать их можно строками в ISO — pandas сам приведёт их к датам, ровно как в фильтре из начала урока. Единственное, что стоит запомнить: between() не принимает разные форматы в одной границе, поэтому «2026-10-01» и «31.10.2026» лучше не смешивать.

чистая нумерация после сортировки
import pandas as pd

df = pd.DataFrame({'d': pd.to_datetime(['2026-10-12', '2026-09-28'])})

out = df.sort_values('d').reset_index(drop=True)
print(out['d'].dt.strftime('%d.%m.%Y').tolist())
Вывод
['28.09.2026', '12.10.2026']

Две даты встали в хронологию, а метки снова 0 и 1. Мелочь, но без неё следующий кусок кода, который скажет df.loc[0, 'total'], возьмёт не ту строку — молча, без всякой ошибки. Мы добавляем reset_index(drop=True) после каждой сортировки, если индекс не несёт смысла.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

import pandas as pd

s = pd.to_datetime('2026-10-21')
print(s.year, s.month)
import pandas as pd

df = pd.DataFrame({'d': pd.to_datetime(['2026-01-15', '2026-03-02'])})
print(df['d'].dt.month.tolist())
import pandas as pd

df = pd.DataFrame({'d': pd.to_datetime(['2026-09-28', '2026-10-12'])})
print(len(df[df['d'] > '2026-10-01']))
import pandas as pd

df = pd.DataFrame({'d': ['2026-10-21', 'битая', '2026-11-02']})
df['d'] = pd.to_datetime(df['d'], errors='coerce')
print(df['d'].isna().sum())
import pandas as pd

df = pd.DataFrame({
    'd': pd.to_datetime(['2026-09-28', '2026-10-12', '2026-10-20']),
    'v': [10, 20, 30],
})
df['m'] = df['d'].dt.month
print(df.groupby('m')['v'].sum().to_dict())
Проверь себя
0 / 9

1. Что делает pd.to_datetime(df['date'])?

2. Как получить месяц каждой строки столбца с датами?

3. Почему фильтр df[df['date'] > '2026-10-01'] корректен, если справа строка?

4. После df.sort_values('date') в левом столбце таблицы оказались метки 1, 3, 2, 0. Что это?

5. Что вернёт type(df['date'][0]).__name__ после вызова pd.to_datetime(df['date'])?

6. Что делает аргумент errors='coerce' при разборе дат?

7. Что произойдёт с pd.to_datetime(['04.10.2026']) без format и без dayfirst?

8. Зачем при разборе unix-времени в pandas указывать unit?

9. Что даст df['d'].dt.month для столбца, где одна строка не распозналась и стала NaT?

Карточки терминов
Запомнено: 0 / 11
Практика

Таблица визитов трёх клиентов пришла со строками-датами. Приведите столбец visit к датам, добавьте столбец month с номером месяца, выведите фамилии клиентов в хронологии визитов списком, затем фамилии октябрьских визитов (строго после 1 октября) и список номеров месяцев. Три списка, каждый на своей строке.

practice.py
Вопросы и ответы по уроку

Как превратить столбец строк в даты в pandas?

Одним вызовом: df['date'] = pd.to_datetime(df['date']). Для ISO-строк вида 2026-10-12 формат угадывается сам; для форматов вида 12.10.2026 передайте format='%d.%m.%Y', иначе день и месяц перепутаются. После приведения у столбца работает dt-аксессуар, а фильтры и сортировки считаются по календарю.

Урок 18: даты в Pandas

Почему df['date'].dt.month выдаёт ошибку AttributeError?

dt-аксессуар работает только над столбцами с типом дат (datetime64). Если столбец остался строковым — pandas не привёл его сам, вызовите сначала df['date'] = pd.to_datetime(df['date']). Ошибка так и говорит: Can only use .dt accessor with datetimelike values.

Как отфильтровать DataFrame по диапазону дат?

Двумя условиями через &: df[(df['date'] >= '2026-10-01') & (df['date'] <= '2026-10-31')]. Границы можно писать строками в ISO-формате «ГГГГ-ММ-ДД» — pandas приводит их к датам сам. Скобки вокруг каждого условия обязательны: у сравнений приоритет выше, чем у &.

Чем Timestamp отличается от datetime в Python?

Timestamp — класс pandas для отдельного значения даты-времени, построенный поверх datetime из стандартной библиотеки: у него те же поля year, month, day и методы strftime, плюс работа с индексами и столбцами. В коде pandas значения столбцов дат — это Timestamp, а тип всего столбца в df.dtypes пишется как datetime64.

Что делать, если в столбце дат есть битые строки?

Разбирать с errors='coerce': нераспознанные строки превращаются в NaT, а не роняют всё вычисление. Но потеря становится тихой, поэтому сразу после разбора считайте df['d'].isna().sum() и печатайте это число в лог — если из тысячи строк не распознались три, это повод разбираться.

Урок 18: даты в Pandas

Как разобрать в pandas даты в русском формате 21.10.2026?

Укажите шаблон явно: df['d'] = pd.to_datetime(df['d'], format='%d.%m.%Y'). Без format pandas читает «04.10.2026» по своему умолчанию — месяц первым — и даёт 10 апреля, не сообщив об этом ни ошибкой, ни предупреждением.

Как превратить столбец unix-времени в даты в pandas?

Аргументом unit: df['when'] = pd.to_datetime(df['ts'], unit='s') для секунд, unit='ms' для миллисекунд. Единицу надо называть явно: миллисекунды, прочитанные как секунды, дадут даты 1970 года, и ошибка не выглядит как ошибка.

Как посчитать сумму по месяцам в pandas?

Добавьте столбец с номером месяца и сгруппируйте: df['m'] = df['d'].dt.month, затем df.groupby('m')['v'].sum(). Номер месяца — целое число, поэтому результат приходит словарём вида {9: 10, 10: 50}. Если в данных есть разные годы, группируйте по паре год-месяц.

Понравился урок? Сошлитесь на него

«Фильтр по дате в pandas выглядит как сравнение с текстом, но работает как сравнение с датой.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.