달나라 노트

Python Pandas : DataFrame.groupby 본문

Python/Python Pandas

Python Pandas : DataFrame.groupby

CosmosProject 2020. 11. 2. 18:23
728x90
반응형

 

 

DataFrame.groupby

groupby는 특정 컬럼에 존재하는 값들에 대해서 동일한 값을 가진 행끼리 그룹화하고 그룹화된 행들에 어떤 연산(합, 평균, 최대, 최소 등)을 해주는 기능을 가집니다.

 

먼저 test용 DataFrame을 생성합니다.

import pandas as pd

dict_item = {
    'date': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104
    ],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4
    ],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'
    ],
    'price': [
        1000, 1000, 1010,
        2000, 2100, 2050,
        3000, 3100, 2950,
        4000, 3950, 3900, 3980
    ],
    'quantity': [
        100, 105, 98,
        50, 51, 55,
        201, 200, 220,
        30, 40, 38, 50
    ]
}
df_item = pd.DataFrame(dict_item)
print(df_item)
print(type(df_item))



 - Output
        date  item_id item_name  price  quantity
0   20200101        1         a   1000       100
1   20200102        1         a   1000       105
2   20200103        1         a   1010        98
3   20200101        2         b   2000        50
4   20200102        2         b   2100        51
5   20200103        2         b   2050        55
6   20200101        3         c   3000       201
7   20200102        3         c   3100       200
8   20200103        3         c   2950       220
9   20200101        4         d   4000        30
10  20200102        4         d   3950        40
11  20200103        4         d   3900        38
12  20200104        4         d   3980        50

<class 'pandas.core.frame.DataFrame'>

일자별 각 item들의 판매가격(price)과 판매수량(quantity)이 있는 DataFrame을 생성하였습니다.




item_id와 item_name 컬럼을 기준으로 그룹화하여 나머지 컬럼들(date, price, quantity)의 평균값을 나타내도록 해봅시다.

import pandas as pd

dict_item = {
    'date': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104
    ],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4
    ],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'
    ],
    'price': [
        1000, 1000, 1010,
        2000, 2100, 2050,
        3000, 3100, 2950,
        4000, 3950, 3900, 3980
    ],
    'quantity': [
        100, 105, 98,
        50, 51, 55,
        201, 200, 220,
        30, 40, 38, 50
    ]
}
df_item = pd.DataFrame(dict_item)

df_grouped = df_item.groupby(by=['item_id', 'item_name']).mean()
print(df_grouped)

df_grouped.reset_index(drop=False, inplace=True)
print(df_grouped)



- Output 1
                         date        price  quantity
item_id item_name                                   
1       a          20200102.0  1003.333333     101.0
2       b          20200102.0  2050.000000      52.0
3       c          20200102.0  3016.666667     207.0
4       d          20200102.5  3957.500000      39.5
<class 'pandas.core.frame.DataFrame'>

- Output 2
   item_id item_name        date        price  quantity
0        1         a  20200102.0  1003.333333     101.0
1        2         b  20200102.0  2050.000000      52.0
2        3         c  20200102.0  3016.666667     207.0
3        4         d  20200102.5  3957.500000      39.5
<class 'pandas.core.frame.DataFrame'>

위 예시를 순서대로 해석해봅시다.

 

1. df_grouped = df_item.groupby -> df_item에 groupby를 적용시켜 df_grouped라는 변수에 저장할 예정

 

2. groupby(by=['item_id', 'item_name']).mean() -> groupby의 parameter로서 그룹화의 기준이 될 컬럼인 'item_id', 'item_name'를 list의 형태로 명시해줌.

그리고 나머지 column들에 대해 mean()(평균)함수 적용.

 

위 예시의 결과를 보면 결과값은 DataFrame인 것을 알 수 있으며 item_id와 item_name은 원래 컬럼이었는데 index로 변환되었습니다. (-> Output 1)

 

이렇게 groupby의 기준이 된 컬럼은 groupby 후에 index로 변환되는데

만약 이 두 값을 다시 column으로 되돌리고 싶다면 reset_index를 이용하면 됩니다.(-> Output 2)

 

위 예시에서 mean()을 sun(), min(), max() 등으로 변경하여 합, 최소값, 최대값 함수를 적용시킬 수 있습니다.

 

또 한가지 주의할 점은 groupby.mean 등을 할 때 text데이터는 제외가 된다는 것입니다.

 

만약 위 데이터에서 date를 숫자로 명시하였는데,

