2017-02-17 29 views
4

У меня есть фрейм данных с DateTimes и целогоPython Панды - найти последовательную группу с максимальной совокупностью значений

import numpy as np 
import pandas as pd 

df = pd.DataFrame() 
df['dt'] = pd.date_range("2017-01-01 12:00", "2017-01-01 12:30", freq="1min") 
df['val'] = np.random.choice(xrange(1, 100), df.shape[0]) 

дает мне

    dt val 
0 2017-01-01 12:00:00 33 
1 2017-01-01 12:01:00 42 
2 2017-01-01 12:02:00 44 
3 2017-01-01 12:03:00 6 
4 2017-01-01 12:04:00 70 
5 2017-01-01 12:05:00 94* 
6 2017-01-01 12:06:00 42* 
7 2017-01-01 12:07:00 97* 
8 2017-01-01 12:08:00 12 
9 2017-01-01 12:09:00 11 
10 2017-01-01 12:10:00 66 
11 2017-01-01 12:11:00 71 
12 2017-01-01 12:12:00 25 
13 2017-01-01 12:13:00 23 
14 2017-01-01 12:14:00 39 
15 2017-01-01 12:15:00 25 

Как я могу найти какую N -minute группы подрядаdt дает мне максимальную сумму val?

В этом случае, если N=3, то результат должен быть:

    dt val 
5 2017-01-01 12:05:00 94 
6 2017-01-01 12:06:00 42 
7 2017-01-01 12:07:00 97 

(отмеченные звездочкой выше)

ответ

5

Вы можете использовать np.convolve, чтобы получить правильный начальный индекс и перейти оттуда.

def cons_max(df, N): 
    max_loc = np.convolve(df.val, np.ones(N, dtype=int), mode='valid').argmax() 
    return df.loc[max_loc:max_loc+N-1] 

Demo

>>> cons_max(df, 3) 
        dt val 
5 2017-01-01 12:05:00 94 
6 2017-01-01 12:06:00 42 
7 2017-01-01 12:07:00 97 

>>> cons_max(df, 5) 
        dt val 
4 2017-01-01 12:04:00 70 
5 2017-01-01 12:05:00 94 
6 2017-01-01 12:06:00 42 
7 2017-01-01 12:07:00 97 
8 2017-01-01 12:08:00 12 

Это работает эффективно «скольжение» ядро ​​(массив из них) через наш вход и многократно аккумулирующие элементы в нашем окне размера N вместе.

+0

спасибо. Это хорошо работает для параметризации 'N' – philshem

+0

@philshem. Добро пожаловать! – miradulo

+0

Это на самом деле очень интересный способ сделать это ... можно было бы расширить множество способов ... спасибо, что указали это! –

5

Вы могли бы использовать rolling/sum и np.nanargmax найти индекс, связанный с первым вхождением максимальное значение:

import numpy as np 
import pandas as pd 

df = pd.DataFrame({'dt': ['2017-01-01 12:00:00', '2017-01-01 12:01:00', '2017-01-01 12:02:00', '2017-01-01 12:03:00', '2017-01-01 12:04:00', '2017-01-01 12:05:00', '2017-01-01 12:06:00', '2017-01-01 12:07:00', '2017-01-01 12:08:00', '2017-01-01 12:09:00', '2017-01-01 12:10:00', '2017-01-01 12:11:00', '2017-01-01 12:12:00', '2017-01-01 12:13:00', '2017-01-01 12:14:00', '2017-01-01 12:15:00'], 'val': [33, 42, 44, 6, 70, 94, 42, 97, 12, 11, 66, 71, 25, 23, 39, 25]}) 
df.index = df.index*10 

N = 3 
idx = df['val'].rolling(window=N).sum() 
i = np.nanargmax(idx) + 1 
print(df.iloc[i-N : i]) 

печатает

     dt val 
50 2017-01-01 12:05:00 94 
60 2017-01-01 12:06:00 42 
70 2017-01-01 12:07:00 97 

iloc uses ordinal indexing. loc использует индексирование на основе меток. При условии, что и i-N и i являются действительными индексами, df.iloc[i-N : i] будет захватывать окно (суб-DataFrame) длиной N. В отличие от этого, df.loc[i-N, i] будет захватывать только окно длины N, если индекс использует последовательные целые числа. В приведенном выше примере показан DataFrame, где df.loc не будет работать, поскольку df.index имеет неотрицательные целочисленные значения.

+0

FWIW - я не могу заставить 'roll()' работать с моей меткой времени ('ops для Rolling для этого dtype datetime64 [ns] не реализованы') или мой индекс (объект 'Int64Index' не имеет атрибута «rolling''). – philshem

+1

'roll' не работает с' datetime64's, потому что (например) суммирование 'datetime64' не определено. Если вы хотите использовать 'roll' в индексе с целочисленным значением, вы можете использовать' df.index.to_series(). Roll (...) '. – unutbu

1

Для простых единичных значений, вы можете использовать что-то вроде:

df['total'] = df.val + df.val.shift(-1) + df.val.shift(-2) 
first = df.dropna().sort('total').index[-1] 
df.iloc[first:first+3] 

Не знаю, как обобщить это ... большинство вещей панд, есть, вероятно, более простой способ, но это работает.

Edit: После того, как немного больше работы, это выглядит как прокатка, что вы хотите:

last = df.val.rolling(3).sum().dropna().sort_values().index[-1] 

Это немного отличается, в том, что индекс вы получите вот конец, поэтому после выполнения выше, вы хотите сделать

df.iloc[last-2:last+1] 

Я думаю, что это могло бы быть обобщено.

+0

хорошая идея. Есть ли способ параметризовать первую строку, в случае, если я хотел N = 100 вместо N = 3? – philshem