2016-04-15 5 views
8

Ich habe zwei 2D-Nummernfelder (in diesem Beispiel in Bezug auf Größe und Inhalt vereinfacht) mit identischen Größen.Summieren von Daten aus Array basierend auf anderen Array in Numpy

Eine ID-Matrix:

1 1 1 2 2 
1 1 2 2 5 
1 1 2 5 5 
1 2 2 5 5 
2 2 5 5 5 

und eine Wertematrix:

14.8 17.0 74.3 40.3 90.2 
25.2 75.9 5.6 40.0 33.7 
78.9 39.3 11.3 63.6 56.7 
11.4 75.7 78.4 88.7 58.6 
79.6 32.3 35.3 52.5 13.3 

Mein Ziel ist es, Zählung und Summe die Werte aus der zweiten Matrix durch die IDs von den gruppierten erste Matrix:

1: (8, 336.8) 
2: (9, 453.4) 
5: (8, 402.4) 

Ich kann dies in einer for Schleife tun, aber wenn die Matrizen Größen in Tausend statt nur 5x5 und Tausende von eindeutigen IDs haben, dauert es eine Menge Zeit zu verarbeiten.

Hat numpy eine clevere Methode oder eine Kombination von Methoden dafür?

Antwort

5

hier ein vektorisiert Ansatz die Zählungen für ID und ID-based summierten Werte für value mit einer Kombination aus np.unique und np.bincount zu bekommen -

unqID,idx,IDsums = np.unique(ID,return_counts=True,return_inverse=True) 

value_sums = np.bincount(idx,value.ravel()) 

Um die endgültigen Ausgabe als ein Wörterbuch zu erhalten, können Sie die Loop-Verständnis verwenden zu sammeln, um die summierten Werte, wie so -

{i:(IDsums[itr],value_sums[itr]) for itr,i in enumerate(unqID)} 

Probelauf -

In [86]: ID 
Out[86]: 
array([[1, 1, 1, 2, 2], 
     [1, 1, 2, 2, 5], 
     [1, 1, 2, 5, 5], 
     [1, 2, 2, 5, 5], 
     [2, 2, 5, 5, 5]]) 

In [87]: value 
Out[87]: 
array([[ 14.8, 17. , 74.3, 40.3, 90.2], 
     [ 25.2, 75.9, 5.6, 40. , 33.7], 
     [ 78.9, 39.3, 11.3, 63.6, 56.7], 
     [ 11.4, 75.7, 78.4, 88.7, 58.6], 
     [ 79.6, 32.3, 35.3, 52.5, 13.3]]) 

In [88]: unqID,idx,IDsums = np.unique(ID,return_counts=True,return_inverse=True) 
    ...: value_sums = np.bincount(idx,value.ravel()) 
    ...: 

In [89]: {i:(IDsums[itr],value_sums[itr]) for itr,i in enumerate(unqID)} 
Out[89]: 
{1: (8, 336.80000000000001), 
2: (9, 453.40000000000003), 
5: (8, 402.40000000000003)} 
+1

Nice one! Ich war mir der 'return_ *' Argumente für 'np.unique' nicht bewusst. – kazemakase

+1

@Divakar: Danke! Dies war genau die Art von Lösung, die ich aufgrund der Vektorisierung mit einer guten Leistung suchte. – Chau

1

Dies ist möglich, mit einer Kombination aus einigen einfachen Methoden:

  1. Verwendung numpy.unique jede ID für jede ID
  2. erstellen boolean Maske
  3. Summe der 1s in der Maske zu finden (count) und die Werte, wo die Maske 1

Dies kann so aussehen:

import numpy as np 

ids = np.array([[1, 1, 1, 2, 2], 
       [1, 1, 2, 2, 5], 
       [1, 1, 2, 5, 5], 
       [1, 2, 2, 5, 5], 
       [2, 2, 5, 5, 5]]) 

values = np.array([[14.8, 17.0, 74.3, 40.3, 90.2], 
        [25.2, 75.9, 5.6, 40.0, 33.7], 
        [78.9, 39.3, 11.3, 63.6, 56.7], 
        [11.4, 75.7, 78.4, 88.7, 58.6], 
        [79.6, 32.3, 35.3, 52.5, 13.3]]) 


for i in np.unique(ids): # loop through all IDs 
    mask = ids == i # find entries that match current ID 
    count = np.sum(mask) # number of matches 
    total = np.sum(values[mask]) # values of matches 
    print('{}: ({}, {:.1f})'.format(i, count, total)) #print result 

# Output: 
# 1: (8, 336.8) 
# 2: (9, 453.4) 
# 5: (8, 402.4) 
+0

Es ist genau diese fiese 'for' Schleife, auf die ich mich in meiner Frage beziehe, ich hätte das aber deutlicher machen sollen. – Chau

+0

Ich denke, es gibt keine wirklich gute Möglichkeit, das ohne die for-Schleife zu machen. Es ist zwar möglich, würde aber wahrscheinlich zu sehr unlesbarem Code führen. Wenn Sie nur ein paar eindeutige IDs haben, sollte die for-Schleife nicht zu groß sein. Wie auch immer, ich werde eine Weile darüber nachdenken ... – kazemakase

+0

Sieht so aus, als hätte ich gerade in [Divakars Antwort] Unrecht bewiesen (http: // stackoverflow.com/a/36643601/3005167). – kazemakase

0

Das numpy_indexed Paket (Disclaimer: Ich bin sein Autor) hat Funktionalität diese Art von Problemen in einer eleganten und vektorisiert Weise zu lösen:

import numpy_indexed as npi 
group_by = npi.group_by(ID.flatten()) 
ID_unique, value_sums = group_by.sum(value.flatten()) 
ID_count = groupy_by.count  

Hinweis: Wenn Sie die Summe berechnet werden sollen und zählen in Um einen Mittelwert zu berechnen, gibt es auch group_by.mean; plus viele andere nützliche Funktionen.

Verwandte Themen