如何修复Python Pandas读取Parquet文件时的时间戳解析问题
这个问题的核心在于Pandas默认的datetime64[ns]类型时间范围有限,而Spark的Timestamp支持的时间范围要宽泛得多,导致超出范围的时间戳在解析时发生整数溢出,显示出错误的日期。
问题根源
datetime64[ns]的有效范围是 1677-09-21 00:12:43.145224192 到 2262-04-11 23:47:16.854775807,你的测试数据里的1050年和2500年都落在这个范围之外,所以Pandas解析时会溢出,产生错误的日期值。而Spark的TimestampType支持从公元前4713年到公元9999年的时间,存储到Parquet时是用INT64表示的时间戳(微秒或纳秒精度,取决于Spark版本),所以Spark读取时不会有问题。
解决方案
下面提供几种可行的解决方法,你可以根据需求选择:
方法1:将时间戳读取为Python原生datetime对象
使用pyarrow引擎,并设置timestamp_as_object=True,这样Pandas会把时间戳列读成Python的datetime.datetime对象,支持更广的时间范围:
import pandas as pd df_pandas = pd.read_parquet("timestamptest", engine="pyarrow", timestamp_as_object=True)
此时valid_to列的类型是object,但里面的datetime对象能正确显示1050年和2500年的时间。
方法2:使用微秒精度的datetime64类型
datetime64[us]的有效范围是1-01-01 00:00:00到9999-12-31 23:59:59.999999,完全覆盖你的数据范围。可以先通过pyarrow读取Parquet表,再转换为Pandas时指定时间戳精度:
import pyarrow.parquet as pq import pandas as pd # 读取为pyarrow Table table = pq.read_table("timestamptest") # 转换为Pandas DataFrame,指定微秒精度 df_pandas = table.to_pandas(timestamp_unit='us')
这时valid_to列的类型是datetime64[us],所有时间戳都能正确解析。
方法3:将超出范围的时间戳转为NaT
如果希望保留datetime64[ns]类型,同时把超出范围的时间戳转为NaT(缺失值),可以先读取为Python datetime对象,再用pd.to_datetime并设置errors='coerce':
import pandas as pd df_pandas = pd.read_parquet("timestamptest", engine="pyarrow", timestamp_as_object=True) # 转换为datetime64[ns],超出范围的转为NaT df_pandas['valid_to'] = pd.to_datetime(df_pandas['valid_to'], errors='coerce')
这样正常范围内的时间戳保留正确值,超出的会变成NaT,避免显示错误的日期。
验证结果
使用方法1或方法2后,你的DataFrame会显示正确的时间戳:
id date-string valid_to 0 1 1050-01-01 23:00:01 1050-01-01 23:00:01 1 2 2014-11-30 12:40:32 2014-11-30 12:40:32 2 3 2016-12-29 09:54:00 2016-12-29 09:54:00 3 4 2500-01-01 00:00:00 2500-01-01 00:00:00
内容的提问来源于stack exchange,提问作者Dominik Steinschaden

