Python Pandas如何将groupby操作结果分配回父数据帧中的列?

ely*_*ely 66 python group-by dataframe pandas

我在IPython中有以下数据框,其中每一行都是一个股票:

In [261]: bdata
Out[261]:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 21210 entries, 0 to 21209
Data columns:
BloombergTicker      21206  non-null values
Company              21210  non-null values
Country              21210  non-null values
MarketCap            21210  non-null values
PriceReturn          21210  non-null values
SEDOL                21210  non-null values
yearmonth            21210  non-null values
dtypes: float64(2), int64(1), object(4)
Run Code Online (Sandbox Code Playgroud)

我想应用groupby操作来计算"年度"列中每个日期的所有内容的上限加权平均回报.

这按预期工作:

In [262]: bdata.groupby("yearmonth").apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum())
Out[262]:
yearmonth
201204      -0.109444
201205      -0.290546
Run Code Online (Sandbox Code Playgroud)

但后来我希望将这些值"广播"回原始数据框中的索引,并将它们保存为日期匹配的常量列.

In [263]: dateGrps = bdata.groupby("yearmonth")

In [264]: dateGrps["MarketReturn"] = dateGrps.apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum())
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/mnt/bos-devrnd04/usr6/home/espears/ws/Research/Projects/python-util/src/util/<ipython-input-264-4a68c8782426> in <module>()
----> 1 dateGrps["MarketReturn"] = dateGrps.apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum())

TypeError: 'DataFrameGroupBy' object does not support item assignment
Run Code Online (Sandbox Code Playgroud)

我意识到这种天真的任务不应该奏效.但是,用于将groupby操作的结果分配到父数据帧的新列中的"正确"Pandas习惯是什么?

最后,我想要一个名为"MarketReturn"的列,而不是所有与groupby操作的输出具有匹配日期的索引的重复常量值.

实现这一目标的一个方法是:

marketRetsByDate  = dateGrps.apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum())

bdata["MarketReturn"] = np.repeat(np.NaN, len(bdata))

for elem in marketRetsByDate.index.values:
    bdata["MarketReturn"][bdata["yearmonth"]==elem] = marketRetsByDate.ix[elem]
Run Code Online (Sandbox Code Playgroud)

但这是缓慢的,糟糕的,而且是非战斗的.

Wou*_*ire 65

In [97]: df = pandas.DataFrame({'month': np.random.randint(0,11, 100), 'A': np.random.randn(100), 'B': np.random.randn(100)})

In [98]: df.join(df.groupby('month')['A'].sum(), on='month', rsuffix='_r')
Out[98]:
           A         B  month       A_r
0  -0.040710  0.182269      0 -0.331816
1  -0.004867  0.642243      1  2.448232
2  -0.162191  0.442338      4  2.045909
3  -0.979875  1.367018      5 -2.736399
4  -1.126198  0.338946      5 -2.736399
5  -0.992209 -1.343258      1  2.448232
6  -1.450310  0.021290      0 -0.331816
7  -0.675345 -1.359915      9  2.722156
Run Code Online (Sandbox Code Playgroud)

  • 我删除了第一种方法.说实话,我觉得代码说明了自己,如果你想添加一些解释或对文档的引用,可以随意编辑.我不是真的投入如此投票的系统,只是为了支持一点熊猫. (11认同)

Gar*_*ett 44

虽然我仍然在探索apply连接所给出的所有碎片的所有非常智能的方法,但这是在groupby操作之后在父级中添加新列的另一种方法.

In [236]: df
Out[236]: 
  yearmonth    return
0    201202  0.922132
1    201202  0.220270
2    201202  0.228856
3    201203  0.277170
4    201203  0.747347

In [237]: def add_mkt_return(grp):
   .....:     grp['mkt_return'] = grp['return'].sum()
   .....:     return grp
   .....: 

In [238]: df.groupby('yearmonth').apply(add_mkt_return)
Out[238]: 
  yearmonth    return  mkt_return
0    201202  0.922132    1.371258
1    201202  0.220270    1.371258
2    201202  0.228856    1.371258
3    201203  0.277170    1.024516
4    201203  0.747347    1.024516
Run Code Online (Sandbox Code Playgroud)

  • 您也可以在不使用 lambda 定义函数并分配的情况下执行此操作: `df.groupby('yearmonth').apply(lambda grp: grp.assign(mkt_return=grp['return'].sum()))` (4认同)

see*_*spi 22

作为使用groupby()的一般规则,如果使用.transform()函数,pandas将返回一个与原始表格长度相同的表.当您使用其他函数(如.sum()或.first())时,pandas将返回一个表,其中每一行都是一个组.

我不确定这如何适用于apply但是使用transform实现精心设计的lambda函数可能相当棘手,因此我认为最有用的策略是创建我需要的变量,将它们放在原始数据集中然后在那里进行操作.

如果我理解你正在尝试做什么(我道歉,如果我弄错了),首先你可以计算每组的总市值:

bdata['group_MarketCap'] = bdata.groupby('yearmonth')['MarketCap'].transform('sum')
Run Code Online (Sandbox Code Playgroud)

这将在您的原始数据中添加一个名为"group_MarketCap"的列,其中包含每个组的市值总和.然后您可以直接计算加权值:

bdata['weighted_P'] = bdata['PriceReturn'] * (bdata['MarketCap']/bdata['group_MarketCap'])
Run Code Online (Sandbox Code Playgroud)

最后,您将使用相同的转换函数计算每个组的加权平均值:

bdata['MarketReturn'] = bdata.groupby('yearmonth')['weighted_P'].transform('sum')
Run Code Online (Sandbox Code Playgroud)

我倾向于以这种方式构建我的变量.有时你可以将它全部放在一个命令中,但这并不总是适用于groupby(),因为大多数时候pandas需要实例化新对象以在完整的数据集范围内对它进行操作(即你不能如果还不存在,则将两列相加.

希望这可以帮助 :)

  • 我认为这是最好的答案。使用“transform”,您可以跳过标准“df.groupby('yearmonth')['weighted_P'].sum()”之后的连接。 (4认同)

Wes*_*ney 20

我可以建议使用该transform方法(而不是聚合)吗?如果你在原始例子中使用它,它应该做你想要的(广播).

  • 我同意,转换是更好的选择, df['A-month-sum'] = df.groupby('month')['A'].transform(sum) (2认同)