2017-07-18 9 views
-1

これをもっとpythonicで効率的な方法で書くのに問題があります。私は、顧客IDによる観察をグループ化し、過去1日、7日、および30日に顧客が拒否された各観測の回数をカウントしようとしています。ここでグループ内の過去x日のカウント値

t = pd.DataFrame({'customerid': [1,1,1,3,3], 
       'leadid': [10,11,12,13,14], 
       'postdate': ["2017-01-25 10:55:25.727", "2017-02-02 10:55:25.727", "2017-02-27 10:55:25.727", "2017-01-25 10:55:25.727", "2017-01-25 11:55:25.727"], 
       'post_status': ['Declined', 'Declined', 'Declined', 'Declined', 'Declined']}) 
t['postdate'] = pd.to_datetime(t['postdate']) 

が出力されます。

customerid leadid post_status postdate 
1 10 Declined 2017-01-25 10:55:25.727 
1 11 Declined 2017-02-02 10:55:25.727 
1 12 Declined 2017-02-27 10:55:25.727 
3 13 Declined 2017-01-25 10:55:25.727 
3 14 Declined 2017-01-25 11:55:25.727 

私の現在のソリューションは非常に遅いです:

final = [] 
for customer in t['customerid'].unique(): 

    temp = t[(t['customerid']==customer) & (t['post_status']=='Declined')].copy() 

    for i, row in temp.iterrows(): 
     date = row['postdate'] 
     final.append({ 
      'leadid': row['leadid'], 
      'decline_1': temp[(temp['postdate'] <= date) & (temp['postdate']>=date-timedelta(days=1))].shape[0]-1, 
      'decline_7': temp[(temp['postdate'] <= date) & (temp['postdate']>=date-timedelta(days=7))].shape[0]-1, 
      'decline_30': temp[(temp['postdate'] <= date) & (temp['postdate']>=date-timedelta(days=30))].shape[0]-1 
     }) 

期待される出力を以下に示します。

decline_1 decline_30 decline_7 leadid 
0 0 0 10 
0 1 0 11 
0 1 0 12 
0 0 0 13 
1 1 1 14 

私は想像する私は、いくつかの種類のダブルグループが必要です私はグループの各行を繰り返しますが、この二重for-loop以外は何もできません。これは完了までに非常に時間がかかります。

ご協力いただければ幸いです。あなたはこのtemp[(temp['postdate'] <= date) & (temp['postdate']>=date-timedelta(days=7))].shape[0]-1ようにするたびに何かをするために余分なデータフレームを生成する必要はありませんので、あなたは、groupbytransformとブール配列の合計がTrue秒の数であるという事実を使用して試みることができる

答えて

0

def find_declinations(df, period): 
    results = pd.Series(index=df.index, name=period) 
    for index, date in df.items(): 
     time_range = df.between(date - period, date) 
     results[index] = time_range.sum() - 1 
    return results.fillna(0).astype(int) 

この

results = pd.DataFrame(index=t.index) 
period=pd.to_timedelta(1, 'd') 
for days in [1, 7, 30]: 
    results['decline%i'% days] = t.groupby('customerid')[['postdate']].transform(lambda x: find_declinations(x, pd.to_timedelta(days, 'd'))) 
results.index = t['leadid'] 

結果のようにそれを呼び出す

decline1 decline7 decline30 
leadid   
10 0 0 0 
11 0 0 1 
12 0 0 1 
13 0 0 0 
14 1 1 1 

わずかに異なるアプローチ

appoach期間当たりGROUPBYしないこと。あなただけの1 GROUPBYを行うことによって、それをスピードアップし、その後

def find_declinations_df(df, periods = [1, 7, 30, 60]): 
#  print(periods, type(df), df) 
    results = pd.DataFrame(index=pd.DataFrame(df).index, columns=periods) 
    for period in periods: 
     for index, date in df['postdate'].items(): 
      time_range = df['postdate'].between(date - pd.to_timedelta(period, 'd'), date) 
      results.loc[index, period] = time_range.sum() - 1 
    return results.fillna(0).astype(int) 

results = pd.concat(find_declinations_df(group[1]) for group in t.groupby('customerid')) 
results['leadid'] = t['leadid'] 

結果

1 7 30 60 leadid 
0 0 0 0 0 10 
1 0 0 1 1 11 
2 0 0 1 2 12 
3 0 0 0 0 13 
4 1 1 1 1 14 
+0

あなたがいまいましい天才だグループごとにすべての期間を計算することができます!ありがとうございました! – fcol

関連する問題