Подтвердить что ты не робот

Что такое эквивалент панды dplyr суммировать/агрегировать по нескольким функциям?

У меня возникли проблемы при переходе на панды из R, где пакет dplyr может легко группироваться и выполнять несколько обобщений.

Пожалуйста, помогите улучшить мой существующий код Python для нескольких агрегатов:

import pandas as pd
data = pd.DataFrame(
    {'col1':[1,1,1,1,1,2,2,2,2,2],
    'col2':[1,2,3,4,5,6,7,8,9,0],
     'col3':[-1,-2,-3,-4,-5,-6,-7,-8,-9,0]
    }
)
result = []
for k,v in data.groupby('col1'):
    result.append([k, max(v['col2']), min(v['col3'])])
print pd.DataFrame(result, columns=['col1', 'col2_agg', 'col3_agg'])

Вопросы:

  • слишком многословный
  • вероятно, может быть оптимизирован и эффективен. (Я переписал реализацию groupby.agg for-loop groupby в groupby.agg и повышение производительности было огромным).

В R эквивалентный код будет:

data %>% groupby(col1) %>% summarize(col2_agg=max(col2), col3_agg=min(col3))

ОБНОВЛЕНИЕ: @ayhan решил мой вопрос, вот дополнительный вопрос, который я опубликую здесь вместо комментария:

Q2) Что является эквивалентом groupby().summarize(newcolumn=max(col2 * col3)) т. groupby().summarize(newcolumn=max(col2 * col3)) Агрегация/суммирование, где функция является составной функцией столбцов 2+?

4b9b3361

Ответ 1

Эквивалент

df %>% groupby(col1) %>% summarize(col2_agg=max(col2), col3_agg=min(col3))

является

df.groupby('col1').agg({'col2': 'max', 'col3': 'min'})

который возвращает

      col2  col3
col1            
1        5    -5
2        9    -9

Возвращаемый объект представляет собой pandas.DataFrame с индексом col1 и столбцами с именем col2 и col3. По умолчанию, когда вы группируете свои данные, pandas устанавливает столбец (группы) группировки в качестве индекса для эффективного доступа и модификации. Однако, если вы этого не хотите, есть две альтернативы для установки col1 в качестве столбца.

  • Pass as_index=False:

    df.groupby('col1', as_index=False).agg({'col2': 'max', 'col3': 'min'})
    
  • Вызов reset_index:

    df.groupby('col1').agg({'col2': 'max', 'col3': 'min'}).reset_index()
    

оба дают

col1  col2  col3           
   1     5    -5
   2     9    -9

Вы также можете передать несколько функций на groupby.agg.

agg_df = df.groupby('col1').agg({'col2': ['max', 'min', 'std'], 
                                 'col3': ['size', 'std', 'mean', 'max']})

Это также возвращает DataFrame, но теперь для столбцов имеется MultiIndex.

     col2               col3                   
      max min       std size       std mean max
col1                                           
1       5   1  1.581139    5  1.581139   -3  -1
2       9   0  3.535534    5  3.535534   -6   0

MultiIndex очень удобен для выбора и группировки. Вот несколько примеров:

agg_df['col2']  # select the second column
      max  min       std
col1                    
1       5    1  1.581139
2       9    0  3.535534

agg_df[('col2', 'max')]  # select the maximum of the second column
Out: 
col1
1    5
2    9
Name: (col2, max), dtype: int64

agg_df.xs('max', axis=1, level=1)  # select the maximum of all columns
Out: 
      col2  col3
col1            
1        5    -1
2        9     0

Ранее (до версия 0.20.0) можно было использовать словари для переименования столбцов в вызове agg. Например

df.groupby('col1')['col2'].agg({'max_col2': 'max'})

вернет максимум второго столбца как max_col2:

      max_col2
col1          
1            5
2            9

Однако он был устарел в пользу метода переименования:

df.groupby('col1')['col2'].agg(['max']).rename(columns={'max': 'col2_max'})

      col2_max
col1          
1            5
2            9

Он может быть подробным для DataFrame, такого как agg_df, определенного выше. Вы можете использовать функцию переименования, чтобы сгладить эти уровни в этом случае:

agg_df.columns = ['_'.join(col) for col in agg_df.columns]

      col2_max  col2_min  col2_std  col3_size  col3_std  col3_mean  col3_max
col1                                                                        
1            5         1  1.581139          5  1.581139         -3        -1
2            9         0  3.535534          5  3.535534         -6         0

Для таких операций, как groupby().summarize(newcolumn=max(col2 * col3)), вы все равно можете использовать agg, сначала добавив новый столбец с assign.

df.assign(new_col=df.eval('col2 * col3')).groupby('col1').agg('max') 

      col2  col3  new_col
col1                     
1        5    -1       -1
2        9     0        0

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

df.assign(new_col=df.eval('col2 * col3')).groupby('col1')['new_col'].agg('max')

col1
1   -1
2    0
Name: new_col, dtype: int64

С groupby.apply это будет короче:

df.groupby('col1').apply(lambda x: (x.col2 * x.col3).max())

col1
1   -1
2    0
dtype: int64

Однако groupby.apply рассматривает это как пользовательскую функцию, поэтому он не является векторизованным. До сих пор функции, которые мы передавали в agg ('min', 'max', 'min', 'size' и т.д.), Векторизованы и являются псевдонимами для этих оптимизированных функций. Вы можете заменить df.groupby('col1').agg('min') на df.groupby('col1').agg(min), df.groupby('col1').agg(np.min) или df.groupby('col1').min(), и все они будут выполнять одну и ту же функцию. Вы не увидите такой же эффективности при использовании пользовательских функций.

Наконец, начиная с версии 0.20, agg можно использовать непосредственно в DataFrames, без необходимости группировки в первую очередь. Примеры здесь.