2014-01-26 4 views
4

Мне интересно, как я могу ускорить слияние двух фреймов данных. В одном из информационных кадров установлены временные метки данных (value col).Merge pandas DataFrames на основе нерегулярных временных интервалов

import pandas as pd 
import numpy as np 

data = pd.DataFrame({'time':np.sort(np.random.uniform(0,100,size=50)), 
        'value':np.random.uniform(-1,1,size=50)}) 

Другой имеет информацию временной интервал (start_time, end_time и связанные с ними interval_id).

intervals = pd.DataFrame({'interval_id':np.arange(9), 
          'start_time':np.random.uniform(0,5,size=9) + np.arange(0,90,10),  
          'end_time':np.random.uniform(5,10,size=9) + np.arange(0,90,10)}) 

Я хотел бы объединить эти два dataframes более эффективно, чем for петли ниже:

data['interval_id'] = np.nan 
for index, ser in intervals.iterrows(): 
    in_interval = (data['time'] >= ser['start_time']) & \ 
        (data['time'] <= ser['end_time']) 
    data['interval_id'][in_interval] = ser['interval_id'] 

result = data.merge(intervals, how='outer').sort('time').reset_index(drop=True) 

Я продолжаю воображать, я буду в состоянии использовать панд time series functionality, как диапазон дат или TimeGrouper , но мне еще предстоит выяснить что-нибудь более питоническое (панда-у?), чем выше.

Пример результата:

 time  value  interval_id start_time end_time 
0 0.575976 0.022727   NaN   NaN  NaN 
1 4.607545 0.222568   0 3.618715 8.294847 
2 5.179350 0.438052   0 3.618715 8.294847 
3 11.069956 0.641269   1 10.301728 19.870283 
4 12.387854 0.344192   1 10.301728 19.870283 
5 18.889691 0.582946   1 10.301728 19.870283 
6 20.850469 -0.027436   NaN   NaN  NaN 
7 23.199618 0.731316   2 21.488868 28.968338 
8 26.631284 0.570647   2 21.488868 28.968338 
9 26.996397 0.597035   2 21.488868 28.968338 
10 28.601867 -0.131712   2 21.488868 28.968338 
11 28.660986 0.710856   2 21.488868 28.968338 
12 28.875395 -0.355208   2 21.488868 28.968338 
13 28.959320 -0.430759   2 21.488868 28.968338 
14 29.702800 -0.554742   NaN   NaN  NaN 

Любые предложения от временных рядов подкованных людей там было бы весьма признателен.


Update, после того, как ответ Джеффа:

Основная проблема заключается в том, что interval_id не имеет никакого отношения к какому-либо регулярному временному интервалу (например, интервалы не всегда около 10 секунд). Один интервал может составлять 10 секунд, следующий может составлять 2 секунды, а следующий может составлять 100 секунд, поэтому я не могу использовать какую-либо регулярную схему округления, как предложил Джефф. К сожалению, мой минимальный пример выше не делает этого ясным.

+0

ли интервалы всегда пересекаются? – unutbu

+0

В этом случае да, интервалы всегда не пересекаются. Было бы интересно найти решение для перекрытия интервалов, теперь, когда вы спрашиваете! – pedmiston

ответ

6

Вы можете использовать np.searchsorted найти индексы, представляющие, где каждое значение в data['time'] впишется между intervals['start_time']. Затем вы можете снова позвонить np.searchsorted, чтобы найти индексы, представляющие, где каждое значение в data['time'] будет соответствовать между intervals['end_time']. Обратите внимание, что использование np.searchsorted основано на interval['start_time'] и interval['end_time'] в отсортированном порядке.

Для каждого соответствующего местоположения в массивах, где эти два показателя равны, data['time'] находится между interval['start_time'] и interval['end_time']. Заметим, что это зависит от того, что интервалы не пересекаются.

Использование searchsorted таким образом примерно в 5 раз быстрее, чем при использовании for-loop:

import pandas as pd 
import numpy as np 

np.random.seed(1) 
data = pd.DataFrame({'time':np.sort(np.random.uniform(0,100,size=50)), 
        'value':np.random.uniform(-1,1,size=50)}) 

intervals = pd.DataFrame(
    {'interval_id':np.arange(9), 
    'start_time':np.random.uniform(0,5,size=9) + np.arange(0,90,10),  
    'end_time':np.random.uniform(5,10,size=9) + np.arange(0,90,10)}) 

def using_loop(): 
    data['interval_id'] = np.nan 
    for index, ser in intervals.iterrows(): 
     in_interval = (data['time'] >= ser['start_time']) & \ 
         (data['time'] <= ser['end_time']) 
     data['interval_id'][in_interval] = ser['interval_id'] 

    result = data.merge(intervals, how='outer').sort('time').reset_index(drop=True) 
    return result 

