2014-11-21 3 views
11

Предположим, у меня ниже кадра данныхпанды прокатные сумму последние пять минут

Date, A 
2014-11-21 11:00:00, 1 
2014-11-21 11:03:00, 4 
2014-11-21 11:04:00, 1 
2014-11-21 11:05:00, 2 
2014-11-21 11:07:00, 4 
2014-11-21 11:08:00, 1 
2014-11-21 11:12:00, 1 
2014-11-21 11:13:00, 2 

Первый столбец DateTime объект и второй столбец представляет собой целое число. Я хочу рассчитать сумму столбца «А» за последние пять минут для каждой строки.

В качестве примера для строки 2014-11-21 11:12:00, 1 сумма столбца «А» будет равна 2 (1 + 1), а сумма столбца «А» для строки 2014-11-21 11:05:00, 2 будет равна 7 (2 + 1 + 4). Важно то, что количество прошлых строк для временного окна (5 минут) не одинаково для каждой строки (поскольку временные ряды нерегулярны).

Как я могу получить сумму последних пяти минут для столбца «A», используя метод roll_sum в пандах? Заранее спасибо.

ответ

13

В общем, если даты совершенно произвольны, я думаю, вы будете вынуждены использовать Python for-loop по рядам или use df.apply, (который под капотом, а также использует цикл Python.)

Однако, если ваши даты имеют общую частоту, как в случае выше, то есть трюк, который должен быть намного быстрее, чем использование df.apply: Расширьте таймеры в соответствии с общей частотой - в этом случае, 1 минута - заполните NaNs с нулями, а затем вызвать rolling_sum:

In [279]: pd.rolling_sum(df.set_index(['Date']).asfreq('1T').fillna(0), window=5, min_periods=1).reindex(df['Date']) 
Out[279]: 
         A 
Date     
2014-11-21 11:00:00 1 
2014-11-21 11:03:00 5 
2014-11-21 11:04:00 6 
2014-11-21 11:05:00 7 
2014-11-21 11:07:00 11 
2014-11-21 11:08:00 8 
2014-11-21 11:12:00 2 
2014-11-21 11:13:00 3 

конечно, любой временной ряд имеет общую частоту, если вы готовы принять достаточно маленькую детализацию, но требуемый размер df.asfreq(...) может сделать этот трюк нецелесообразным.


Ниже приведен пример более общего подхода с использованием df.apply. Обратите внимание, что вызов searchsorted опирается на df['Date'] в отсортированном порядке.

import numpy as np 
import pandas as pd 
df = pd.read_csv('data', parse_dates=[0], sep=',\s*') 
start_dates = df['Date'] - pd.Timedelta(minutes=5) 
df['start_index'] = df['Date'].values.searchsorted(start_dates, side='right') 
df['end_index'] = np.arange(len(df)) 

def sum_window(row): 
    return df['A'].iloc[row['start_index']:row['end_index']+1].sum() 
df['rolling_sum'] = df.apply(sum_window, axis=1) 

print(df[['Date', 'A', 'rolling_sum']]) 

дает

    Date A rolling_sum 
0 2014-11-21 11:00:00 1   1 
1 2014-11-21 11:03:00 4   5 
2 2014-11-21 11:04:00 1   6 
3 2014-11-21 11:05:00 2   7 
4 2014-11-21 11:07:00 4   11 
5 2014-11-21 11:08:00 1   8 
6 2014-11-21 11:12:00 1   2 
7 2014-11-21 11:13:00 2   3 

Вот тест сравнения df.asfreq трюк против вызова df.apply:

import numpy as np 
import pandas as pd 
df = pd.read_csv('data', parse_dates=[0], sep=',\s*') 

def big_df(df): 
    df = df.copy() 
    for i in range(7): 
     dates = df['Date'] + pd.Timedelta(df.iloc[-1]['Date']-df.iloc[0]['Date']) + pd.Timedelta('1 minute') 
     df2 = pd.DataFrame({'Date': dates, 'A': df['A']}) 
     df = pd.concat([df, df2]) 
    df = df.reset_index(drop=True) 
    return df 

def using_apply(): 
    start_dates = df['Date'] - pd.Timedelta(minutes=5) 
    df['start_index'] = df['Date'].values.searchsorted(start_dates, side='right') 
    df['end_index'] = np.arange(len(df)) 

    def sum_window(row): 
     return df['A'].iloc[row['start_index']:row['end_index']+1].sum() 

    df['rolling_sum'] = df.apply(sum_window, axis=1) 
    return df[['Date', 'rolling_sum']] 

def using_asfreq(): 
    result = (pd.rolling_sum(
     df.set_index(['Date']).asfreq('1T').fillna(0), 
     window=5, min_periods=1).reindex(df['Date'])) 
    return result 

In [364]: df = big_df(df) 

In [367]: %timeit using_asfreq() 
1000 loops, best of 3: 1.21 ms per loop 

In [368]: %timeit using_apply() 
1 loops, best of 3: 208 ms per loop 
+0

Большое спасибо за быстрый ответ. Я не могу использовать метод df.asfreq (...), так как наименьшая степень детализации в моем наборе данных составляет секунды, и у меня есть миллионы строк. Но метод df.apply делает трюк. –

+0

В общем подходе работает только одна вещь, которую нужно иметь в виду, если она используется в длинном коде: функция 'sum_window' не использует вход' df' как явно, поэтому нужно быть осторожным. – nilesh

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