#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()
было бы достаточно