[python] 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


답변

groupby ()를 사용할 때 일반적으로 .transform () 함수를 사용하면 pandas는 원본과 동일한 길이의 테이블을 반환합니다. .sum () 또는 .first ()와 같은 다른 함수를 사용하면 pandas는 각 행이 그룹 인 테이블을 반환합니다.

이것이 apply와 어떻게 작동하는지 잘 모르겠지만 변환을 사용하여 정교한 람다 함수를 구현하는 것은 상당히 까다로울 수 있으므로 가장 도움이되는 전략은 필요한 변수를 만들고 원래 데이터 세트에 배치 한 다음 거기에서 작업을 수행하는 것입니다.

먼저 올바르게하려는 작업을 이해하면 각 그룹의 총 시가 총액을 계산할 수 있습니다.

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 ()와 함께 작동하지는 않습니다. 아직 존재하지 않는 경우 두 개의 열을 함께 추가).

도움이 되었기를 바랍니다 🙂


답변

transform집계 대신 방법을 제안해도 됩니까? 원래 예제에서 사용하면 원하는대로해야합니다 (방송).


답변

원래 데이터 프레임에 할당하는 방법을 찾지 못했습니다. 따라서 그룹의 결과를 저장하고 연결합니다. 그런 다음 연결된 데이터 프레임을 인덱스별로 정렬하여 원래 순서를 입력 데이터 프레임으로 얻습니다. 다음은 샘플 코드입니다.

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

이 방법은 매우 빠르고 확장 가능합니다. 여기에서 모든 기능을 파생 할 수 있습니다.


답변