2017-02-13 94 views
0

我的數據框的每一行都是由date1和date2表示的時間間隔以及用戶標識。對於每個用戶ID,我需要將間隔分開一定的閾值以下的間隔。聚類間隔

到目前爲止,對於每個用戶ID,我按開始和結束日期對行進行排序。然後,我計算差距並根據這些值對行進行分組。然後,我將修改後的行添加到新的數據框中(這是我發現的將數據框分組的方式)。

但是,這很慢。你看到了改進分組方式的方法嗎?

def gap(group): 
    return group[['date1', 'date2']].min(axis = 1) - \ 
     group.shift()[['date1', 'date2']].max(axis = 1) 

def cluster(df, threshold): 
    df['clusters'] = 0 
    grouped = df.groupby('user_id') 
    newdf = pd.DataFrame() 
    for name, group in grouped: 
     group = group.sort_values(['date1', 'date2'], ascending = True) 
     group['gap'] = gap(group) 
     cuts = group['gap'] > timedelta(threshold) 
     df2 = group.copy() 
     for g, d, r in zip(group.loc[cuts, 'gap'], group.loc[cuts, 'date1'], group.loc[cuts, 'date2']): 
      df2.loc[((df2['date1'] >= d) & (df2['date2'] >= r)), 'clusters'] +=1 
     df2 = df2.drop('gap', axis = 1) 
     newdf = pd.concat([newdf, df2]) 
    return newdf 

這裏是它使用的數據的最小的樣品:

df = pd.DataFrame(dict([('user_id', np.array(['a', 'a', 'a', 'a', 'a', 'a', 'a'])), 
    ('date1', np.array([datetime.strptime(x, "%y%m%d") for x in ['160101', '160103', '160110', '160120', '160130', '160308', '160325']])), 
    ('date2', np.array([datetime.strptime(x, "%y%m%d") for x in ['160107', '160109', '160115', '160126', '160206', '160314', '160402']]))])) 
+0

您能發佈此代碼以數據開始的最小數據示例嗎? – languitar

+0

對於你的例子,合理的閾值是多少? – IanS

+0

7,14天。類似的東西 – Pop

回答

2

一個簡單的改進是對布爾矢量cuts使用cumsum

def cluster2(df, threshold): 
    df['clusters'] = 0 
    grouped = df.groupby('user_id') 
    df_list = [] 
    for name, group in grouped: 
     group = group.sort_values(['date1', 'date2'], ascending = True) 
     group['gap'] = gap(group) 
     print(group) 
     cuts = group['gap'] > timedelta(threshold) 
     df2 = group.copy() 
     df2['clusters'] = cuts.cumsum() 
     df_list.append(df2) 
    return pd.concat(df_list) 

編輯:在OP的評論之後,我將鏈接移出循環以提高性能。

進一步的改進可能是不排序在groupby操作的基團(如果有許多用戶):

grouped = df.groupby('user_id', sort=False) 

或者甚至手動分組通過排序由user_iddf,然後直接添加條件cuts在原始數據幀上:

df = df.sort_values(['user_id', 'date1', 'date2'], ascending = True) 
df['gap'] = gap(df) 
cuts = (df['user_id'] != df['user_id'].shift()) | (df['gap'] > timedelta(threshold)) 
df['clusters'] = cuts.cumsum() 
+1

我也將所有'df2'數據幀存儲在一個列表中,並且只進行了一個連接。它有很大的幫助 – Pop

+0

非常感謝您的想法! – Pop

+1

關於連接的好處,我應該抓到:) – IanS