Как увеличить кумулятивный максимум

#python #pandas

#python #pandas

Вопрос:

У меня есть столбец (price), значения которого меняются со временем. От одной строки к другой значение увеличивается, уменьшается или остается неизменным. Я хочу записать, сколько раз значение достигало нового максимума.

Итак, я добавил столбец currenthigh , который отслеживает наибольшее значение на данный момент. Затем я добавил еще один столбец, currenthigh_prev который является currenthigh столбцом, сдвинутым на одну строку. Таким образом, я могу сравнить оба значения: текущее и предыдущее. Если currenthigh > currenthigh_prev тогда у меня есть новый максимум, который записан в newhighscount .

Я пытался использовать .cummax() для этого, что показалось подходящим.

 df.loc[df['currenthigh'] > df['currenthigh_shift'], 'newhighscount'] = df['newhighscount'].cummax()   1
  

Я ожидал этого :

               datetime      last  currenthigh  currenthigh_shift  **newhighscount** 
31 2019-04-02 07:57:33  389.8400       389.84                NaN              0 
32 2019-04-02 07:57:33  389.8400       389.84             389.84              0 
33 2019-04-02 07:57:33  389.8700       389.87             389.84              **1** 
34 2019-04-02 07:57:33  389.8800       389.88             389.87              **2** 
35 2019-04-02 07:57:33  389.9000       389.90             389.88              **3** 
36 2019-04-02 07:57:33  389.9600       389.96             389.90              **4** 
37 2019-04-02 07:57:35  389.9000       389.96             389.96              **4** 
38 2019-04-02 07:57:36  389.9000       389.96             389.96              **4** 
39 2019-04-02 08:00:00  389.3603       389.96             389.96              **4** 
40 2019-04-02 08:00:00  388.8500       389.96             389.96              **4** 
41 2019-04-02 08:00:00  390.0000       390.00             389.96              **5** 
42 2019-04-02 08:00:01  389.7452       390.00             390.00              **5** 
43 2019-04-02 08:00:01  389.4223       390.00             390.00              5 
44 2019-04-02 08:00:01  389.8000       390.00             390.00              5 
  

И я получаю это:

               datetime      last  currenthigh  currenthigh_shift  newhighscount 
31 2019-04-02 07:57:33  389.8400       389.84                NaN              0 
32 2019-04-02 07:57:33  389.8400       389.84             389.84              0 
33 2019-04-02 07:57:33  389.8700       389.87             389.84              1 
34 2019-04-02 07:57:33  389.8800       389.88             389.87              1 
35 2019-04-02 07:57:33  389.9000       389.90             389.88              1 
36 2019-04-02 07:57:33  389.9600       389.96             389.90              1 
37 2019-04-02 07:57:35  389.9000       389.96             389.96              0 
38 2019-04-02 07:57:36  389.9000       389.96             389.96              0 
39 2019-04-02 08:00:00  389.3603       389.96             389.96              0 
40 2019-04-02 08:00:00  388.8500       389.96             389.96              0 
41 2019-04-02 08:00:00  390.0000       390.00             389.96              1 
42 2019-04-02 08:00:01  389.7452       390.00             390.00              0 
43 2019-04-02 08:00:01  389.4223       390.00             390.00              0 
44 2019-04-02 08:00:01  389.8000       390.00             390.00              0 
  

В принципе, df['newhighscount'].cummax() похоже, ничего не возвращает.

Ответ №1:

 df['newhighscount'] = df['last'].cummax().diff().gt(0).cumsum()
  

Это вычисляет кумулятивный максимум последнего столбца, вычисляет разницу (cummax_t — cummax_{t-1}), проверяет, больше ли разница нуля, и подсчитывает, сколько раз это было верно.

Комментарии:

1. Красивые. Спасибо.

2. Что, если я хочу начать подсчет только через определенное время, скажем, в 8:00? У меня уже есть другой столбец bool, который имеет значение True, когда он после 8:00 и False в противном случае.

3. Вы можете использовать: df.loc[df['yourboolcol'], 'newhighscount2'] = df.loc[df['yourboolcol'], 'last'].cummax().diff().gt(0).cumsum() . Имейте в виду, что это оставит NaN s в строках, где ваш логический столбец равен false.

4. Спасибо. Это работает почти нормально: первый новый максимум не учитывается. Все еще пытаюсь выяснить, почему..

5. На самом деле df['newhighscount2'] = (df['last'].cummax().diff().gt(0) amp; df['yourboolcol'].shift(1)).cumsum() могло бы быть более элегантно. Сдвиг заключается в том, чтобы переместить первое значение true во второе значение price, чтобы вы не получили newhigh в вашей первой записи. Если вы хотите, чтобы ваше первое значение (после 8) можно было считать новым максимумом, вы можете убрать сдвиг.

Ответ №2:

Вы хотите помечать уникальные 'currenthigh' значения. Есть много способов сделать это:

ngroup

 df['NewCount'] = df.groupby('currenthigh', sort=False).ngroup()
  

rank :

Здесь будет работать, поскольку cummax гарантированно будет монотонно увеличиваться.

 df['NewCount'] = (df.currenthigh.rank(method='dense')-1).astype(int)
  

map

 import pandas as pd

arr = pd.Series.unique(df.currenthigh) # Preserves order
df['NewCount'] = df.currenthigh.map(dict((arr[i], i) for i in range(len(arr))))
  

