Python Pandas groupby 작업 결과를 부모 데이터 프레임의 열에 다시 할당하는 방법은 무엇입니까?


83

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]

그러나 이것은 느리고, 나쁘고, 비파이 토닉입니다.


원래 프레임 대신 그룹화 된 개체에 다시 할당하고 있습니다.
Wouter Overmeire 2012 년

2
저는 그것을 알고 있으며 오류 바로 아래에 이렇게 말했습니다. "이 순진한 할당이 작동하지 않아야한다는 것을 알고 있습니다. 그러나 groupby 연산의 결과를 부모의 새 열에 할당하는"올바른 "Pandas 관용구는 무엇입니까? 데이터 프레임? " LHS에서 원래 데이터 프레임으로 할당을 수행하는 것도 작동하지 않으며 GroupBy 오브젝트 수준에서 열을 추가하는 것보다 훨씬 덜 직관적입니다.
ely

답변:


74
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

이것은 여전히 ​​내가 groupby 연산을 수행하는 라인의 LHS에 직접 할당하는 대신 groupby 계산을 저장해야합니다. Apply가 질문 하단의 내 해킹 루프보다 약간 더 나을 수도 있지만 기본적으로 동일한 아이디어입니다.
ely aug

Join이이를 수행 할 수 있지만 추가 된 열의 이름을 변경해야합니다. 이 경우 A_r은 new_col입니다.
Wouter Overmeire 2012 년

하단의 조인 예제는 작동하지만 명확하게 표시되지 않습니다. 답변의 첫 번째 부분을 삭제하고 뒷부분을 좀 더 명확하게 만들고 싶다면 수락하는 것 외에도 업 투표를하겠습니다.
ely

12
나는 첫 번째 접근 방식을 제거했습니다. 솔직히 말해서 코드가 그 자체로 말하는 것처럼 느껴지므로 문서에 대한 설명이나 참조를 추가하려면 자유롭게 편집하십시오. 저는 그렇게 투표 시스템에 관심이 없습니다. 판다를 조금 지원하기 위해 여기에 있습니다.
Wouter Overmeire 2012 년

1
나는이 답변, 약간의 네크로 게시물을 찾는 데 오랜 시간을 보냈지 만 감사합니다! +1
Dan Carter

52

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

1
: 당신은 또한 람다 및 할당 사용하여 함수 정의하지 않고이 작업을 수행 할 수 있습니다df.groupby('yearmonth').apply(lambda grp: grp.assign(mkt_return=grp['return'].sum()))
krassowski을

32

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

도움이 되었기를 바랍니다 :)


24

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


내 이해는 변환이 전달 된 것과 같은 객체를 생성한다는 것입니다. 따라서 DataFrame을 변환하면 열만 반환하는 것이 아니라 DataFrame을 반환합니다. 제 경우에는 원래 데이터 프레임에 새 결과를 추가하고 싶습니다. 아니면 데이터 프레임을 가져 와서 새 열을 계산하고 새 열을 추가 한 다음 해당 함수 변환 하는 별도의 함수를 작성해야한다는 말입니까?
ely

2
동의합니다. 변환이 더 나은 선택입니다. df [ 'A-month-sum'] = df.groupby ( 'month') [ 'A']. transform (sum)
Wouter Overmeire

하지만 왜 더 좋을까요? 똑같죠? 더 빠릅니까?
K.-Michael Aye 2013

1
IMHO, transform깨끗해 보입니다. 확인을위한 EMS 데이터가 없지만 작동 할 수 있습니다 (람다 함수를 수정해야 할 수도 있음).bdata['mkt_return'] = bdata.groupby("yearmonth").transform(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum())
cd98

1
내가 틀렸다면 나를 수정하고, transform하나가 이후 여러 열에서 작동하도록 허용하지 않습니다 groupby. 예를 들어 df.groupby('col_3')[['col_1','col_2']].transform(lambda x: ((1-x.col_1.mean()) - x.col_2.std()))'속성 XXX 없음'을 불평하는 오류가 발생합니다
Jason Goal

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.