Подход № 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)