2014-11-21 15 views
11

supponga che ho sotto frame di datipanda rotolamento somma degli ultimi cinque minuti

Date, A 
2014-11-21 11:00:00, 1 
2014-11-21 11:03:00, 4 
2014-11-21 11:04:00, 1 
2014-11-21 11:05:00, 2 
2014-11-21 11:07:00, 4 
2014-11-21 11:08:00, 1 
2014-11-21 11:12:00, 1 
2014-11-21 11:13:00, 2 

prima colonna è oggetto datetime e seconda colonna è un numero intero. Quello che voglio è calcolare la somma della colonna "A" per gli ultimi cinque minuti per ogni riga.

Come esempio per la riga 2014-11-21 11:12:00, 1, la somma della colonna "A" sarebbe 2 (1 + 1) e la somma della colonna "A" per la riga 2014-11-21 11:05:00, 2 sarebbe 7 (2 + 1 + 4). La cosa importante è che il numero di righe passate per la finestra temporale (5 minuti) non è lo stesso per ogni riga (perché la serie storica è irregolare).

Come posso ottenere la somma degli ultimi cinque minuti per la colonna 'A' con il metodo rolling_sum in panda? Grazie in anticipo.

risposta

13

In generale, se sono del tutto arbitrarie le date, penso che si sarebbe costretti ad usare un Python for-loop sopra le righe o le use df.apply, (che sotto il cofano, utilizza anche un ciclo di Python.)

Tuttavia, se le tue date condividono una frequenza comune, come sopra, allora c'è un trucco che dovrebbe essere molto più veloce dell'uso di df.apply: Espandi gli orari in base alla frequenza comune - in questo caso, 1 minuto - compila i NaN con zeri, e quindi chiamare rolling_sum:

In [279]: pd.rolling_sum(df.set_index(['Date']).asfreq('1T').fillna(0), window=5, min_periods=1).reindex(df['Date']) 
Out[279]: 
         A 
Date     
2014-11-21 11:00:00 1 
2014-11-21 11:03:00 5 
2014-11-21 11:04:00 6 
2014-11-21 11:05:00 7 
2014-11-21 11:07:00 11 
2014-11-21 11:08:00 8 
2014-11-21 11:12:00 2 
2014-11-21 11:13:00 3 

Naturalmente, qualsiasi serie temporale ha una frequenza comune se si è disposti ad accettare una granularità sufficientemente piccola, ma la dimensione richiesta di df.asfreq(...) potrebbe rendere questo trucco poco pratico.


Ecco un esempio dell'approccio più generale utilizzando df.apply. Si noti che la chiamata searchsorted si basa su df['Date'] in ordine.

import numpy as np 
import pandas as pd 
df = pd.read_csv('data', parse_dates=[0], sep=',\s*') 
start_dates = df['Date'] - pd.Timedelta(minutes=5) 
df['start_index'] = df['Date'].values.searchsorted(start_dates, side='right') 
df['end_index'] = np.arange(len(df)) 

def sum_window(row): 
    return df['A'].iloc[row['start_index']:row['end_index']+1].sum() 
df['rolling_sum'] = df.apply(sum_window, axis=1) 

print(df[['Date', 'A', 'rolling_sum']]) 

cede

    Date A rolling_sum 
0 2014-11-21 11:00:00 1   1 
1 2014-11-21 11:03:00 4   5 
2 2014-11-21 11:04:00 1   6 
3 2014-11-21 11:05:00 2   7 
4 2014-11-21 11:07:00 4   11 
5 2014-11-21 11:08:00 1   8 
6 2014-11-21 11:12:00 1   2 
7 2014-11-21 11:13:00 2   3 

Ecco un punto di riferimento confrontando il df.asfreq trucco contro chiamando df.apply:

import numpy as np 
import pandas as pd 
df = pd.read_csv('data', parse_dates=[0], sep=',\s*') 

def big_df(df): 
    df = df.copy() 
    for i in range(7): 
     dates = df['Date'] + pd.Timedelta(df.iloc[-1]['Date']-df.iloc[0]['Date']) + pd.Timedelta('1 minute') 
     df2 = pd.DataFrame({'Date': dates, 'A': df['A']}) 
     df = pd.concat([df, df2]) 
    df = df.reset_index(drop=True) 
    return df 

def using_apply(): 
    start_dates = df['Date'] - pd.Timedelta(minutes=5) 
    df['start_index'] = df['Date'].values.searchsorted(start_dates, side='right') 
    df['end_index'] = np.arange(len(df)) 

    def sum_window(row): 
     return df['A'].iloc[row['start_index']:row['end_index']+1].sum() 

    df['rolling_sum'] = df.apply(sum_window, axis=1) 
    return df[['Date', 'rolling_sum']] 

def using_asfreq(): 
    result = (pd.rolling_sum(
     df.set_index(['Date']).asfreq('1T').fillna(0), 
     window=5, min_periods=1).reindex(df['Date'])) 
    return result 

In [364]: df = big_df(df) 

In [367]: %timeit using_asfreq() 
1000 loops, best of 3: 1.21 ms per loop 

In [368]: %timeit using_apply() 
1 loops, best of 3: 208 ms per loop 
+0

Grazie mille per la rapida risposta. Non riesco a utilizzare il metodo df.asfreq (...) dal momento che la più piccola granularità nel mio set di dati è secondi e ho milioni di righe. Ma il metodo df.apply fa il trucco. –

+0

L'approccio generale funziona, solo una cosa da tenere a mente se viene usata nel codice lungo: la funzione 'sum_window' non usa l'input' df' come esplicitamente, quindi bisogna stare attenti. – nilesh

Problemi correlati