我正在尝试构建一个返回数组元素子集产品的函数。基本上我想构建一个执行此操作的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)
答案 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]