date의 모든 요소들에 따옴표를 붙여서 글자로 만들면 결과 데이터에서 date에 대한 컬럼이 자동으로 나오지 않는 것을 알 수 있습니다.




만약 date와 price는 결과값에서 보고싶지 않고, item_id, item_name, quantity의 평균값만 보고싶으면

아래 예시처럼 mean()함수 전에 mean()함수가 적용될 컬럼을 명시해주면 됩니다.

import pandas as pd

dict_item = {
    'date': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104
    ],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4
    ],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'
    ],
    'price': [
        1000, 1000, 1010,
        2000, 2100, 2050,
        3000, 3100, 2950,
        4000, 3950, 3900, 3980
    ],
    'quantity': [
        100, 105, 98,
        50, 51, 55,
        201, 200, 220,
        30, 40, 38, 50
    ]
}
df_item = pd.DataFrame(dict_item)

se_grouped = df_item.groupby(by=['item_id', 'item_name'])['quantity'].mean()
print(se_grouped)
print(type(se_grouped))

df_grouped_2 = pd.DataFrame(se_grouped_2)
print(df_grouped)
print(type(df_grouped))
df_grouped_2.reset_index(drop=False, inplace=True)
print(df_grouped)
print(type(df_grouped))



- Output 1
item_id  item_name
1        a            101.0
2        b             52.0
3        c            207.0
4        d             39.5
<class 'pandas.core.series.Series'>

- Output 2
                   quantity
item_id item_name          
1       a             101.0
2       b              52.0
3       c             207.0
4       d              39.5
<class 'pandas.core.frame.DataFrame'>

- Output 3
   item_id item_name  quantity
0        1         a     101.0
1        2         b      52.0
2        3         c     207.0
3        4         d      39.5
<class 'pandas.core.frame.DataFrame'>

df_item.groupby(by=['item_id', 'item_name'])['quantity'].mean()

위 코드를 말로 해석해보면 좀 더 알기 쉽습니다.

 

df_item item_id, item_name을 기준으로 groupby한 후,

quantity 데이터의 평균(mean)을 나타내어라.

 

주의할 점은 위처럼 어느 하나의 컬럼(위 예시에서는 quantity)만을 mean함수의 대상으로 명시하면 그 결과물은 Series가 됩니다.

groupby 기준 컬럼인 item_id, item_name은 Series의 index가 되구요.

 

따라서 만약 이를 다시 DataFrame으로 되돌리고 싶다면 pd.DataFrame(se_grouped_2) 처럼 Series를 DataFrame으로 전환한 후(-> Output 2)

reset_index를 이용하여 index였던 item_id, item_name을 컬럼으로 전환해주면 됩니다.(-> Output 3)

 

 

 

 

 

 

 

import pandas as pd

dict_item = {
    'date': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104
    ],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4
    ],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'
    ],
    'price': [
        1000, 1000, 1010,
        2000, 2100, 2050,
        3000, 3100, 2950,
        4000, 3950, 3900, 3980
    ],
    'quantity': [
        100, 105, 98,
        50, 51, 55,
        201, 200, 220,
        30, 40, 38, 50
    ]
}
df_item = pd.DataFrame(dict_item)

df_item_grouped = df_item.groupby(by=['date'])['item_name'].apply(list)
print(df_item_grouped)
print(type(df_item_grouped))



- Output 1
    date
20200101    [a, b, c, d]
20200102    [a, b, c, d]
20200103    [a, b, c, d]
20200104             [d]
Name: item_name, dtype: object
<class 'pandas.core.series.Series'>

 

groupby에는 단순 sum, mean 같은 연산함수가 아니라 apply를 이용하여 여러 함수를 적용시킬 수 있습니다.

 

list함수는 date 기준으로 group화된 data의 item_name column에 있는 data를 list의 형태로 묶어줍니다.

 

따라서 결과를 보면 date 별로 존재하는 item_name을 list의 형태로 묶인 것을 알 수 있습니다.

 

 

 

 

 

import pandas as pd
import numpy as np

dict_item = {
    'date': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104
    ],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4
    ],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'
    ],
    'price': [
        1000, 1000, 1010,
        2000, 2100, 2050,
        3000, 3100, 2950,
        4000, 3950, 3900, 3980
    ],
    'quantity': [
        100, 105, 98,
        50, 51, 55,
        201, 200, 220,
        30, 40, 38, 50
    ]
}
df_item = pd.DataFrame(dict_item)

