Условие
В колонке ts встречаются форматы:
2024-03-15 12:30:0015.03.2024 12:3003/15/2024Mar 15, 2024- пустые строки и
'N/A'
Привести всё к единому datetime.
Решение
Подход
pd.to_datetime(errors='coerce') пробует dateutil-парсер для каждой строки. На странных форматах он медленный, но универсальный.
import pandas as pd
import numpy as np
df['ts'] = df['ts'].replace({'N/A': np.nan, '': np.nan})
# Универсально, но медленно (dateutil на каждой строке):
df['ts_parsed'] = pd.to_datetime(df['ts'], errors='coerce', dayfirst=True)
# dayfirst=True важно: '03/04' = 4 марта, не 3 апреляБыстрее — несколько проходов с явным форматом
def parse_multiformat(s: pd.Series) -> pd.Series:
out = pd.to_datetime(s, format='%Y-%m-%d %H:%M:%S', errors='coerce')
mask = out.isna()
out.loc[mask] = pd.to_datetime(s[mask], format='%d.%m.%Y %H:%M', errors='coerce')
mask = out.isna()
out.loc[mask] = pd.to_datetime(s[mask], format='%m/%d/%Y', errors='coerce')
mask = out.isna()
out.loc[mask] = pd.to_datetime(s[mask], format='%b %d, %Y', errors='coerce')
return out
df['ts_parsed'] = parse_multiformat(df['ts'])В 10–100 раз быстрее, чем dateutil.
Контроль качества
n_failed = df['ts_parsed'].isna().sum() - df['ts'].isna().sum()
print(f'{n_failed} строк не распарсились — посмотреть:')
print(df[df['ts_parsed'].isna() & df['ts'].notna()]['ts'].value_counts())Подводные камни
dayfirstрешает неоднозначность01/02/2024. Без него pandas использует US-формат (Jan 2). Для RU-данных всегдаdayfirst=True.errors='coerce'молча превращает мусор вNaT— обязательно логировать сколько строк потерялось.- Таймзоны:
2024-03-15T12:00:00+03:00парсится, а2024-03-15 12:00 MSK— нет. Сначала чистить. - Excel даёт даты как «43891.5» (Excel-serial) —
pd.to_datetime(..., unit='D', origin='1899-12-30'). - Mixed формат +
format='mixed'(pandas ≥ 2.0) — однострочное решение, но медленное.
Эталонный ответ
pd.to_datetime(errors='coerce', dayfirst=True) для универсальности; для скорости — каскад с явными форматами и mask = out.isna(). Логировать число неудачных парсингов.