Комбинация столбцов для агрегации после группового - PullRequest
1 голос
/ 02 февраля 2020

Вопрос

Ищите что-то вроде

df.groubpy('key').aggregate(combination(columnA, columnB))

вместо

df['combination'] = combination(columnA, columnB)
df.groupby('key')['combination'].aggregate()

Единственное требование состоит в том, что комбинация столбцов вычисляется после группировки.


Описание

Мне кажется естественным, логически целесообразным в некоторых случаях сначала группировать, а затем агрегировать.

Одним примером могут быть различные функции агрегирования для разных комбинаций столбцов которые используют те же группы.

Ищите

  1. groupby
  2. выберите комбинацию столбцов
  3. используйте вместо этого соответствующую функцию агрегирования

  1. создать все необходимые столбцы (для каждой функции агрегирования)
  2. groupby (для каждой функции агрегирования)
  3. применить спецификацию c функцию агрегирования

Пример

key     ColumnA  ColumnB
key1       1        1
key2       2        2
key1       3        3
key3       4        4
key2       5        5

#can do
df['combination'] = df.columnA * df.columnB
df.groupby('key').mean()

#looking for
grouped = df.groupby('key')
grouped.mean(columnA * columnB)

1 Ответ

2 голосов
/ 02 февраля 2020

Хорошо, я думаю, что вы ищете ответ - мы этого не делаем из-за векторизации в python

Рассмотрим приведенный ниже код.

Теперь по существу - python часто оптимизируется для выполнения определенных математических операций векторизованным способом (например, numpy или pandas), что означает - применять его ко всему вектору быстрее, чем разбивать его на куски и выполнять это тогда.

Так, например, df["A"].mul(df["B"]) будет быстрее, чем: df.apply(lambda X: X["A"]*X["B"], axis=0). То же самое относится и к группировке - это просто гораздо более масштабируемый способ.

Попробуйте приведенный ниже код - по сути, это то, на что вы ссылались - так что выполняйте операцию до Vs после groupby(...). Векторизованное решение довольно быстро масштабируется, даже если вы материализуете дополнительный столбец - чем больше строк вы обрабатываете, тем большую разницу вы увидите.

Edit

Я добавил векторизованное решение на сгруппированных данных, поэтому мы имеем:

(1) мы группируем, мы лениво оцениваем строку за строкой

(2) мы обрабатываем full df векторизованным способом, мы группируем, мы применяем встроенные в агрегирующей функции

(3) мы группируем, мы обрабатываем векторизованным способом, группа, по группе, мы делаем агрегирующую функцию

по сути - из результата, который мы видим, разбивка на куски замедляется обработка, независимо от того, идет ли речь о группах или записях, - таким образом, векторизованное решение масштабируется лучше, чем любое другое пользовательское решение, которое мы можем применить поверх.

import pandas as pd
import numpy as np
import time

x=np.random.randint(1,9,(3000,5))
df=pd.DataFrame(x, columns=[f"column{l}" for l in list("ABCDE")])
df["cat"]=np.random.choice([f"key{l}" for l in list("ABCDEFG")], size=3000)
df2=df3=df
#print(df)
s=time.time()
df.groupby("cat").apply(lambda z: np.prod(z.values, axis=1).mean()).pipe(print)
e=time.time()-s
print(f"method 1: {e} s")

s=time.time()
df2["prod"]=np.prod(df[[f"column{l}" for l in list("ABCDE")]], axis=1)
df2.groupby("cat")["prod"].mean().pipe(print)
e=time.time()-s
print(f"method 2: {e} s")

s=time.time()
df3=list(map(lambda x: (x[0], np.prod(x[1][[f"column{l}" for l in list("ABCDE")]], axis=1).mean()), df3.groupby("cat")))
print(df3)
e=time.time()-s
print(f"method 3: {e} s")
...