2015-07-03 10 views
7

Mam dużą ramkę danych z indeksem datetime i muszę przeprowadzić ponowną próbę danych do dokładnie 10 równorzędnych okresów.Jak dokonać ponownej próby porównania pliku df z indeksem datetime do dokładnie n równych okresów?

Do tej pory próbowałem znaleźć pierwsze i ostatnie daty w celu określenia całkowitej liczby dni w danych, podzielenie przez 10 w celu określenia rozmiaru każdego okresu, a następnie ponowne próbkowanie z użyciem tej liczby dni. np .:

first = df.reset_index().timesubmit.min() 
last = df.reset_index().timesubmit.max() 
periodsize = str((last-first).days/10) + 'D' 

df.resample(periodsize,how='sum') 

Nie gwarantuje to dokładnie 10 okresów w df po ponownym próbkowaniu, ponieważ periodize jest zaokrągloną w dół int. Użycie float nie działa przy ponownym próbkowaniu. Wydaje się, że albo jest coś prostego, czego tu brakuje, albo atakuję cały problem.

Odpowiedz

1
import numpy as np 
import pandas as pd 

n = 10 
nrows = 33 
index = pd.date_range('2000-1-1', periods=nrows, freq='D') 
df = pd.DataFrame(np.ones(nrows), index=index) 
print(df) 
#    0 
# 2000-01-01 1 
# 2000-01-02 1 
# ... 
# 2000-02-01 1 
# 2000-02-02 1 

