2016-02-06 20 views
9

Ho un dataframe panda con date con intervalli irregolari. C'è un modo per usare 7days come finestra mobile per calcolare median absolute deviation, mediana ecc.? Mi sento come se potessi in qualche modo usare pandas.rolling_apply ma non ci vogliono date con intervalli irregolari per il parametro della finestra. Ho trovato un post simile https://stackoverflow.com/a/30244019/3128336 e sto provando a creare la mia funzione personalizzata ma non riesco ancora a capire ... Qualcuno può aiutare per favore?Come utilizzare i giorni come finestra per la funzione rolling_apply di panda

import pandas as pd 
from datetime import datetime 

person = ['A','B','C','B','A','C','A','B','C','A',] 
ts = [ 
    datetime(2000, 1, 1), 
    datetime(2000, 1, 1), 
    datetime(2000, 1, 10), 
    datetime(2000, 1, 20), 
    datetime(2000, 1, 25), 
    datetime(2000, 1, 30), 
    datetime(2000, 2, 8), 
    datetime(2000, 2, 12), 
    datetime(2000, 2, 17), 
    datetime(2000, 2, 20), 
] 
score = [9,2,1,3,8,4,2,3,1,9] 
df = pd.DataFrame({'ts': ts, 'person': person, 'score': score}) 

df assomiglia a questo

person score ts 
0 A  9  2000-01-01 
1 B  2  2000-01-01 
2 C  1  2000-01-10 
3 B  3  2000-01-20 
4 A  8  2000-01-25 
5 C  4  2000-01-30 
6 A  2  2000-02-08 
7 B  3  2000-02-12 
8 C  1  2000-02-17 
9 A  9  2000-02-20 
+0

Vuoi una finestra mobile o di una finestra in espansione? – Goyo

+0

Voglio spostare la finestra. Quindi qualcosa come questo 'pd.rolling_apply (df, window = relativedelta (days = 7), func, min_periods = 1)' Sono stato confuso tra questi due. Lasciami correggere il mio post. Grazie per aver segnalato –

+0

Puoi spiegare cosa non funziona con la soluzione alla domanda a cui ti sei collegato? Suppongo che il ricampionamento dei dati su dati giornalieri prima di applicare l'applicazione continua rimuova le date duplicate? – joris

risposta

5

È possibile utilizzare un delta di tempo per selezionare le righe all'interno della vostra finestra e quindi utilizzare applicano a correre attraverso ogni riga e di aggregazione:

>>> from datetime import timedelta 
>>> delta = timedelta(days=7) 
>>> df_score_mean = df.apply(lambda x: np.mean(df['score'][df['ts'] <= x['ts'] + delta]), axis=1) 
0 5.500000 
1 5.500000 
2 4.000000 
3 4.600000 
4 4.500000 
5 4.500000 
6 4.555556 
7 4.200000 
8 4.200000 
9 4.200000 
+0

Questo è vicino a ciò di cui ho bisogno! Solo una domanda qui, come può cambiare il modo in cui funziona il turno con apply? La funzione lambda che hai suggerito funziona in modo opposto. 'pd.rolling_median (df.score, window = 2)', ad esempio, restituisce NA per la prima riga, non l'ultima riga. (In realtà voglio aggiungere una funzione equivalente a 'min_periods = 1' per copiare i valori per la prima settimana però) –

+1

Credo che la funzione lambda non debba restituire NA per qualsiasi riga perché selezionerà sempre almeno una riga per eseguire la funzione np.mean() su. Stai chiedendo come cambiare la finestra in modo da poter guardare avanti o indietro? Nella funzione lambda, selezioniamo qualsiasi riga minore o uguale alla riga corrente + 7 giorni. Se si desidera guardare indietro di 7 giorni, è possibile selezionare righe maggiori o uguali alla riga corrente - 7 giorni. –

+0

Ah questo ha senso! Sì, ho bisogno di guardare indietro. –

0

I Non ho abbastanza familiarità con le funzioni di data di rotazione - quindi mi sono chiesto se aggiungere i dati mancanti (in effetti un Dataframe pieno di dati mancanti) E poi la tua finestra mobile dovrebbe essere più facile da implementare.

from datetime import date 
import pandas as pd 
##############Your Initial DataFrame ############## 
person = ['A','B','C','B','A','C','A','B','C','A',] 
ts = [ 
    datetime(2000, 1, 1), 
    datetime(2000, 1, 1), 
    datetime(2000, 1, 10), 
    datetime(2000, 1, 20), 
    datetime(2000, 1, 25), 
    datetime(2000, 1, 30), 
    datetime(2000, 2, 8), 
    datetime(2000, 2, 12), 
    datetime(2000, 2, 17), 
    datetime(2000, 2, 15), 
] 
score = [9,2,1,3,8,4,2,3,1,9] 
df = pd.DataFrame({'ts': ts, 'person': person, 'score': score}) 
################## Blank DataFrame in Same Format ############### 
#Create some dates 
start = date(2000,1,1) 
end = date(2000,3,1) 
#We have 3 people 
Eperson=['A','B','C'] 
#They Score 0 
Escore=[0] 
#Need a date range in Days 
ets=pd.date_range(start, end, freq='D') 
dfEmpty=pd.DataFrame([(c,b,0) for b in Eperson for c in ets]) 
dfEmpty.columns=['ts','person','score'] 

################# Now Join them 

dfJoin=dfEmpty.merge(df,how='outer',on=['ts','person']) 
dfJoin['score']=dfJoin.score_x+dfJoin.score_y 
dfJoin.score.fillna(0,inplace=True) 
del dfJoin['score_x'] 
del dfJoin['score_y']' 

Ora avete il frame di dati non saranno date mancanti per persona - e se la data originale mancava allora la persona/punteggio sarà 0.

Apprezzo questo potrebbe non funzionare dovrebbe avere a che fare con milioni di record.

Ci scusiamo per i commenti di tipo non PEP ... è ancora in corso.

0

Pubblicare semplicemente la mia soluzione basata su suggestion di Brian Huey.

from datetime import datetime, timedelta 
import statsmodels.api as sm 

delta = timedelta(days=7) 

def calc_mad_mean(row): 
    start = row['ts'] 
    end = start + delta 
    subset = df['score'][(start <= df['ts']) & (df['ts'] < end)] 
    return pd.Series({'mad': sm.robust.mad(subset), 'med': np.median(subset)}) 

first_wk = df.ts.iloc[0] + delta 
results = df[first_wk < df.ts].apply(calc_mad_mean, axis=1) 
df.join(results, how='outer') 

Risultati

person score ts   mad  med 
0 A  9  2000-01-01 NaN  NaN 
1 B  2  2000-01-01 NaN  NaN 
2 C  1  2000-01-10 0.000000 1.0 
3 B  3  2000-01-20 3.706506 5.5 
4 A  8  2000-01-25 2.965204 6.0 
5 C  4  2000-01-30 0.000000 4.0 
6 A  2  2000-02-08 0.741301 2.5 
7 B  3  2000-02-12 1.482602 2.0 
8 C  1  2000-02-17 5.930409 5.0 
9 A  9  2000-02-20 0.000000 9.0 
Problemi correlati