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)
「yearmonth」列の各日付ごとに、すべてのキャップ加重平均収益を計算する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=イディオムは何ですか?
最後に、groupby操作の出力と一致する日付を持つすべてのインデックスの繰り返し定数値になる「MarketReturn」という列が必要です。
これを達成するための1つのハックは次のとおりです。
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]
しかし、これは遅く、悪く、Pythonに反しています。
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
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
(集約の代わりに)transform
メソッドを提案できますか?元の例で使用する場合は、必要なこと(ブロードキャスト)を行う必要があります。
Groupby()を使用する際の一般的なルールとして、.transform()関数を使用する場合、pandasは元のテーブルと同じ長さのテーブルを返します。 )または.first()then pandasは、各行がグループであるテーブルを返します。
これがapplyでどのように機能するかはわかりませんが、transformを使用して手の込んだラムダ関数を実装するのはかなり難しいので、最も役立つと思う戦略は、必要な変数を作成し、元のデータセットに配置してから操作を行うことです。
あなたがしようとしていることを正しく理解している場合(私が間違っている場合は謝罪します)、最初に各グループの合計時価総額を計算できます:
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')
この方法で変数を作成する傾向があります。時々、それをすべて単一のコマンドに入れることができますが、ほとんどの場合pandasはそれで操作するために新しいオブジェクトをインスタンス化する必要があるため、groupby()で常に動作するとは限りません完全なデータセットスケール(つまり、1つがまだ存在しない場合、2つの列を一緒に追加することはできません)。
お役に立てれば :)
これは機能しますか?
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')