본문 바로가기

Programming/Data mining

7. Pandas 심화 (groupby)

Coursera.org 에서 Michigan University의 Applied Data Science with Python의 강의를 토대로 정리한 내용입니다.

이번 글에서도 역시 pandas의 여러가지 자주쓰는 기능에 대해서 알아볼텐데요. 만약 어떤 데이터프레임에서 여러 데이터들이 어떤 그룹에 속하게 나누어질 수 있다면 우리는 이것을 어떻게 다룰까요?

%%timeit -n 10
for state in df['STNAME'].unique():
    avg = np.average(df.where(df['STNAME']==state).dropna()['CENSUS2010POP'])
    print('Counties in state ' + state + ' have an average population of ' + str(avg))

이렇게 'STNAME' column에서 중복되는 데이터들을 하나의 리스트로 저장한 다음, for문을 통해서 해당 그룹의 평균을 구할 수 있겠죠?
하지만 생각보다 이러한 프로그래밍은 엄청나게 많은 러닝타임을 소모한답니다.
하지만 pandas에서는 이러한 러닝타임을 몇배나 빠르게 줄여줄 수 있는 여러가지 기능을 가지고 있는데요. 
예를 들면 데이터프레임에서 일일이 요소값에 for문을 통해 +2를 시켜주는 것보다 df += 2라는 식은 그 러닝타임을 훨씬 줄여준답니다.
그리고 이렇게 그룹화를 할 때 사용하는 함수가 바로 groupby()입니다.

%%timeit -n 10
for group, frame in df.groupby('STNAME'):
    avg = np.average(frame['CENSUS2010POP'])
    print('Counties in state ' + group + ' have an average population of ' + str(avg))

이렇게 돌려보니 위에 코드에서는 루프당 2.43s가 걸렸는데, 이번에는 루프당 74ms밖에 안 걸리는 걸 볼 수 있네요.

df = df.set_index('STNAME')

def fun(item):
    if item[0]<'M':
        return 0
    if item[0]<'Q':
        return 1
    return 2

for group, frame in df.groupby(fun):
    print(frame)

이렇게 함수를 설정하여 함수를 기준으로 그룹핑도 가능하답니다.
위의 코드에서는 첫 시작글자를 기준으로 세 그룹으로 나누었다는 걸 알 수 있겠네요.
group은 0,1,2 가 출력이 되고 frame 은 데이터프레임 형태로 출력이 되겠네요.

groupby로 정렬된 데이터프레임은 agg()함수를 지원합니다.

df.groupby('STNAME').agg({'CENSUS2010POP': np.average})
>>>
						CENSUS2010POP
STNAME	
Alabama					71339.343284
Alaska					24490.724138
Arizona					426134.466667
Arkansas				38878.906667
California				642309.586207
Colorado				78581.187500
Connecticut				446762.125000
Delaware				299311.333333
District of Columbia	601723.000000

#이렇게 그룹별 해당 열에 대한 평균을 series형태로 받을 수도 있고,

(df.set_index('STNAME').groupby(level=0)['CENSUS2010POP']
    .agg({'avg': np.average, 'sum': np.sum}))

#위와 같이 이전시간에 배웠던 pandorable한 code를 통해서 그룹핑한 후 agg function으로 
#각 열의 인덱스가 avg와 sum인 dataframe 으로 받을 수도 있네요.
#여기서 level의 의미는 첫벗째 인덱스를 기준으로 그룹핑하느냐 두번쨰를 기준으로 그룹핑하느냐가 됩니다.
#바로 뒤에 ['CENSUS2010POP']을 기준으로 그룹핑하는 것이 아닌 해당 열에 대해 뒤에 agg함수가 적용되는 거에요!

(df.set_index('STNAME').groupby(level=0)['POPESTIMATE2010','POPESTIMATE2011']
    .agg({'avg': np.average, 'sum': np.sum}))
#이렇게 여러 열에 대한 정보 역시 저장이 가능합니다.

(df.set_index('STNAME').groupby(level=0)['POPESTIMATE2010','POPESTIMATE2011']
    .agg({'POPESTIMATE2010': np.average, 'POPESTIMATE2011': np.sum}))
#그렇다면 이 코드위 위의 코드의 차이는 뭘까요? 눈치채셨겠지만 column의 index를 넣음으로써
#2010은 avg만 2011은 sum만 출력하게 된답니다.