호쌤
호쌤 Just For Fun

[Python 데이터 분석] 그룹분석

크리에이티브 커먼즈 라이선스 ITPAPER(호쌤,쭈쌤)에 의해 작성된 ≪[Python 데이터 분석] 그룹분석≫은(는) 크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.
이 라이선스의 범위 이외의 이용허락을 얻기 위해서는 leekh4232@gmail.com으로 문의하십시오.

[Python 데이터 분석] 그룹분석

그룹 분석이란 “지역별 인구수, 학년별 평균점수, 연령별 평균소득”과 같이 데이터를 집단별로 나누어 그룹함수를 적용한 분석 방법입니다.

#01. 필요한 기본 패키지와 샘플 데이터 준비

1) 패키지 가져오기

1
2
3
import numpy                   # 수치연산 패키지
from pandas import DataFrame   # 데이터프레임 클래스
from pandas import read_csv    # csv 로드 함수

2) 샘플 데이터 가져오기

1
2
성적표csv = read_csv("http://itpaper.co.kr/demo/py/grade.csv", encoding="euc-kr")
성적표csv
▶ 출력결과
이름 학년 성별 국어 영어 수학 과학
0 철수 1 남자 98 NaN 88.0 64.0
1 영희 2 여자 88 90.0 62.0 72.0
2 민수 1 남자 92 70.0 NaN NaN
3 수현 3 여자 63 60.0 31.0 70.0
4 호영 4 남자 120 50.0 NaN 88.0

3) 성적표 데이터 전처리

이름 컬럼을 인덱스로 지정하고 원래의 이름 컬럼은 삭제한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
현재인덱스 = list(성적표csv.index)
이름 = list(성적표csv['이름'])

indexDict = {}
for i, v in enumerate(이름):
    before = 현재인덱스[i];
    indexDict[before] = v

성적표 = 성적표csv.rename(index=indexDict)

# 이름컬럼 삭제 (axis=1, 열단위 / inplace=True, 원본에 반영)
성적표.drop('이름', axis=1, inplace=True)
성적표
▶ 출력결과
학년 성별 국어 영어 수학 과학
철수 1 남자 98 NaN 88.0 64.0
영희 2 여자 88 90.0 62.0 72.0
민수 1 남자 92 70.0 NaN NaN
수현 3 여자 63 60.0 31.0 70.0
호영 4 남자 120 50.0 NaN 88.0

#02. 집계함수 사용

데이터프레임에서 집계함수를 사용하면 열단위(세로방향)에 대해 수행된다.

사용 가능한 집계 함수의 종류

  • 그룹별 데이터의 갯수: size, count
  • 평균, 중앙값, 최소, 최대: mean, median, min, max
  • 합계, 곱, 표준편차, 분산, 사분위수: sum, prod, std, var, quantile
  • 그룹 안에서 첫번째, 마지막 데이터: first, last

1) 전체 열에 대한 집계

총점

데이터타입이 문자열인 컬럼에 대해서는 모든 데이터를 하나의 문자열로 병합한 값이 표시된다.

1
2
# 과목별 총점
성적표.sum()
▶ 출력결과
1
2
3
4
5
6
7
학년            11
성별    남자여자남자여자남자
국어           461
영어           270
수학           181
과학           294
dtype: object

평균

숫자형태가 아니기 때문에 값을 계산할 수 없는 컬럼은 자동으로 제외된다.

1
2
# 과목별 평균
성적표.mean()
▶ 출력결과
1
2
3
4
5
6
학년     2.200000
국어    92.200000
영어    67.500000
수학    60.333333
과학    73.500000
dtype: float64

2) 특정 열에 대한 집계

1
2
# 국어 과목에서 최고점수
성적표['국어'].max()
▶ 출력결과
1
120

3) 행 단위 집계

집계함수에 axis=1 파라미터를 지정한다.

집계함수에서의 axis의미

  • axis=0 : y축, 열단위, 기본값
  • axis=1 : x축, 행단위, 필요한 경우 명시

