Как использовать групповое преобразование через несколько столбцов
У меня есть большой фреймворк данных, и я группирую от одного до n столбцов и хочу применить функцию к этим группам через два столбца (например, foo и bar).
Здесь примерный кадр:
foo_function = lambda x: np.sum(x.a+x.b)
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[1,2,3,4,5,6],
'c':['q', 'q', 'q', 'q', 'w', 'w'],
'd':['z','z','z','o','o','o']})
# works with apply, but I want transform:
df.groupby(['c', 'd'])[['a','b']].apply(foo_function)
# transform doesn't work!
df.groupby(['c', 'd'])[['a','b']].transform(foo_function)
TypeError: cannot concatenate a non-NDFrame object
Но transform
, по-видимому, не может объединить несколько столбцов вместе, потому что он смотрит на каждый столбец отдельно (в отличие от приложения). Какая следующая лучшая альтернатива с точки зрения скорости/элегантности? например Я мог бы использовать apply
, а затем создать df['new_col']
с помощью pd.match
, но это потребует сопоставления иногда нескольких столбцов groupw (col1 и col2), которые кажутся действительно взломанными/будут принимать достаточное количество кода.
- > Есть ли функция, подобная groupby(). transform, которая может использовать функции, которые работают над несколькими столбцами? Если этого не существует, какой лучший взлом?
Ответы
Ответ 1
Вместо этого, если вам нужно выполнить групповое вычисление по нескольким столбцам, сначала выполните вычисления с несколькими столбцами, а затем groupby:
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[1,2,3,4,5,6],
'c':['q', 'q', 'q', 'q', 'w', 'w'],
'd':['z','z','z','o','o','o']})
df['e'] = df['a'] + df['b']
df['e'] = (df.groupby(['c', 'd'])['e'].transform('sum'))
print(df)
дает
a b c d e
0 1 1 q z 12
1 2 2 q z 12
2 3 3 q z 12
3 4 4 q o 8
4 5 5 w o 22
5 6 6 w o 22
Оригинальный ответ:
Сообщение об ошибке:
TypeError: cannot concatenate a non-NDFrame object
предполагает, что для конкатенации foo_function
должен возвращать NDFrame (например, Series или DataFrame). Если вы вернете серию, то:
In [99]: df.groupby(['c', 'd']).transform(lambda x: pd.Series(np.sum(x['a']+x['b'])))
Out[99]:
a b
0 12 12
1 12 12
2 12 12
3 8 8
4 22 22
5 22 22
Ответ 2
Как я читаю вопрос, вы хотите иметь возможность делать что-то произвольное с обоими значениями из обоих столбцов. Вам просто нужно убедиться, что вы вернете рамку данных того же размера, что и вы передали. Я думаю, что лучший способ - просто создать новый столбец, например:
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[1,2,3,4,5,6],
'c':['q', 'q', 'q', 'q', 'w', 'w'],
'd':['z','z','z','o','o','o']})
df['e']=0
def f(x):
y=(x['a']+x['b'])/sum(x['b'])
return pd.DataFrame({'e':y,'a':x['a'],'b':x['b']})
df.groupby(['c','d']).transform(f)
:
a b e
0 1 1 0.333333
1 2 2 0.666667
2 3 3 1.000000
3 4 4 2.000000
4 5 5 0.909091
5 6 6 1.090909
Если у вас очень сложный фреймворк, вы можете выбрать свои столбцы (например, df.groupby(['c'])['a','b','e'].transform(f)
)
Это конечно выглядит очень неэлегантным для меня, но все еще намного быстрее, чем apply
на больших наборах данных.
Другой альтернативой является использование set_index
для захвата всех столбцов, которые вам нужны, а затем передать только один столбец в transform
.