我刚开始使用Polars(Python),所以这可能是一个无知的问题.我有一个DF,如图所示,其中一列(系列)包含长度为18的numpyarray.我想在group列上做一个groupby,在series列上做一个成对求和聚合,但是我想不出在Polars中做这件事的好方法.当然,我可以只做一个map_elements和np.sum数组(就像在例子中),但我希望有一种方法可以优化它.

下面是我目前的实现,它达到了预期的效果,但我不认为它是最佳的,因为它使用了map_elements.有没有一个polars表达式可以实现同样的事情,或者这是我能做的最好的事情(没有学习Rust,我总有一天会学习的)?

import polars as pl
import numpy as np
data = [
{'group': 1,
  'series': np.array([ 2398,  2590,  3000,  3731,  3986,  4603,  4146,  4325,  6068,
          6028,  7486,  7759,  8323,  8961,  9598, 10236, 10873, 11511])},
{'group': 1,
  'series': np.array([ 2398,  2590,  3000,  3731,  3986,  4603,  4146,  4325,  6068,
          6028,  7486,  7759,  8323,  8961,  9598, 10236, 10873, 11511])},
 {'group': 2,
  'series': np.array([1132, 1269, 1452, 1687, 1389, 1655, 1532, 1661, 1711, 1528, 1582,
         1638, 1603, 1600, 1597, 1594, 1591, 1588])},
 {'group': 3,
  'series': np.array([ 2802,  3065,  3811,  4823,  4571,  4817,  4668,  5110,  6920,
          7131, 10154, 11138, 11699, 12840, 13981, 15123, 16264, 17405])},
]
df = pl.DataFrame(data)
# this performs the desired aggregation (pairwise sum of 'series' arrays)
# sums first two rows together (group 1), leaves others unchanged
df.groupby('group').agg([
  pl.col('series').map_elements(lambda x: np.sum(x.to_list(), axis=0))
]).to_dicts()
'''
desired output

group    series
i64    object
2    [1132 1269 1452 1687 1389 1655 1532 1661 1711 1528 1582 1638 1603 1600
 1597 1594 1591 1588]
1    [ 4796  5180  6000  7462  7972  9206  8292  8650 12136 12056 14972 15518
 16646 17922 19196 20472 21746 23022]
3    [ 2802  3065  3811  4823  4571  4817  4668  5110  6920  7131 10154 11138
 11699 12840 13981 15123 16264 17405]

'''

事先感谢您的帮助.

推荐答案

首先,任何时候构建一个Polar df并看到其中一列是Object,然后停下来并返回并弄清楚如何使它成为真正的dtype,否则您所做的其他一切要么将无法工作,要么将依赖于使其变得非常慢的python迭代.

假设你从data个单词开始,然后你可以做一些类似的事情

df = pl.DataFrame(
    [
        {a:pl.Series(b) if isinstance(b,np.ndarray) else b 
         for a,b in x.items()} 
        for x in data
        ]
    )

在那之后,没有一个明显的方法可以做到这一点,但有一条经验法则是,列出explode个 list 几乎总是更容易的.在这种情况下,因为列表的顺序对于以后的聚合很重要,所以您需要创建一个索引列.然后按series列和索引列进行分解.然后将group与索引相加,并将这些值相加.因为您想要以列表结束,所以您需要group_by组将值放回到列表中.总而言之,就是这样:

(
    df
    .with_columns(series_idx=pl.int_ranges(0,pl.col('series').list.len()))
     .explode('series','series_idx')
     .group_by('group','series_idx', maintain_order=True)
     .agg(pl.col('series').sum())
     .group_by('group', maintain_order=True)
     .agg('series')
)
shape: (3, 2)
┌───────┬───────────────────────┐
│ group ┆ series                │
│ ---   ┆ ---                   │
│ i64   ┆ list[i64]             │
╞═══════╪═══════════════════════╡
│ 1     ┆ [4796, 5180, … 23022] │
│ 2     ┆ [1132, 1269, … 1588]  │
│ 3     ┆ [2802, 3065, … 17405] │
└───────┴───────────────────────┘

基于 struct 的方法

如果所有列表的宽度相等,您可以这样做:

cols_to_add=['series']
(
    df
    .with_columns(
        pl.col(col).list.to_struct(
            fields=lambda x, col=col:f"_{col}_{x}"
            )
        for col in cols_to_add
        )
    .unnest(cols_to_add)
    .group_by('group', maintain_order=True)
    .agg(pl.col(f'^_{col}_.+$').sum() for col in cols_to_add)
    .select('group', 
            *[pl.concat_list(pl.col(f'^_{col}_.+$')).alias(col) 
              for col in cols_to_add]
            )
)

这应该也适用于多个series列,尽管我没有使用多个列对其进行测试.

Python-3.x相关问答推荐

如何匹配字母,数字,短划线,逗号,但不是如果没有数字和字母?

Python根据阈值对数字进行分组

如何使用PySide6创建切换框架?

将f-字符串放置在f-字符串内

Heroku 中的未知错误代码缺少一个或多个参数

PyTest:尽管明确运行了测试,但是被标记为没有运行测试

基于组/ID从原始数据框中创建两个子数据框

如何根据索引子列表对元素列表进行分组或批处理?

为什么不能用格式字符串 '-' 绘制点?

在一行中读写一个csv文件

以不规则频率识别数据框日期时间列上缺失的日期,并用关联值填充它们

考虑到Pandas 系列中的不同索引,如何正确估计两列的百分比变化? Python相关

如何使用 Selenium by class_name 从大学橄榄球数据中抓取图像 url 列表

判断是否存在大文件而不下载它

在python中基于列表理解的条件下跳过元素

获取嵌套字典的所有键

如何使我的课程在 Python 中非常可打印?

Python 3:函数参数中的省略号?

如何在多核上运行 Keras?

AttributeError:系列对象没有属性iterrows