pandas 读取 Parquet 按 Period 过滤,报 ArrowInvalid 或没有匹配 kernel
Parquet 中的 Period 列能读取却不能直接下推过滤,区分标量转换与 Arrow 扩展类型比较失败,选择读后筛选或另存筛选键。
Parquet 文件能完整读出,普通整数过滤也正常,只有 filters 中传入 pd.Period("2024-07", freq="M") 时失败:
ArrowInvalid: Could not convert Period('2024-07', 'M') ...
did not recognize Python value type when inferring an Arrow data type
再把条件换成 period.ordinal,又出现 ArrowNotImplementedError,提示 greater_equal 没有适合扩展类型与整数的 kernel。这两次错误并不矛盾:标量无法转换与比较函数无法处理列类型,是两个不同环节。
这个场景有 pandas 2.3.3、PyArrow 21.0.0、Python 3.13.2 的明确环境记录。可核对的 pandas 3.0.6 仍把 Period 保存为带频率信息的 Arrow 扩展类型;不能由“底层存储是整数”推断读取过滤已支持整数比较,也不能把升级 pandas 当作已确认的通用修复。
先确认列是 Period,还是普通日期
下面假设文件中有月频率列 per_m。可以先只读这列检查其逻辑类型,并查看 Arrow schema:
import pandas as pd
import pyarrow.parquet as pq
path = "input.parquet" # 换成待检查文件,先保留原件
sample = pd.read_parquet(path, columns=["per_m"], engine="pyarrow")
print(sample.dtypes)
print(pq.read_schema(path))
若 schema 显示 extension<pandas.period<ArrowPeriodType>>,它携带了 Period 的逻辑含义和频率,不等于一个普通 int64 列。把 Period 换成 .ordinal 只改了条件值,左侧扩展类型没有改变,所以仍可能缺少比较 kernel。
如果文件是分区数据集,分区键又可能由目录解析成字符串或分类值。这时要看数据集实际 schema,不能拿普通文件中 timestamp 列的成功过滤来证明分区键也能接收 Timestamp。
数据量允许时,先读需要的列,再在 pandas 中筛选
只需处理一个可装入内存的文件时,最直接的绕过方式是在读取后比较 Period:
import pandas as pd
df = pd.read_parquet(
"input.parquet",
engine="pyarrow",
columns=["n", "per_m"],
)
cutoff = pd.Period("2024-07", freq="M")
mask = df["per_m"].notna() & (df["per_m"] >= cutoff)
selected = df.loc[mask]
示例要求 per_m 的频率也是月。不要用月频率阈值直接比较另一种频率的 Period;先明确业务要比较什么,再统一转换规则。这里显式排除了缺失月份,其他空值取舍应按业务决定。
这种方法没有把月份条件下推到 Parquet 扫描端。columns 可以减少读取列数,却不会让月份过滤自动减少需要读取的全部行。文件或数据集太大时,应先评估内存和读取成本,不能把它当作等价性能修复。
长期需要下推过滤时,另存可比较的键
如果数据结构由自己维护,可以保留原 Period 列,同时增加一个明确约定的普通筛选列。例如月数据额外保存月初 timestamp:
out = df.copy()
out["month_start"] = out["per_m"].dt.to_timestamp(how="start")
out.to_parquet("with_month_start.parquet", engine="pyarrow", index=False)
selected = pd.read_parquet(
"with_month_start.parquet",
engine="pyarrow",
filters=[("month_start", ">=", pd.Timestamp("2024-07-01"))],
)
代码写到新路径,避免覆盖原件。月初时间是额外的筛选键,不应冒充原来的 Period 值;季度、周或带特殊起点的频率需要各自定义边界。若选择整数月份键,也要让写入列和过滤标量都是约定一致的普通整数类型,不能只对条件调用 .ordinal。
修改后先比较 pandas 读后筛选与新键下推筛选返回的业务主键集合,再核对阈值前后月份、恰好等于阈值和缺失月份。新旧筛选行数一致仍可能选中了不同记录。旧文件继续保留到下游读取与频率语义核对完成,再按原有数据迁移流程切换。


