2017-05-08 18 views
2

私は項目(列)のカウントにマッピングするユーザー(インデックス)を保持する大規模なデータフレームがあります。ユーザーごとにカスタムグループを含むパンダの計算をベクトル化する方法は?

users_items = pd.DataFrame(np.array([[0, 1, 1, 0],  # user 0 
            [1, 0, 0, 0],  # user 1 
            [5, 0, 0, 9],  # user 2 
            [0, 3, 5, 0],  # user 3 
            [0, 2, 2, 0],  # user 4 
            [7, 0, 0, 1],  # user 5 
            [3, 5, 0, 4]]), # user 6 
          columns=list('ABCD')) 

を、私は、少なくとも同じのための非ゼロカウントを持つすべてのユーザーを見つけたいですその数を合計します。したがって、ユーザー1の場合、これはユーザー1,2,5および6であり、カウントの合計は[16, 5, 0, 14]になります。これは、 "類似した"ユーザーが持っているアイテムに基づいて、新しいアイテムをユーザーに提案するために使用できます。

このナイーブな実装では、すべての署名をループするループのための関連する行およびフィルタリングする正規表現として署名を使用する:

def create_signature(request_counts): 
    return ''.join('x' if count else '.' for count in request_counts) 

users_items['signature'] = users_items.apply(create_signature, axis=1).astype('category') 

current_items = users_items.groupby('signature').sum() 

similar_items = pd.DataFrame(index=current_items.index, 
          columns=current_items.columns) 

for signature in current_items.index: 
    row = current_items.filter(regex=signature, axis='index').sum() 
    similar_items.loc[signature] = row 

結果は:

  A B C D 
signature    
.xx.  0 6 8 0 
x...  16 5 0 14 
x..x  15 5 0 14 
xx.x  3 5 0 4 

この作品100kユーザーと約600項目からなる実際のデータセットでは遅すぎます。署名の生成にはわずか10秒しかかかりませんが、すべての署名(40k)の繰り返しには数時間かかります。

ループをベクトル化するとパフォーマンスは大幅に向上しますが、パンダの経験は限られているため、どうやったらいいか分かりません。このタイプの計算をベクトル化することも可能です。おそらくマスクを使用していますか?代わりに署名としてstring

+0

で最後に

for signature in current_items.index: row = current_items[signature <= current_items.index].sum() similar_items.loc[signature] = row 

結果を、あなたのフィルターをvectoriseすることができます.index

reset_index()への呼び出しを挿入この例では4つの署名しかありませんか? .x ..、.xxx、x.xxなどの他の組み合わせが必要ですか?これらの4つはなぜ選ばれたのですか? – Allen

+0

@Allenシグネチャのセットは、users_itemsデータフレーム内にあるデータが何であっても単純に発生します。すべての可能な署名が発生するわけではありません。 –

+0

'x ...'は、その署名のサブセットであるすべての行の合計である必要があります –

答えて

0

、あなたは私が1よりも高速であるかどうかを確認するのに十分な大きさのデータセットを持っていないfrozenset

​​

代替が

def create_signature(request_counts): 
    return frozenset(request_counts.replace({0: None}).dropna().index) 

で使用することができますその他。

あなたが重複する列を持っている場合は、これがあるなぜあなたは

signature     A B C D 
frozenset({'B', 'C'})  0 6 8 0 
frozenset({'A'})   16 5 0 14 
frozenset({'A', 'D'})  15 5 0 14 
frozenset({'B', 'A', 'D'}) 3 5 0 4 
+0

私はfrozensets(私は自分自身のビット配列を考えていた)が、文字列の代わりに署名の方がはるかに高速であると思っていたでしょう。残念なことに、frozensetシグニチャの生成にはかなりの時間がかかります(10秒間で2分以上)。つまり、fronzesetsを使用するとループは8時間でなく6時間しかかかりませんので、ここでは明確な改善があります。しかし、私はforループをベクトル化するだけで、この操作を十分に高速化できると思います。 –

+0

forループは 'regex'よりも速い' set'比較を期待しているので、文字列1と同じくらい長い時間がかかります。ビット配列はもっと速いかもしれません –

+0

コメントを投稿した後にしました。私は今それを更新しました。 –

関連する問題