2016-05-08 14 views
1

の一意の識別子を作成するための高速な方法私はデータを持っている目標は、そのそれぞれについて、特定の価格でID1のグループごとに一意のIDを取得することで、このPythonのパンダ:グループ

df 
Out[10]: 
    ID1 ID2 Price  Date 
0 11 21 10.99 3/15/2016 
1 11 22 11.99 3/15/2016 
2 12 23  5 3/15/2016 
3 11 21 10.99 3/16/2016 
4 11 22 12.99 3/16/2016 
5 11 21 10.99 3/17/2016 
6 11 22 11.99 3/17/2016 

ようになりますID2のように:

# Desired Result 
df 
Out[14]: 
    ID1 ID2 Price  Date UID 
0 11 21 10.99 3/15/2016 1 
1 11 22 11.99 3/15/2016 1 
2 12 23  5 3/15/2016 7 
3 11 21 10.99 3/16/2016 5 
4 11 22 12.99 3/16/2016 5 
5 11 21 10.99 3/17/2016 1 
6 11 22 11.99 3/17/2016 1 

データのサイズのためにスピードが問題になります。私が思いつくことができる最善の方法は以下の通りですが、依然として望ましい量よりもゆっくりとかなりの量です。誰かが自然に速くなるべきと思う方法があれば、それを聞いてみたいです。あるいは、グループ内の操作を並行して実行する簡単な方法があります。

私の方法では、基本的にIDと価格を連結し(同じ長さを確保するために0で埋めた後に)、最後のIDを簡略化するためにランクをとります。ボトルネックは.transform(np.sum)で行われるグループ内連結です。

# concatenate ID2 and Price 
df['ID23'] = df['ID2'] + df['Price'] 

df 
Out[12]: 
    ID1 ID2 Price  Date  ID23 
0 11 21 10.99 3/15/2016 2110.99 
1 11 22 11.99 3/15/2016 2211.99 
2 12 23  5 3/15/2016  235 
3 11 21 10.99 3/16/2016 2110.99 
4 11 22 12.99 3/16/2016 2212.99 
5 11 21 10.99 3/17/2016 2110.99 
6 11 22 11.99 3/17/2016 2211.99 


# groupby ID1 and Date and then concatenate the ID23's 
grouped = df.groupby(['ID1','Date']) 
df['summed'] = grouped['ID23'].transform(np.sum) 

df 
Out[16]: 
    ID1 ID2 Price  Date  ID23   summed    UID 
0 6 3 0010.99 3/15/2016 30010.99 30010.9960011.99 630010.9960011.99 
1 6 6 0011.99 3/15/2016 60011.99 30010.9960011.99 630010.9960011.99 
2 7 7 0000005 3/15/2016 70000005   70000005   770000005 
3 6 3 0010.99 3/16/2016 30010.99 30010.9960012.99 630010.9960012.99 
4 6 6 0012.99 3/16/2016 60012.99 30010.9960012.99 630010.9960012.99 
5 6 3 0010.99 3/17/2016 30010.99 30010.9960011.99 630010.9960011.99 
6 6 6 0011.99 3/17/2016 60011.99 30010.9960011.99 630010.9960011.99 

# Concatenate ID1 on the front and take rank to get simpler ID's  
df['UID'] = df['ID1'] + df['summed'] 
df['UID'] = df['UID'].rank(method = 'min') 

# Drop unnecessary columns 
df.drop(['ID23','summed'], axis=1, inplace=True) 

UPDATE:

明確にするために、次のようにグループ化された元のデータを検討します。そのグループについてのすべては日付を無視して、同一である場合

grouped = df.groupby(['ID1','Date']) 
    for name, group in grouped: 
    print group 

    ID1 ID2 Price  Date 
0 11 21 10.99 3/15/2016 
1 11 22 11.99 3/15/2016 

    ID1 ID2 Price  Date 
3 11 21 10.99 3/16/2016 
4 11 22 12.99 3/16/2016 

    ID1 ID2 Price  Date 
5 11 21 10.99 3/17/2016 
6 11 22 11.99 3/17/2016 

    ID1 ID2 Price  Date 
2 12 23  5 3/15/2016 

UIDのは、グループレベルと一致でなければなりません。この場合、最初と3番目の印刷グループは同じです。つまり、行0,1,5,6がすべて同じUIDを取得する必要があります。行3と4は異なるグループに属します。価格が変更され、異なるUIDが必要なためです。行2も異なるグループです。

この問題を見る方法が少し違うのは、私がここにあるようにグループ化したいのですが、最初にグループを作成するために重要だった日付列を削除してから、一度削除したグループ日付。

+1

どのようにそれは ' 'UID''は'行でも同じです0'と '1'ということになりましたでしょうか?このような例'' ID1''は両方の行で同じですが、 '' ID2''と '' Price''は実際には異なっています。 – Thanos

+0

良い質問です。私はもっ​​と説明していたはずです。 ID1はID2の親IDと似ています。だから、行0と1は同じグループに属しています。そのグループのID1 = 11とその日のすべての子(ID2)です。そのグループレベルで一意のIDが必要です。 UIDは、ID1、ID2、および価格が同じであるため、異なる日であっても最後の2行と同じ行になります。 – Archimedes