Вывод:

                          last  currenthigh  NewCount
datetime                                            
2019-04-02 07:57:33  389.8400       389.84         0
2019-04-02 07:57:33  389.8400       389.84         0
2019-04-02 07:57:33  389.8700       389.87         1
2019-04-02 07:57:33  389.8800       389.88         2
2019-04-02 07:57:33  389.9000       389.90         3
2019-04-02 07:57:33  389.9600       389.96         4
2019-04-02 07:57:35  389.9000       389.96         4
2019-04-02 07:57:36  389.9000       389.96         4
2019-04-02 08:00:00  389.3603       389.96         4
2019-04-02 08:00:00  388.8500       389.96         4
2019-04-02 08:00:00  390.0000       390.00         5
2019-04-02 08:00:01  389.7452       390.00         5
2019-04-02 08:00:01  389.4223       390.00         5
2019-04-02 08:00:01  389.8000       390.00         5
  

Комментарии:

1. Большое вам спасибо за ваш вклад. Я собираюсь это изучить. Я здесь новичок и, честно говоря, я не знал, какой ответ принять, ваш или Джоземза…

2. Что, если я хочу начать подсчет только через определенное время, скажем, в 8:00? У меня уже есть другой столбец bool, который имеет значение True, когда он после 8:00 и False в противном случае. (Я также спросил автора принятого ответа …).

3. @fredericf тогда вы должны быть в состоянии нарезать RHS и просто назначить обратно. Затем вы можете выбрать сохранить остальные NaN или .fillna(0) . Т.е. что-то вроде df['NewCount'] = df[Bool_Series].groupby('currenthigh', sort=False).ngroup() . К счастью, у вас, похоже, мультииндекс, поэтому при попытке выравнивания у него не возникнет проблем с повторяющимися временами.

4. ОК. Я тоже изучу это. Могу ли я спросить, как вы могли бы отформатировать мой фрейм данных в вопросе?

5. Отлично. Еще раз спасибо!

Ответ №3:

Редактировать: основываясь на ваших данных, одной команды ниже было бы достаточно

 df['newhighscount'] = (df['currenthigh'] > df['currenthigh_shift']).astype(int).cumsum()
  

Оригинал:
Ваша логика все еще работает, но она не такая элегантная, как другие ответы. Это просто нужно немного подкрутить.

 In [983]: df
Out[983]:
               datetime      last  currenthigh  currenthigh_shift   newhighscount
31 2019-04-02  07:57:33  389.8400       389.84                NaN               0
32 2019-04-02  07:57:33  389.8400       389.84             389.84               0
33 2019-04-02  07:57:33  389.8700       389.87             389.84               0
34 2019-04-02  07:57:33  389.8800       389.88             389.87               0
35 2019-04-02  07:57:33  389.9000       389.90             389.88               0
36 2019-04-02  07:57:33  389.9600       389.96             389.90               0
37 2019-04-02  07:57:35  389.9000       389.96             389.96               0
38 2019-04-02  07:57:36  389.9000       389.96             389.96               0
39 2019-04-02  08:00:00  389.3603       389.96             389.96               0
40 2019-04-02  08:00:00  388.8500       389.96             389.96               0
41 2019-04-02  08:00:00  390.0000       390.00             389.96               0
42 2019-04-02  08:00:01  389.7452       390.00             390.00               0
43 2019-04-02  08:00:01  389.4223       390.00             390.00               0
44 2019-04-02  08:00:01  389.8000       390.00             390.00               0

In [985]: df.loc[df['currenthigh'] > df['currenthigh_shift'], 'newhighscount'] = (df['currenthigh'] > df['currenthigh_shift']).astype(int).cumsum()
In [989]: df['newhighscount'] = df['newhighscount'].cummax()
In [990]: df
Out[990]:
               datetime      last  currenthigh  currenthigh_shift  newhighscount
31 2019-04-02  07:57:33  389.8400       389.84                NaN              0
32 2019-04-02  07:57:33  389.8400       389.84             389.84              0
33 2019-04-02  07:57:33  389.8700       389.87             389.84              1
34 2019-04-02  07:57:33  389.8800       389.88             389.87              2
35 2019-04-02  07:57:33  389.9000       389.90             389.88              3
36 2019-04-02  07:57:33  389.9600       389.96             389.90              4
37 2019-04-02  07:57:35  389.9000       389.96             389.96              4
38 2019-04-02  07:57:36  389.9000       389.96             389.96              4
39 2019-04-02  08:00:00  389.3603       389.96             389.96              4
40 2019-04-02  08:00:00  388.8500       389.96             389.96              4
41 2019-04-02  08:00:00  390.0000       390.00             389.96              5
42 2019-04-02  08:00:01  389.7452       390.00             390.00              5
43 2019-04-02  08:00:01  389.4223       390.00             390.00              5
44 2019-04-02  08:00:01  389.8000       390.00             390.00              5
  

Комментарии:

1. Спасибо Энди, это работает. Теперь я должен понять, как 🙂

2. это просто, так что, я думаю, вы очень быстро с этим разберетесь. Дайте мне знать, нужен ли вам какой-либо указатель. Кстати, основываясь на ваших данных, этой единственной команды df['newhighscount'] = (df['currenthigh'] > df['currenthigh_shift']). astype(int).cumsum() было бы достаточно