2009-12-01 2 views
5

Ich mag würde eine nette Funktion zu aggregierten Daten unter einem Array machen (es ist ein numpy Rekord-Array, aber es ändert nichts)pythonic Weg zu aggregieren Arrays (numpy oder nicht)

Sie haben eine Reihe von Daten, die Sie wollen unter einer Achse aggregieren: zum Beispiel eine Reihe von dtype=[(name, (np.str_,8), (job, (np.str_,8), (income, np.uint32)] und Sie das durchschnittliche Einkommen pro Job

ich diese Funktion habe haben wollen, und im Beispiel sollte es als aggregate(data,'job','income',mean)


genannt werden
def aggregate(data, key, value, func): 

    data_per_key = {} 

    for k,v in zip(data[key], data[value]): 

     if k not in data_per_key.keys(): 

      data_per_key[k]=[] 

     data_per_key[k].append(v) 

    return [(k,func(data_per_key[k])) for k in data_per_key.keys()] 

das Problem ist, dass ich es nicht sehr schön finde ich möchte es in einer Zeile haben: Hast du irgendwelche Ideen? Louis

Vielen Dank für Ihre Antwort

PS: Ich möchte die func im Aufruf halten, so dass Sie auch für mittlere fragen, mindestens ...

+0

Ich weiß nicht numpy, aber Ihr 'dtype' scheint ein Problem mit den Klammern zu haben .. – int3

+0

Die Klammern stimmen nicht überein. Macht etwas zusätzliche Verwirrung. –

+0

Ich verstehe Ihren Kommentar nicht, dass Sie "es in einer Zeile haben möchten". Wenn Sie die Funktion aufrufen, wird das eine Zeile sein. Spielt es eine Rolle, wie viele Zeilen die Funktion selbst hat? Wie auch immer, ich denke deine beste Wette ist, 'defaultdict' zu verwenden, wie die Antworten sagen. – steveha

Antwort

5

Vielleicht ist die Funktion, die Sie suchen sind, ist matplotlib.mlab.rec_groupby:

import matplotlib.mlab 

data=np.array(
    [('Aaron','Digger',1), 
    ('Bill','Planter',2), 
    ('Carl','Waterer',3), 
    ('Darlene','Planter',3), 
    ('Earl','Digger',7)], 
    dtype=[('name', np.str_,8), ('job', np.str_,8), ('income', np.uint32)]) 

result=matplotlib.mlab.rec_groupby(data, ('job',), (('income',np.mean,'avg_income'),)) 

Ausbeuten

('Digger', 4.0) 
('Planter', 2.5) 
('Waterer', 3.0) 

matplotlib.mlab.rec_groupby Gibt eine recarray:

print(result.dtype) 
# [('job', '|S7'), ('avg_income', '<f8')] 

Sie können auch bei der Prüfung pandas interessiert sein, die für den Umgang mit group-by operations sogar more versatile facilities hat.

+0

genau das, was ich suchte: die Arbeit in einer Zeile! Außerdem gibt es direkt ein Array zurück! Perfekt! – Louis

5

Ihre if k not in data_per_key.keys() könnte als if k not in data_per_key umgeschrieben werden, aber Sie können noch besser mit defaultdict. Hier ist eine Version, die defaultdict, um loszuwerden, die Existenzprüfung verwendet:

import collections 

def aggregate(data, key, value, func): 
    data_per_key = collections.defaultdict(list) 
    for k,v in zip(data[key], data[value]): 
     data_per_key[k].append(v) 

    return [(k,func(data_per_key[k])) for k in data_per_key.keys()] 
+3

Ich würde die letzte Zeile in 'return [(k, f (v)) für k, v in data_per_key.items()] ändern' –

+0

Das ist ein guter Ruf, aber ich habe versucht, das 'defaultdict'-Zeug zu markieren Das ist die einzige Veränderung. Ihre Rückkehr ist jedoch definitiv besser. –

+0

danke für den Defaultdict-Trick! und auch für die letzte Iteration – Louis

2

Here ist ein Rezept, das die Funktionalität von Matlabs accumarray ziemlich gut emuliert. Es benutzt Pythons-Iteratoren ganz nett, trotzdem ist es Performance-mäßig im Vergleich zur Matlab-Implementierung. Da ich das gleiche Problem hatte, hatte ich eine Implementierung mit scipy.weave geschrieben.Sie können es hier finden: https://github.com/ml31415/accumarray

2

Beste Flexibilität und Lesbarkeit wird erhalten mit pandas:

import pandas 

data=np.array(
    [('Aaron','Digger',1), 
    ('Bill','Planter',2), 
    ('Carl','Waterer',3), 
    ('Darlene','Planter',3), 
    ('Earl','Digger',7)], 
    dtype=[('name', np.str_,8), ('job', np.str_,8), ('income', np.uint32)]) 

df = pandas.DataFrame(data) 
result = df.groupby('job').mean() 

Ausbeuten an:

  income 
job 
Digger  4.0 
Planter  2.5 
Waterer  3.0 

Pandas Dataframe ist eine große Klasse, mit zu arbeiten, aber man kann Erhalten Sie Ihre Ergebnisse zurück, wie Sie benötigen:

result.to_records() 
result.to_dict() 
result.to_csv() 

Und so weiter ...

+1

'Pandas' ist etwa eine Größenordnung langsamer als meine oben gegebene Lösung. Siehe den Geschwindigkeitsvergleich dort. – Michael

+0

@Michael, sorry, eigentlich wollte ich nicht Leistung, ich bin mir bewusst, dass Pandas keine Bibliothek mit dem Ziel Top-Performance ist, ich selbst bevorzuge Ansätze wie bincount für die Leistung. Ich habe den ursprünglichen Post bearbeitet. – caiohamamura

2

Die beste Leistung wird mit ndimage.mean von erreicht. Dies wird zweimal schneller als akzeptierte Antwort für diese kleine Datenmenge, und etwa 3,5-mal schneller für größere Eingänge:

from scipy import ndimage 

data=np.array(
    [('Aaron','Digger',1), 
    ('Bill','Planter',2), 
    ('Carl','Waterer',3), 
    ('Darlene','Planter',3), 
    ('Earl','Digger',7)], 
    dtype=[('name', np.str_,8), ('job', np.str_,8), ('income', np.uint32)]) 

unique = np.unique(data['job']) 
result=np.dstack([unique, ndimage.mean(data['income'], data['job'], unique)]) 

Wird ergeben:

array([[['Digger', '4.0'], 
     ['Planter', '2.5'], 
     ['Waterer', '3.0']]], 
     dtype='|S32') 

EDIT: mit BinCount

(schneller!)

Dies ist etwa 5-fach schneller als akzeptierte Antwort für den kleinen Beispiel-Eingang, wenn Sie die Daten 100000-mal wiederholen wird es um 8,5x schneller sein:

unique, uniqueInd, uniqueCount = np.unique(data['job'], return_inverse=True, return_counts=True) 
means = np.bincount(uniqueInd, data['income'])/uniqueCount 
return np.dstack([unique, means])