【问题标题】:Pandas: aggregate when column contains numpy arraysPandas:当列包含numpy数组时聚合
【发布时间】:2013-06-03 05:48:50
【问题描述】:

我正在使用 pandas DataFrame,其中一列包含 numpy 数组。当尝试通过聚合对该列求和时,我收到一条错误消息,指出“必须生成聚合值”。

例如

import pandas as pd
import numpy as np

DF = pd.DataFrame([[1,np.array([10,20,30])],
               [1,np.array([40,50,60])], 
               [2,np.array([20,30,40])],], columns=['category','arraydata'])

这符合我的预期:

DF.groupby('category').agg(sum)

输出:

             arraydata
category 1   [50 70 90]
         2   [20 30 40]

但是,由于我的真实数据框有多个数字列,因此未选择 arraydata 作为默认列进行聚合,我必须手动选择它。这是我尝试过的一种方法:

g=DF.groupby('category')
g.agg({'arraydata':sum})

这是另一个:

g=DF.groupby('category')
g['arraydata'].agg(sum)

两者都给出相同的输出:

Exception: must produce aggregated value

但是,如果我有一列使用数字而不是数组数据,它就可以正常工作。我可以解决这个问题,但这很令人困惑,我想知道这是一个错误,还是我做错了什么。我觉得在这里使用数组可能有点边缘情况,并且确实不确定它们是否受支持。想法?

谢谢

【问题讨论】:

  • 在风格上,全大写的变量名通常表示“常量”变量(大写/驼峰式命名往往是为类保留的)。使用 pythonic 风格会让其他人更容易阅读你的代码 - PEP8 has guidelines on this.
  • 请注意,这仅适用于 2017 年。
  • @Ieezu 会不会在 2021 年再次停止工作?

标签: python numpy pandas aggregation


【解决方案1】:

一种可能更笨重的方法是遍历GroupBy 对象(它会生成(grouping_value, df_subgroup) 元组。例如,要在此处实现您想要的,您可以这样做:

grouped = DF.groupby("category")
aggregate = list((k, v["arraydata"].sum()) for k, v in grouped)
new_df = pd.DataFrame(aggregate, columns=["category", "arraydata"]).set_index("category")

这与 pandas 在后台所做的非常相似 [groupby,然后进行一些聚合,然后重新合并],因此您并没有真正失去太多。


深入了解内部

这里的问题是 pandas 明确检查输出 notndarray,因为它想智能地重塑您的数组,正如您在 _aggregate_named 的这个 sn-p 中看到的那样发生错误的地方。

def _aggregate_named(self, func, *args, **kwargs):
    result = {}

    for name, group in self:
        group.name = name
        output = func(group, *args, **kwargs)
        if isinstance(output, np.ndarray):
            raise Exception('Must produce aggregated value')
        result[name] = self._try_cast(output, group)

    return result

我的猜测是,这是因为 groupby 被明确设置为尝试智能地将具有相同索引的 DataFrame 重新组合在一起,并且所有内容都很好地对齐。由于在 DataFrame 中很少有嵌套数组,因此它会检查 ndarrays 以确保您实际使用的是聚合函数。在我的直觉中,这感觉像是 Panel 的工作,但我不确定如何完美地转换它。顺便说一句,您可以通过将输出转换为列表来回避这个问题,如下所示:

DF.groupby("category").agg({"arraydata": lambda x: list(x.sum())})

Pandas 不会抱怨,因为现在您有了一个 Python 对象数组。 [但这实际上只是在类型检查中作弊]。如果你想转换回数组,只需将np.array 应用到它。

result = DF.groupby("category").agg({"arraydata": lambda x: list(x.sum())})
result["arraydata"] = result["arraydata"].apply(np.array)

你想如何解决这个问题实际上取决于为什么你有ndarray的列,以及你是否想同时聚合其他任何东西。也就是说,您始终可以像上面显示的那样遍历GroupBy

【讨论】:

  • 有趣。将数组填充到数据框中是一种快速解决方法,但结果却是麻烦多于其价值,所以我想我会找到另一种方法。
【解决方案2】:

如果您这样做,Pandas 的工作效率会更高(例如,按照您的建议使用数字数据)。另一种选择是对这种多维数据使用Panel 对象。

也就是说,这看起来像一个错误,引发异常纯粹是因为结果是一个数组:

Exception: Must produce aggregated value

In [11]: %debug
> /Users/234BroadWalk/pandas/pandas/core/groupby.py(1511)_aggregate_named()
   1510             if isinstance(output, np.ndarray):
-> 1511                 raise Exception('Must produce aggregated value')
   1512             result[name] = self._try_cast(output, group)

ipdb> output
array([50, 70, 90])

如果你不顾一切地从源代码中删除这两行,它会按预期工作:

In [99]: g.agg(sum)
Out[99]:
             arraydata
category
1         [50, 70, 90]
2         [20, 30, 40]

注意:几乎可以肯定他们在那里是有原因的......

【讨论】:

  • 旁注:要制作 Panel 对象,您还需要将类别广播为 2d。所以每个输入都需要是[1,1,1], [20,30,40],等等。我对面板的经验为 0,所以我无法发表评论。
  • 有趣的是,我得到的唯一失败的测试是没有引发异常......以及示例“工作”。
  • 是的,我对此完全不确定。可能只是他们想保持groupbytransform 之间的关注点分离?或者可能是检查聚合是否按照您的预期进行? (即,要让 groupby 以您想要的方式工作,它需要返回一个“标量”值)
【解决方案3】:

由于 sum 函数仅迭代行,或者 sum 函数仅计算沿第一个轴的总和。 你可以定义一个聚合函数:

def mySum(dataframe):

    return np.sum(np.sum(dataframe))

然后把这个函数传入agg()

DF.groupby('category').agg(mySum)

【讨论】:

    猜你喜欢
    • 2022-11-18
    • 2017-07-19
    • 2017-08-08
    • 2022-11-10
    • 2019-01-12
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    • 2015-08-23
    相关资源
    最近更新 更多