2025, Dec 22 21:02
Как читать CSV в pandas со строками и одним столбцом datetime
Разбираем, как в pandas.read_csv сохранить все колонки строками и корректно распарсить один столбец в datetime64[ns]: используем dtype string и parse_dates.
При загрузке CSV в pandas автоматическое определение типов может сыграть злую шутку. Похожие на числа идентификаторы теряют ведущие нули, а строковые флаги превращаются в булевы значения. При этом вполне может требоваться, чтобы один конкретный столбец парсился как дата и время. Цель проста: оставить почти всё строками, кроме одного столбца, который должен быть полноценным datetime.
Пример кода, демонстрирующий проблему
Ниже приведён фрагмент, который читает CSV и выводит первую строку вместе с типами столбцов. Он показывает, как поведение по умолчанию и разные варианты dtype влияют на результат.
import pandas
def show_sample_types(frame: pandas.DataFrame):
first = frame.iloc[0]
for col in frame.columns:
print(f"{col}: {first[col]} (type: {frame[col].dtype})")
csv_path = "test.csv"
sample = pandas.read_csv(csv_path, parse_dates=["send_date"])
show_sample_types(sample)
sample = pandas.read_csv(csv_path, dtype=object, parse_dates=["send_date"])
show_sample_types(sample)
sample = pandas.read_csv(csv_path, dtype=str, parse_dates=["send_date"])
show_sample_types(sample)
kinds = {"name": "object", "zip_code": "object", "send_date": "datetime64", "is_customer": "object"}
sample = pandas.read_csv(csv_path, dtype=kinds, parse_dates=["send_date"]) # TypeError: тип dtype datetime64 не поддерживается для парсинга, передайте этот столбец через parse_dates
Что на самом деле происходит и почему
Если не задавать dtype, pandas включает автоматическое приведение типов. В результате почтовый индекс 04321 превращается в целое число с потерей ведущего нуля, а строковые значения true или false — в булевы. Указание dtype=object сохраняет такие поля как строкоподобные, но мешает send_date стать datetime64[ns], потому что object говорит pandas оставлять «сырые» Python-объекты. Попытка насильно задать дату через dtype={"send_date": "datetime64"} вызывает явную ошибку с рекомендацией использовать parse_dates для разбора дат. Наконец, сочетание dtype=str и parse_dates приводит к тому, что send_date материализуется как строка с числоподобной меткой времени, а не как datetime, что снова сводит на нет задачу.
Решение
Рабочая комбинация — запросить у pandas специализированный строковый dtype для всех столбцов и одновременно поручить parse_dates обработку единственного столбца с датой. Так идентификаторы и строковые флаги остаются строками, а столбец даты корректно парсится в datetime64[ns].
import pandas
result = pandas.read_csv("test.csv", dtype="string", parse_dates=["send_date"])
print(result.dtypes)
# name string[python]
# zip_code string[python]
# send_date datetime64[ns]
# is_customer string[python]
# dtype: object
print(result)
# name zip_code send_date is_customer
# 0 Madeline 04321 2025-04-13 true
# 1 Theo 32255 2025-04-08 true
# 2 Granny 84564 2025-04-15 false
Если какое-то из значений в поле даты не удаётся распознать как дату, dtype столбца будет прочитан как object вместо datetime64[ns].
Почему это важно
Конвейеры данных часто зависят от стабильной, без потерь, загрузки. Почтовые индексы должны сохранять ведущие нули, категориальные флаги могут намеренно храниться как строки, а последующие объединения или валидации ожидают согласованных типов. Избирательный разбор предотвращает тихие преобразования и при этом даёт полноценный datetime для операций по времени.
Вывод
Используйте pandas.read_csv с dtype="string" для общего сохранения типов и parse_dates=["send_date"] для единственного столбца, который должен быть datetime. Не пытайтесь принудительно задавать datetime через dtype и учитывайте, что непарсибельные значения дат приведут к чтению столбца как object.