pandas read_csv以冒号为分隔符时无法正确解析含冒号的时间戳怎么办
问题场景
- 持有200MB规模的TXT格式数据集,文件固定用冒号作为字段分隔符,直接调用
pandas.read_csv尝试解析内置时间戳列时会触发解析报错,希望不需要预先手动清洗、修改源文件,就能让pandas正确识别解析时间列。 - 触发的报错不是时间格式识别错误,而是参数逻辑异常:当设置
sep=":"、通过列名指定parse_dates参数时,会抛出TypeError: 'NoneType' object is not subscriptable;如果把文件分隔符换成逗号、去掉sep=":"参数,同样的代码就不会报错,但修改源文件分隔符的操作对大文件而言成本过高,不可行。
复现测试代码
import pandas as pd from datetime import datetime from io import StringIO to_dt = lambda x: datetime.strptime(x, "%m/%d/%Y %I:%M:%S %p") ss = """first_name:date_registered Philip:9/13/2020 12:03:05 AM""" df = pd.read_csv(StringIO(ss), sep=":", parse_dates=["date_registered"], date_parser=to_dt) print(df)
完整报错栈
File "test.py", line 13, in <module> df = pd.read_csv(StringIO(ss), sep=":", parse_dates=["date_registered"], date_parser=to_dt) File "/home/mark/uk/.venv/lib/python3.8/site-packages/pandas/io/parsers.py", line 610, in read_csv return _read(filepath_or_buffer, kwds) File "/home/mark/uk/.venv/lib/python3.8/site-packages/pandas/io/parsers.py", line 468, in _read return parser.read(nrows) File "/home/mark/uk/.venv/lib/python3.8/site-packages/pandas/io/parsers.py", line 1057, in read index, columns, col_dict = self._engine.read(nrows) File "/home/mark/uk/.venv/lib/python3.8/site-packages/pandas/io/parsers.py", line 2103, in read values = self._maybe_parse_dates(values, i, try_parse_dates=True) File "/home/mark/uk/.venv/lib/python3.8/site-packages/pandas/io/parsers.py", line 2167, in _maybe_parse_dates if try_parse_dates and self._should_parse_dates(index): File "/home/mark/uk/.venv/lib/python3.8/site-packages/pandas/io/parsers.py", line 1439, in _should_parse_dates j = self.index_col[i] TypeError: 'NoneType' object is not subscriptable
实际数据样例
真实数据集的单行内容如下:
12345:888888:Tom:Corn:builder:United Kingdom:London:four years:Travis:9/10/2017 12:00:00 AM::
需要注意的是,时间戳本身包含冒号(用于分隔时分秒),但不会干扰read_csv的字段拆分逻辑——因为时间字段作为完整值在分隔后的独立列中,拆分动作本身是准确的,报错完全来自pandas解析引擎的参数匹配逻辑缺陷。
无需手动清洗的可行解决方案
- 方案1(适配所有pandas版本):给
read_csv显式传入index_col=False参数,强制pandas不要自动推导隐式索引列,即可绕开空值匹配的bug,导入时直接完成时间解析:
df = pd.read_csv( StringIO(ss), sep=":", index_col=False, # 关键配置项 parse_dates=["date_registered"], date_parser=to_dt )
- 方案2(低版本兼容):对pandas 1.3.0以下的版本,不用列名字符串指定
parse_dates目标,直接传列的整数索引位置,也能绕开内部的索引匹配逻辑:
# date_registered为第2列(索引从0开始计数),直接传位置值 df = pd.read_csv(StringIO(ss), sep=":", parse_dates=[1], date_parser=to_dt)
- 方案3(通用无兼容问题):跳过导入阶段的时间解析,先把全表以字符串格式读入内存,读入完成后再单独对时间列做批量转换。200MB的文件内存占用完全可控,转换速度和导入时解析无明显差异,完全规避所有版本的解析引擎bug:
# 先完整读入全表,不做任何时间格式处理 df = pd.read_csv(StringIO(ss), sep=":") # 读入完成后再批量转换时间列 df["date_registered"] = pd.to_datetime( df["date_registered"], format="%m/%d/%Y %I:%M:%S %p" )
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

