如何在Polars DataFrame中按ID分组应用Scaler进行归一化
按ID分组实现MinMax归一化的正确方法
你的两种错误方法核心问题在于没搞清楚Polars表达式和sklearn工具的适配逻辑:
- 第一种方法里,
scaler.fit_transform()返回的是numpy数组,不是Polars表达式,所以没法调用.over()方法。 - 第二种方法里,
agg()只接受Polars表达式,不能直接把sklearn的函数套在pl.col()上,因为scaler不认识Polars的Expr对象。
下面提供两种可行的实现方式:
方法一:用Polars原生表达式手动实现(推荐,无需依赖sklearn)
MinMax归一化的公式是 (x - min(x)) / (max(x) - min(x)),直接用Polars的窗口函数over()就能按ID分组计算:
import polars as pl df = pl.DataFrame( { "ID": [1,1,2,2,3,3], "Values": [1,2,3,4,5,6] } ) df = df.with_columns( Value_scaled=(pl.col("Values") - pl.col("Values").min().over("ID")) / (pl.col("Values").max().over("ID") - pl.col("Values").min().over("ID")) ) print(df)
输出结果:
shape: (6, 3) ┌─────┬────────┬──────────────┐ │ ID ┆ Values ┆ Value_scaled │ │ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ f64 │ ╞═════╪════════╪══════════════╡ │ 1 ┆ 1 ┆ 0.0 │ │ 1 ┆ 2 ┆ 1.0 │ │ 2 ┆ 3 ┆ 0.0 │ │ 2 ┆ 4 ┆ 1.0 │ │ 3 ┆ 5 ┆ 0.0 │ │ 3 ┆ 6 ┆ 1.0 │ └─────┴────────┴──────────────┘
如果有多列需要归一化,可以用循环批量处理:
cols_to_scale = ["Values", "Other_col"] # 假设还有Other_col需要处理 for col in cols_to_scale: df = df.with_columns( (pl.col(col) - pl.col(col).min().over("ID")) / (pl.col(col).max().over("ID") - pl.col(col).min().over("ID")) .alias(f"{col}_scaled") )
方法二:结合group_by.apply调用sklearn的MinMaxScaler
如果你一定要用sklearn的工具,可以用group_by().apply()对每个分组单独处理:
import polars as pl from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() df = pl.DataFrame( { "ID": [1,1,2,2,3,3], "Values": [1,2,3,4,5,6] } ) def scale_group(group): # sklearn要求输入是2D数组,转换后将结果展平为1D Series scaled_values = scaler.fit_transform(group.select("Values").to_numpy()).flatten() return group.with_columns(pl.Series("Value_scaled", scaled_values)) df_scaled = df.group_by("ID").apply(scale_group) print(df_scaled)
这个方法的逻辑是:每个ID分组的子DataFrame会被传入scale_group函数,在函数内用scaler处理指定列,再把结果作为新列返回,最后Polars会自动合并所有分组结果。
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