drop() 함수와는 반대 개념으로 동작하므로 유의해야 한다.

a) 합계

문자열 데이터는 제외된다.

여기서는 학년 데이터까지 합계에 포함되므로 정확한 값이라고 볼 수는 없다.

1
2
# 학생별 총점
성적표.sum(axis=1)
▶ 출력결과
1
2
3
4
5
6
철수    251.0
영희    314.0
민수    163.0
수현    227.0
호영    262.0
dtype: float64

b) 평균

문자열 데이터는 제외된다.

여기서는 학년 데이터까지 평균에 포함되므로 정확한 값이라고 볼 수는 없다.

1
2
# 학생별 평균
성적표.mean(axis=1)
▶ 출력결과
1
2
3
4
5
6
철수    62.750000
영희    62.800000
민수    54.333333
수현    45.400000
호영    65.500000
dtype: float64

4) 특정 행에 대한 집계

특정 행의 집계는 성별과 같이 숫자가 아닌 형태가 포함되어 있을 경우 에러가 발생한다.

a) 평균을 산출하기 위한 컬럼만 추출

1
2
성적df = 성적표.filter(['국어','영어','수학','과학'])
성적df
▶ 출력결과
국어 영어 수학 과학
철수 98 NaN 88.0 64.0
영희 88 90.0 62.0 72.0
민수 92 70.0 NaN NaN
수현 63 60.0 31.0 70.0
호영 120 50.0 NaN 88.0

b) 영희에 대한 행만 추출하여 평균 집계

1
2
영희df = 성적df.loc['영희'].mean()
영희df
▶ 출력결과
1
78.0

5) 행단위 집계 결과를 새로운 열로 추가하기

axis 파라미터의 값이 drop() 함수와 반대로 적용되므로 주의!!!

a) 평균을 산출하기 위한 컬럼만 추출

1
2
성적df = 성적표.filter(['국어','영어','수학','과학'])
성적df
▶ 출력결과
국어 영어 수학 과학
철수 98 NaN 88.0 64.0
영희 88 90.0 62.0 72.0
민수 92 70.0 NaN NaN
수현 63 60.0 31.0 70.0
호영 120 50.0 NaN 88.0

b) 학생별 총점과 평균 산출

1
2
총점 = 성적df.sum(axis=1)
총점
▶ 출력결과
1
2
3
4
5
6
철수    250.0
영희    312.0
민수    162.0
수현    224.0
호영    258.0
dtype: float64
1
2
평균 = 성적df.mean(axis=1)
평균
▶ 출력결과
1
2
3
4
5
6
철수    83.333333
영희    78.000000
민수    81.000000
수현    56.000000
호영    86.000000
dtype: float64

c) 데이터프레임의 복사본을 생성한 후 산출한 집계 결과를 새로운 열로 추가

1
2
3
4
성적표cp = 성적표.copy()
성적표cp['총점'] = 총점
성적표cp['평균'] = 평균
성적표cp
▶ 출력결과
학년 성별 국어 영어 수학 과학 총점 평균
철수 1 남자 98 NaN 88.0 64.0 250.0 83.333333
영희 2 여자 88 90.0 62.0 72.0 312.0 78.000000
민수 1 남자 92 70.0 NaN NaN 162.0 81.000000
수현 3 여자 63 60.0 31.0 70.0 224.0 56.000000
호영 4 남자 120 50.0 NaN 88.0 258.0 86.000000

#03. 집단별로 나누기

  • 동일한 값을 갖는 데이터들끼리 그룹으로 묶고, 그 이외의 다른 데이터들에게 집계를 수행하는 형태.
  • SQL의 group by절과 같은 기능.

1) 샘플 데이터 만들기

