2016-02-06 8 views
9

Mam parsę danych z nieregularnie rozmieszczonymi datami. Czy istnieje sposób użycia 7 dni jako ruchomego okna do obliczenia median absolute deviation, mediany itp ..? Czuję, że mogłem w jakiś sposób użyć pandas.rolling_apply, ale nie zajmuje to nieregularnych odstępów dla parametru okna. Znalazłem podobny post https://stackoverflow.com/a/30244019/3128336 i próbuję utworzyć moją funkcję niestandardową, ale nie mogę jeszcze dowiedzieć się ... Czy ktoś może ci pomóc?Jak używać dni jako okna dla pandy funkcja rolling_apply

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 wygląda to

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

Chcesz ruchomego okna lub rozszerzający okno? – Goyo

+0

Chcę przenieść okno. Tak więc coś takiego 'pd.rolling_apply (df, window = relativedelta (days = 7), func, min_periods = 1)' Byłem zmieszany między tymi dwoma. Pozwól mi poprawić mój post. Dzięki za wskazanie –

+0

Czy możesz wyjaśnić, co nie działa z rozwiązaniem problemu, z którym się łączysz? Przypuszczam, że resampling danych do codziennych danych przed wykonaniem walcowania usuwa zduplikowane daty? – joris

Odpowiedz

5

Można użyć delta czasu, aby wybrać wiersze w swoim oknie, a następnie użyć zastosowanie do uruchomienia przez każdego wiersza i kruszywa:

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

To jest blisko tego, czego potrzebuję! Tylko jedno pytanie, w jaki sposób można zmienić sposób zmiany pracy z zastosowaniem? Funkcja lambda, którą zasugerowałeś, działa w odwrotny sposób. 'pd.rolling_median (df.score, window = 2)' na przykład zwraca NA dla pierwszego wiersza, a nie ostatniego wiersza. (Naprawdę chcę dodać funkcję równoważną 'min_periods = 1', aby skopiować wartości na pierwszy tydzień) –

+1

Wierzę, że funkcja lambda nie powinna zwracać NA dla żadnych wierszy, ponieważ zawsze wybierze co najmniej jeden wiersz do uruchom funkcję np.mean(). Czy pytasz, jak zmienić okno, aby wyglądało to na przyszłość lub na przyszłość? W funkcji lambda wybieramy dowolne wiersze mniejsze lub równe bieżącemu rzędowi + 7 dni. Jeśli chcesz spojrzeć wstecz w ciągu 7 dni, możesz wybrać wiersze większe lub równe bieżącemu rzędowi - 7 dni. –

+0

Ach, to ma sens! Tak, muszę spojrzeć wstecz. –

0

I nie jestem dostatecznie zaznajomiony z funkcjami daty kroczącej - więc zastanawiałem się nad dodaniem brakujących danych (w rzeczywistości DataFrame pełne brakujących danych) A potem twoje okno ruchome okno powinny być łatwiejsze do wdrożenia.

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

Teraz masz ramkę danych będzie żadnych brakujących dat za osobę - a jeśli pierwotna data brakowało wówczas osoba/wynik będzie 0.

Doceniam to może nie działać powinny mieć do czynienia z milionami rekordów.

Przeprosiny za komentarze typu innego niż PEP ... nadal trwają.

0

Po prostu publikuję moje rozwiązanie na podstawie Briana Huey'a suggestion.

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

Wyniki

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 
Powiązane problemy