2016-06-08 3 views
2

У меня есть 2 серии панд: данные и события.Ускорение поиска панд

  • данных упорядоченный ряд точек данных
  • событий содержит индексы точек интереса данных

Я хотел бы, чтобы извлечь окно фиксированного размера вокруг каждой точки интереса.

я придумал:

res = [] 
for k in events: 
    win = data.loc[k - ticks_before:k + ticks_after].values 
    res.append(win) 

new_df = pd.DataFrame(res) 

который работает, но очень медленно. Любой Panda-fu, чтобы сделать это быстро?

Edit: нашел в 5 раз быстрее решение, которое:

res = np.zeros((len(events), win_len)) 
i = 0 
for k in events: 
    res[i] = data.loc[k - ticks_before:k + ticks_after] 
    i+=1 

new_df = pd.DataFrame(res) 

Любая идея, чтобы сделать его еще быстрее?

Ниже входной и выходной код:

Вход:

data = pd.Series(xrange(200)) 
events = [50, 77, 98, 125, 133, 159, 161] 
ticks_before = 32 
ticks_after = 16 
def slow_loop(data, events, ticks_before, ticks_after): 
    res = [] 
    for k in events: 
     win = data.loc[k - ticks_before:k + ticks_after].values 
     res.append(win) 
    new_df = pd.DataFrame(res) 
    return new_df.mean() 

def fast_loop(data, events, ticks_before, ticks_after): 
    win_len = ticks_before + ticks_after + 1 
    res = np.zeros((len(events), win_len)) 
    i = 0 
    for k in events: 
     res[i] = data.loc[k - ticks_before:k + ticks_after] 
     i+=1 

    new_df = pd.DataFrame(res) 
    return new_df.mean() 


assert(all(slow_loop(data, events, ticks_before, ticks_after) == 
      fast_loop(data, events, ticks_before, ticks_after))) 
%timeit slow_loop(data, events, ticks_before, ticks_after) 
%timeit fast_loop(data, events, ticks_before, ticks_after) 
fast_loop(data, events, ticks_before, ticks_after) 

Выход:

100 loops, best of 3: 3.66 ms per loop 
1000 loops, best of 3: 632 µs per loop 

0  82.714286 
1  83.714286 
2  84.714286 
3  85.714286 
4  86.714286 
5  87.714286 
6  88.714286 
7  89.714286 
8  90.714286 
9  91.714286 
10  92.714286 
11  93.714286 
12  94.714286 
13  95.714286 
14  96.714286 
15  97.714286 
16  98.714286 
17  99.714286 
18 100.714286 
19 101.714286 
20 102.714286 
21 103.714286 
22 104.714286 
23 105.714286 
24 106.714286 
25 107.714286 
26 108.714286 
27 109.714286 
28 110.714286 
29 111.714286 
30 112.714286 
31 113.714286 
32 114.714286 
33 115.714286 
34 116.714286 
35 117.714286 
36 118.714286 
37 119.714286 
38 120.714286 
39 121.714286 
40 122.714286 
41 123.714286 
42 124.714286 
43 125.714286 
44 126.714286 
45 127.714286 
46 128.714286 
47 129.714286 
48 130.714286 
dtype: float64 
+3

Просьба предоставить образцы данных и выборки. – root

+0

Я думал, что 'pandas.core.window.Rolling.apply' может помочь, но, с другой стороны, это не так. Я тоже хотел бы использовать ввод и вывод проб. – shane

+0

Как вы определяете медленный, по сравнению с чем? – Merlin

ответ

0

Я считаю, фильтрация по индексу времени должно быть очень эффективным, с Панды.

Что-то вроде

df.set_index('my_time_variable',inplace=True) 
df[time - ticks_before:time + ticks_after] 

должны ускорить Поиск много. и тогда вы все еще можете перебирать все свои даты. Обязательно используйте формат для time - ticks_before, который recognizible по панд в качестве даты, такие как '2015-05-05 13:30'

+0

Оно уже проиндексировано по времени – Fra

0

немного упрощен, Вопрос: Почему существует ряд числа для xxx.mean(). Среднее должно быть одно число! Вероятно, это добавляет время обработки.

def fast_loop(data, events, ticks_before, ticks_after): 
     win_len = ticks_before + ticks_after + 1 
     res  = np.zeros((len(events), win_len)) 

     for k in enumerate(events): 
      res[i] = data.loc[k - ticks_before:k + ticks_after] 

     return pd.DataFrame(res).mean() 
