2016-06-29 16 views
0

私は、商品購入ログのデータセットを、 purchase_date、user_address、user_id、product_id、brand_id、retailer_idという6つの列で持っています。 すべては整数を含みますが、user_addressは文字列です。numpy再配列での効率的なGROUP BYクエリ

データセット全体で最も多くのアイテムを販売するトップ5ブランド、つまりデータに最も多くのエントリを持つブランドを取得する必要があります。 SQLで

、私は(私が間違っているなら、私を修正)、それは次のようになりますと信じて:

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

私は次のようにnumpyのrecarrayとpythonでそれをやってみました:

items_sold_per_brand = np.empty(len(data), dtype=[('brand_id', 'int'), ('count', 'int')]) 

brands = np.unique(data['brand_id']) # array of unique brands 
for i, brand in enumerate(np.nditer(brands)):  # For any unique brand 
    items_sold_per_brand[i] = (brand, len(data[data['brand_id'] == brand])) # get the number of rows with the current brand 

top5 = np.sort(items_sold_per_brand, order='count')[-5:] # sort the array over the count values 
print(top5[::-1]) # print the last five entries 

〜100000行のデータセットで実行するのに約15秒かかり、長すぎると思われる約12000種類のブランドがあります。 forループは最も長くかかるものです。

これを行うには、numpyの再配列検索メソッドを使用すると、より洗練された効率的な方法がありますか?

ありがとうございました!

+0

「一意の」 'return_counts'パラメータを試しましたか? – hpaulj

+0

私はこの問題を全面的に思っていたようですが、 "return_counts"が働いていました。ありがとうございました! – schwiftybot

答えて

0

指定された複製numpy: most efficient frequency counts for unique values in an arrayは関連性がありますが、このコードでは重要な問題は無視されています。受け入れられた答え、bincountはおそらく役に立たない。あなたは多くの場合、より新しいものを適用する際にいくつかの助けが必要ですuniquereturn_counts答えです。

テストスクリプト:

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) 

が空でitems_sold_per_branddataの大きさは、潜在的に反復中に書かれた上で取得しないランダムな数の葉を初期化

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] 

生成します。 zerosの小さい方がbrandsのサイズで処理されます。

nditerは、このような単純な繰り返しには必要ありません。

bincountは高速ですが、dataの範囲内のすべての潜在的な値のビンを作成します。したがって、潜在的に0サイズのビンがあります。

関連する問題