使用Pandas和PyArrow读取含超24小时时间值的Parquet文件失败
问题分析与解决方案
1. Parquet.NET是否创建了无效列类型?
是的,这里存在类型映射错误。Parquet中的TIME逻辑类型严格遵循SQL标准,要求时间值必须在00:00:00到23:59:59.999区间内,而你要存储的是超过24小时的时长间隔,并非时刻类型的time。
你在.NET代码中用new DataField<DateTime>("duration")定义列,但DateTime代表的是具体时间点,不是时长。当你把超过24小时的数值存入该字段时,Parquet.NET会将其映射为TIME_MILLIS类型,这直接违反了Parquet TIME类型的规范,最终导致PyArrow读取时触发合法性校验报错。
2. 能否让PyArrow按原始类型而非逻辑类型加载列?
可以,通过PyArrow的低级API绕过逻辑类型解析,直接读取原始INT32值,再手动转换为Python的timedelta或Pandas的timedelta64类型:
实现代码示例
import pyarrow.parquet as pq import pandas as pd # 读取Parquet文件 parquet_file = pq.ParquetFile('myfile.parquet') schema = parquet_file.schema # 定位duration列的索引 duration_col_idx = schema.get_field_index('duration') # 读取原始INT32数据 raw_duration = [] for batch in parquet_file.iter_batches(): raw_col = batch.column(duration_col_idx).to_pandas() raw_duration.append(raw_col) raw_duration = pd.concat(raw_duration) # 转换为毫秒级timedelta timedelta_duration = pd.to_timedelta(raw_duration, unit='ms') # 读取其他列并合并到DataFrame other_columns = [name for name in schema.names if name != 'duration'] df = parquet_file.read(columns=other_columns).to_pandas() df['duration'] = timedelta_duration
3. 更优的源头解决方法
直接修正.NET代码中的字段定义,用时间间隔类型替代DateTime存储时长:
Parquet.NET支持TimeSpan类型,对应Parquet的标准间隔类型,或者也可以直接存储为毫秒数(INT64类型),两种方式都能被PyArrow正确识别为timedelta类型:
.NET代码修正示例
// 方式1:使用TimeSpan类型直接映射为Parquet间隔类型 var durationField = new DataField<TimeSpan>("duration"); // 方式2:直接存储毫秒数(INT64),兼容性更强 var durationField = new DataField<long>("duration");
用这种方式导出的Parquet文件,直接执行pd.read_parquet('myfile.parquet', engine="pyarrow")就能自动将duration列识别为timedelta64[ns]类型,无需额外处理。
内容的提问来源于stack exchange,提问作者Wouter
相关产品推荐
相关产品推荐

