numpy中的组数组元素的产品(Python)

时间:2012-11-16 19:48:11

标签: python numpy

我正在尝试构建一个返回数组元素子集产品的函数。基本上我想构建一个执行此操作的prod_by_group函数:

values = np.array([1, 2, 3, 4, 5, 6])
groups = np.array([1, 1, 1, 2, 3, 3])

Vprods = prod_by_group(values, groups)

结果Vprods应为:

Vprods
array([6, 4, 30])

这里有一个很好的答案,我认为它应该类似于以下元素: https://stackoverflow.com/a/4387453/1085691

我首先尝试log,然后sum_by_group,然后exp,但遇到了数字问题。

此处还有一些其他类似的答案,分组的最小和最大元素: https://stackoverflow.com/a/8623168/1085691

编辑:感谢您的快速解答!我正在尝试它们。我应该补充一点,我希望它尽可能快(这就是我试图以某种矢量化的方式将它变为numpy的原因,就像我给出的例子一样)。

编辑:我评估了到目前为止给出的所有答案,最好的答案由@seberg给出。这是我最终使用的全部功能:

def prod_by_group(values, groups):
    order = np.argsort(groups)
    groups = groups[order]
    values = values[order]
    group_changes = np.concatenate(([0], np.where(groups[:-1] != groups[1:])[0] + 1))
    return np.multiply.reduceat(values, group_changes)

5 个答案:

答案 0 :(得分:2)

如果您的群组已经排序(如果不是,您可以使用np.argsort执行此操作),则可以使用reduceat功能ufunc来执行此操作(如果他们不是排序后,你必须先对它们进行排序以便有效地进行排序):

# you could do the group_changes somewhat faster if you care a lot
group_changes = np.concatenate(([0], np.where(groups[:-1] != groups[1:])[0] + 1))
Vprods = np.multiply.reduceat(values, group_changes)

如果您的团体很少,或者mgilson回答。但是如果你有很多小组,那么效率会更高。因为您为每个组避免了原始数组中每个元素的布尔索引。另外,你可以避免使用reduceat在python循环中切片。

当然,大熊猫可以方便地进行这些操作。

编辑:抱歉,prod在那里。 ufunc是multiply。您可以将此方法用于任何二进制文件ufunc。这意味着它适用于基本上所有numpy函数,它们可以在两个输入数组上以元素方式工作。 (即,乘法通常将两个数组元素相乘,添加它们,最大值/最小值等等。)

答案 1 :(得分:1)

首先为组设置掩码,以便在另一个维度中展开组

mask=(groups==unique(groups).reshape(-1,1))
mask
array([[ True,  True,  True, False, False, False],
       [False, False, False,  True, False, False],
       [False, False, False, False,  True,  True]], dtype=bool)

现在我们乘以val

mask*val
array([[1, 2, 3, 0, 0, 0],
       [0, 0, 0, 4, 0, 0],
       [0, 0, 0, 0, 5, 6]])

现在你已经可以沿着轴1做刺激,除了那些容易修复的零点之外:

prod(where(mask*val,mask*val,1),axis=1)
array([ 6,  4, 30])

答案 2 :(得分:1)

根据评论中的建议,您还可以使用Pandas module。使用grouby()函数,此任务变为单行:

import numpy as np
import pandas as pd

values = np.array([1, 2, 3, 4, 5, 6])
groups = np.array([1, 1, 1, 2, 3, 3])

df = pd.DataFrame({'values': values, 'groups': groups})

所以df如下所示:

   groups  values
0       1       1
1       1       2
2       1       3
3       2       4
4       3       5
5       3       6

现在,您可以groupby() groups列和apply numpy的prod()功能为每个群组添加

 df.groupby(groups)['values'].apply(np.prod)

为您提供所需的输出:

1     6
2     4
3    30

答案 3 :(得分:0)

嗯,我怀疑这是一个很好的答案,但这是我能想到的最好的答案:

np.array([np.product(values[np.flatnonzero(groups == x)]) for x in np.unique(groups)])

答案 4 :(得分:0)

这不是一个笨拙的解决方案,但它具有相当的可读性(我发现有时候numpy解决方案不是!):

from operator import itemgetter, mul
from itertools import groupby

grouped = groupby(zip(groups, values), itemgetter(0))
groups = [reduce(mul, map(itemgetter(1), vals), 1) for key, vals in grouped]
print groups
# [6, 4, 30]