2

здесь является NumPy решение, которое, кажется, в 10 раз быстрее, по сравнению с fast_loop:

# numpy solution 
def np1(data, events, ticks_before, ticks_after): 
    return pd.Series(
       np.concatenate(
        [data.values[x - ticks_before: x + ticks_after+1] for x in events]) 
       .reshape(len(events), ticks_before + ticks_after+1) 
       .mean(0)) 

# similar Pandas solution 
def pd1(data, events, ticks_before, ticks_after): 
    return pd.Series(
      pd.concat(
       [data[x - ticks_before : x + ticks_after +1] for x in events], 
       ignore_index=True) 
       .reshape((len(events), ticks_before + ticks_after +1)) 
       .mean(0)) 

Timing против серии 20M строк:

In [440]: %timeit slow_loop(data2, events, ticks_before, ticks_after) 
The slowest run took 10.67 times longer than the fastest. This could mean that an intermediate result is being cached. 
100 loops, best of 3: 4.7 ms per loop 

In [441]: %timeit fast_loop(data2, events, ticks_before, ticks_after) 
1000 loops, best of 3: 936 µs per loop 

In [442]: %timeit pir5(data2, events, ticks_before, ticks_after) 
1000 loops, best of 3: 436 µs per loop 

In [443]: %timeit pd1(data2, events, ticks_before, ticks_after) 
1000 loops, best of 3: 804 µs per loop 

In [444]: %timeit np1(data2, events, ticks_before, ticks_after) 
10000 loops, best of 3: 75.8 µs per loop 

Установка:

In [435]: data2 = data.copy() 

In [436]: data2 = pd.concat([data2] * 10**5, ignore_index=True) 

In [437]: data2.shape 
Out[437]: (20000000,) 

OLD Ответ:

Timing (на другой/медленнее машина):

In [353]: %timeit fast_loop(data, events, ticks_before, ticks_after) 
100 loops, best of 3: 2.27 ms per loop 

In [354]: %timeit np1(data, events, ticks_before, ticks_after) 
1000 loops, best of 3: 222 ┬╡s per loop 

In [360]: %timeit slow_loop(data, events, ticks_before, ticks_after) 
100 loops, best of 3: 12.5 ms per loop 

проверка:

In [356]: (fast_loop(data, events, ticks_before, ticks_after) == np1(data, events, ticks_before, ticks_after)).all() 
Out[356]: True 
+0

/@ MaxU Wow, 96,7 мкс за цикл – Merlin

+0

@Merlin, да, я также был удивлен, что 'numpy' (np1) намного быстрее, чем почти подобное решение 'pandas' (pd1) – MaxU

1

Я сдаюсь! Я пробовал кучу вещей. Ниже приведены лишь некоторые из них:

def pir1(data, events, ticks_before, ticks_after): 
    rng = np.add.outer(events, [-1 * ticks_before, ticks_after + 1]) 
    res = np.zeros(ticks_before + ticks_after + 1) 
    for r in rng: 
     res += data[r[0]:r[1]] 
    res /= len(rng) 
    return res 

def pir2(data, events, ticks_before, ticks_after): 
    rng = np.add.outer(events, [-1 * ticks_before, ticks_after + 1]) 
    return np.array([data[r[0]:r[1]] for r in rng]).mean(axis=0) 

def pir3(data, events, ticks_before, ticks_after): 
    events = np.asarray(events) 
    return pd.DataFrame([data[offset + events].mean() for offset in range(-ticks_before, ticks_after + 1)]) 

def pir4(data, events, ticks_before, ticks_after): 
    events = np.asarray(events) 
    return pd.DataFrame([data[offset + events] for offset in range(-ticks_before, ticks_after + 1)]).mean(axis=1) 

def pir5(data, events, ticks_before, ticks_after): 
    events = np.asarray(events) 
    data = data.values 
    return np.dstack((data[offset + events] for offset in range(-ticks_before, ticks_after + 1))).mean(axis=1) 

def pir6(data, events, ticks_before, ticks_after): 
    events = np.asarray(events) 
    cums = data.cumsum() 
    return np.dstack((data[offset + events] for offset in range(-ticks_before, ticks_after + 1))).mean(axis=1) 

Времена: pir5 немного бьет.

enter image description here

+0

/@ piRSquared pir5-- 86,7 мкс за цикл – Merlin

+0

@Merlin bah! моя машина должна быть ужасной. – piRSquared

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