2017-07-23 38 views
7

Mam dwa numpy tablice, jeden zawierający wartości i jeden zawierający każdą kategorię wartości.NumPy suma jednej tablicy na podstawie wartości w innej tablicy dla każdego pasującego elementu w trzeciej tablicy

values=np.array([1,2,3,4,5,6,7,8,9,10]) 
valcats=np.array([101,301,201,201,102,302,302,202,102,301]) 

Mam inną tablicę zawierającą unikalne kategorie, które chciałbym podsumować.

categories=np.array([101,102,201,202,301,302]) 

Mój problem polega na tym, że będę przeprowadzał ten sam proces sumowania kilka miliardów razy i każda mikrosekunda ma znaczenie.

Moje obecne wdrożenie jest następujące.

catsums=[] 
for x in categories: 
    catsums.append(np.sum(values[np.where(valcats==x)])) 

Powstałe catsums powinno być:

[1, 14, 7, 8, 12, 13] 

Mój obecny czas pracy wynosi około 5 ms. W Pythonie wciąż jestem trochę nowy i miałem nadzieję znaleźć szybkie rozwiązanie, potencjalnie łącząc dwie pierwsze tablice lub lamdbę lub coś fajnego, o czym nawet nie wiem.

Dzięki za przeczytanie!

+1

Jaki jest Twój oczekiwany wynik, biorąc pod uwagę przykład dałeś? – piRSquared

+0

Dodano do tekstu, dzięki za wskazanie tego niedopatrzenia! – hrschbck

+1

przegłosował twoje pytanie, masz teraz 15 przedstawicieli, nie wahaj się upvote i zaakceptuj @piRSquared odpowiedź – Wen

Odpowiedz

7

@Divakar właśnie wysłał bardzo dobrą odpowiedź. Jeśli masz już zdefiniowaną tablicę kategorii, skorzystam z odpowiedzi @ Divakara. Jeśli nie masz jeszcze określonych wartości, użyłbym mojego.


Chciałbym użyć pd.factorize do faktoryzacji kategorii. Następnie użyj np.bincount z weights parametru ustawiana na tablicę values

f, u = pd.factorize(valcats) 
np.bincount(f, values).astype(values.dtype) 

array([ 1, 12, 7, 14, 13, 8]) 

pd.factorize produkuje również unikalne wartości w zmiennej u. Możemy wyrównać wyniki z u, aby zobaczyć, że dotarliśmy do właściwego rozwiązania.

np.column_stack([u, np.bincount(f, values).astype(values.dtype)]) 

array([[101, 1], 
     [301, 12], 
     [201, 7], 
     [102, 14], 
     [302, 13], 
     [202, 8]]) 

Można uczynić to bardziej oczywiste przy użyciu pd.Series

f, u = pd.factorize(valcats) 
pd.Series(np.bincount(f, values).astype(values.dtype), u) 

101  1 
301 12 
201  7 
102 14 
302 13 
202  8 
dtype: int64 

Dlaczego pd.factorize i nie np.unique?

Mogliśmy zrobić to równoważnie z

u, f = np.unique(valcats, return_inverse=True) 

ale np.unique sortuje wartości i który działa w nlogn czasie. Z drugiej strony pd.factorize nie sortuje i działa w czasie liniowym. W przypadku większych zestawów danych dominować będzie wydajność pd.factorize.

+4

Przyjemne rozwiązanie ~ +1 – Wen

+0

Dziękuję @Wen (-: – piRSquared

+0

Bardzo dziękuję za wyjaśnienie! – Wen

7

Można użyć searchsorted i bincount -

np.bincount(np.searchsorted(categories, valcats), values) 
+0

Co byś dodał, gdyby tablica kategorii nie była już posortowana? – piRSquared

+0

@piRSquared Chciałbym posortować a następnie wprowadziłem go do rozwiązania: – Divakar

+0

Myślałem o tym dalej, przekazując parametr "sorter" ..., sorter = categories.argsort() ' – piRSquared