2017-08-24 1 views
0

Ich migiere jetzt eines meiner Spark-Projekt zu Pandas, und eines der Probleme, die ich traf, ist die Implementierung einer rollenden Summe wie Funktion in Pandas innerhalb jeder Gruppe.Rolling sum filling Zwischenwerte auf gruppierten DataFrames

Angenommen, ich habe:

key time value 
A 1 10 
A 2 20 
A 4 30 
A 8 10 
B 1 15 
B 2 30 
B 3 15 

Ich mag erste Gruppe von key, dann einen rolling-sum artige Funktion berechnen, die auf value bezüglich time Zwischenzeitschlitze füllt. wenn die Fenstergröße 2 ist zum Beispiel, wird mein Wunsch ausgegeben werden:

key time output 
A 1 10  
A 2 30 (10+20)  
A 3 30 (10+20+0) 
A 4 50 (20+0+30) 
A 5 30 (0+30+0) 
A 6 30 (the same as above) 
A 8 10 (7 is 0 so it is omitted) 
A 9 10 
A 10 10 
B 1 15 
B 2 45 
B 3 60 
B 4 45 
B 5 15 

Ich habe auf group by und apply einen ganzen Nachmittag zu kämpfen. Gibt es dafür einen cleveren Weg? Im Funken kann ich collect_list dann select ein udf zu time und value, um dies zu tun, aber Pandas scheint anders zu denken als Spark.

Vielen Dank!

+0

Sie scheinen "Erstellen" -Daten werden. Es ist nicht klar, wie die Ausgabe Zeit von 1 bis 5 für A und nur 1 bis 4 für B hat. –

+0

@COLDSPEED Sorry für Unklarheiten, der letzte Wert des Datensatzes dauert für 1 Zeitschlitz, weil die Fenstergröße 1 ist. – DarkZero

+0

ist Ihr Ergebnis für Fenstergröße 2 oder 3? –

Antwort

0

IIUIC, hier ist ein Ansatz.

Erste dff mit neuen Zeitfenster schaffen

In [1458]: dff = (df.groupby('key') 
        .apply(lambda x: pd.Series(range(x.time.min(), x.time.max()+2))) 
        .reset_index(name='time').drop('level_1', 1)) 
In [1459]: dff 
Out[1459]: 
    key time 
0 A  1 
1 A  2 
2 A  3 
3 A  4 
4 A  5 
5 B  1 
6 B  2 
7 B  3 
8 B  4 

Dann verschmelzen auf früheren df, groupby auf key, fügen Sie aktuelle und frühere value mit shift

In [1460]: dff.assign(ouput=dff.merge(df, how='left') 
           .fillna(0).groupby('key').value 
           .apply(lambda x: x+x.shift().fillna(0))) 
Out[1460]: 
    key time ouput 
0 A  1 10.0 
1 A  2 30.0 
2 A  3 20.0 
3 A  4 30.0 
4 A  5 30.0 
5 B  1 15.0 
6 B  2 45.0 
7 B  3 45.0 
8 B  4 15.0 
+0

Danke, aber ich frage mich, ob es zwei Probleme mit diesem Ansatz gibt: erstens erzeugt es aufeinanderfolgende Zeitschlitze von time.min bis time.max, wenn die Zeit "1, 2, 8, 16" ist, wird es viel geben von verschwendeten Slots generiert. Zweitens gilt 'shift' nur, wenn die Fenstergröße 1 ist, aber das ist nicht immer der Fall ... – DarkZero

+0

Ich habe jetzt die Beispieldaten in der Fragebeschreibung geändert. – DarkZero

0

Eine Möglichkeit, dies zu bewältigen ist erzeuge den gesamten Bereich von time in einer bestimmten Gruppe und tue dann eine dropna danach

def make_rolling_sum(key, group, window): 
    time_range = range(group['time'].min(), group['time'].max() + 1 + window,) 
    df = group.set_index('time').reindex(time_range) 
    result = df.rolling(window, min_periods=1).sum() 
    result['key'] = key # the reindex drops a lot of NaN's in this column 
    return result.dropna().reset_index().reindex(columns=group.columns) 

window = 3 
pd.concat((make_rolling_sum(*group, window)for group in df.groupby('key')), ignore_index=True) 

ergibt

key  time value 
0 A 1 10.0 
1 A 2 30.0 
2 A 3 30.0 
3 A 4 50.0 
4 A 5 30.0 
5 A 6 30.0 
6 A 8 10.0 
7 A 9 10.0 
8 A 10 10.0 
9 B 1 15.0 
10 B 2 45.0 
11 B 3 60.0 
12 B 4 45.0 
13 B 5 15.0