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)
"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"이라는 열이 필요합니다.
이를 달성하기위한 한 가지 해킹은 다음과 같습니다.
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]:
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
주어진 조각 을 연결 하는 믿을 수 없을 정도로 현명한 방법을 모두 탐색하는 동안 그룹 별 작업 후에 부모에 새 열을 추가하는 또 다른 방법이 있습니다.
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
집계 대신 방법을 제안해도 됩니까? 원래 예제에서 사용하면 원하는 작업 (방송)을 수행해야합니다.
As a general rule when using groupby(), if you use the .transform() function pandas will return a table with the same length as your original. When you use other functions like .sum() or .first() then pandas will return a table where each row is a group.
I'm not sure how this works with apply but implementing elaborate lambda functions with transform can be fairly tricky so the strategy that I find most helpful is to create the variables I need, place them in the original dataset and then do my operations there.
If I understand what you're trying to do correctly (I apologize if I'm mistaken) first you can calculate the total market cap for each group:
bdata['group_MarketCap'] = bdata.groupby('yearmonth')['MarketCap'].transform('sum')
This will add a column called "group_MarketCap" to your original data which would contain the sum of market caps for each group. Then you can calculate the weighted values directly:
bdata['weighted_P'] = bdata['PriceReturn'] * (bdata['MarketCap']/bdata['group_MarketCap'])
And finally you would calculate the weighted average for each group using the same transform function:
bdata['MarketReturn'] = bdata.groupby('yearmonth')['weighted_P'].transform('sum')
I tend to build my variables this way. Sometimes you can pull off putting it all in a single command but that doesn't always work with groupby() because most of the time pandas needs to instantiate the new object to operate on it at the full dataset scale (i.e. you can't add two columns together if one doesn't exist yet).
Hope this helps :)
Does this work?
capWeighting = lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum()
bdata["MarketReturn"] = bdata.groupby("yearmonth").transform(capWeighting)
I use reindex_like
for this:
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')
'programing tip' 카테고리의 다른 글
MySQL INSERT INTO 텍스트에 줄 바꿈 추가 (0) | 2020.11.14 |
---|---|
HTML5 동영상이 반복되지 않습니다. (0) | 2020.11.14 |
SVN은 커밋 된 코드에서 패치를 생성합니까? (0) | 2020.11.14 |
컨트롤러를 전역으로 만들지 않고 Angular의 단위 테스트 지시어 컨트롤러 (0) | 2020.11.14 |
사용자 정의 http 헤더를 어떻게 추가합니까? (0) | 2020.11.14 |