我有以下numpy数组:
import numpy as np
arr = np.array([[1,2,3,4,2000],
[5,6,7,8,2000],
[9,0,1,2,2001],
[3,4,5,6,2001],
[7,8,9,0,2002],
[1,2,3,4,2002],
[5,6,7,8,2003],
[9,0,1,2,2003]
])
我理解np.sum(arr, axis=0)
提供结果:
array([ 40, 28, 36, 34, 16012])
我想做什么(没有for循环)是根据最后一列的值对列进行求和,以便提供的结果为:
array([[ 6, 8, 10, 12, 4000],
[ 12, 4, 6, 8, 4002],
[ 8, 10, 12, 4, 4004],
[ 14, 6, 8, 10, 4006]])
我意识到没有循环可能是一个延伸,但希望最好......
如果必须使用for循环,那该怎么办?
我尝试了np.sum(arr[:, 4]==2000, axis=0)
(我会将2000
替换为for循环中的变量),但是它给出了 2
答案 0 :(得分:3)
我发布了一个简单的解决方案pandas
和一个itertools
import pandas as pd
df = pd.DataFrame(arr)
x = df.groupby(4).sum().reset_index()[range(5)] #range(5) adjusts ordering
x[4] *= 2
np.array(x)
array([[ 6, 8, 10, 12, 4000],
[ 12, 4, 6, 8, 4002],
[ 8, 10, 12, 4, 4004],
[ 14, 6, 8, 10, 4006]])
您也可以使用itertools
np.array([sum(x[1]) for x in itertools.groupby(arr, key = lambda k: k[-1])])
array([[ 6, 8, 10, 12, 4000],
[ 12, 4, 6, 8, 4002],
[ 8, 10, 12, 4, 4004],
[ 14, 6, 8, 10, 4006]])
答案 1 :(得分:2)
方法#1:基于NumPy的总和减少
以下是基于np.add.reduceat
-
def groupbycol(a, assume_sorted_col=False, colID=-1):
if assume_sorted_col==0:
# If a is not already sorted by that col, use argsort indices for
# that colID and re-arrange rows accordingly
sidx = a[:,colID].argsort()
a_s = a[sidx] # sorted by colID col of input array
else:
a_s = a
# Get group shifting indices
cut_idx = np.flatnonzero(np.r_[True, a_s[1:,colID] != a_s[:-1,colID]])
# Use those indices to setup sum reduction at intervals along first axis
return np.add.reduceat(a_s, cut_idx, axis=0)
示例运行 -
In [64]: arr
Out[64]:
array([[ 1, 2, 3, 4, 2000],
[ 5, 6, 7, 8, 2000],
[ 9, 0, 1, 2, 2001],
[ 3, 4, 5, 6, 2001],
[ 7, 8, 9, 0, 2002],
[ 1, 2, 3, 4, 2002],
[ 5, 6, 7, 8, 2003],
[ 9, 0, 1, 2, 2003]])
In [65]: # Shuffle rows off input array to create a generic last col (not sorted)
...: np.random.seed(0)
...: np.random.shuffle(arr)
In [66]: arr
Out[66]:
array([[ 5, 6, 7, 8, 2003],
[ 9, 0, 1, 2, 2001],
[ 5, 6, 7, 8, 2000],
[ 9, 0, 1, 2, 2003],
[ 3, 4, 5, 6, 2001],
[ 1, 2, 3, 4, 2000],
[ 1, 2, 3, 4, 2002],
[ 7, 8, 9, 0, 2002]])
In [67]: groupbycol(arr, assume_sorted_col=False, colID=-1)
Out[67]:
array([[ 6, 8, 10, 12, 4000],
[ 12, 4, 6, 8, 4002],
[ 8, 10, 12, 4, 4004],
[ 14, 6, 8, 10, 4006]])
方法#2:利用矩阵 - 乘法
我们基本上可以用广播掩码创建+矩阵乘法替换np.add.reduceat
,因此利用快速BLAS,这也适用于通用的非排序列 -
import pandas as pd
def groupbycol_matmul(a, colID=-1):
mask = pd.Series(a[:,colID]).unique()[:,None] == arr[:,colID]
return mask.dot(arr)
答案 2 :(得分:2)
您可以使用np.diff
和np.add.reduceat
的聪明应用程序在纯粹的numpy中执行此操作。 np.diff
将为您提供最右列更改的索引:
d = np.diff(arr[:, -1])
np.where
会将您的布尔索引d
转换为np.add.reduceat
期望的整数索引:
d = np.where(d)[0]
reduceat
也期望看到零索引,并且所有内容都需要移动一个:
indices = np.r_[0, e + 1]
这里使用np.r_
比np.concatenate
更方便,因为它允许使用标量。总和然后变成:
result = np.add.reduceat(arr, indices, axis=0)
当然,这可以合并成一行:
>>> result = np.add.reduceat(arr, np.r_[0, np.where(np.diff(arr[:, -1]))[0] + 1], axis=0)
>>> result
array([[ 6, 8, 10, 12, 4000],
[ 12, 4, 6, 8, 4002],
[ 8, 10, 12, 4, 4004],
[ 14, 6, 8, 10, 4006]])
答案 3 :(得分:0)
您可能需要查看numpy_indexed
。有了它,你可以做到:
import numpy as np
import numpy_indexed as npi
arr = np.array([[1,2,3,4,2000],
[5,6,7,8,2000],
[9,0,1,2,2001],
[3,4,5,6,2001],
[7,8,9,0,2002],
[1,2,3,4,2002],
[5,6,7,8,2003],
[9,0,1,2,2003]
])
result = npi.GroupBy(arr[:, 4]).sum(arr)[1]
>>>[[ 6 8 10 12 4000]
[ 12 4 6 8 4002]
[ 8 10 12 4 4004]
[ 14 6 8 10 4006]]