Панды добавляют столбец при условии: Если значение ячейки равно True, установите значение наибольшего числа в периоде равным true

#python #pandas

Вопрос:

У меня есть фрейм данных pandas, скажем, с двумя столбцами, например:

 value boolean 0 1 0 1 5 1 2 0 0 3 3 0 4 9 1 5 12 0 6 4 0 7 7 1 8 8 1 9 2 0 10 17 0 11 15 1 12 6 0  

Теперь я хочу добавить третий столбец (new_boolean) со следующими критериями: Я указываю период, для этого примера период = 4. Теперь я взгляну на все строки, где логическое значение == 1. значение new_boolean будет равно 1 для максимального значения в строках последнего периода.

Например, у меня есть логическое значение == 1 для строки 2. Поэтому я смотрю на последние строки периода. Значения [1, 5], 5-это максимум, поэтому значение new_boolean в строке 2 будет равно единице.

Второй пример: строка 8 (значение = 7): я получаю значения [7, 4, 12, 9], 12 является максимальным, поэтому значение new_boolean в строке со значением 12 будет равно 1

Результат:

 value boolean new_boolean 0 1 0 0 1 5 1 1 2 0 0 0 3 3 0 0 4 9 1 1 5 12 0 1 6 4 0 0 7 7 1 0 8 8 1 0 9 2 0 0 10 17 0 1 11 15 1 0 12 6 0 0  

введите описание изображения здесь

Как я могу сделать это алгоритмически?

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

1. я не уверен, что понимаю проблему. вот предложение: для каждого i логического (перечисленного), если true -gt; если max ( df[‘значение’].iloc[i-4:i]) == df[‘значение’].iloc[i]) -gt;gt; df[‘new_boolean’].iloc[i] = 1

2. Почему строка 10 не получает 1? Его значение равно максимальному в любом 4-строчном окне, которое его включает…

3. Разве строка 11 value = 15 не должна иметь new_boolean значение 0 , так как 15 меньше 17?

Ответ №1:

Вычислите скользящий максимум столбца «значение»

 gt;gt;gt; rolling_max_value = df.rolling(window=4, min_periods=1)['value'].max() gt;gt;gt; rolling_max_value   0 1.0 1 5.0 2 5.0 3 5.0 4 9.0 5 12.0 6 12.0 7 12.0 8 12.0 9 8.0 10 17.0 11 17.0 12 17.0 Name: value, dtype: float64  

Выберите только соответствующие значения, т. е. где ‘логическое значение’ = 1

 gt;gt;gt; on_values = rolling_max_value[df.boolean == 1].unique() gt;gt;gt; on_values  array([ 5., 9., 12., 17.])  

Строки, в которых ‘new_boolean’ = 1, являются строками, в которых ‘значение’ принадлежит on_values

 gt;gt;gt; df['new_boolean'] = df.value.isin(on_values).astype(int) gt;gt;gt; df   value boolean new_boolean 0 1 0 0 1 5 1 1 2 0 0 0 3 3 0 0 4 9 1 1 5 12 0 1 6 4 0 0 7 7 1 0 8 8 1 0 9 2 0 0 10 17 0 1 11 15 1 0 12 6 0 0   

Редактировать:

ОП поднял хороший вопрос

Работает ли это также, если у меня несколько столбцов с одинаковым значением и у них разные логические значения?

Предыдущее решение этого не учитывает. Чтобы решить эту проблему, вместо вычисления максимального значения прокатки мы собираем метки строк, связанные со значениями максимального значения прокатки, т. е. с числом прокатки argmax или idxmax . Насколько мне известно, Rolling у объектов нет idxmax метода, но мы можем легко вычислить его с помощью apply .

 def idxmax(values):  return values.idxmax()  rolling_idxmax_value = (  df.rolling(min_periods=1, window=4)['value']  .apply(idxmax)  .astype(int) )  on_idx = rolling_idxmax_value[df.boolean == 1].unique()  df['new_boolean'] = 0 df.loc[on_idx, 'new_boolean'] = 1   

Результаты:

 gt;gt;gt; rolling_idxmax_value   0 0 1 1 2 1 3 1 4 4 5 5 6 5 7 5 8 5 9 8 10 10 11 10 12 10 Name: value, dtype: int64  gt;gt;gt; on_idx  [ 1 4 5 10]  gt;gt;gt; df   value boolean new_boolean 0 1 0 0 1 5 1 1 2 0 0 0 3 3 0 0 4 9 1 1 5 12 0 1 6 4 0 0 7 7 1 0 8 8 1 0 9 2 0 0 10 17 0 1 11 15 1 0 12 6 0 0  

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

1. Работает ли это также, если у меня несколько столбцов с одинаковым значением и у них разные логические значения?

2. Хорошая мысль, я этого не учел. Я отредактировал свой пост с помощью более надежного решения. Теперь это должно сработать.

3. Это работает, но для моего использования все еще есть одна проблема: мой столбец indey имеет тип datetime, и кажется, что он не работает, если это так. Как бы вы с этим справились? Временно добавьте столбец со значениями 0 в len-1 и установите его в качестве индекса? И если да, то как мне добавить эту колонку?

4. Просто сначала сбросьте индекс, добавив df = df.reset_index() его перед определением rolling_idxmax_value . Индекс даты и времени будет добавлен в df качестве нового столбца.

Ответ №2:

Я сделал это в 2 шага, но я думаю, что решение намного яснее:

 df = pd.read_csv(StringIO(''' id value boolean 0 1 0 1 5 1 2 0 0 3 3 0 4 9 1 5 12 0 6 4 0 7 7 1 8 8 1 9 2 0 10 17 0 11 15 1 12 6 0'''),delim_whitespace=True,index_col=0)  df['new_bool'] = df['value'].rolling(min_periods=1, window=4).max() df['new_bool'] = df.apply(lambda x: 1 if ((x['value'] == x['new_bool']) amp; (x['boolean'] == 1)) else 0, axis=1) df  

Результат:

 value boolean new_bool id  0 1 0 0 1 5 1 1 2 0 0 0 3 3 0 0 4 9 1 1 5 12 0 0 6 4 0 0 7 7 1 0 8 8 1 0 9 2 0 0 10 17 0 0 11 15 1 0 12 6 0 0