python-numpy按组对数组中的值进行聚合

bf1o4zei  于 2022-12-13  发布在  Python
关注(0)|答案(1)|浏览(169)

假设有一个2D数组,它被分成几个子区域,如regions。还有一个数组,里面充满了值。我想按子区域聚合值。下面的代码是我的解决方案。
但是,当子区域的数量很大时,迭代花费很多时间。我想问一下,有没有什么方法可以加速程序?我想也许numpy可以做到这一点,但我不知道如何做到。

import numpy as np

regions = np.array([[0,0,0,1],
                    [0,0,1,1],
                    [1,1,1,2],
                    [2,2,2,2]], dtype=np.int32)
value_array = np.array([[9,5,8,4],
                        [6,4,8,5],
                        [4,5,9,7],
                        [4,7,3,0]], dtype=np.float32)

aggre_array = np.zeros_like(value_array)
for r in range(regions.max()+1):
    region = regions==r
    aggre_array[region] = value_array[region].mean()
print(aggre_array)
'''output
[[6.4       6.4       6.4       5.8333335]
 [6.4       6.4       5.8333335 5.8333335]
 [5.8333335 5.8333335 5.8333335 4.2      ]
 [4.2       4.2       4.2       4.2      ]]
'''
4zcjmb1e

4zcjmb1e1#

在这样的分组中,你需要使用按索引排序的数组的扁平化变体来对第一个数组进行排序,比如:

regions_sort = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2]
value_array_sort = [9, 5, 8, 6, 4, 4, 8, 5, 4, 5, 9, 7, 4, 7, 3, 0]

后一部分是找出区分各组的指数,并将其用于进一步计算各组的总和、计数和平均值:

marker_idx = [5, 11]
group_counts = [5, 6, 5]
group_sums = [32, 35, 21]
group_means = [6.4, 5.83333, 4.2]

最后,重复这些值,使其适合value_array,按相反顺序重新排列这些值,并将其调整为初始形状。

sorter = np.argsort(regions.ravel())
_, inverse_sorter = np.unique(sorter, return_index=True) #could be optimised...
regions_sort = regions.ravel()[sorter]
value_array_sort = value_array.ravel()[sorter]

marker_idx = np.flatnonzero(np.diff(regions_sort))+1
reduceat_idx = np.r_[0, marker_idx]
group_counts = np.diff(marker_idx, prepend=0, append=regions.size) #could also use np.bincount...
group_sums = np.add.reduceat(regions_sort, reduceat_idx)
group_means = group_sums / group_counts

new_values = np.repeat(group_means, group_counts)
new_value_array = new_values[inverse_sorter].reshape(value_array.shape)

>>> new_value_array    
array([[6.4       , 6.4       , 6.4       , 5.83333333],
       [6.4       , 6.4       , 5.83333333, 5.83333333],
       [5.83333333, 5.83333333, 5.83333333, 4.2       ],
       [4.2       , 4.2       , 4.2       , 4.2       ]])

我还找到了一种方法来实现它numpy_indexed包设计用于以有效的方式解决分组问题:

import numpy_indexed as npi
groupby = npi.group_by(regions.ravel())
keys, values = groupby.mean(value_array.ravel())
>>> values[groupby.inverse].reshape(regions.shape)

array([[6.4       , 6.4       , 6.4       , 5.83333333],
       [6.4       , 6.4       , 5.83333333, 5.83333333],
       [5.83333333, 5.83333333, 5.83333333, 4.2       ],
       [4.2       , 4.2       , 4.2       , 4.2       ]])

相关问题