Python Pandas 데이터 분석 groupby 사용 방법 예제

파이썬에서 데이터 분석, 처리 할 때 많이 팬더스(Pandas) 사용합니다.

그중에서 groupby를 사용해야 하는 경우가 있어 정리하게 되었습니다.

 

Python Pandas groupby

라이브러리 import

import pandas as pd
import numpy as np

 

DataFrame으로 샘플 데이터 작성

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'city': ['부산', '부산', '부산', '부산', '서울', '서울', '서울'],
    'fruits': ['apple', 'orange', 'banana', 'banana', 'apple', 'apple', 'banana'],
    'price': [100, 200, 250, 300, 150, 200, 400],
    'quantity': [1, 2, 3, 4, 5, 6, 7]
})

print(df)

 

결과

  city  fruits  price  quantity
0   부산   apple    100         1
1   부산  orange    200         2
2   부산  banana    250         3
3   부산  banana    300         4
4   서울   apple    150         5
5   서울   apple    200         6
6   서울  banana    400         7

 

groupby 사용 방법

같은 값을 하나로 묶어 통계 또는 집계 결과를 얻기 위해 사용하는 것이 groupby입니다.

예를 들어 도시(city) 별로 가격(price) 평균을 구하고 싶은 경우 다음과 같이 groupby를 사용하여 평균값을 구할 수 있습니다.

평균값을 구해주는 메서드로 mean을 사용합니다.

df.groupby('city').mean()

 

결과

      price  quantity
city                 
부산    212.5       2.5
서울    250.0       6.0

 

그룹 지정은 여러 개를 지정할 수도 있습니다.

도시(city)와 과일(fruits)로 평균을 구해보겠습니다.

df.groupby(['city', 'fruits']).mean()

 

결과

             price  quantity
city fruits                 
부산   apple   100.0       1.0
     banana  275.0       3.5
     orange  200.0       2.0
서울   apple   175.0       5.5
     banana  400.0       7.0

 

도시별로 그룹화하고 다시 과일 종류별로 그룹이 된 평균값을 얻을 수 있습니다.

groupby를 사용하면 기본으로 그룹 라벨이 index가 됩니다.

index를 사용하고 싶은 않은 경우에는 as_index=False 를 설정하면 됩니다.

df.groupby(['city', 'fruits'], as_index=False).mean()

 

GroupBy 오브젝트 특성

get_group()

그룹 안에 데이터를 확인하고 싶은 경우 사용합니다.

df.groupby('city').get_group('부산')

 

결과

  city  fruits  price  quantity
0   부산   apple    100         1
1   부산  orange    200         2
2   부산  banana    250         3
3   부산  banana    300         4

 

size()

각 그룹의 사이즈를 취득할 수 있습니다.

df.groupby('city').size()

결과

city
부산    4
서울    3
dtype: int64

 

size() 결과는 Series 이라는 1차원 배열 오브젝트로 반환합니다.

반환된 배열에서 특정 그룹의 사이즈만을 취득할 수 있습니다.

df.groupby('city').size()['부산']

 

결과

4

 

Aggregation

GroupBy.mean()처럼 그룹별로 결과를 얻는 조작을 Aggregation이라고 부릅니다.

GroupBy 오브젝트에는 Aggregation에 사용할 수 있는 함수가 있습니다.

Aggregation를 사용하고 싶은 경우에는 agg()를 사용해서 처리할 수 있습니다.

df.groupby('city').agg(np.mean)

 

결과

      price  quantity
city                 
부산    212.5       2.5
서울    250.0       6.0

 

agg에 mean()을 사용하여 도시별 가격과 수량의 평균값을 구했습니다.

만약 가격의 평균과 수량의 합계를 동시에 구하고 싶은 경우에는 어떻게 해야 할까요?

agg를 사용하여 동시에 처리할 수 있습니다.

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'city': ['부산', '부산', '부산', '부산', '서울', '서울', '서울'],
    'fruits': ['apple', 'orange', 'banana', 'banana', 'apple', 'apple', 'banana'],
    'price': [100, 200, 250, 300, 150, 200, 400],
    'quantity': [1, 2, 3, 4, 5, 6, 7]
})

def my_mean(s):
    return np.mean(s)

print(df.groupby('city').agg({'price': my_mean, 'quantity': np.sum}))

 

결과

      price  quantity
city                 
부산    212.5        10
서울    250.0        18

 

apply

Aggregation을 사용하여 그룹 별로 결과를 얻을 수 있지만 더욱 활용하기 편리한 결과를 만들고 싶을 때 apply를 사용합니다.

apply를 사용하면 그룹별로 DataFrame를 사용할 수 있습니다.

