数字:条件和



我有以下 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)(我将用 for 循环中的变量替换2000(,但它给出了2

您可以使用巧妙的np.diffnp.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]])

我正在发布一个简单的解决方案,其中包含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:基于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)

你可能想看看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]]

最新更新