2017-07-18 3 views
-1

Ich habe Probleme, dies auf eine pythischere und effizientere Weise zu schreiben. Ich versuche, Beobachtungen nach customerid zu gruppieren und die Anzahl der Wiederholungen für jede Beobachtung, die der Kunde in den letzten 1, 7 und 30 Tagen abgelehnt hat, zu zählen. HierZählwerte für vergangene x Tage in Gruppen

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']) 

ist die Ausgabe:

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 

Meine aktuelle Lösung ist sehr langsam:

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 
     }) 

Die erwartete Ausgabe ist unten dargestellt:

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 

Ich stelle mir vor ich brauche eine Art doppelte Gruppe von wo Ich iteriere über jede Zeile in der Gruppe, aber ich kann nichts anderes als diese doppelte for-Schleife zur Arbeit bringen, die sehr lange dauert.

Jede Hilfe wäre willkommen.

Antwort

0

Sie könnten versuchen, groupby und transform und mit der Tatsache, dass die Summe eines boolean-Array ist die Anzahl der True s, so dass Sie einen zusätzlichen Datenrahmen nicht etwas generieren müssen, um tun, wie diese temp[(temp['postdate'] <= date) & (temp['postdate']>=date-timedelta(days=7))].shape[0]-1 jedes Mal: ​​

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) 

und nennen es so

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'] 

Ergebnisse

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

etwas anderen Ansatz

Das appoach eine groupby pro Periode der Fall ist. Sie können es nur tun 1 groupby beschleunigen und dann alle Perioden für jede Gruppe

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'] 

Ergebnisse

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

Du bist ein verdammt Genie berechnen! Vielen Dank! – fcol