← Все курсы · Курс Python

Даты в pandas: один раз разобрать их правильно

Майкл Носито · Обновлено в августе 2026 г. · Все числа на этой странице были обработаны до их публикации.

Даты — это столбец, который молча разрывает конвейер. Неправильный номер ошибочен или выглядит абсурдно. Неправильная дата выглядит как дата, находится в правом столбце, сортируется и неверна только в те дни, которые не могут быть месяцем.

Что вы делаете: анализируете один раз, при загрузке, с явным форматом и подсчитываете, что не удалось.

Краткая версия. Если вы не указали формат, синтаксический анализатор догадался и никогда не скажет вам, в какую сторону.

Демонстрация

import pandas as pd

s = pd.Series(['03/04/2026', '11/12/2026'])

pd.to_datetime(s)                          # 2026-03-04, 2026-11-12
pd.to_datetime(s, format='%d/%m/%Y')       # 2026-04-03, 2026-12-11

Те же две строки, два разных результата. Предоставленные сами себе, pandas читают их в первый месяц. Согласно заявленному формату, они идут первым днем. Ни один из вызовов ничего не дал, и оба дали действительные даты.

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

Расскажите. Сгруппируйте по месяцам и посмотрите на распределение. Файл, проанализированный неправильно, имеет тенденцию создавать странную концентрацию в первые двенадцать дней каждого месяца или месяцев, которые заканчиваются на 12-м дне. И то, и другое видно через десять секунд и невидимо в необработанном столбце.

Разбор при загрузке

df = pd.read_csv('orders.csv',
                 parse_dates=['order_date'],
                 date_format='%d/%m/%Y')

Делаю это в read_csv «а не потом» означает, что каждый последующий шаг видит реальную дату и время, и он удаляет окно, в котором кто-то записывает сравнение со строкой, и получает сортировку, которая выполняется в алфавитном порядке.

Коды форматов, которые стоит знать: %Y четырехзначный год, %y двузначный, %m номер месяца, %b короткое название месяца, %B полное название месяца, %d день, %H:%M:%S время. строки ISO, 2026-08-23, вообще не нуждаются в формате и везде однозначны, поэтому на них стоит настаивать в любой системе, на которую вы можете повлиять.

Подсчет неудач

parsed = pd.to_datetime(df['order_date'], format='%d/%m/%Y', errors='coerce')
bad = parsed.isna() & df['order_date'].notna()
print(bad.sum())
print(df.loc[bad, 'order_date'].head(10))

errors='coerce' превращает неудачи в NaT вместо того, чтобы останавливать скрипт, что правильно, только если вы потом на них посмотрите. Печать десяти ошибочных строк обычно мгновенно определяет проблему: второй формат в том же файле, строка нижнего колонтитула или заполнитель, например 0000-00-00.

Опасная закономерность заключается в том, что coerce за которым позже последовал dropna(), который удаляет именно те строки, даты которых были необычными. Это одна из шести операций, которым предстоит бросить вызов. просмотр сценария очистки.

Аксессор, в котором находится значение

d = df['order_date']

d.dt.year          # 2026
d.dt.month         # 4
d.dt.day_name()    # 'Friday'
d.dt.quarter       # 2
d.dt.to_period('M')          # 2026-04
d.dt.days_in_month
(d.max() - d.min()).days     # span in days

.dt существует только тогда, когда столбец является реальным датой и временем, что делает его полезным тестом: если .dt выдает ошибку атрибута, столбец по-прежнему является текстовым, хотя и выглядит как дата.

Группировка по месяцам, два способа

# labels, only the months that occur
df.groupby(df['order_date'].dt.to_period('M'))['amount'].sum()

# a continuous axis, including months with no rows
df.set_index('order_date').resample('MS')['amount'].sum()

Разница имеет значение для отчетности. to_period возвращает только те месяцы, по которым есть данные, поэтому в результате отсутствует месяц без заказов, и диаграмма соединяется прямо через него. resample вставляет его как ноль, что обычно и требуется для временного ряда, и это именно тот разрыв, который обсуждался в чтение восходящей линии.

Арифметика дат

df['days_to_ship'] = (df['shipped_date'] - df['order_date']).dt.days

cutoff = pd.Timestamp('2026-01-01')
df[df['order_date'] >= cutoff]

df['order_date'] + pd.DateOffset(months=1)   # calendar-aware
df['order_date'] + pd.Timedelta(days=30)     # exactly 30 days

Последние два не одинаковы, и разница ощутима в конце месяца. DateOffset(months=1) с 31 января дает 28 февраля; Timedelta(days=30) дает 2 марта. Выбирайте осознанно и скажите, какое из них означает бизнес-правило.

Часовые пояса, кратко

Наивная временная метка не имеет зоны, и сравнение ее со знакомой меткой повышает значение. Если какая-либо часть ваших данных зависит от зоны, сделайте это все и храните в формате UTC:

d.dt.tz_localize('Europe/London').dt.tz_convert('UTC')

Последствия для отчетности, о которых следует знать: событие в 23:30 UTC принадлежит другому местному дню, поэтому дневные счетчики могут отличаться на количество строк за целый день в зависимости от того, какую зону использовала группировка. Скажите в отчете, в какой зоне находятся дни.

Как применить это в своей работе

  1. добавлять format= каждому to_datetime называть тебя своим. Это изменение одной строки для каждого вызова, которое устраняет целый класс скрытых ошибок.
  2. После анализа выведите минимальное и максимальное значения, а также количество по месяцам. Две строки, и они обнаруживают неправильный анализ.
  3. Подсчитайте вынужденные неудачи и посмотрите на десять из них, а не отбрасывайте их.
  4. Спросите любую систему, которой вы управляете, о датах ISO. Это самое дешевое улучшение качества данных.
  5. Решите, нужно ли отображать в ежемесячном отчете пустые месяцы, и выберите resample или to_period соответственно.

Единственная привычка, которую нужно сохранить

Проверьте проанализированные даты перед их использованием. Мин, макс и количество по месяцам. Десять секунд, и это единственный надежный способ поймать столбец даты, который был проанализирован успешно или ошибочно.

Знаете ли вы, в каком порядке были прочитаны даты в вашем основном файле?

Каждый результат здесь был просчитан до его публикации, это pandas 3.0.2. pd.to_datetime(pd.Series(["03/04/2026"])) возвращает 04.03.2026 и тот же вызов с format="%d/%m/%Y" возвращается 03 апреля 2026 г.
Дата, проанализированная неправильно, неверна только в двух третях строк, поэтому никто этого не замечает.

Python для аналитиков — это рабочая книга по аналитике pandas: прочитайте файл, исправьте его типы, сгруппируйте его, присоединитесь к нему и проверьте ответ, прежде чем его увидит кто-либо еще.

Python для аналитиков, 19 $ →
Исправляйте типы при загрузке, а не позже.

read_csv охватывает шаг нагрузки, просмотр сценария очистки охватывает решения и даты в SQL это версия на стороне базы данных.

Читать read_csv →