Включает в себя: условное сумму
У меня есть следующий массив 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
4 ответов:
Вы можете сделать это в чистом numpy, используя умное приложение
np.diffи ещеnp.add.reduceat.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и одно сitertoolsimport 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]])
Вы также можете использовать
itertoolsnp.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]]
Comments