
在 polars 中对超大 parquet 文件进行流式读取时,若所有列均为 string 类型,需通过两遍扫描完成类型推断(如转为 int64/float64)并保存为带正确 schema 的新文件。本文提供可复用的两阶段方案及健壮的类型探测函数。
Polars 的流式(streaming=True)执行模型要求 输出 schema 必须在开始计算前完全确定——这意味着无法在单次流式扫描中边读取边动态决定列类型。尤其当原始 Parquet 文件所有列均为 pl.String 时,直接尝试 .cast(pl.Int64, strict=False) 会导致无效字符串被转为 null,而非跳过或降级处理,从而破坏数据完整性。
因此,必须采用两阶段策略:
- 探查阶段(Schema Discovery):对每列 string 字段分别尝试强制转换为目标数值类型(先 Int64,再 Float64),捕获 ComputeError 判断是否全域兼容;
- 转换阶段(Streaming Sink):基于探查结果构建确定性表达式,执行一次流式 select + sink_parquet,确保高效、内存友好的写入。
以下为完整可运行示例:
import polars as pl
from polars.exceptions import ComputeError
def try_dtype(lf: pl.LazyFrame, dtype: pl.DataType, cols_to_skip: list[str] | None = None) -> list[str]:
"""
探查 LazyFrame 中哪些 string 列可安全 cast 到指定 dtype。
使用 streaming.collect() 避免全量加载,仅验证可行性。
"""
schema = lf.schema
cols_to_check = [
col for col, typ in schema.items()
if typ == pl.String and (cols_to_skip is None or col not in cols_to_skip)
]
if not cols_to_check:
return []
good_cols = []
for col in cols_to_check:
try:
# 仅验证:流式执行 cast,不收集结果
lf.select(pl.col(col).cast(dtype)).collect(streaming=True)
good_cols.append(col)
except ComputeError:
continue
return good_cols
# --- 主流程 ---
lf = pl.scan_parquet("large_file.parquet")
# 阶段1:识别可转为整数的列(优先 int,避免 float 误吞整数)
int_cols = try_dtype(lf, pl.Int64)
# 阶段2:在剩余 string 列中识别可转为浮点的列
float_cols = try_dtype(lf, pl.Float64, cols_to_skip=int_cols)
# 构建最终转换表达式列表
exprs = []
for col in lf.columns:
if col in int_cols:
exprs.append(pl.col(col).cast(pl.Int64))
elif col in float_cols:
exprs.append(pl.col(col).cast(pl.Float64))
else:
exprs.append(pl.col(col)) # 保持原 string 类型
# 阶段3:流式执行并写入新 Parquet(schema 已确定)
lf.select(*exprs).sink_parquet("final_cast.parquet")✅ 关键优势说明:
- try_dtype 内部使用 collect(streaming=True),仅触发轻量计算图执行,不将全部数据载入内存;
- 严格按 Int64 → Float64 顺序探测,避免将 "123" 错判为 float 而丢失精度;
- sink_parquet 保证最终文件具备精确的物理 schema,下游系统(如 DuckDB、Spark)可直接按类型读取,无需额外解析。
⚠️ 注意事项:
- 若某列含混合格式(如 "123" 和 "123.45"),它将既不匹配 Int64 也不匹配 Float64,最终保留为 string —— 这是预期行为,保障数据安全;
- 对极宽表(数百 string 列),探查阶段会有一定 I/O 开销(但仍是 O(列数) 次轻量扫描,非 O(行数×列数));
- 如需支持日期/布尔等类型,可扩展 try_dtype 并增加对应探测逻辑。
该方案在 Polars 0.20+ 版本中稳定可用,兼顾工程鲁棒性与性能可预测性,是处理“schema-less 字符串大数据”的标准实践路径。