first = df.index.min() 
last = df.index.max() + pd.Timedelta('1D') 
secs = int((last-first).total_seconds()//n) 
periodsize = '{:d}S'.format(secs) 

result = df.resample(periodsize, how='sum') 
print('\n{}'.format(result)) 
assert len(result) == n 

rentowności

     0 
2000-01-01 00:00:00 4 
2000-01-04 07:12:00 3 
2000-01-07 14:24:00 3 
2000-01-10 21:36:00 4 
2000-01-14 04:48:00 3 
2000-01-17 12:00:00 3 
2000-01-20 19:12:00 4 
2000-01-24 02:24:00 3 
2000-01-27 09:36:00 3 
2000-01-30 16:48:00 3 

wartości w 0 -kolumna wskazuje liczbę zagregowanych wierszy, ponieważ oryginalna ramka DataFrame została wypełniona wartościami 1. Wzorzec 4 i 3 jest mniej więcej taki, jaki można uzyskać, ponieważ 33 wiersze nie mogą być równo podzielone na 10 grup.


Wyjaśnienie: Rozważmy to prostsze DataFrame:

n = 2 
nrows = 5 
index = pd.date_range('2000-1-1', periods=nrows, freq='D') 
df = pd.DataFrame(np.ones(nrows), index=index) 
#    0 
# 2000-01-01 1 
# 2000-01-02 1 
# 2000-01-03 1 
# 2000-01-04 1 
# 2000-01-05 1 

Korzystanie df.resample('2D', how='sum') daje złą liczbę grup

In [366]: df.resample('2D', how='sum') 
Out[366]: 
      0 
2000-01-01 2 
2000-01-03 2 
2000-01-05 1 

Korzystanie df.resample('3D', how='sum') daje prawo liczbę grup, ale druga grupa zaczyna się od 2000-01-04, która nie ev enly podzielić DataFrame na dwie jednakowo rozmieszczonych grup:

In [367]: df.resample('3D', how='sum') 
Out[367]: 
      0 
2000-01-01 3 
2000-01-04 2 

Aby to zrobić lepiej, musimy pracować w lepszej rozdzielczości czasowej niż w dniach. Od Timedelta s mają metodę total_seconds, pracujmy w kilka sekund.Tak na powyższym przykładzie, żądany ciąg częstotliwość byłaby

In [374]: df.resample('216000S', how='sum') 
Out[374]: 
        0 
2000-01-01 00:00:00 3 
2000-01-03 12:00:00 2 

ponieważ istnieją 216000 * 2 sekundy w 5 dni:

In [373]: (pd.Timedelta(days=5)/pd.Timedelta('1S'))/2 
Out[373]: 216000.0 

Ok, więc teraz wszyscy musimy to sposób uogólnić to . Będziemy potrzebować minimalnych i maksymalnych dat w indeksie:

first = df.index.min() 
last = df.index.max() + pd.Timedelta('1D') 

Dodajemy dodatkowy dzień, ponieważ sprawia to, że różnica w dniach wychodzi dobrze. W powyższym przykładzie Są tylko 4 dni pomiędzy znaczniki czasu do 2000-01-05 i 2000-01-01,

In [377]: (pd.Timestamp('2000-01-05')-pd.Timestamp('2000-01-01')).days 
Out[378]: 4 

Ale jak widać w pracował przykład DataFrame ma 5 rzędów reprezentujących 5 dni. Ma więc sens dodanie dodatkowego dnia.

Teraz możemy obliczyć prawidłową liczbę sekund w każdym równo rozmieszczone z grupy:

secs = int((last-first).total_seconds()//n) 
+0

Wydaje się to dość proste. Doceniam również dodatkowe wyjaśnienie. Dzięki! –

1

Oto jeden ze sposobów zapewnienia podokresów o równych rozmiarach przy użyciu np.linspace() na pd.Timedelta, a następnie klasyfikowanie każdego obs za pomocą pd.cut.

import pandas as pd 
import numpy as np 

# generate artificial data 
np.random.seed(0) 
df = pd.DataFrame(np.random.randn(100, 2), columns=['A', 'B'], index=pd.date_range('2015-01-01 00:00:00', periods=100, freq='8H')) 

Out[87]: 
          A  B 
2015-01-01 00:00:00 1.7641 0.4002 
2015-01-01 08:00:00 0.9787 2.2409 
2015-01-01 16:00:00 1.8676 -0.9773 
2015-01-02 00:00:00 0.9501 -0.1514 
2015-01-02 08:00:00 -0.1032 0.4106 
2015-01-02 16:00:00 0.1440 1.4543 
2015-01-03 00:00:00 0.7610 0.1217 
2015-01-03 08:00:00 0.4439 0.3337 
2015-01-03 16:00:00 1.4941 -0.2052 
2015-01-04 00:00:00 0.3131 -0.8541 
2015-01-04 08:00:00 -2.5530 0.6536 
2015-01-04 16:00:00 0.8644 -0.7422 
2015-01-05 00:00:00 2.2698 -1.4544 
2015-01-05 08:00:00 0.0458 -0.1872 
2015-01-05 16:00:00 1.5328 1.4694 
...      ...  ... 
2015-01-29 08:00:00 0.9209 0.3187 
2015-01-29 16:00:00 0.8568 -0.6510 
2015-01-30 00:00:00 -1.0342 0.6816 
2015-01-30 08:00:00 -0.8034 -0.6895 
2015-01-30 16:00:00 -0.4555 0.0175 
2015-01-31 00:00:00 -0.3540 -1.3750 
2015-01-31 08:00:00 -0.6436 -2.2234 
2015-01-31 16:00:00 0.6252 -1.6021 
2015-02-01 00:00:00 -1.1044 0.0522 
2015-02-01 08:00:00 -0.7396 1.5430 
2015-02-01 16:00:00 -1.2929 0.2671 
2015-02-02 00:00:00 -0.0393 -1.1681 
2015-02-02 08:00:00 0.5233 -0.1715 
2015-02-02 16:00:00 0.7718 0.8235 
2015-02-03 00:00:00 2.1632 1.3365 

[100 rows x 2 columns] 


# cutoff points, 10 equal-size group requires 11 points 
# measured by timedelta 1 hour 
time_delta_in_hours = (df.index - df.index[0])/pd.Timedelta('1h') 
n = 10 
ts_cutoff = np.linspace(0, time_delta_in_hours[-1], n+1) 
# labels, time index 
time_index = df.index[0] + np.array([pd.Timedelta(str(time_delta)+'h') for time_delta in ts_cutoff]) 

# create a categorical reference variables 
df['start_time_index'] = pd.cut(time_delta_in_hours, bins=10, labels=time_index[:-1]) 
# for clarity, reassign labels using end-period index 
df['end_time_index'] = pd.cut(time_delta_in_hours, bins=10, labels=time_index[1:]) 

Out[89]: 
          A  B start_time_index  end_time_index 
2015-01-01 00:00:00 1.7641 0.4002 2015-01-01 00:00:00 2015-01-04 07:12:00 
2015-01-01 08:00:00 0.9787 2.2409 2015-01-01 00:00:00 2015-01-04 07:12:00 
2015-01-01 16:00:00 1.8676 -0.9773 2015-01-01 00:00:00 2015-01-04 07:12:00 
2015-01-02 00:00:00 0.9501 -0.1514 2015-01-01 00:00:00 2015-01-04 07:12:00 
2015-01-02 08:00:00 -0.1032 0.4106 2015-01-01 00:00:00 2015-01-04 07:12:00 
2015-01-02 16:00:00 0.1440 1.4543 2015-01-01 00:00:00 2015-01-04 07:12:00 
2015-01-03 00:00:00 0.7610 0.1217 2015-01-01 00:00:00 2015-01-04 07:12:00 
2015-01-03 08:00:00 0.4439 0.3337 2015-01-01 00:00:00 2015-01-04 07:12:00 
2015-01-03 16:00:00 1.4941 -0.2052 2015-01-01 00:00:00 2015-01-04 07:12:00 
2015-01-04 00:00:00 0.3131 -0.8541 2015-01-01 00:00:00 2015-01-04 07:12:00 
2015-01-04 08:00:00 -2.5530 0.6536 2015-01-04 07:12:00 2015-01-07 14:24:00 
2015-01-04 16:00:00 0.8644 -0.7422 2015-01-04 07:12:00 2015-01-07 14:24:00 
2015-01-05 00:00:00 2.2698 -1.4544 2015-01-04 07:12:00 2015-01-07 14:24:00 
2015-01-05 08:00:00 0.0458 -0.1872 2015-01-04 07:12:00 2015-01-07 14:24:00 
2015-01-05 16:00:00 1.5328 1.4694 2015-01-04 07:12:00 2015-01-07 14:24:00 
...      ...  ...     ...     ... 
2015-01-29 08:00:00 0.9209 0.3187 2015-01-27 09:36:00 2015-01-30 16:48:00 
2015-01-29 16:00:00 0.8568 -0.6510 2015-01-27 09:36:00 2015-01-30 16:48:00 
2015-01-30 00:00:00 -1.0342 0.6816 2015-01-27 09:36:00 2015-01-30 16:48:00 
2015-01-30 08:00:00 -0.8034 -0.6895 2015-01-27 09:36:00 2015-01-30 16:48:00 
2015-01-30 16:00:00 -0.4555 0.0175 2015-01-27 09:36:00 2015-01-30 16:48:00 
2015-01-31 00:00:00 -0.3540 -1.3750 2015-01-30 16:48:00 2015-02-03 00:00:00 
2015-01-31 08:00:00 -0.6436 -2.2234 2015-01-30 16:48:00 2015-02-03 00:00:00 
2015-01-31 16:00:00 0.6252 -1.6021 2015-01-30 16:48:00 2015-02-03 00:00:00 
2015-02-01 00:00:00 -1.1044 0.0522 2015-01-30 16:48:00 2015-02-03 00:00:00 
2015-02-01 08:00:00 -0.7396 1.5430 2015-01-30 16:48:00 2015-02-03 00:00:00 
2015-02-01 16:00:00 -1.2929 0.2671 2015-01-30 16:48:00 2015-02-03 00:00:00 
2015-02-02 00:00:00 -0.0393 -1.1681 2015-01-30 16:48:00 2015-02-03 00:00:00 
2015-02-02 08:00:00 0.5233 -0.1715 2015-01-30 16:48:00 2015-02-03 00:00:00 
2015-02-02 16:00:00 0.7718 0.8235 2015-01-30 16:48:00 2015-02-03 00:00:00 
2015-02-03 00:00:00 2.1632 1.3365 2015-01-30 16:48:00 2015-02-03 00:00:00 

[100 rows x 4 columns] 

df.groupby('start_time_index').agg('sum') 

Out[90]: 
          A  B 
start_time_index     
2015-01-01 00:00:00 8.6133 2.7734 
2015-01-04 07:12:00 1.9220 -0.8069 
2015-01-07 14:24:00 -8.1334 0.2318 
2015-01-10 21:36:00 -2.7572 -4.2862 
2015-01-14 04:48:00 1.1957 7.2285 
2015-01-17 12:00:00 3.2485 6.6841 
2015-01-20 19:12:00 -0.8903 2.2802 
2015-01-24 02:24:00 -2.1025 1.3800 
2015-01-27 09:36:00 -1.1017 1.3108 
2015-01-30 16:48:00 -0.0902 -2.5178 

Innym potencjalnie krótszym sposobem na to jest określenie częstotliwości próbkowania jako delty czasowej. Ale problem, jak pokazano poniżej, polega na tym, że dostarcza 11 podpróbek zamiast 10. Uważam, że powodem jest to, że resample implementuje schemat podpróbkowania left-inclusive/right-exclusive (or left-exclusive/right-inclusive), tak że ostatni obs na "2015-02-03 00 : 00: 00 'jest traktowany jako osobna grupa. Jeśli używamy pd.cut zrobić to samodzielnie, możemy określić include_lowest=True tak, że daje nam dokładnie 10 sub-próbek zamiast 11.

n = 10 
time_delta_str = str((df.index[-1] - df.index[0])/(pd.Timedelta('1s') * n)) + 's' 
df.resample(pd.Timedelta(time_delta_str), how='sum') 

Out[114]: 
          A  B 
2015-01-01 00:00:00 8.6133 2.7734 
2015-01-04 07:12:00 1.9220 -0.8069 
2015-01-07 14:24:00 -8.1334 0.2318 
2015-01-10 21:36:00 -2.7572 -4.2862 
2015-01-14 04:48:00 1.1957 7.2285 
2015-01-17 12:00:00 3.2485 6.6841 
2015-01-20 19:12:00 -0.8903 2.2802 
2015-01-24 02:24:00 -2.1025 1.3800 
2015-01-27 09:36:00 -1.1017 1.3108 
2015-01-30 16:48:00 -2.2534 -3.8543 
2015-02-03 00:00:00 2.1632 1.3365 
+0

To ciekawe, dzięki! Myślę, że odpowiedź @HappyLeapSecond jest nieco prostsza dla moich potrzeb. –

Powiązane problemy