Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Pandas - find consecutive group with max aggregate values

I have a data frame with datetimes and integers

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])

Gives me

                    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

How can I find which N-minute group of consecutive dt gives me the maximum sum of val?

In this case, if N=3, then the result should be:

                    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

(marked with stars above)

like image 559
philshem Avatar asked Feb 17 '17 22:02

philshem


2 Answers

You could use np.convolve to get the correct starting index and go from there.

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

This works be effectively "sliding" the kernel (array of ones) across our input and multiply-accumulating the elements in our window of size N together.

like image 164
miradulo Avatar answered Sep 28 '22 15:09

miradulo


You could use rolling/sum and np.nanargmax to find the index associated with the first occurrence of the maximum value:

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])

prints

                     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 uses label-based indexing. Provided that both i-N and i are valid indices, df.iloc[i-N : i] will grab a window (sub-DataFrame) of length N. In contrast, df.loc[i-N, i] will only grab a window of length N if the index uses consecutive integers. The example above shows a DataFrame where df.loc would not work since df.index has non-consecutive integer values.

like image 33
unutbu Avatar answered Sep 28 '22 15:09

unutbu