【问题标题】:pythonic way to aggregate arrays (numpy or not)聚合数组的pythonic方法(numpy与否)
【发布时间】:2009-12-01 22:17:54
【问题描述】:

我想做一个很好的函数来聚合数组中的数据(它是一个 numpy 记录数组,但它不会改变任何东西)

您想要在一个轴上聚合一组数据:例如,dtype=[(name, (np.str_,8), (job, (np.str_,8), (income, np.uint32)] 的数组,并且您希望获得每个工作的平均收入

我做了这个函数,在示例中它应该被称为aggregate(data,'job','income',mean)


def aggregate(data, key, value, func):

    data_per_key = {}

    for k,v in zip(data[key], data[value]):

        if k not in data_per_key.keys():

            data_per_key[k]=[]

        data_per_key[k].append(v)

    return [(k,func(data_per_key[k])) for k in data_per_key.keys()]

问题是我觉得它不是很好我想把它放在一行中:你有什么想法吗?

感谢路易斯的回答

PS:我想在调用中保留 func,以便您也可以要求中位数、最小值...

【问题讨论】:

  • 我不知道 numpy,但你的 dtype 的括号似乎有问题..
  • 括号不匹配。造成一些额外的混乱。
  • 我不理解您的评论,即您“希望将它放在一行中”。当您调用该函数时,这将是一行。函数本身有多少行是否重要?无论如何,我认为您最好的选择是使用defaultdict 作为答案。
  • 对于不匹配的问题,我将名称和类型更改为明确并忘记了一些括号......在 matplotlib.mlab 答案中的 1 行中
  • Michael 并且我创建了一个名为 numpy-groupies 的包,其中包含一个函数。包在 pypi 上。

标签: python arrays numpy aggregate


【解决方案1】:

也许你要找的函数是matplotlib.mlab.rec_groupby:

import matplotlib.mlab

data=np.array(
    [('Aaron','Digger',1),
     ('Bill','Planter',2),
     ('Carl','Waterer',3),
     ('Darlene','Planter',3),
     ('Earl','Digger',7)],
    dtype=[('name', np.str_,8), ('job', np.str_,8), ('income', np.uint32)])

result=matplotlib.mlab.rec_groupby(data, ('job',), (('income',np.mean,'avg_income'),))

产量

('Digger', 4.0)
('Planter', 2.5)
('Waterer', 3.0)

matplotlib.mlab.rec_groupby 返回一个重新数组:

print(result.dtype)
# [('job', '|S7'), ('avg_income', '<f8')]

您可能也有兴趣查看pandas,其中甚至还有more versatile facilities 用于处理group-by operations

【讨论】:

  • 这正是我所寻找的:一行完成的工作!而且它直接返回一个数组!完美!
【解决方案2】:

您的if k not in data_per_key.keys() 可以重写为if k not in data_per_key,但您可以使用defaultdict 做得更好。这是一个使用defaultdict 摆脱存在检查的版本:

import collections

def aggregate(data, key, value, func):
    data_per_key = collections.defaultdict(list)
    for k,v in zip(data[key], data[value]):
        data_per_key[k].append(v)

    return [(k,func(data_per_key[k])) for k in data_per_key.keys()]

【讨论】:

  • 我会把最后一行改成return [(k,f(v)) for k,v in data_per_key.items()]
  • 这是一个很好的电话,但我试图通过将 defaultdict 设为唯一的更改来突出显示该内容。不过,你的回报肯定会更好。
  • 感谢 defaultdict 技巧!以及最后的迭代
【解决方案3】:

Here 是一个很好地模拟了 matlabs accumarray 功能的配方。它很好地使用了 python 迭代器,但是,与 matlab 实现相比,它在性能方面很糟糕。因为我遇到了同样的问题,所以我使用scipy.weave 编写了一个实现。你可以在这里找到它:https://github.com/ml31415/accumarray

【讨论】:

    【解决方案4】:

    最好的灵活性和可读性是使用pandas

    import pandas
    
    data=np.array(
        [('Aaron','Digger',1),
         ('Bill','Planter',2),
         ('Carl','Waterer',3),
         ('Darlene','Planter',3),
         ('Earl','Digger',7)],
        dtype=[('name', np.str_,8), ('job', np.str_,8), ('income', np.uint32)])
    
    df = pandas.DataFrame(data)
    result = df.groupby('job').mean()
    

    收益:

             income
    job
    Digger      4.0
    Planter     2.5
    Waterer     3.0
    

    Pandas DataFrame 是一个很棒的类,但您可以根据需要取回结果:

    result.to_records()
    result.to_dict()
    result.to_csv()
    

    等等……

    【讨论】:

    • pandas 比我上面给出的解决方案慢一个数量级。看看那里的速度比较。
    • @Michael,抱歉,实际上我不是指性能,我知道 pandas 不是一个以性能为目标的库,我自己更喜欢使用 bincount 之类的方法来提高性能。我已经编辑了原始帖子。
    【解决方案5】:

    使用来自scipyndimage.mean 可获得最佳性能。对于这个小数据集,这将比接受的答案快两倍,对于更大的输入,速度会快约 3.5 倍:

    from scipy import ndimage
    
    data=np.array(
        [('Aaron','Digger',1),
         ('Bill','Planter',2),
         ('Carl','Waterer',3),
         ('Darlene','Planter',3),
         ('Earl','Digger',7)],
        dtype=[('name', np.str_,8), ('job', np.str_,8), ('income', np.uint32)])
    
    unique = np.unique(data['job'])
    result=np.dstack([unique, ndimage.mean(data['income'], data['job'], unique)])
    

    将屈服于:

    array([[['Digger', '4.0'],
            ['Planter', '2.5'],
            ['Waterer', '3.0']]],
          dtype='|S32')
    

    编辑:使用 bincount(更快!)

    这比小示例输入的公认答案快约 5 倍,如果您重复数据 100000 次,它将快约 8.5 倍:

    unique, uniqueInd, uniqueCount = np.unique(data['job'], return_inverse=True, return_counts=True)
    means = np.bincount(uniqueInd, data['income'])/uniqueCount
    return np.dstack([unique, means])
    

    【讨论】:

      【解决方案6】:

      http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#dictionary-get-method

      应该有助于使它更漂亮,更pythonic,更高效。我稍后会回来检查您的进度。也许您可以考虑到这一点来编辑该功能?另请参阅接下来的几节。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-07
        • 1970-01-01
        • 1970-01-01
        • 2014-07-03
        • 1970-01-01
        • 2018-02-09
        • 2019-02-19
        相关资源
        最近更新 更多