df_item_grouped = df_item.groupby(by=['item_id', 'item_name'])['price', 'quantity'].apply(np.sum)
print(df_item_grouped)
print(type(df_item_grouped))



-- Result
                   price  quantity
item_id item_name                 
1       a           3010       303
2       b           6150       156
3       c           9050       621
4       d          15830       158
<class 'pandas.core.frame.DataFrame'>

 

위 예시는 goupby에서 sum을 price와 quantity 2개의 column에 동시에 적용한 예시입니다.

 

df_item.groupby(by=['item_id', 'item_name'])['price', 'quantity'].apply(np.sum)

위처럼 groupby에서 연산(np.sum)을 적용할 column을 여러 개 적어주면 됩니다.

 

근데 위처럼 적으면 결과는 잘 나오는데 아래와 같은 Error가 발생합니다.

FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be deprecated, use a list instead.

 

내용을 읽어보면 groupby의 연산 대상 column으로 여러 column을 동시에 전달하려면 list를 사용하라는 것이죠.

 

위 Error가 발생하지 않게 하려면 아래처럼 코드를 수정하면 됩니다.

 

import pandas as pd
import numpy as np

dict_item = {
    'date': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104
    ],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4
    ],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'
    ],
    'price': [
        1000, 1000, 1010,
        2000, 2100, 2050,
        3000, 3100, 2950,
        4000, 3950, 3900, 3980
    ],
    'quantity': [
        100, 105, 98,
        50, 51, 55,
        201, 200, 220,
        30, 40, 38, 50
    ]
}
df_item = pd.DataFrame(dict_item)

df_item_grouped = df_item.groupby(by=['item_id', 'item_name'])[['price', 'quantity']].apply(np.sum)
print(df_item_grouped)
print(type(df_item_grouped))



-- Result
                   price  quantity
item_id item_name                 
1       a           3010       303
2       b           6150       156
3       c           9050       621
4       d          15830       158
<class 'pandas.core.frame.DataFrame'>

달라진 점이 보이시나요?

df_item_grouped = df_item.groupby(by=['item_id', 'item_name'])[['price', 'quantity']].apply(np.sum)

 

위처럼 groupby의 연산 대상 column인 price, quantity column을 단순히 column 이름만 적어서 전달한 것이 아니라

['price', 'quantity'] 의 list 형태로 묶어서 전달한겁니다.

 

 

 

 

Error를 없애고 깔끔한 코드를 만들려면 이렇게 list의 형태로 묶어서 전달하는게 좋습니다.

 

 

 

아래 예시처럼 groupby의 연산 대상 column이 price 1개뿐인 경우에도 list의 형태로 묶어서 전달하는 것이 가능합니다.

따라서 groupby를 쓸 땐 groupby 연산 대상 컬럼은 그냥 모두 list속에 담아서 전달합니다.

import pandas as pd
import numpy as np

dict_item = {
    'date': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104
    ],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'
    ],
    'price': [
        1000, 1000, 1010,
        2000, 2100, 2050,
        3000, 3100, 2950,
        4000, 3950, 3900, 3980
    ],
    'quantity': [
        100, 105, 98,
        50, 51, 55,
        201, 200, 220,
        30, 40, 38, 50
    ]
}
df_item = pd.DataFrame(dict_item)

df_item_grouped = df_item.groupby(by=['item_id', 'item_name'])[['price']].apply(np.sum)
print(df_item_grouped)
print(type(df_item_grouped))



-- Result
                   price
item_id item_name       
1       a           3010
2       b           6150
3       c           9050
4       d          15830
<class 'pandas.core.frame.DataFrame'>

 

 

 

 

 

지금까지 groupby를 어떻게 사용할 수 있는지를 알아봤습니다.

그러면 이제 groupby가 어떤식으로 작동하는지를 이해해봅시다.

이를 이해하면 더 많은 응용을 할 수 있습니다.

 

import pandas as pd
import numpy as np

dict_item = {
    'date': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104
    ],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4
    ],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'
    ],
    'price': [
        1000, 1000, 1010,
        2000, 2100, 2050,
        3000, 3100, 2950,
        4000, 3950, 3900, 3980
    ],
    'quantity': [
        100, 105, 98,
        50, 51, 55,
        201, 200, 220,
        30, 40, 38, 50
    ]
}
df_item = pd.DataFrame(dict_item)

print(df_item)
df_item_grouped = df_item.groupby(by=['item_id', 'item_name'])[['price']]\
    .apply(lambda df: print(type(df), '\n', df))



