
Polars 原生不提供 median_horizontal(),但可通过 pl.concat_list() + list.median() 组合实现高效、纯表达式式的行中位数计算,自动忽略 null 值,语义清晰且性能优异。
polars 原生不提供 `median_horizontal()`,但可通过 `pl.concat_list()` + `list.median()` 组合实现高效、纯表达式式的行中位数计算,自动忽略 null 值,语义清晰且性能优异。
在 Polars 中计算水平中位数(即对每一行中指定数值列求中位数),是数据清洗与特征工程中的常见需求。虽然 Polars 提供了 pl.mean_horizontal() 这样的便捷函数,但截至当前稳定版本(v1.0+),尚未内置 median_horizontal() 表达式。不过,借助 Polars 强大的列表表达式能力,我们可优雅地实现等效功能,且完全保持惰性计算与零拷贝优势。
✅ 推荐方案:concat_list + list.median()(纯 Polars 表达式)
该方法将目标数值列(如所有 Int64 列)按行合并为列表,再调用 .list.median() —— 此操作天然支持 null 值跳过,且无需 Python 层循环或外部依赖:
import polars as pl
df = pl.DataFrame({
"ABC": ["foo", "bar", "foo"],
"A": [1, 2, 3],
"B": [2, 1, None],
"C": [1, 2, 3]
})
result = df.with_columns(
pl.concat_list(pl.col(pl.Int64)).list.median().alias("Horizontal Median")
)
print(result)
输出:
shape: (3, 5) ┌─────┬─────┬──────┬─────┬───────────────────┐ │ ABC ┆ A ┆ B ┆ C ┆ Horizontal Median │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ i64 ┆ i64 ┆ i64 ┆ f64 │ ╞═════╪═════╪══════╪═════╪═══════════════════╡ │ foo ┆ 1 ┆ 2 ┆ 1 ┆ 1.0 │ │ bar ┆ 2 ┆ 1 ┆ 2 ┆ 2.0 │ │ foo ┆ 3 ┆ null ┆ 3 ┆ 3.0 │ └─────┴─────┴──────┴─────┴───────────────────┘
? 原理说明:
pl.concat_list(pl.col(pl.Int64))每行生成一个动态长度列表(如[1, 2, 1],[2, 1, 2],[3, null, 3]),而list.median()内部会自动过滤null并对剩余值排序取中位,结果类型统一为Float64(即使输入全为整数)。
⚠️ 注意事项与最佳实践
-
列选择需明确:
pl.col(pl.Int64)会匹配所有整型列,若数据含非目标数值列(如ID或timestamp),建议显式指定列名列表,例如pl.col(["A", "B", "C"]),避免意外包含。 -
null 处理行为一致:
.list.median()默认跳过 null,与np.nanmedian语义对齐;若需保留 null(如全行为 null 时返回 null),此行为已默认满足。 - 性能优势显著:相比 NumPy 方案,该表达式全程在 Polars Rust 引擎内执行,无 Python GIL 限制与内存复制,大数据集下速度通常快 2–5 倍。
-
类型安全:输出列为
f64,符合中位数可能为浮点的数学定义;如需强制转整型(仅当确定结果恒为整数),可链式调用.cast(pl.Int64, strict=False),但需自行承担精度风险。
❌ 替代方案(不推荐用于生产):NumPy 集成
尽管可行,但以下方式存在明显短板:
import numpy as np
df.with_columns(
pl.Series("Horizontal Median", np.nanmedian(df.select(pl.col(pl.Int64)), axis=1))
)
- ✖️ 触发 eager 计算:
df.select(...)返回 eager DataFrame,破坏 Polars 的惰性优化链; - ✖️ 内存冗余:需将整列数据复制到 NumPy 数组,增加内存压力;
- ✖️ 扩展性差:无法与后续
filter()/group_by()等惰性操作无缝衔接。
✅ 总结
| 方法 | 是否惰性 | null 安全 | 性能 | 推荐度 |
|---|---|---|---|---|
concat_list().list.median() |
✅ 是 | ✅ 是 | ⭐⭐⭐⭐⭐ | ★★★★★ |
np.nanmedian() + pl.Series
|
❌ 否 | ✅ 是 | ⭐⭐ | ★☆☆☆☆ |
因此,始终优先使用 pl.concat_list(...).list.median() —— 它是 Polars 生态中计算水平中位数最地道、最高效、最可维护的标准模式。