+0

__unique__識別子は任意の行を識別できる必要があります。そのため、列の名前を付けたり、行の一意性を保証したり、GUIDなどの人工的な固有キーを使用することができます。それ以外の場合は、すぐに、または固有のキーアルゴリズムに問題が発生します。ちょうど私の0.02 $。 – MaxU

答えて

0

編集:以下のコードは実際にはOPのソリューションよりも遅いです。私は今のように誰かがそれを使ってより良い解決策を書く場合に備えて、それを残しています。


可視化のために、私は次のデータを使用しています:

df 
Out[421]: 
    ID1 ID2 Price  Date 
0 11 21 10.99 3/15/2016 
1 11 22 11.99 3/15/2016 
2 12 23 5.00 3/15/2016 
3 11 21 10.99 3/16/2016 
4 11 22 12.99 3/16/2016 
5 11 21 10.99 3/17/2016 
6 11 22 11.99 3/17/2016 
7 11 22 11.99 3/18/2016 
8 11 21 10.99 3/18/2016 
9 12 22 11.99 3/18/2016 
10 12 21 10.99 3/18/2016 
11 12 23 5.00 3/19/2016 
12 12 23 5.00 3/19/2016 

まず、「ID1」と「日付」やタプルとして結果を集計することによりましょうグループには、(ソート)。私はまた、インデックスをリセットしたので、 'index'という名前の新しい列があります。

gr = df.reset_index().groupby(['ID1','Date'], as_index = False) 
df1 = gr.agg(lambda x : tuple(sorted(x))) 
df1 
Out[425]: 
    ID1  Date  index  ID2   Price 
0 11 3/15/2016 (0, 1) (21, 22) (10.99, 11.99) 
1 11 3/16/2016 (3, 4) (21, 22) (10.99, 12.99) 
2 11 3/17/2016 (5, 6) (21, 22) (10.99, 11.99) 
3 11 3/18/2016 (7, 8) (21, 22) (10.99, 11.99) 
4 12 3/15/2016  (2,)  (23,)   (5.0,) 
5 12 3/18/2016 (9, 10) (21, 22) (10.99, 11.99) 
6 12 3/19/2016 (11, 12) (23, 23)  (5.0, 5.0) 

すべてのグループ化が行われた後、私は(彼らがより良いユニークであると思います)dfから行にアクセスするために、カラム'index'からインデックスを使用します。 (注意してくださいまた、そのdf1.indexdf1['index']は全く異なるものです。)今

、のは、グループ'index'(日付を飛ばして)みましょう:

df2 = df1.groupby(['ID1','ID2','Price'], as_index = False)['index'].sum() 
df2 
Out[427]: 
    ID1  ID2   Price    index 
0 11 (21, 22) (10.99, 11.99) (0, 1, 5, 6, 7, 8) 
1 11 (21, 22) (10.99, 12.99)    (3, 4) 
2 12 (21, 22) (10.99, 11.99)    (9, 10) 
3 12  (23,)   (5.0,)    (2,) 
4 12 (23, 23)  (5.0, 5.0)   (11, 12) 

私は、この問題のために必要なグループであると考えているので、私たちは今、追加することができますラベルはdfになります。

df['GID'] = -1 
for i, t in enumerate(df2['index']): 
    df.loc[t,'GID'] = i 

df 
Out[430]: 
    ID1 ID2 Price  Date GID 
0 11 21 10.99 3/15/2016 0 
1 11 22 11.99 3/15/2016 0 
2 12 23 5.00 3/15/2016 3 
3 11 21 10.99 3/16/2016 1 
4 11 22 12.99 3/16/2016 1 
5 11 21 10.99 3/17/2016 0 
6 11 22 11.99 3/17/2016 0 
7 11 22 11.99 3/18/2016 0 
8 11 21 10.99 3/18/2016 0 
9 12 22 11.99 3/18/2016 2 
10 12 21 10.99 3/18/2016 2 
11 12 23 5.00 3/19/2016 4 
12 12 23 5.00 3/19/2016 4 

または可能性速いでなく、トリッキーな方法:

# EXPERIMENTAL CODE! 
df3 = df2['index'].apply(pd.Series).stack().reset_index() 
df3.index = df3[0].astype(int) 
df['GID'] = df3['level_0'] 
+0

私はあなたのソリューションptrjが好きです。タプルを使用することは、私がやっていたような束の束を連結することよりもはるかに滑らかです。私は悲しいことにあなたの方法が少し遅くなってしまうことに驚いていました。私はこれを推測しているのは、groupbyを合計すると[cythonized](http://pandas.pydata.org/pandas-docs/stable/groupby.html#cython-optimized-aggregation-functions)(?)になるので、より滑らかなコードでは、gr.agg()ステップが遅くなります。 – Archimedes

+0

@Archimedesパフォーマンスをテストしませんでした。 groupbyは 'tuple(ソート済み(x))'でとても遅いのですか、コード全体ですか? – ptrj

+0

はい、ほとんどの時間を要するgr.agg(lambda x:tuple(ソート済み(x)))行です。 – Archimedes

関連する問題