apply 결과를 스칼라로 반환한 경우

・groupby로 작성한 lable이 row index가 됨.

・행수는 그룹수와 동일

・as_index 적용 안됨

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'city': ['부산', '부산', '부산', '부산', '서울', '서울', '서울'],
    'fruits': ['apple', 'orange', 'banana', 'banana', 'apple', 'apple', 'banana'],
    'price': [100, 200, 250, 300, 150, 200, 400],
    'quantity': [1, 2, 3, 4, 5, 6, 7]
})


print(df.groupby(['city', 'fruits'], as_index=False).apply(lambda d: (d.price * d.quantity).sum()))

 

결과

  city  fruits  None
0   부산   apple   100
1   부산  banana  1950
2   부산  orange   400
3   서울   apple  1950
4   서울  banana  2800

 

apply 결과를 Series로 반환한 경우

・groupby로 작성한 label과 더해져 apply 함수 결과 index가 결과 전체의 row index가 됨.

・전체 행수는 결과에 따라 다름.

・as_index=False를 지정하여 index를 삭제 가능

 

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'city': ['부산', '부산', '부산', '부산', '서울', '서울', '서울'],
    'fruits': ['apple', 'orange', 'banana', 'banana', 'apple', 'apple', 'banana'],
    'price': [100, 200, 250, 300, 150, 200, 400],
    'quantity': [1, 2, 3, 4, 5, 6, 7]
})


def total_series(d):
    return d.price * d.quantity

print(df.groupby(['city', 'fruits']).apply(total_series))

 

결과

city  fruits   
부산    apple   0     100
      banana  2     750
              3    1200
      orange  1     400
서울    apple   4     750
              5    1200
      banana  6    2800
dtype: int64

 

allpy 결과에 row index를 지정하여 DataFrame으로 반환한 경우.

・apply 결과를 연결한 DataFrame을 작성 가능.

・groupby에 사용한 label은 index가 되지 않음.

・as_index=False 사용 불가.

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'city': ['부산', '부산', '부산', '부산', '서울', '서울', '서울'],
    'fruits': ['apple', 'orange', 'banana', 'banana', 'apple', 'apple', 'banana'],
    'price': [100, 200, 250, 300, 150, 200, 400],
    'quantity': [1, 2, 3, 4, 5, 6, 7]
})


def total_keepindex(d):
    return pd.DataFrame({
        'total': d.price * d.quantity # 여기서 반환된 DataFrame row index와 d의 row index는 같음
})

print(df.groupby(['city', 'fruits']).apply(total_keepindex))

 

결과

   total
0    100
1    400
2    750
3   1200
4    750
5   1200
6   2800

 

allpy 결과에 row index를 지정 안하고 DataFrame으로 반환한 경우.

・groupby에 사용한 label이 index가 됨.

・as_index=False 사용 가능.

 

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'city': ['부산', '부산', '부산', '부산', '서울', '서울', '서울'],
    'fruits': ['apple', 'orange', 'banana', 'banana', 'apple', 'apple', 'banana'],
    'price': [100, 200, 250, 300, 150, 200, 400],
    'quantity': [1, 2, 3, 4, 5, 6, 7]
})


def total_keepnoindex(d):
    return pd.DataFrame({
        'total': (d.price * d.quantity).sum()
    }, index=['hoge'])

print(df.groupby(['city', 'fruits']).apply(total_keepnoindex))

 

결과

                  total
city fruits            
부산   apple  hoge    100
     banana hoge   1950
     orange hoge    400
서울   apple  hoge   1950
     banana hoge   2800

 

allpy 함수 사용시 주의점

데이터 레코드 행수가 하나도 없는 DataFrame의 경우 apply 결과에 컬럼이 작성되지 않습니다.

먼저 데이터가 있는 경우를 보겠습니다.

pd.DataFrame({'hoge': [1,1,3], 'fuga': [10, 20, 30]}).groupby('hoge').apply(np.sum)

 

결과

      hoge  fuga
hoge            
1        2    30
3        3    30

 

이번에는 DataFrame에 데이터가 없는 경우를 보겠습니다.

pd.DataFrame({'hoge': [], 'fuga': []}).groupby('hoge').apply(np.sum)

결과

Empty DataFrame
Columns: [hoge, fuga]
Index: []

 

‘fuga’ 컬럼이 안 만들어졌습니다.

apply를 사용하여 DataFrame을 처리를 하는 경우에 데이터가 존재하는지 확인을 먼저 해주는 것이 좋을거 같습니다.

안 그러면 apply 실행 후 값을 취득하려고 할 때 해당 컬럼명이 존재하지 않아 예상치 못한 에러가 발생할 수도 있습니다.

댓글