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.