我在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操作来计算“年度”列中每个日期的所有内容的上限加权平均回报。
这可以按预期工作:
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
但是我希望将这些值“广播”回原始数据框中的索引,并将它们保存为日期匹配的常量列。
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操作的输出具有匹配日期的索引的重复常量值。
实现这一目标的一个方法是:
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]
但这很慢,很糟糕,而且是非音乐。
答案 0 :(得分:56)
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
答案 1 :(得分:38)
虽然我仍然在探索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
答案 2 :(得分:19)
我可以建议transform
方法(而不是聚合)吗?如果你在原始例子中使用它,它应该做你想要的(广播)。
答案 3 :(得分:17)
作为使用groupby()的一般规则,如果使用.transform()函数,pandas将返回一个与原始表格长度相同的表。当您使用其他函数(如.sum()或.first())时,pandas将返回一个表,其中每一行都是一个组。
我不确定这如何适用于apply但是使用transform实现精心设计的lambda函数可能相当棘手,因此我认为最有用的策略是创建我需要的变量,将它们放在原始数据集中然后在那里做我的业务。
如果我理解你正在尝试做什么(我道歉,如果我错了),首先你可以计算每个群体的总市值:
bdata['group_MarketCap'] = bdata.groupby('yearmonth')['MarketCap'].transform('sum')
这将添加一个名为&#34; group_MarketCap&#34;的列。原始数据,其中包含每个组的市值总和。然后你可以直接计算加权值:
bdata['weighted_P'] = bdata['PriceReturn'] * (bdata['MarketCap']/bdata['group_MarketCap'])
最后,您将使用相同的转换函数计算每个组的加权平均值:
bdata['MarketReturn'] = bdata.groupby('yearmonth')['weighted_P'].transform('sum')
我倾向于以这种方式构建我的变量。有时你可以把它全部放在一个命令中但是它并不总是与groupby()一起使用,因为大多数时候pandas需要实例化新对象以在完整的数据集范围内对它进行操作(即你可以& #39; t如果还没有存在,则将两列相加。
希望这有帮助:)
答案 4 :(得分:0)
这有用吗?
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')
答案 5 :(得分:0)
我没有找到分配给原始数据框的方法。因此,我只存储组中的结果并将它们连接起来。然后,我们按索引对连接的数据帧进行排序,以将原始顺序作为输入数据帧。这是示例代码:
In [10]: df = pd.DataFrame({'month': np.random.randint(0,11, 100), 'A': np.random.randn(100), 'B': np.random.randn(100)})
In [11]: df.head()
Out[11]:
month A B
0 4 -0.029106 -0.904648
1 2 -2.724073 0.492751
2 7 0.732403 0.689530
3 2 0.487685 -1.017337
4 1 1.160858 -0.025232
In [12]: res = []
In [13]: for month, group in df.groupby('month'):
...: new_df = pd.DataFrame({
...: 'A^2+B': group.A ** 2 + group.B,
...: 'A+B^2': group.A + group.B**2
...: })
...: res.append(new_df)
...:
In [14]: res = pd.concat(res).sort_index()
In [15]: res.head()
Out[15]:
A^2+B A+B^2
0 -0.903801 0.789282
1 7.913327 -2.481270
2 1.225944 1.207855
3 -0.779501 1.522660
4 1.322360 1.161495
此方法非常快速且可扩展。您可以在此处派生任何功能。