4

我正在尝试构建一个返回数组元素子集乘积的函数。基本上我想构建一个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)
4

5 回答 5

2

如果您的组已经排序(如果不是,您可以使用 snp.argsort执行此操作),您可以使用 s 的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 循环中进行切片。

当然,pandas 很方便地进行这些操作。

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

于 2012-11-16T20:04:18.497 回答
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 了,除了那些零点,这很容易修复:

prod(where(mask*val,mask*val,1),axis=1)
array([ 6,  4, 30])
于 2012-11-16T20:03:52.467 回答
1

正如评论中所建议的,您也可以使用Pandas 模块。使用该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列和applynumpy的功能prod()

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

这为您提供了所需的输出:

1     6
2     4
3    30
于 2016-06-30T23:11:50.440 回答
0

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

np.array([np.product(values[np.flatnonzero(groups == x)]) for x in np.unique(groups)])
于 2012-11-16T20:02:31.340 回答
0

这不是一个 numpy 解决方案,但它相当易读(我发现有时 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]
于 2012-11-16T20:07:44.743 回答