Python 广播groupby结果作为原始数据帧中的新列
我正在尝试基于分组数据帧中的两列在Pandas数据帧中创建一个新列Python 广播groupby结果作为原始数据帧中的新列,python,pandas,dataframe,group-by,pandas-groupby,Python,Pandas,Dataframe,Group By,Pandas Groupby,我正在尝试基于分组数据帧中的两列在Pandas数据帧中创建一个新列 df['ab_weighted'] = \ df.groupby('c', group_keys = False)['a', 'b'].apply( lambda x: pd.Series(x.a.sum()/x.b.sum(), index = x.index).to_frame() ).iloc[:,0] print(df) # output # a b
df['ab_weighted'] = \
df.groupby('c', group_keys = False)['a', 'b'].apply(
lambda x: pd.Series(x.a.sum()/x.b.sum(),
index = x.index).to_frame()
).iloc[:,0]
print(df)
# output
# a b c ab_weighted
# 0 1 7 q 0.294118
# 1 2 8 q 0.294118
# 2 3 9 q 0.294118
# 3 4 10 q 0.294118
# 4 5 11 w 0.478261
# 5 6 12 w 0.478261
具体来说,我正在尝试复制此R代码的输出:
library(data.table)
df = data.table(a = 1:6,
b = 7:12,
c = c('q', 'q', 'q', 'q', 'w', 'w')
)
df[, ab_weighted := sum(a)/sum(b), by = "c"]
df[, c('c', 'a', 'b', 'ab_weighted')]
输出:
a b c result
0 1 7 q NaN
1 2 8 q 8.0
2 3 9 q 10.0
3 4 10 q 12.0
4 5 11 w NaN
5 6 12 w 16.0
到目前为止,我在Python中尝试了以下内容:
import pandas as pd
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[7,8,9,10,11,12],
'c':['q', 'q', 'q', 'q', 'w', 'w']
})
df.groupby(['c'])['a', 'b'].apply(lambda x: sum(x['a'])/sum(x['b']))
输出:
a b c result
0 1 7 q NaN
1 2 8 q 8.0
2 3 9 q 10.0
3 4 10 q 12.0
4 5 11 w NaN
5 6 12 w 16.0
当我将上面代码中的apply
更改为transform
时,我得到一个错误:
TypeError:需要一个整数
如果我只使用一列,则Transform工作正常:
import pandas as pd
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[7,8,9,10,11,12],
'c':['q', 'q', 'q', 'q', 'w', 'w']
})
df.groupby(['c'])['a', 'b'].transform(lambda x: sum(x))
但显然,这不是相同的答案:
有没有一种方法可以从Pandas中的data.table代码中获取结果,而不必生成中间列(因为这样我就可以在最后一列上使用transform
非常感谢您的帮助:)离您只有一步之遥
v = df.groupby('c')[['a', 'b']].transform('sum')
df['ab_weighted'] = v.a / v.b
df
a b c ab_weighted
0 1 7 q 0.294118
1 2 8 q 0.294118
2 3 9 q 0.294118
3 4 10 q 0.294118
4 5 11 w 0.478261
5 6 12 w 0.478261
只需使用
map
修复代码,R
和pandas
仍然有不同的功能,这意味着并非每个R
功能都可以在pandas
中找到替代功能
df.c.map(df.groupby(['c'])['a', 'b'].apply(lambda x: sum(x['a'])/sum(x['b'])))
Out[67]:
0 0.294118
1 0.294118
2 0.294118
3 0.294118
4 0.478261
5 0.478261
Name: c, dtype: float64
这也会起作用。我不知道为什么,但如果我让apply返回一个序列而不是一个数据帧,我会遇到一个bug
df['ab_weighted'] = \
df.groupby('c', group_keys = False)['a', 'b'].apply(
lambda x: pd.Series(x.a.sum()/x.b.sum(),
index = x.index).to_frame()
).iloc[:,0]
print(df)
# output
# a b c ab_weighted
# 0 1 7 q 0.294118
# 1 2 8 q 0.294118
# 2 3 9 q 0.294118
# 3 4 10 q 0.294118
# 4 5 11 w 0.478261
# 5 6 12 w 0.478261
更新2021-03-28:我不推荐这个答案;我会推荐我的另一个,因为它更干净、更高效
试试@BENY的答案。如果它不起作用,可能是由于不同的索引
下面的解决方案很难看,而且更复杂,但它应该提供足够的线索,使其能够与任何数据帧一起工作,而不仅仅是玩具帧。这是熊猫的一个领域,API不可否认地笨拙且容易出错,有时,如果不进行大量的跳转,就根本无法获得任何有效的结果
诀窍是确保公共索引可用并且具有相同的名称
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[7,8,9,10,11,12],
'c':['q', 'q', 'q', 'q', 'w', 'w']
})
df.reset_index(drop=True, inplace=True)
values = df.groupby(['c']).apply(lambda x: sum(x['a'])/sum(x['b']))
# Convert result to dataframe.
df_to_join = values.to_frame()
# Ensure indexes have common names.
df_to_join.index.set_names(["index"], inplace=True)
df.set_index("c", inplace=True)
df.index.set_names(["index"], inplace=True)
# Set column name of result we want.
df_to_join.rename(columns={0: "ab_weighted"}, inplace=True, errors='raise')
# Join result of groupby to original dataframe.
df_result = df.merge(df_to_join, on=["index"])
print(df_result)
# output
a b ab_weighted
index
q 1 7 0.294118
q 2 8 0.294118
q 3 9 0.294118
q 4 10 0.294118
w 5 11 0.478261
w 6 12 0.478261
并将索引转换回列c
:
df_result.reset_index(inplace=True)
df_result.rename(columns={"index": "c"}, inplace=True)
这很有效:
import numpy as np
import pandas as pd
df = pd.DataFrame({'a':[1,2,3,4,5,6],
'b':[7,8,9,10,11,12],
'c':['q', 'q', 'q', 'q', 'w', 'w']
})
def groupby_transform(df: pd.DataFrame, group_by_column: str, lambda_to_apply) -> np.array:
"""
Groupby and transform. Returns a column for the original dataframe.
:param df: Dataframe.
:param group_by_column: Column(s) to group by.
:param lambda_to_apply: Lambda.
:return: Column to append to original dataframe.
"""
df = df.reset_index(drop=True) # Dataframe index is now strictly in order of the rows in the original dataframe.
values = df.groupby(group_by_column).apply(lambda_to_apply)
values.sort_index(level=1, inplace=True) # Sorts result into order of original rows in dataframe (as groupby will undo that order when it groups).
result = np.array(values) # Sort rows into same order as original dataframe.
if result.shape[0] == 1: # e.g. if shape is (1,1003), make it (1003,).
result = result[0]
return result # Return column.
df["result"] = groupby_transform(df, "c", lambda x: x["a"].shift(1) + x["b"].shift(1))
输出:
a b c result
0 1 7 q NaN
1 2 8 q 8.0
2 3 9 q 10.0
3 4 10 q 12.0
4 5 11 w NaN
5 6 12 w 16.0
与上面的a相同: 这将提供与之前相同的输出:
df["result"] = df.ex.groupby_transform("c", lambda x: x["a"].shift(1) + x["b"].shift(1))
我喜欢这种方法,但是创建整个数据帧的副本,对于大型数据集,这可能会很昂贵。我正在寻找一种只创建附加列而不复制数据帧或保存中间结果的方法(data.table只为附加列分配内存,不复制数据帧)Thx Through and happy holiday season:)@Christoph噢,我的坏消息。这只是一个有代表性的例子。只要这样做:
df['new']=v.a/v.b
嘿,恐怕你把我弄丢了:)当你这样做时,df['new']=v.a/v.b
如果我理解正确的话,你仍然需要创建中间数据帧v?@Christoph err,不。这是就地赋值,语法,因此它比dataframe.assign有效得多(复制数据)。是的,但您仍然使用v=df.groupby('c')[[a','b']]]创建数据帧v
。转换('sum')
我说得对吗?我的咖啡还没有开始供应。我担心:)Thx,这回答了我的特定示例,但您如何处理像'df.groupby(['c','d'])这样的多个分组?然后使用join and merge@christophy进行检查,注意这一个-我更喜欢@BENY的答案或我的答案,因为如果groupby中有多个类别,则使用iloc[:,0]会扰乱结果,这意味着结果的顺序与输入不同。需要加入或执行post排序来修复此问题。