def using_searchsorted(): 
    start_idx = np.searchsorted(intervals['start_time'].values, data['time'].values)-1 
    end_idx = np.searchsorted(intervals['end_time'].values, data['time'].values) 
    mask = (start_idx == end_idx) 
    result = data.copy() 
    result['interval_id'] = result['start_time'] = result['end_time'] = np.nan 
    result['interval_id'][mask] = start_idx 
    result.ix[mask, 'start_time'] = intervals['start_time'][start_idx[mask]].values 
    result.ix[mask, 'end_time'] = intervals['end_time'][end_idx[mask]].values 
    return result 

In [254]: %timeit using_loop() 
100 loops, best of 3: 7.74 ms per loop 

In [255]: %timeit using_searchsorted() 
1000 loops, best of 3: 1.56 ms per loop 

In [256]: 7.74/1.56 
Out[256]: 4.961538461538462 
1

Вы можете захотеть, чтобы интервалы времени были несколько разными, но должны дать вам начало.

In [34]: data['on'] = np.round(data['time']/10) 

In [35]: data.merge(intervals,left_on=['on'],right_on=['interval_id'],how='outer') 
Out[35]: 
     time  value on end_time interval_id start_time 
0 1.301658 -0.462594 0 7.630243   0 0.220746 
1 2.202654 0.054903 0 7.630243   0 0.220746 
2 10.253593 0.329947 1 17.715596   1 10.299464 
3 13.803064 -0.601021 1 17.715596   1 10.299464 
4 17.086290 0.484119 2 27.175455   2 24.710704 
5 21.797655 0.988212 2 27.175455   2 24.710704 
6 26.265165 0.491410 3 37.702968   3 30.670753 
7 27.777182 -0.121691 3 37.702968   3 30.670753 
8 34.066473 0.659260 3 37.702968   3 30.670753 
9 34.786337 -0.230026 3 37.702968   3 30.670753 
10 35.343021 0.364505 4 49.489028   4 42.948486 
11 35.506895 0.953562 4 49.489028   4 42.948486 
12 36.129951 -0.703457 4 49.489028   4 42.948486 
13 38.794690 -0.510535 4 49.489028   4 42.948486 
14 40.508702 -0.763417 4 49.489028   4 42.948486 
15 43.974516 -0.149487 4 49.489028   4 42.948486 
16 46.219554 0.893025 5 57.086065   5 53.124795 
17 50.206860 0.729106 5 57.086065   5 53.124795 
18 50.395082 -0.807557 5 57.086065   5 53.124795 
19 50.410783 0.996247 5 57.086065   5 53.124795 
20 51.602892 0.144483 5 57.086065   5 53.124795 
21 52.006921 -0.979778 5 57.086065   5 53.124795 
22 52.682896 -0.593500 5 57.086065   5 53.124795 
23 52.836037 0.448370 5 57.086065   5 53.124795 
24 53.052130 -0.227245 5 57.086065   5 53.124795 
25 57.169775 0.659673 6 65.927106   6 61.590948 
26 59.336176 -0.893004 6 65.927106   6 61.590948 
27 60.297771 0.897418 6 65.927106   6 61.590948 
28 61.151664 0.176229 6 65.927106   6 61.590948 
29 61.769023 0.894644 6 65.927106   6 61.590948 
30 64.221220 0.893012 6 65.927106   6 61.590948 
31 67.907417 -0.859734 7 78.192671   7 72.463468 
32 71.460483 -0.271364 7 78.192671   7 72.463468 
33 74.514028 0.621174 7 78.192671   7 72.463468 
34 75.822643 -0.351684 8 88.820139   8 83.183825 
35 84.252778 -0.685043 8 88.820139   8 83.183825 
36 84.838361 0.354365 8 88.820139   8 83.183825 
37 85.770611 -0.089678 9  NaN   NaN   NaN 
38 85.957559 0.649995 9  NaN   NaN   NaN 
39 86.498339 0.569793 9  NaN   NaN   NaN 
40 91.006735 0.731006 9  NaN   NaN   NaN 
41 91.941862 0.964376 9  NaN   NaN   NaN 
42 94.617522 0.626889 9  NaN   NaN   NaN 
43 95.318288 -0.088918 10  NaN   NaN   NaN 
44 95.595243 0.539685 10  NaN   NaN   NaN 
45 95.818267 -0.989647 10  NaN   NaN   NaN 
46 98.240444 0.931445 10  NaN   NaN   NaN 
47 98.722869 0.442502 10  NaN   NaN   NaN 
48 99.349198 0.585264 10  NaN   NaN   NaN 
49 99.829372 -0.743697 10  NaN   NaN   NaN 

[50 rows x 6 columns] 
+0

Спасибо, Джефф! К сожалению, я не думаю, что это работает во всех случаях. Например, если у меня есть событие со временем = 0.19, но первый интервал не начинается до 3.21, округление помещает это событие в первый интервал.Есть обходные пути, которые я предполагаю, это то, что вы имели в виду, указав время немного по-другому, но я думаю, что есть более глубокая проблема, что интервалы не являются регулярными. Я улучшил свой вопрос после вашего решения, поэтому спасибо (и жаль, что не стал более ясным). – pedmiston

Смежные вопросы