2017-02-25 5 views
3

Ich habe vor einiger Zeit eine Frage gestellt (here), wie man die Summe einer Spalte in einem Datenframe durch Vergleich von Datumsangaben in einem anderen Datenframe findet. Allerdings möchte ich jetzt etwas Ähnliches machen, aber eine ID enthalten, die ein genaues Übereinstimmungsdatum mit einer anderen ID haben kann.Pandas: Betrag im Dataframe basierend auf Datumsbereichen und IDs hinzufügen

Hier ist mein Code:

df_a = pd.DataFrame({ 
     'end':pd.to_datetime(['1/15/2016','1/15/2016','3/15/2016','5/15/2016','5/15/2016','7/15/2016']), 
     'ID':[1,2,1,2,1,1] 
    }) 

df_a['start'] = df_a.groupby('ID')['end'].shift().fillna(0) 
df_a = df_a[['start','end','ID']] 

df_b = pd.DataFrame({ 
     'date':pd.to_datetime(['1/1/2016','1/1/2016','2/1/2016','2/1/2016','3/1/2016','3/1/2016','6/1/2016','6/1/2016','7/1/2016','7/1/2016','8/1/2016']), 
     'ID':  [1, 2, 1, 2, 1, 2, 2, 1, 1,  2,  2], 
     'amount': [1, 2, 10, 20, 100, 200, 2000, 1000, 10000, 20000, 200000] 
    }) 

Und meine gewünschte Ausgabe:

  start  end ID amount 
0 1970-01-01 2016-01-15 1  1 
1 1970-01-01 2016-01-15 2  2 
2 2016-01-15 2016-03-15 1  110 
3 2016-01-15 2016-05-15 2  220 
4 2016-03-15 2016-05-15 1  0 
5 2016-05-15 2016-07-15 1 11000 

ich merge() habe versucht, merge_asof(), combine_first(), groupby() und in der Nähe bekommen, aber nicht den ganzen Weg.

Hier ist eine nicht-Pandas Version, die aber für eine große Datenmenge arbeitet, stelle ich mir dies sehr langsam sein würde:

amount = [] 
for s, e, i in zip(df_a['start'], df_a['end'], df_a['ID']): 
    amount.append(df_b['amount'][(s < df_b['date']) & (df_b['date'] <= e) & (df_b['ID'] == i)].sum()) 

df_a['amount'] = pd.Series(amount) 

Würde etwas Hilfe, danke im voraus lieben.

Antwort

2

Ihre Antwort ist ziemlich gut. Ich mag das, weil mit reindex mit fill_value die intdtype

mux = pd.MultiIndex.from_arrays(df_a.values.T, names=df_a.columns) 

kws = dict(
    left_on='date', right_on='start', 
    allow_exact_matches=True, by='ID') 
mrg = pd.merge_asof(df_b, df_a, **kws).query('date <= end') 
grp = mrg.groupby(['start', 'end', 'ID']).amount.sum() 
grp.reindex(mux, fill_value=0).reset_index() 

     start  end ID amount 
0 1970-01-01 2016-01-15 1  1 
1 1970-01-01 2016-01-15 2  2 
2 2016-01-15 2016-03-15 1  110 
3 2016-01-15 2016-05-15 2  220 
4 2016-03-15 2016-05-15 1  0 
5 2016-05-15 2016-07-15 1 11000 
+0

Ich werde dies als die richtige Antwort markieren, weil es funktioniert, aber * wow *. Das sind einige Pandas auf höherer Ebene, die ich etwas verdauen muss. Tolle Lernerfahrung, danke! – pshep123

2

Ok, ich denke, ich habe meine eigene Frage basierend auf dem Link beantwortet, den ich in meiner ursprünglichen Frage geteilt habe (danke @piRSquared), musste aber ein wenig Code hinzufügen. Ich vermute, das ist nicht die effizienteste Methode und wäre daran interessiert, andere Gedanken zu bekommen.

# Merge DataFrames, find date ranges, and add amounts  
df_c = pd.merge_asof(df_b, df_a, left_on = 'date', right_on = 'start', by = 'ID') \ 
     .query('date <= end').groupby(['end','ID'])['amount'].sum().reset_index() 

# But that leaves out ranges for which there is no data 
# so need to merge back in the original dates and fill NaNs with 0  
df_c = df_a.merge(df_c, how = 'outer').fillna(0) 

     start  end ID amount 
0 1970-01-01 2016-01-15 1  1.0 
1 1970-01-01 2016-01-15 2  2.0 
2 2016-01-15 2016-03-15 1 110.0 
3 2016-01-15 2016-05-15 2 220.0 
4 2016-03-15 2016-05-15 1  0.0 
5 2016-05-15 2016-07-15 1 11000.0 
+0

bewahrt, die ziemlich gut aussieht. Ich habe es ein bisschen anders gemacht, je nach Vorliebe, aber ich denke, Merge am Ende zu verwenden, um diese Null in Zeile 4 zu bekommen, ist schlau. – piRSquared

Verwandte Themen