
本文详解 Polars 中 mode() 的使用误区与正确实践,涵盖数值型和字符串型列的众数聚合、Expr.mode() 的调用方式、多众数处理策略,并提供可直接复用的工程化表达式模板。
本文详解 polars 中 `mode()` 的使用误区与正确实践,涵盖数值型和字符串型列的众数聚合、`expr.mode()` 的调用方式、多众数处理策略,并提供可直接复用的工程化表达式模板。
在 Polars 中,初学者常误以为存在类似 pl.mode() 这样的顶层聚合函数(如 pl.mean() 或 pl.max()),从而在 .agg() 中直接调用而报错:AttributeError: module 'polars' has no attribute 'mode'。根本原因在于:mode() 并非顶层函数,而是 Expr 类的原生方法(即 pl.col(...).mode()),且它返回的是一个 list 类型的 Series(因可能存在多个众数),不能直接用于标量聚合上下文——这正是你原始代码失败的核心。
✅ 正确用法:Expr.mode() + 后续规约
pl.col("col").mode() 会返回该列中所有出现频率最高的值组成的列表(List[DataType])。若需在 .group_by().agg() 中得到单个标量结果(如最常见值),必须配合规约操作,例如:
- .first():取第一个众数(推荐用于确定性输出)
- .last():取最后一个众数
- .list.unique().first():去重后取首项(防重复众数干扰)
- .list.len().eq(1).all():可额外校验是否唯一众数(调试用)
以下为完整可运行示例,同时支持数值型与字符串型列:
import polars as pl
import polars.selectors as cs
# 构造混合类型测试数据(含字符串列)
df = pl.DataFrame({
"case_id": [1, 1, 1, 2, 2, 2],
"aD": [200, 200, 300, 400, 1, 3], # 数值型
"bD": ["X", "X", "Y", "Z", "Z", "Z"], # 字符串型
"cD": [None, "A", "A", "B", "B", "B"] # 含空值的字符串型
})
# 定义聚合表达式:统一处理所有以 "D" 结尾的列
def date_expr():
# 注意:mode() 返回 list,必须用 .first() 转为标量
expr_max = cs.ends_with("D").max().name.prefix("max_")
expr_min = cs.ends_with("D").min().name.prefix("min_")
expr_mean = cs.ends_with("D").mean().name.prefix("mean_")
expr_std = cs.ends_with("D").std().name.prefix("std_")
# 关键:mode + first() → 单值;对字符串列同样有效
expr_mode = cs.ends_with("D").mode().first().name.prefix("mode_")
return [expr_max, expr_min, expr_mean, expr_std, expr_mode]
# 执行分组聚合
result = df.group_by("case_id").agg(date_expr())
print(result)
输出结果(截选):
shape: (2, 9) ┌─────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┐ │ case_id ┆ max_aD ┆ max_bD ┆ max_cD ┆ min_aD ┆ min_bD ┆ min_cD ┆ mode_aD ┆ mode_bD │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ str ┆ str ┆ i64 ┆ str ┆ str ┆ i64 ┆ str │ ╞═════════╪════════╪════════╪════════╪════════╪════════╪════════╪═════════╪═════════╡ │ 1 ┆ 300 ┆ "X" ┆ "A" ┆ 200 ┆ "X" ┆ "A" ┆ 200 ┆ "X" │ │ 2 ┆ 400 ┆ "Z" ┆ "B" ┆ 1 ┆ "Z" ┆ "B" ┆ 3 ┆ "Z" │ └─────────┴────────┴────────┴────────┴────────┴────────┴────────┴─────────┴─────────┘
✅ 关键结论与最佳实践:
- 无 pl.mode(),只有 pl.col(...).mode():必须通过列表达式调用,不可脱离上下文;
- mode() 天然支持任意可哈希类型:包括 str, int, float, bool, date, datetime 等,无需类型转换;
- 空值(null)自动被忽略:Polars 的 mode() 默认跳过 null,行为与 Pandas 一致;
- 多众数时务必规约:.mode().first() 是生产环境最安全的选择;若需保留全部众数,可用 .mode().alias("modes_col"),但注意该列类型为 List;
- 性能友好:mode() 在底层经高度优化,即使在千万行数据上也能高效完成分组众数计算。
⚠️ 注意事项:若某组内所有值均为 null,mode() 将返回 null(符合直觉);若某组仅有一个非空值,该值即为众数。无需额外空值防护,Polars 已内置鲁棒逻辑。
通过本方案,你不仅能修复原始报错,还能构建出兼容数值与文本、稳定可维护、符合特征工程规范的众数聚合流水线。










