You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复Rust Polars自定义gmean表达式,实现无别名聚合?

问题描述

在Rust Polars中实现自定义几何均值(gmean)表达式,希望复刻.mean()的行为:无需手动添加.alias()即可执行聚合操作并保留原列名。但当前实现运行时会报重复列名错误,必须使用.alias()才能正常执行。

期望代码逻辑:

let df = self
    .lazy()
    .select([cols(apply_to).gmean()])
    .collect()
    .expect("Error implementing mean returns df");
Ok(df)

当前自定义实现代码:

fn geometric_mean(values: &[f64]) -> f64 {
    let adjusted_values: Vec<f64> = values.iter().map(|&v| 1.0 + v).collect();
    let product: f64 = adjusted_values.iter().product();
    let count = adjusted_values.len() as f64;
    product.powf(1.0 / count)
}

fn gmean(series: &[Series]) -> PolarsResult<Series> {
    let _series = &series[0];
    let _chunk_array = _series.f64()?;

    let values: Vec<f64> = _chunk_array.into_iter().filter_map(|opt| opt).collect();

    let geo_mean= geometric_mean(&values) - 1.0;

    Ok(Series::from_iter(vec![geo_mean]))
}

fn gmean_column(series: &Column) -> Result<Column,PolarsError> {
    let materialized_series_slice = std::slice::from_ref(series.as_materialized_series());
    Ok(gmean(materialized_series_slice)?.into_column())
}

pub trait GeoMean {
    fn gmean(self) -> Expr;
}

impl GeoMean for Expr {
    fn gmean(self) -> Expr {
        self.map(
            |column| {
                let geom_mean_series = gmean_column(&column)?;
                Ok(Some(geom_mean_series.into_column()))
            },
            GetOutput::same_type()
        )
    }
}

报错信息:

Error implementing mean returns df: Duplicate(ErrString("the name 'literal' is duplicate

It's possible that multiple expressions are returning the same default column name. If this is the case, try renaming the columns with `.alias(\"new_name\")` to avoid duplicate column names."))

预期结果:

shape: (1, 2)
┌──────────┬──────────┐
│ eer      ┆ pegy     │
│ ---      ┆ ---      │
│ f64      ┆ f64      │
╞══════════╪══════════╡
│ 0.229572 ┆ -0.11088 │
└──────────┴──────────┘
问题原因

当前实现中,Series::from_iter(vec![geo_mean])会默认生成名为literal的Series。当对多列调用.gmean()时,所有结果列都会使用这个默认名称,导致Polars检测到重复列名报错。而.mean()这类内置聚合函数会自动继承原列的名称,所以不会有这个问题。

修复方案

核心是让自定义聚合后的Series继承原列的名称,具体修改如下:

  • 给gmean函数新增参数接收目标列名,创建Series时指定该名称
  • 在gmean_column中获取原列的名称,传递给gmean函数
  • 确保表达式逻辑中保留原列的元信息
修改后的完整代码
use polars::prelude::*;

fn geometric_mean(values: &[f64]) -> f64 {
    let adjusted_values: Vec<f64> = values.iter().map(|&v| 1.0 + v).collect();
    let product: f64 = adjusted_values.iter().product();
    let count = adjusted_values.len() as f64;
    product.powf(1.0 / count)
}

// 新增name参数,用于指定结果Series的名称
fn gmean(series: &[Series], name: &str) -> PolarsResult<Series> {
    let _series = &series[0];
    let _chunk_array = _series.f64()?;

    let values: Vec<f64> = _chunk_array.into_iter().filter_map(|opt| opt).collect();

    let geo_mean = geometric_mean(&values) - 1.0;

    // 使用指定的列名创建Series,而非默认的"literal"
    Ok(Series::new(name, vec![geo_mean]))
}

fn gmean_column(series: &Column) -> Result<Column, PolarsError> {
    let materialized_series = series.as_materialized_series();
    // 获取原列的名称
    let column_name = materialized_series.name();
    let result_series = gmean(std::slice::from_ref(&materialized_series), column_name)?;
    Ok(result_series.into_column())
}

pub trait GeoMean {
    fn gmean(self) -> Expr;
}

impl GeoMean for Expr {
    fn gmean(self) -> Expr {
        self.map(
            |column| {
                let geom_mean_column = gmean_column(&column)?;
                Ok(Some(geom_mean_column))
            },
            GetOutput::same_type()
        )
    }
}
验证效果

修改后运行期望的代码逻辑,即可得到保留原列名的聚合结果,无需手动添加.alias(),与内置.mean()的行为一致。

内容的提问来源于stack exchange,提问作者Trevor Seibert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:27:31