2016-06-29 10 views
0

Ich habe einen Datensatz von Produkt Einkäufe Protokolle mit 6 Spalten: Kaufdatum, Benutzer_Adresse, Benutzer_ID, Produkt_ID, Marke_ID, Händler-ID. Alle enthalten Integer, außer user_address, das eine Zeichenfolge ist.Effiziente GROUP BY-Abfrage auf numpy recarray

Ich brauche die Top 5 Marken, die die meisten Artikel über den gesamten Datensatz verkaufen, d. H. Diejenigen mit den meisten Einträgen in den Daten.

In SQL, ich glaube, es wie folgt aus (korrigiert mich wenn ich falsch liege) aussehen:

SELECT brand_id, COUNT(*) 
FROM data 
GROUP BY brand_id 

Ich habe mit einem numpy recarray in Python, dass versuchte dabei wie folgt:

Es funktioniert, außer es dauert ungefähr 15 Sekunden, um auf einem ~ 100000 Reihen-Datensatz mit ungefähr 12000 verschiedenen Marken zu laufen, der zu lang scheint. Die For-Schleife ist die längste.

Gibt es eine elegantere und effizientere Methode, dies zu tun, indem Sie vielleicht die Sucharray-Methoden von numpy verwenden?

Danke für Ihre Hilfe!

+0

Haben Sie den 'unique'' return_counts' Parameter versucht? – hpaulj

+0

Sieht so aus, als hätte ich das ganze Problem übertrieben, "return_counts" hat funktioniert. Vielen Dank! – schwiftybot

Antwort

0

Während das nominierte Duplikat numpy: most efficient frequency counts for unique values in an array relevant ist, denke ich, dass es einige wichtige Probleme in diesem Code ignoriert. Die angenommene Antwort bincount ist wahrscheinlich nicht nützlich. Und Sie brauchen einige Hilfe bei der Anwendung der neueren uniquereturn_counts Antwort.

Ein Testskript:

import numpy as np 
# simple data array 
data=np.zeros((20,),dtype=[('brand_id',int),('id',int)]) 
data['brand_id']=np.random.randint(0,10,20) 
data['id']=np.arange(20) 

items_sold_per_brand = np.empty(len(data), dtype=[('brand_id', 'int'), ('count', 'int')]) 
brands = np.unique(data['brand_id']) 
print('brands',brands) 
for i, brand in enumerate(np.nditer(brands)): 
    items_sold_per_brand[i] = (brand, len(data[data['brand_id'] == brand])) 
top5 = np.sort(items_sold_per_brand, order='count')[-5:]  
print('top5',top5[::-1]) 

# a bit of simplification 
brandids = data['brand_id'] 
brands = np.unique(brandids) 
# only need space for the unique ids 
items_sold_per_brand = np.zeros(len(brands), dtype=[('brand_id', 'int'), ('count', 'int')]) 
items_sold_per_brand['brand_id'] = brands 
for i, brand in enumerate(brands): # dont need nditer 
    items_sold_per_brand['count'][i] = (brandids == brand).sum() 
top5 = np.sort(items_sold_per_brand, order='count')[-5:]  
print('top5',top5[::-1])  

brands,counts = np.unique(data['brand_id'],return_counts=True) 
print('counts',counts) 

items_sold_per_brand = np.empty(len(brands), dtype=[('brand_id', 'int'), ('count', 'int')]) 
items_sold_per_brand['brand_id']=brands 
items_sold_per_brand['count']=counts 
tops = np.sort(items_sold_per_brand, order='count')[::-1] 
print('tops',tops) 

ii = np.bincount(data['brand_id']) 
print('bin',ii) 

produziert

1030:~/mypy$ python3 stack38091849.py 
brands [0 2 3 4 5 6 7 9] 
top5 [(99072, 1694566490) (681217, 1510016618) (1694566234, 1180958979) 
(147063168, 147007976) (-1225886932, 139383040)] 
top5 [(7, 4) (2, 4) (0, 3) (9, 2) (6, 2)] 
counts [3 4 2 1 2 2 4 2] 
tops [(7, 4) (2, 4) (0, 3) (9, 2) (6, 2) (5, 2) (3, 2) (4, 1)] 
bin [3 0 4 2 1 2 2 4 0 2] 

initialisieren items_sold_per_brand mit leeren und die Größe der data verlässt möglicherweise Zufallszahlen, die nicht über während der Iteration geschrieben bekommt. zeros mit der kleineren brands Größe kümmert sich darum.

nditer wird für eine einfache Iteration wie diese nicht benötigt.

bincount ist schnell, aber erstellt Bins für alle möglichen Werte im Bereich data. Es gibt also potenziell 0 Größenfächer.