2010-12-07 3 views
15

Предполагая, что у меня есть массив numpy: [1,2,3,4,5,6] и еще один массив: [0,0,1,2,2,1] Я хочу суммировать элементы в первом массиве по группе (второй массив) и получить результаты n-групп в порядке порядкового номера группы (в этом случае результат будет [3, 9, 9]). Как это сделать в numpy?Сумма массива по номеру в numpy

+0

Зачем вам нужно numpy для этого? Разве вы не используете списки ванильных питонов? Если нет, какой тип numpy вы используете? – 2010-12-07 05:19:40

+1

Мне нужно numpy для этого, потому что я не хочу прокручивать массив n-times для n групп, так как размеры массива могут быть сколь угодно большими. Я не использую списки python, я просто показывал пример данных в скобках. Тип данных - int. – 2010-12-07 05:31:37

+0

http://www.stackoverflow.com/questions/7089379/most-efficient-way-to-sum-huge-2d-numpy-array-grouped-by-id-column – TooTone 2014-04-11 10:42:59

ответ

8

Там больше, чем один из способов сделать это, но вот один из способов:

import numpy as np 
data = np.arange(1, 7) 
groups = np.array([0,0,1,2,2,1]) 

unique_groups = np.unique(groups) 
sums = [] 
for group in unique_groups: 
    sums.append(data[groups == group].sum()) 

Вы можете Vectorize вещи так, что там нет для петли на всех, но я рекомендовал бы против него. Он становится нечитаемым и потребует пару временных временных массивов 2D, которые могут потребовать больших объемов памяти, если у вас много данных.

Редактировать: Вот один из способов, которым вы можете полностью векторизовать. Имейте в виду, что это может (и, скорее всего, будет) медленнее, чем версия выше. (И может быть лучший способ его векторизации, но уже поздно, и я устал, так что это первое, что появляется в моей голове ...)

Однако имейте в виду, что это плохой пример ... Вы действительно лучше (как с точки зрения скорости и читаемости) с указанным выше петли ...

import numpy as np 
data = np.arange(1, 7) 
groups = np.array([0,0,1,2,2,1]) 

unique_groups = np.unique(groups) 

# Forgive the bad naming here... 
# I can't think of more descriptive variable names at the moment... 
x, y = np.meshgrid(groups, unique_groups) 
data_stack = np.tile(data, (unique_groups.size, 1)) 

data_in_group = np.zeros_like(data_stack) 
data_in_group[x==y] = data_stack[x==y] 

sums = data_in_group.sum(axis=1) 
+0

Спасибо! Память не проблема, и я бы хотел избежать циклов. Как бы вы это обозначили? – 2010-12-07 05:57:15

+0

@Scribble Master - см. Редактирование ... Однако нет ничего плохого в том, чтобы перебирать уникальные группы. Вторая версия, вероятно, будет медленной, и чертовски трудно читать. С циклом вы только зацикливаете (в python, во всяком случае) на количество уникальных групп. Внутреннее сравнение `data [groups == group]` будет довольно быстрым. – 2010-12-07 06:14:38

0

чистая реализация питон:

l = [1,2,3,4,5,6] 
g = [0,0,1,2,2,1] 

from itertools import izip 
from operator import itemgetter 
from collections import defaultdict 

def group_sum(l, g): 
    groups = defaultdict(int) 
    for li, gi in izip(l, g): 
     groups[gi] += li 
    return map(itemgetter(1), sorted(groups.iteritems())) 

print group_sum(l, g) 

[3, 9, 9] 
7

Если группы индексированные целыми целыми числами, вы можете злоупотреблять функцией numpy.histogram(), чтобы получить результат:

data = numpy.arange(1, 7) 
groups = numpy.array([0,0,1,2,2,1]) 
sums = numpy.histogram(groups, 
         bins=numpy.arange(groups.min(), groups.max()+2), 
         weights=data)[0] 
# array([3, 9, 9]) 

Это позволит избежать любых петель Python.

24

Это векторный метод выполнения этой суммы, основанный на реализации numpy.unique. По моим таймингам это до 500 раз быстрее, чем метод петли и в 100 раз быстрее, чем метод гистограммы.

def sum_by_group(values, groups): 
    order = np.argsort(groups) 
    groups = groups[order] 
    values = values[order] 
    values.cumsum(out=values) 
    index = np.ones(len(groups), 'bool') 
    index[:-1] = groups[1:] != groups[:-1] 
    values = values[index] 
    groups = groups[index] 
    values[1:] = values[1:] - values[:-1] 
    return values, groups 
22

NumPy функция bincount была сделана именно для этой цели, и я уверен, что это будет гораздо быстрее, чем другие методы для всех размеров входов:

data = [1,2,3,4,5,6] 
ids = [0,0,1,2,2,1] 

np.bincount(ids, weights=data) #returns [3,9,9] as a float64 array 

Я-й элемент выход представляет собой сумму всех элементов data, соответствующих «id» i.

Надеюсь, что это поможет.

4

Я попробовал сценарии из всех, и мои соображения:

Джо: Будет работать только если у вас есть несколько групп.

kevpie: Слишком медленно из-за петель (это не вещий способ)

Bi_Rico и Sven: выполнять хорошо, но будет работать только для Int32 (если сумма берется по 2^32/2 она не будет выполнена)

Alex: самый быстрый, полезный для суммы.

Но если вы хотите больше гибкости и возможности группировать по другой статистике использование SciPy:

from scipy import ndimage 

data = np.arange(10000000) 
groups = np.arange(1000).repeat(10000) 
ndimage.sum(data, groups, range(1000)) 

Это хорошо, потому что у вас есть много статистических данных для группы (суммы, среднего значения, дисперсии, ...).

0

я заметил numpy тег, но в случае, если вы не возражаете против использования pandas, эта задача становится один вкладыш:

import pandas as pd 
import numpy as np 

data = np.arange(1, 7) 
groups = np.array([0, 0, 1, 2, 2, 1]) 

df = pd.DataFrame({'data': data, 'groups': groups}) 

Так df будет выглядеть следующим образом:

data groups 
0  1  0 
1  2  0 
2  3  1 
3  4  2 
4  5  2 
5  6  1 

сейчас вы можете использовать функции groupby() и sum()

print df.groupby(['groups'], sort=False).sum() 

, который дает желаемый результат

 data 
groups  
0   3 
1   9 
2   9 

По умолчанию dataframe будет отсортирован, поэтому я использую флаг sort=False, которые могли бы улучшить скорость для огромных dataframes.

2

Вы все ошибаетесь! Наилучший способ сделать это:

a = [1,2,3,4,5,6] 
ix = [0,0,1,2,2,1] 
accum = np.zeros(np.max(ix)+1) 
np.add.at(accum, ix, a) 
print accum 
> array([ 3., 9., 9.]) 
Смежные вопросы