Прокатка максимального значения с помощью groupby по нескольким столбцам в панд

#python #pandas

Вопрос:

У меня есть фрейм данных, на котором я хочу вычислить максимальное значение за предыдущие 3 месяца. Ниже приведен фрейм данных:

 VIN   Year_Month    Amount  
  V1    2012-01      196     
  V2    2012-01      113     
  V3    2012-01      177     
  V1    2012-02      154     
  V2    2012-02      129     
  V4    2012-02      156     
  V2    2012-03      100     
  V3    2012-03      174     
  V4    2012-03      127     
  V1    2012-04      139    
  V3    2012-04      194     
  V4    2012-04      178     
 

Используя следующий фрагмент кода, я пытаюсь вычислить максимальное значение для каждого VIN за последние 3 месяца

 df['Max_3M_Value'] = df.groupby(['VIN','Year_Month'])['Amount'].rolling(3).max().fillna(0)
 

Но приведенный выше код выдает ошибку:

 TypeError: incompatible index of inserted column with frame index
 

Результирующий кадр данных, который я ищу, выглядит так :

   VIN  Year_Month   Amount  Max_3M_Value
   V1   2012-01      196     196
   V2   2012-01      113     129
   V3   2012-01      177     194
   V1   2012-02      154     196
   V2   2012-02      129     129
   V4   2012-02      156     178
   V2   2012-03      100     129
   V3   2012-03      174     194
   V4   2012-03      127     178
   V1   2012-04      139     196
   V3   2012-04      194     194
   V4   2012-04      178     178
 

Я должен вычислить максимальное значение, используя rolling только .Я знаю , что мы можем решить эту проблему с помощью pd.pivot_table() , но в этом нет необходимости.

Чего мне здесь не хватает.

Ответ №1:

Вы можете избежать ошибки, используя только возвращаемые значения, но вы должны убедиться, что порядок строк не меняется во время операции:

 df['Max_3M_Value'] = df.groupby(['VIN','Year_Month'])['Amount'].rolling(3).max().fillna(0).values
 

В приведенном вами примере ваши группы слишком малы (по 1 элементу в каждой), чтобы можно было применить прокрутку с окном 3:

 >>> df.groupby(['VIN','Year_Month'])['Amount'].count()

VIN  Year_Month
V1   2012-01       1
     2012-02       1
     2012-04       1
V2   2012-01       1
     2012-02       1
     2012-03       1
V3   2012-01       1
     2012-03       1
     2012-04       1
V4   2012-02       1
     2012-03       1
     2012-04       1
 

Я считаю, что на самом деле вы хотите группироваться только по «VIN»:

 df['Max_3M_Value'] = df.groupby('VIN')['Amount'].transform(lambda s: s.rolling(3, min_periods=1).max())
 

выход:

    VIN Year_Month  Amount  Max_3M_Value
0   V1    2012-01     196           196
1   V2    2012-01     113           113
2   V3    2012-01     177           177
3   V1    2012-02     154           196
4   V2    2012-02     129           129
5   V4    2012-02     156           156
6   V2    2012-03     100           129
7   V3    2012-03     174           177
8   V4    2012-03     127           156
9   V1    2012-04     139           196
10  V3    2012-04     194           194
11  V4    2012-04     178           178
 

выход для скользящего окна 2:

    VIN Year_Month  Amount  Max_3M_Value
0   V1    2012-01     196           196
1   V2    2012-01     113           113
2   V3    2012-01     177           177
3   V1    2012-02     154           196
4   V2    2012-02     129           129
5   V4    2012-02     156           156
6   V2    2012-03     100           129
7   V3    2012-03     174           177
8   V4    2012-03     127           156
9   V1    2012-04     139           154
10  V3    2012-04     194           194
11  V4    2012-04     178           178
 

Если на самом деле вам нужно простое максимальное значение для каждой группы, вы можете просто сделать:

 >>> df['Max_3M_Value'] = df.groupby('VIN')['Amount'].transform('max')
>>> df
   VIN Year_Month  Amount  Max_3M_Value
0   V1    2012-01     196           196
1   V2    2012-01     113           129
2   V3    2012-01     177           194
3   V1    2012-02     154           196
4   V2    2012-02     129           129
5   V4    2012-02     156           178
6   V2    2012-03     100           129
7   V3    2012-03     174           194
8   V4    2012-03     127           178
9   V1    2012-04     139           196
10  V3    2012-04     194           194
11  V4    2012-04     178           178
 

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

1. Хорошее объяснение. 1

2. @mozway: Что, если мы воспользуемся rolling(2,min_periods=1) ? Все еще ваш код будет работать? Кстати, я получаю V1 и т. V2 Д. В Max_3M_Value.

3. Я заменил свой ответ альтернативой, которая должна сработать в любом случае. Дайте мне знать…

4. @mozway. Ваши результаты не соответствуют ожиданиям операции.

5. @Corralien Я думаю, что вывод OP неверен. То, что описывает текущий выход, — это простой максимум для каждой группы ( не скользящий максимум), что не представляет никакой проблемы. Я добавляю эту возможность в свой ответ.

Ответ №2:

Попробуй:

 >>> df['Max_3M_Value'] = df.groupby('VIN')['Amount'] 
                           .rolling(3).max().bfill().astype(int) 
                           .reset_index(level=0, drop=True)
 
 >>> df
   VIN Year_Month  Amount  Max_3M_Value
0   V1    2012-01     196           196
1   V2    2012-01     113           129
2   V3    2012-01     177           194
3   V1    2012-02     154           196
4   V2    2012-02     129           129
5   V4    2012-02     156           178
6   V2    2012-03     100           129
7   V3    2012-03     174           194
8   V4    2012-03     127           178
9   V1    2012-04     139           196
10  V3    2012-04     194           194
11  V4    2012-04     178           178
 

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

1. Это не сработает, если вы попытаетесь снова назначить исходный df (NB. Я не стал понижать голос)

2. @pythondumb, я обновил свой ответ. Не могли бы вы проверить, что это то, чего вы ожидаете, пожалуйста?