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

我在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) 

我想要应用一个groupby操作,计算每个date在“yearmonth”列中的所有内容的上限加权平均回报。

这按预期工作:

 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 

但是接下来,我想将这些值“广播”回原始数据框中的索引,并将它们保存为date匹配的常量列。

 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 

我意识到这个天真的任务不应该工作。 但是,将“groupby”操作的结果分配给父数据框的新列的“正确的”Pandas成语是什么?

最后,我想要一个名为“MarketReturn”的列,它将成为与groupby操作的输出具有匹配date的所有索引的重复常量值。

一个窍门就是:

 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] 

但这是缓慢的,不好的,不和谐的。

 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]: AB 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 

虽然我仍然在探索所有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 

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

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

我不确定这是如何适用的,但是使用转换来实现精心devise的lambda函数可能相当棘手,所以我发现最有用的策略是创build我需要的variables,将它们放在原始数据集中,然后在那里执行我的操作。

如果我明白你想要做的是正确的(如果我错了,我很抱歉)首先你可以计算出每个组的总市值:

 bdata['group_MarketCap'] = bdata.groupby('yearmonth')['MarketCap'].transform('sum') 

这将在您的原始数据中添加一个名为“group_MarketCap”的列,其中包含每个组的市值上限。 那么你可以直接计算加权值:

 bdata['weighted_P'] = bdata['PriceReturn'] * (bdata['MarketCap']/bdata['group_MarketCap']) 

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

 bdata['MarketReturn'] = bdata.groupby('yearmonth')['weighted_P'].transform('sum') 

我倾向于通过这种方式来构build我的variables。 有时候你可以把所有的东西全部放在一个命令中,但是这并不总是和groupby()一起工作,因为大部分的时间pandas都需要实例化新的对象来在全数据集上进行操作(也就是说你不能如果还不存在,则将两列一起添加)。

希望这可以帮助 :)

这是否工作?

 capWeighting = lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum() bdata["MarketReturn"] = bdata.groupby("yearmonth").transform(capWeighting) 

我使用reindex_like这个:

 summedbdata = bdata.groupby("yearmonth").apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum()) summedbdata.set_index('yearmonth').reindex_like(bdata.set_index('yearmonth').sort_index(), method='ffill')