본문 바로가기

Computer/Pandas

Pandas groupby (1)

반응형

엑셀, csv 데이터를 다루면서 특정 칼럼 값이 같은 행들만 추리는 작업을 많이 해보셨을겁니다. 엑셀에서는 filter 기능을 이용해 손쉽게 할 수 있지만 pandas 라이브러리에서는 groupby 함수를 이용해야 하는데요, 처음 groupby 함수를 사용하다보면 동작 방식이 명확히 이해되지 않았습니다. 이번 포스트에서는 미국 역대 의원 정보를 담은 다음 데이터를 이용해서 pandas groupby 함수의 동작 방식을 살펴보도록 하겠습니다.

legislators-historical.csv
1.39MB

먼저 데이터를 읽습니다. 문자열로 되어있는 칼럼은 공간효율성을 위해 category 타입으로 dtype을 정의합니다.

import pandas as pd

dtypes = {
    "first_name": "category",
    "gender": "category",
    "type": "category",
    "state": "category",
    "party": "category",
}
df = pd.read_csv(
    "groupby-data/legislators-historical.csv",
    dtype=dtypes,
    usecols=list(dtypes) + ["birthday", "last_name"],
    parse_dates=["birthday"]
)

>>> df.tail()
      last_name first_name   birthday gender type state       party
12044    Wright        Ron 1953-04-08      M  rep    TX  Republican
12045     Fudge     Marcia 1952-10-29      F  rep    OH    Democrat
12046   Haaland      Debra 1960-12-02      F  rep    NM    Democrat
12047  Hastings      Alcee 1936-09-05      M  rep    FL    Democrat
12048   Stivers      Steve 1965-03-24      M  rep    OH  Republican

Grouping할 컬럼으로는 주, 성별 (state, gender)가 있겠네요. 먼저 state, gender 칼럼을 이용해 groupby 함수를 수행해서 state/gender가 일치하는 행이 몇 개인지 살펴보겠습니다. 결과를 보면 pandas.series 데이터 타입이 되고 인덱스가 MultiIndex 형태가 됩니다. 만약, dataframe 형식으로 만드려면은 groupby 함수에서 as_index=False로 설정합니다. (이때 index는 RangeIndex가 됩니다.)

>>> df.groupby(["state", "gender"])["last_name"].count()
state  gender
AK     F           0
       M          16
AL     F           4
       M         205
AR     F           5
                ... 
WI     M         198
WV     F           1
       M         119
WY     F           1
       M          39
Name: last_name, Length: 116, dtype: int64

>>> df.groupby(["state", "gender"], as_index=False)["last_name"].count()
    state gender  last_name
0      AK      F          0
1      AK      M         16
2      AL      F          4
3      AL      M        205
4      AR      F          5
..    ...    ...        ...
111    WI      M        198
112    WV      F          1
113    WV      M        119
114    WY      F          1
115    WY      M         39
[116 rows x 3 columns]

그렇다면 groupby 함수는 어떻게 동작할까요? groupby 함수만 적용하면 다음과 같이 DataFrameGroupby 객체가 리턴되고 아무것도 안하는 것처럼 보입니다. 즉, groupby 함수 객체에 무엇인가를 더 해주어야만 (.count() 함수와 같은 메소드) 실제 결과물이 출력된다는 것이죠.

>>> df.groupby(['state', 'gender'])
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x00000180DBABAB80>

groupby 함수는 1) 테이블을 그룹으로 나누는 split, 2) 나눠진 그룹에 대해 어떤 operation을 적용하는 apply, 3) operation이 적용된 결과물을 합치는 combine 순서로 동작합니다. DataFrameGroupBy 객체는 __iter__() 메소드가 정의되어 iteration을 거침으로서 1번 split 과정을 볼 수 있습니다.

>>> by_state = df.groupby("state")
for state, frame in by_state:
    print(f"First 2 entries for {state!r}")
    print("------------------------")
    print(frame.head(2), end="\n\n")
    
First 2 entries for 'AK'
------------------------
     last_name first_name   birthday gender type state        party
6617    Waskey      Frank 1875-04-20      M  rep    AK     Democrat
6645      Cale     Thomas 1848-09-17      M  rep    AK  Independent
First 2 entries for 'AL'
------------------------
    last_name first_name   birthday gender type state       party