-- Result
        date  item_id item_name  price  quantity
0   20200101        1         a   1000       100
1   20200102        1         a   1000       105
2   20200103        1         a   1010        98
3   20200101        2         b   2000        50
4   20200102        2         b   2100        51
5   20200103        2         b   2050        55
6   20200101        3         c   3000       201
7   20200102        3         c   3100       200
8   20200103        3         c   2950       220
9   20200101        4         d   4000        30
10  20200102        4         d   3950        40
11  20200103        4         d   3900        38
12  20200104        4         d   3980        50

<class 'pandas.core.frame.DataFrame'> 
    price
0   1000
1   1000
2   1010

<class 'pandas.core.frame.DataFrame'> 
    price
3   2000
4   2100
5   2050

<class 'pandas.core.frame.DataFrame'> 
    price
6   3000
7   3100
8   2950

<class 'pandas.core.frame.DataFrame'> 
     price
9    4000
10   3950
11   3900
12   3980

이 예시는 groupby에 적용된 apply method에 lamda 식을 적용한 코드입니다.

단순히 groupby에 적용된 apply라는 method에 어떤 값이 전달 되는지 파악하기 위해 위 같은 코드를 작성했습니다.

 

결과를 보면 동일한 item_id와 동일한 item_name을 가진 행(row)들의 price 컬럼값들만 모여서 새로운 DataFrame의 타입으로 출력되고 있습니다.

 

즉, groupby는 동일한 item_id와 동일한 item_name을 가진 행(row)들의 price 컬럼값들만 모아서 새로운 DataFrame을 만들고,

이렇게 만들어진 DataFrame을 하나씩 apply method의 인자로서 전달하고 있는 것입니다.

 

이것을 이해하면 groupby에 적용되는 apply method에서 DataFrame을 이용한 여러 가지의 작업들이 가능하다는 의미가 됩니다.

대표적인 예시로 아래 예시를 보시죠.

 

 

import pandas as pd
import numpy as np

dict_item = {
    'date': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104
    ],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4
    ],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'
    ],
    'price': [
        1000, 1000, 1010,
        2000, 2100, 2050,
        3000, 3100, 2950,
        4000, 3950, 3900, 3980
    ],
    'quantity': [
        100, 105, 98,
        50, 51, 55,
        201, 200, 220,
        30, 40, 38, 50
    ]
}
df_item = pd.DataFrame(dict_item)


df_item_grouped = df_item.groupby(by=['item_id', 'item_name'])[['price']].apply(lambda df: ','.join(list(df.loc[:, 'price'].astype(str))))
print(df_item_grouped)



-- Result
item_id  item_name
1        a                 1000,1000,1010
2        b                 2000,2100,2050
3        c                 3000,3100,2950
4        d            4000,3950,3900,3980

위 예시의 핵심은 아래와 같습니다.

 

 

보시면 groupby에 의해 그룹화된 DataFrame이 apply method의 인자로 전달됩니다.

 

price 컬럼 값들을 string으로 변환한 후

 

groupby(by=['item_id', 'item_name'])[['price']].apply(lambda df: ~~~)

보시면 groupby에 의해 그룹화된 DataFrame이 apply method의 인자로 전달됩니다.

apply method의 parameter로서 전달된다는 것은 lambda 식의 df라는 변수에 전달된다는 의미와 같습니다.

따라서 lambda식에서는 df 변수에 전달받은 DataFrame을 가지고 원하는 연산을 할 수 있다는 의미이죠.

 

lambda df: ','.join(list(df.loc[:, 'price'].astype(str)))

이 예시에서는 df 변수에 전달받은 DataFrame을 loc를 이용해 Series로 추출하였고, 그 과정에서 astype method를 이용해 price 컬럼에 있는 모든 데이터의 형식을 string으로 변환하였습니다.

그리고 이렇게 만들어진 Series를 list로 변환한 후 list 속에 있는 값들을 join method를 이용해 연결한 것이죠.

 

 

item_id  item_name
1        a                 1000,1000,1010
2        b                 2000,2100,2050
3        c                 3000,3100,2950
4        d            4000,3950,3900,3980

그래서 그 결과를 보면 동일한 item_id와 동일한 item_name을 가진 행(row)들의 price 컬럼값들이 콤마로 연결된 것을 볼 수 있습니다.

 

이것을 이해한다면 groupby, apply를 이용해서 상당히 많은 것을 할 수 있게 될겁니다.

 

 

 

 

 

 

728x90
반응형
Comments