
Polars 不提供 pl.mode() 这样的顶层聚合函数,但可通过 pl.col(...).mode() 表达式结合 .first() 或 .list() 等后置操作实现稳定、可扩展的众数计算,同时兼容数值型与字符串型列。
polars 不提供 `pl.mode()` 这样的顶层聚合函数,但可通过 `pl.col(...).mode()` 表达式结合 `.first()` 或 `.list()` 等后置操作实现稳定、可扩展的众数计算,同时兼容数值型与字符串型列。
在使用 Polars 进行特征工程时,常需按主键(如 case_id)分组聚合,并为多列统一计算统计量(如最大值、均值、标准差等)。然而,当尝试添加众数(mode)计算时,初学者容易遇到 AttributeError: module 'polars' has no attribute 'mode' 错误——这是因为 Polars 没有 pl.mode() 这一语法糖函数,不同于 pl.max() 或 pl.mean();其众数计算必须通过列表达式(Expr.mode())显式调用。
✅ 正确用法:pl.col("col").mode() 是核心
mode() 是 Expr 类的方法,而非顶层模块函数。它返回一个包含所有最高频值的 Series(可能多个),因此在聚合上下文中需进一步处理以保证单值输出:
import polars as pl
import polars.selectors as cs
# 示例数据
df = pl.DataFrame({
"aD": [200, 200, 300, 400, 1, 3],
"bD": [2, 3, 6, 4, 5, 1],
"case_id": [1, 1, 1, 2, 2, 2],
"category": ["A", "A", "B", "C", "C", "C"], # 字符串列示例
})
# ✅ 正确:对每列应用 .mode(),并用 .first() 取首个众数值(推荐用于聚合)
mode_expr = cs.ends_with("D").mode().first().name.prefix("mode_")
# ✅ 同样适用于字符串列(无需额外转换)
str_mode_expr = pl.col("category").mode().first().alias("mode_category")
# 完整聚合表达式(含 max/min/mean/std/mode)
aggs = [
cs.ends_with("D").max().name.prefix("max_"),
cs.ends_with("D").min().name.prefix("min_"),
cs.ends_with("D").mean().name.prefix("mean_"),
cs.ends_with("D").std().name.prefix("std_"),
mode_expr,
str_mode_expr,
]
result = df.group_by("case_id").agg(aggs)
print(result)
输出示例:
shape: (2, 9) ┌─────────┬────────┬────────┬────────┬────────┬────────────┬──────────┬─────────┬───────────────┐ │ case_id ┆ max_aD ┆ max_bD ┆ min_aD ┆ min_bD ┆ std_aD ┆ std_bD ┆ mode_aD ┆ mode_category │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ f64 ┆ f64 ┆ i64 ┆ str │ ╞═════════╪════════╪════════╪════════╪════════╪════════════╪══════════╪═════════╪═══════════════╡ │ 1 ┆ 300 ┆ 6 ┆ 200 ┆ 2 ┆ 57.735027 ┆ 2.081666 ┆ 200 ┆ "A" │ │ 2 ┆ 400 ┆ 5 ┆ 1 ┆ 1 ┆ 229.787583 ┆ 2.081666 ┆ 3 ┆ "C" │ └─────────┴────────┴────────┴────────┴────────┴────────────┴──────────┴─────────┴───────────────┘
⚠️ 关键注意事项
- .mode() 返回 List 类型(当存在多个众数时):若某列有多个值并列最高频(如 [1,1,2,2]),mode() 返回 list[i64]。直接用于 agg() 会报错或导致类型不一致。务必链式调用 .first()(取第一个)、.last() 或 .list().first() 等明确降维。
- 字符串列完全支持:pl.col("col").mode() 对 Utf8、Categorical 等非数值类型同样有效,无需预处理。
- 空值与全空组处理:若某组内所有值为 null,mode() 返回 null;若组为空(无匹配行),结果取决于 Polars 版本,建议配合 pl.all().is_not_null() 做前置校验。
- 性能提示:mode() 在底层需排序计数,大数据集下略慢于 max/min;如仅需“最常见值”且允许近似,可考虑 pl.col(...).value_counts().first().struct.field("counts") 替代,但语义不同。
? 总结:推荐聚合模板
def build_agg_exprs(
numeric_suffix: str = "D",
mode_strategy: str = "first" # "first", "last", "list"
) -> list[pl.Expr]:
sel = cs.ends_with(numeric_suffix)
mode_func = getattr(sel.mode(), mode_strategy)
return [
sel.max().name.prefix("max_"),
sel.min().name.prefix("min_"),
sel.mean().name.prefix("mean_"),
sel.std().name.prefix("std_"),
mode_func.name.prefix("mode_"),
]
# 使用
df.group_by("case_id").agg(build_agg_exprs())
掌握 Expr.mode() 的正确调用方式,不仅能解决当前报错,更能构建健壮、可复用的 Polars 特征聚合流水线——无论面对时间差、评分、类别标签还是任意可哈希类型,众数计算从此清晰可控。