911   Crowell       John 1780-09-18      M  rep    AL  Republican
990    Walker       John 1783-08-12      M  sen    AL  Republican

또한, GroupBy 객체의 .groups 속성은 {group name: group label} 사전 형태의 grouping 결과를 제공하고 .get_group() 함수는 split된 특정 그룹을 불러옵니다. 이것은 (df.loc[df["state"] == "PA"] 의 동작과 같죠.)

>>> by_state.groups["PA"]
Int64Index([    4,    19,    21,    27,    38,    57,    69,    76,    84,
               88,
            ...
            11838, 11862, 11871, 11873, 11883, 11887, 11926, 11938, 11952,
            11965],
           dtype='int64', length=1053)
           
>>> by_state.get_group("PA")
      last_name first_name   birthday gender type state                party
4        Clymer     George 1739-03-16      M  rep    PA                  NaN
19       Maclay    William 1737-07-20      M  sen    PA  Anti-Administration
21       Morris     Robert 1734-01-20      M  sen    PA   Pro-Administration
27      Wynkoop      Henry 1737-03-02      M  rep    PA                  NaN
38       Jacobs     Israel 1726-06-09      M  rep    PA                  NaN
         ...        ...        ...    ...  ...   ...                  ...
11887     Brady     Robert 1945-04-07      M  rep    PA             Democrat
11926   Shuster       Bill 1961-01-10      M  rep    PA           Republican
11938   Rothfus      Keith 1962-04-25      M  rep    PA           Republican
11952  Costello       Ryan 1976-09-07      M  rep    PA           Republican
11965    Marino        Tom 1952-08-15      M  rep    PA           Republican
[1053 rows x 7 columns]

또한, GroupBy 객체가 iterator 이므로 next/iter 함수를 이용할 수 있고 이를 사용하면 group 명, group table을 얻을 수 있습니다.

>>> state, frame = next(iter(by_state))
>>> state
'AK'
>>> frame.head(3)
     last_name first_name   birthday gender type state        party
6617    Waskey      Frank 1875-04-20      M  rep    AK     Democrat
6645      Cale     Thomas 1848-09-17      M  rep    AK  Independent
7440   Grigsby     George 1874-12-02      M  rep    AK          NaN

 


이후의 apply 단계는 각 subgroup 별로 지정한 칼럼에 대해 특정한 operation을 적용합니다. 이후에 combine 단계는 operation을 적용한 결과를 전체 데이터프레임으로 합치는 것이죠. 종합해보면 groupby 함수 자체는 split을 수행하고 지정한 칼럼에 대해서 subgroup 순대로 iteration을 돌면서 operation을 적용한 뒤 합치는 과정이라 볼 수 있습니다.

>>> frame["last_name"].count()
16

만약 칼럼을 따로 지정하지 않고 operation만 적용한다면 groupby 대상 칼럼 이외의 칼럼에 대한 통계치를 데이터프레임 타입으로 제공합니다. 따라서 operation을 적용하고 컬럼을 filtering 하거나 컬럼을 지정하고 operation을 수행해도 동일한 결과가 리턴됩니다.

df = pd.DataFrame({'group': ['a', 'a', 'a', 'b', 'b', 'b'], 
                   'value_1': np.arange(6), 
                   'value_2': np.random.randn(6)})
                   
>>> df.groupby('group').mean()
       value_1   value_2
group                   
a          1.0  0.270852
b          4.0 -0.917279

>> df.groupby('group').mean()['value_1']
group
a    1.0
b    4.0
Name: value_1, dtype: float64

>>> df.groupby('group')['value_1'].mean()
group
a    1.0
b    4.0
Name: value_1, dtype: float64

 


Pandas groupby (2)

Pandas groupby (3)

반응형

'Computer > Pandas' 카테고리의 다른 글

Pandas - SettingWithCopyWarning  (0) 2021.08.11
Pandas groupby (2)  (0) 2021.08.07
Pandas Categorical Data  (0) 2021.08.01
Onehot 인코딩의 역변환 (inverse transform)  (0) 2021.07.25
Pandas Multiple Columns Label Encoding  (1) 2021.07.14