1
2
인구조사 = read_csv("http://itpaper.co.kr/demo/py/city_people.csv", encoding="euc-kr")
인구조사
▶ 출력결과
도시 연도 인구 지역
0 서울 2015 9904312 수도권
1 서울 2010 9631482 수도권
2 서울 2005 9762546 수도권
3 부산 2015 3448737 경상권
4 부산 2010 3393191 경상권
5 부산 2005 3512547 경상권
6 인천 2015 2890451 수도권
7 인천 2010 2632035 수도권

2) 하나의 컬럼을 집단별로 나누고 그룹분석 수행하기

15년간의 도시별 최대 인구수

분석을 위해 그룹으로 묶을 컬럼(하나 이상)과 집계를 수행할 컬럼을 추출

1
2
도시별인구 = 인구조사.filter(['도시', '인구'])
도시별인구
▶ 출력결과
도시 인구
0 서울 9904312
1 서울 9631482
2 서울 9762546
3 부산 3448737
4 부산 3393191
5 부산 3512547
6 인천 2890451
7 인천 2632035

특정 컬럼을 그룹으로 묶고 다른 컬럼으로 집계 수행

그룹으로 묶인 컬럼은 인덱스로 구성된다.

1
2
도시별_최대_인구수 = 도시별인구.groupby('도시').max()
도시별_최대_인구수
▶ 출력결과
인구
도시
부산 3512547
서울 9904312
인천 2890451

3) 두 개 이상의 컬럼을 집단별로 나누고 그룹분석 수행하기

각 지역별로 5년단위 최대 인구수

기준이 될 컬럼 2건과 집계를 수행할 컬럼 추출

1
2
지역_연도_인구 = 인구조사.filter(['지역', '연도', '인구'])
지역_연도_인구
▶ 출력결과
지역 연도 인구
0 수도권 2015 9904312
1 수도권 2010 9631482
2 수도권 2005 9762546
3 경상권 2015 3448737
4 경상권 2010 3393191
5 경상권 2005 3512547
6 수도권 2015 2890451
7 수도권 2010 2632035

두 개 이상의 컬럼을 그룹으로 묶은 후 집계 수행

그룹으로 묶인 결과가 인덱스로 구성된다.

1
2
지역_연도별_최대인구 = 지역_연도_인구.groupby([ '지역', '연도' ]).max()
지역_연도별_최대인구
▶ 출력결과
인구
지역 연도
경상권 2005 3512547
2010 3393191
2015 3448737
수도권 2005 9762546
2010 9631482
2015 9904312

인덱스를 구성하지 않고 그룹으로 묶기

1
지역_연도_인구.groupby([ '지역', '연도' ], as_index=False).max()
▶ 출력결과
지역 연도 인구
0 경상권 2005 3512547
1 경상권 2010 3393191
2 경상권 2015 3448737
3 수도권 2005 9762546
4 수도권 2010 9631482
5 수도권 2015 9904312

4) 하나의 컬럼에 대해 여러 개의 집계함수 동시 사용

agg()함수에 집계 함수의 이름을 문자열 원소로 갖는 리스트로 설정한다.

1
2
도시인구 = 인구조사.filter(['도시', '인구'])
도시인구.groupby('도시').agg(['min','max','std','sum'])
▶ 출력결과
인구
min max std sum
도시
부산 3393191 3512547 59725.663038 10354475
서울 9631482 9904312 136449.978473 29298340
인천 2632035 2890451 182727.705967 5522486

5) 여러 컬럼에 대해 서로 다른 집계함수 적용하기

딕셔너리 형식으로 {컬럼명: [함수리스트], 컬럼명: [함수리스트]} 형태로 지정한다.

1
2
지역도시인구 = 인구조사.filter(['지역', '도시', '인구'])
지역도시인구
▶ 출력결과
지역 도시 인구
0 수도권 서울 9904312
1 수도권 서울 9631482
2 수도권 서울 9762546
3 경상권 부산 3448737
4 경상권 부산 3393191
5 경상권 부산 3512547
6 수도권 인천 2890451
7 수도권 인천 2632035
1
지역도시인구.groupby('지역').agg({'도시': ['count'], '인구': ['sum', 'max']})
▶ 출력결과
도시 인구
count sum max
지역
경상권 3 10354475 3512547
수도권 5 34820826 9904312

