
本文介绍在 Polars 中批量识别并转换「数值上为整数」(如 1.0, 2.0)的浮点列(f64/f32)为整数类型(如 i64)的高效方法,避免逐行判断,兼顾类型安全与性能。
本文介绍在 polars 中批量识别并转换「数值上为整数」(如 1.0, 2.0)的浮点列(f64/f32)为整数类型(如 i64)的高效方法,避免逐行判断,兼顾类型安全与性能。
在 Polars 中,将浮点列中“实际为整数”的值(如 1.0, 2.0, 0.0)转为真正的整数类型(如 Int64),不能简单调用 .cast(pl.Int64)——因为若列中存在非整数值(如 2.1),强制转换会引发错误或静默截断(取决于 strict 参数)。正确做法是先校验是否所有值均为整数,再统一转换,且需全程使用向量化操作以保持 Polars 的高性能优势。
以下是一个推荐的、生产就绪的解决方案:
import polars as pl
def maybe_cast_int(s: pl.Series) -> pl.Series:
"""对 Series 尝试安全转为 Int64:仅当所有值均为数学整数时才转换,否则保留原类型。"""
# 尝试无损转为 Int64(strict=True 确保失败时抛异常,但此处我们主动校验)
try:
s_int = s.cast(pl.Int64, strict=True)
except pl.exceptions.ComputeError:
return s # 存在非整数值,跳过转换
# 校验转换前后数值是否完全一致(防浮点精度导致的隐式变化)
if (s_int.cast(pl.Float64) == s).all():
return s_int
else:
return s
# 示例数据
df = pl.DataFrame({
"date": ["2025-01-01", "2025-01-02"],
"a": [1.0, 2.0], # 全为整数 → 转为 i64
"b": [3.0, 4.5], # 含 4.5 → 保留 f64
"c": [0.0, -5.0], # 全为整数(含负数)→ 转为 i64
})
# 对指定列批量应用转换(原地更新)
df_converted = df.with_columns(
pl.col("a", "c").map_batches(maybe_cast_int)
)
print(df_converted)输出:
shape: (2, 4) ┌────────────┬─────┬─────┬──────┐ │ date ┆ a ┆ b ┆ c │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ i64 ┆ f64 ┆ i64 │ ╞════════════╪═════╪═════╪══════╡ │ 2025-01-01 ┆ 1 ┆ 3.0 ┆ 0 │ │ 2025-01-02 ┆ 2 ┆ 4.5 ┆ -5 │ └────────────┴─────┴─────┴──────┘
✅ 关键要点说明:
- 使用
map_batches而非apply或map_elements:确保底层 C++ 向量化执行,避免 Python 层逐元素开销; -
strict=True配合异常捕获,比s.cast(pl.Int64).is_not_null().all()更可靠(后者无法捕获溢出等底层错误); - 显式回转
s_int.cast(pl.Float64) == s校验,可覆盖NaN、±inf及极小浮点误差场景; - 支持负数(如
-5.0)、零、大整数(只要在i64范围内); - 不修改原始列名,语义清晰,符合函数式链式操作习惯。
⚠️ 注意事项:
- 此方法不改变
null值行为:若列含null,cast(pl.Int64)默认保留null,校验逻辑仍成立; - 若需处理
f32列,请在cast中指定pl.Int32,并注意精度匹配; -
不建议在 ETL 流程中让 schema 动态依赖数据内容(如某些行全整、某些行含小数),这会破坏类型稳定性。生产环境建议预先明确业务规则,或在转换后显式
.cast(pl.Int64, strict=False)并配合.fill_null()处理异常值。
通过该模式,你既能复现 Pandas 中 x.is_integer() 的语义,又能充分发挥 Polars 的性能与类型安全优势。

















