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

nodejs-polars中如何简便实现DataFrame各列与对应列均值的运算

环境

macos:             monterey
node:              v18.1.0
nodejs-polars:     0.5.3

实现目标

对polars DataFrame中的每一列,减去该列自身的均值。

Pandas实现方案

在pandas中该操作实现非常简洁,可借助DataFrame.sub(other, axis='columns', level=None, fill_value=None)方法实现,该方法的other参数支持传入标量、序列、Series或DataFrame类型:

df.sub(df.mean())
df - df.mean()

nodejs-polars实现尝试

在nodejs-polars中,查看源码可见,sub方法定义为sub: (other) => wrap("sub", prepareOtherArg(other).inner()),其other参数最初看起来仅支持Series类型,实际测试中踩了不少坑。

1. 准备测试数据

测试用DataFrame内容如下:

console.log(df)
┌─────────┬─────────┬─────────┬─────────┐
│   A     ┆   B     ┆   C     ┆   D     │
│ ---     ┆ ---     ┆ ---     ┆ ---     │
│ i64     ┆ i64     ┆ i64     ┆ i64     │
╞═════════╪═════════╪═════════╪═════════╡
│ 13520   ┆ -16     ┆ 384     ┆ 208     │
│ 13472   ┆ -16     ┆ 384     ┆ 176     │
│ 13456   ┆ -16     ┆ 368     ┆ 160     │
│ 13472   ┆ -16     ┆ 368     ┆ 160     │
│ 13472   ┆ -16     ┆ 352     ┆ 176     │
└─────────┴─────────┴─────────┴─────────┘

各列均值计算结果:

console.log(df.mean())
┌─────────┬─────────┬─────────┬─────────┐
│   A     ┆   B     ┆   C     ┆   D     │
│ ---     ┆ ---     ┆ ---     ┆ ---     │
│ f64     ┆ f64     ┆ f64     ┆ f64     │
╞═════════╪═════════╪═════════╪═════════╡
│ 13478.4 ┆ -16.0   ┆ 371.2   ┆ 176.0   │
└─────────┴─────────┴─────────┴─────────┘

2. 第一次尝试

直接传入均值结果DataFrame调用sub方法,直接触发类型错误:

df.sub(df.mean())

Error: Failed to determine supertype of Int64 and Struct([Field { name: "A", dtype: Int32 }, Field { name: "B", dtype: Int32 }, Field { name: "C", dtype: Int32 }, Field { name: "D", dtype: Int32 }])

3. 第二次尝试

把均值行提取出来转成Series传入,直接触发内存问题导致程序崩溃:

df.sub(pl.Series(df.mean().row(0)))

Program crashes due to memory problems.

4. 第三次尝试

对照官方测试用例能发现,0.5.3版本的sub方法仅支持两类入参:传入标量时,全表所有元素统一减去该标量值;传入Series时,按行位置对齐做逐行减法,和pandas默认按列对齐广播的逻辑完全不匹配。

test("sub", () => {
  const actual = pl.DataFrame({
    "foo": [1, 2, 3],
    "bar": [4, 5, 6]
  }).sub(1);
  const expected = pl.DataFrame({
    "foo": [0, 1, 2],
    "bar": [3, 4, 5]
  });
  expect(actual).toFrameEqual(expected);
});
test("sub:series", () => {
  const actual = pl.DataFrame({
    "foo": [1, 2, 3],
    "bar": [4, 5, 6]
  }).sub(pl.Series([1, 2, 3]));
  const expected = pl.DataFrame({
    "foo": [0, 0, 0],
    "bar": [3, 3, 3]
  });
  expect(actual).toFrameEqual(expected);
});

最初只能用比较繁琐的方案实现:逐列单独计算减均值,再把所有列横向拼接成结果表。

pl.concat(df.columns.map((col) => df.select(col).sub(df.select(col).mean(0).toSeries())), {how:'horizontal'})

5. 更简洁的实现方案

后续摸索出一个更简洁的写法,不需要逐列遍历拼接:

df.select(pl.col('*').sub(pl.col('*').mean()))

这个写法的运行逻辑符合polars的表达式设计规则:pl.col('*')是通配选中所有列,表达式链上的运算会对每一列独立执行,所以.mean()会单独计算每一列自身的均值,再和原列做逐元素减法,天然实现了按列广播减均值的效果,执行效率比逐列拼接高很多。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 22:40:50