6) 사용자 정의 함수를 적용하기

1
2
3
4
5
6
def my_range(x):
    # 파라미터로 전달되는 객체는 특정 컬럼에 대한 Series 객체
    return numpy.max(x) - numpy.min(x)

# 사용자 정의 함수의 이름은 문자열로 지정하지 않는다.
도시인구.groupby('도시').agg(['min','max','std','sum', my_range])
▶ 출력결과
인구
min max std sum my_range
도시
부산 3393191 3512547 59725.663038 10354475 119356
서울 9631482 9904312 136449.978473 29298340 272830
인천 2632035 2890451 182727.705967 5522486 258416

#04. 피벗테이블

데이터 열 중 두 개의 열을 각각 행, 열로 사용하여 데이터를 재배치 한 것.

1) 각 도시별 연도에 따른 인구수

  • 첫 번째 파라미터로 지정한 컬럼은 인덱스로 사용됨
  • 두 번째 파라미터로 지정한 컬럼은 컬럼으로 사용됨
  • 세 번째 파라미터로 지정한 컬럼은 데이터로 사용됨
1
2
pv1 = 인구조사.pivot('도시', '연도', '인구')
pv1
▶ 출력결과
연도 2005 2010 2015
도시
부산 3512547.0 3393191.0 3448737.0
서울 9762546.0 9631482.0 9904312.0
인천 NaN 2632035.0 2890451.0

2) 그룹분석 결과를 피벗테이블로 조합하기

각 지역의 연도별 평균 인구수를 피벗테이블로 생성한다.

집계 대상을 추출

1
2
filter_df = 인구조사.filter(['지역', '연도', '인구'])
filter_df
▶ 출력결과
지역 연도 인구
0 수도권 2015 9904312
1 수도권 2010 9631482
2 수도권 2005 9762546
3 경상권 2015 3448737
4 경상권 2010 3393191
5 경상권 2005 3512547
6 수도권 2015 2890451
7 수도권 2010 2632035

그룹분석

as_index=False 파라미터를 사용하여 기준열이 인덱스로 지정되는 것을 방지한다.

1
2
group_df = filter_df.groupby(['지역', '연도'], as_index=False).sum()
group_df
▶ 출력결과
지역 연도 인구
0 경상권 2005 3512547
1 경상권 2010 3393191
2 경상권 2015 3448737
3 수도권 2005 9762546
4 수도권 2010 12263517
5 수도권 2015 12794763

피벗테이블 구성

1
2
pivot_df = group_df.pivot('지역', '연도', '인구')
pivot_df
▶ 출력결과
연도 2005 2010 2015
지역
경상권 3512547 3393191 3448737
수도권 9762546 12263517 12794763

3) 피벗테이블 생성 제약

컬럼과 인덱스이름으로 사용되는 데이터의 쌍이 중복되는 경우가 있다면 에러.

지역과 연도에 대한 조합은 두 쌍이 나타므로 에러.

1
인구조사.pivot('지역', '연도', '인구')
▶ 출력결과
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-29-be917af902e1> in <module>
----> 1 인구조사.pivot('지역', '연도', '인구')

... 생략 ...

c:\python36\lib\site-packages\pandas\core\reshape\reshape.py in _make_selectors(self)
    177 
    178         if mask.sum() < len(self.index):
--> 179             raise ValueError("Index contains duplicate entries, cannot reshape")
    180 
    181         self.group_index = comp_index


ValueError: Index contains duplicate entries, cannot reshape
Rating:

크리에이티브 커먼즈 라이선스 ITPAPER(호쌤,쭈쌤)에 의해 작성된 ≪[Python 데이터 분석] 그룹분석≫은(는) 크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.
이 라이선스의 범위 이외의 이용허락을 얻기 위해서는 leekh4232@gmail.com으로 문의하십시오.

comments powered by Disqus