대한민국 사람들은 어떻게 살아가고 있을까? 데이터를 분석해 낱낱이 파헤쳐보자.
09-1 '한국복지패널 데이터' 분석 준비하기
분석 기술을 익혔으니 실제 데이터 분석해 보겠음. 실습에는 '한국복지패널 데이터'를 이용하겠음.
한국복지패널 데이터는 한국보건사회연구원에서 우리나라 가구의 경제활동을 연구해 복지 정책에 반영할 목적으로 발간하는 조사 자료임. 전국에서 7,000여 가구를 선정해 2006년부터 매년 추적 조사한 자료로, 경제활동, 생활상태, 복지욕구 등 천여 개 변수로 구성되어 있음. 다양한 분야의 연구자와 정책전문자들이 복지패널 데이터를 활용해 논문과 연구보고서를 발표하고 있음.
복지패널 데이터는 엄밀한 절차를 따라 수집되었고 다양한 변수를 담고 있으므로 데이터 분석 기술을 연습하는데 훌륭한 재료임. 데이터에는 다양한 삶의 모습이 담겨 있음. 한국복지패널 데이터를 분석해 대한민국 사람들이 어떻게 살아가고 있는지 알아보겠음.
[Do it! 실습] 데이터 분석 준비하기
데이터를 분석하기 위해 준비 작업을 하겠음
1. 데이터 준비하기
깃허브에서 Koweps_hpwc14_2019_beta4.sav 파일을 다운로드해 워킹 디렉토리에 삽입함. 이 파일은 2020년에 발간한 복지패널 데이터로, 6,331가구, 14,418명의 정보를 담고 있음.
tip) 한국복지패널 데이터 출처: bit.ly/easypy_91
2. 패키지 설치 및 로드하기
실습에 사용할 데이터 파일은 통계 분석 소프트웨어인 SPSS 전용 파일임. pyreadstat 패키지를 설치하면 pandas 패키지 함수를 이용해 SPSS, SAS, STATA 등 다양한 통계 분석 소프트웨어의 데이터 파일을 불러올 수 있음. 아나콘다 프롬프트에서 다음 코드를 실행해 pyreadstat 패키지를 설치함.
pip install pyreadstat
노트북을 열고 데이터를 분석하는데 필요한 패키지를 로드함.
import pandas as pd
import numpy as np
import seaborn as sns
3. 데이터 불러오기
pd.read_spss()를 이용해 복지패널 데이터를 불러옴. 데이터 원본은 복구할 상활을 대비해 그대로 두고 복사본을 만들어 분석에 활용하겠음.
# 데이터 불러오기
raw_welfare = pd.read_spss('Koweps_hpwc14_2019_beta4.sav')
# 복사본 만들기
welfare = raw_welfare.copy()
4. 데이터 검토하기
데이터의 구조와 특징을 파악해 보겠음.
welfare # 앞부분, 뒷부분 출력
welfare.shape # 행, 열 개수 출력
welfare.info() # 변수 속성 출력
welfare.describe() # 요약통계량
# output: (...생략...)
앞 장에서 예제로 사용한 데이터들은 변수가 적고 변수명도 이해할 수 있는 단어로 되어 있어서 데이터 구조를 쉽게 파악할 수 있었음. 반면 복지패널 데이터와 같은 대규모 데이터는 변수의 수가 많고 변수명이 코드로 되어 있어 전체 구조를 한눈에 파악하기 어려움. 규모가 큰 데이터는 데이터 전체를 한눈에 파악하기보다 변수명을 쉬운 단어로 바꾼 다음 분석에 사용할 변수를 하나씩 살펴봐야 함. 변수를 파악하는 작업은 데이터를 본격적으로 분석하는 과정에서 진행하겠음.
5. 변수명 바꾸기
분석에 사용할 변수 몇 개를 이해하기 쉬운 변수면으로 바꾸겠음. 규모가 큰 조사 자료는 데이터의 특징을 설명해 놓은 코드북(codebook)을 함께 제공함. 코드북에는 코드로 된 변수명과 값의 의미가 설명되어 있음. 코드북을 보면 데이터의 특징이 어떠한지 감을 잡을 수 있고, 분석에 어떤 변수를 활용할지, 분석 방향의 아이디어를 얻을 수 있음.
한국복지패널 사이트에서 제공하는 코드북에서 실습에 사용할 변수를 선정해 Koweps_Codebook_2019.xlsx 파일에 정리해 두었음. 파일을 열어 변수의 특징을 미리 파악하자.
tip) 코드북은 한국복지패널 사이트 또는 저자의 깃허브에서 다운로드할 수 있음.
코드북을 참고해 분석에 사용할 변수 7개의 이름을 알아보기 쉬운 단어로 바꾸겠음.
welfare = welfare.rename(
columns = {
'h14_g3' : 'sex', # 성별
'h14_g4' : 'birth', # 태어난 연도
'h14_g10' : 'marriage_type', # 혼인 상태
'h14_g11' : 'religion', # 종교
'p1402_8aq1' : 'income', # 월급
'h14_eco9' : 'code_job', # 직업 코드
'h14_reg7' : 'code_region' # 지역 코드
}
)
데이터 분석 절차 살펴보기
데이터를 분석하는데 필요한 준비를 마쳤음. 이제 앞으로 선정한 변수 7개를 이용해 분석하겠음. 9장은 다양한 분석 주제를 다루는데, 분석마다 두 단계로 진행함.
1단계 변수 검토 및 전처리
분석에 활용할 변수를 전처리함. 변수의 특징을 파악하고 이상치와 결측치를 정제한 다음 변수의 값을 다루기 편하게 바꿈. 전처리는 분석에 활용할 변수 각각 진행함. 예를 들어 '성별에 따른 월급 차이'를 분석한다면 성별, 월급 두 변수를 각각 전처리함.
2단계 변수 간 관계 분석
전처리를 완료하면 본격적으로 변수 간 관계를 파악하는 분석을 함. 데이터를 요약한 표와 데이터의 특징을 쉽게 이해할 수 있는 그래프를 만든 다음 분석 결과를 해석함.
09-2 성별에 따른 월급 차이 – 성별에 따라 월급이 다를까?
여성들이 예전에 비해 활발하게 사회 진출을 하고 있지만 직장에서 받는 대우에는 여전히 차별이 있었음. 데이터를 분석해 성별에 따라 월급 차이가 있는지 알아보겠음. 먼저 성별과 월급 두 변수를 검토하고 전처리한 다음 변수 간의 관계를 분석하겠음.
분석 절차
[Do it! 실습] 성별 변수 검토 및 전처리하기
1. 변수 검토하기
df.dtypes로 sex(성별) 변수의 타입을 파악하고, df.value_counts()로 범주마다 몇 명이 있는지 알아보겠음. 출력 결과를 보면 sex는 float64(실수) 타입이고 1과 2로 구성됨. 1은 6,505명, 2는 7,913명이 있음.
welfare['sex'].dtypes
# output: dtype('float64')
welfare['sex'].value_counts()
tip) df.dtypes는 데이터 프레임의 변수 타입을 담은 어트리뷰트임.
2. 전처리 하기
코드북을 보면 성별 변수의 값이 1이면 남자, 2면 여자를 의미함. 모든다고 답하거나 응답하지 않으면 9로 입력되어 있음. 이 정보를 바탕으로 데이터에 이상치가 있는지 검토하고, 분석할 때 제거하기 편하도록 NaN을 부여해 결측 처리하겠음. 값이 9인 경우도 성별을 알 수 없어 분석에서 제외해야 하므로 결측 처리함.
welfare['sex'].value_counts()
sex에 1과 2만 있고 9나 다른 값이 없음. 이상치가 없으므로 이상치를 결측 처리하는 절차는 건너뛰어도 됨. 만약 이상치가 있다면 다음과 같이 이상치를 결측 처리하는 작업을 먼저 한 다음 결측치를 확인해야 함.
# 이상치 결측 처리
welfare['sex'] = np.where(welfare['sex'] == 9, np.nan, welfare['sex'])
# 결측치 확인
welfare['sex'].isna().sum()
sex 변수의 값은 숫자 1과 2로 되어 있음. 값의 의미를 이해하기 쉽도록 문자 'male'과 'female'로 바꾼 다음 df,value_counts()와 sns.countplot을 이용해 바꾼 값이 잘 반영됐는지 출력 결과를 확인함.
# 성별 항목 이름 부여
welfare['sex'] = np.where(welfare['sex'] == 1, 'male', 'female')
# 빈도 구하기
welfare['sex'].value_counts()
# 빈도 막대 그래프 만들기
sns.countplot(data = welfare, x = 'sex')
성별 변수의 전처리 작업을 완료했음. 이제 같은 절차로 월급 변수를 전처리하겠음.
[Do it! 실습] 월급 변수 검토 및 전처리하기
1. 변수 검토하기
코드북을 보면 월급은 '일한 달의 월 평균 임금'을 의미하며 1만 원 단위로 기록되어 있음. income(월급) 변수를 검토하겠음.
성별은 범주 변수이므로 df.value_counts()를 이용해 범주별 빈도를 확인하면 특징을 파악할 수 있었음. 하지만 월급은 연속 변수이므로 df.value_counts()를 이용하면 너무 많은 항목이 출력되어 알아보기 어려움. 연속 변수는 df.describe()로 요약 통계량을 확인해야 특징을 파악할 수 있음.
welfare['income'].dtypes # 변수 타입 출력
# output: dtype('float64')
welfare['income'].describe() # 요약 통계량 출력
출력 결과를 보면 income은 float64 타입이고, 0~1,892만 원의 값을 지님. 150~345만 원에 가장 많은 분포하고 평균은 268만 원, 중앙값은 220만 원으로 전반적으로 낮은 값으로 치우쳐 있음.
sns.histplot(data = welfare, x = 'income') # 히스토그램 만들기
출력된 그래프를 보면 0~250만 원에 가장 많은 사람이 분포하고, 그 뒤로는 점차 빈도가 감소함.
2. 전처리하기
코드북을 보면 월급은 만 원 단위로 되어 있고, '모름/무응답'은 9999로 코딩되어 있음. 이 정보를 바탕으로 변수를 전처리하겠음.
welfare['income'].describe() # 이상치 확인
welfare['income'].isna().sum() # 결측치 확인
# output: 9884
출력 결과를 보면 최솟값이 0, 최댓값이 1,892이고, 결측치가 9,884개 있음. 직업이 없어서 월급을 받지 않는 응답자가 있으므로 데이터에 결측치가 있는 것임. 따라서 월급 변수를 이용해 분석할 때는 먼저 결측치를 제거해야 함.
코드북에는 '모름/무응답'이면 9999로 코딩했다고 되어 있음. income의 최댓값이 1,892이므로 income이 9999인 행은 없음. 이상치가 없으므로 이상치를 결측 처리하는 절차는 건너뛰어도 됨. 만약 최댓값이 9999로 이상치가 있다면 다음과 같이 이상치를 결측한 다음 결측치가 제대로 만들어졌는지 확인하는 절차를 거쳐야 함.
# 이상치 결측 처리
welfare['income'] = np.where(welfare['income'] == 9999,
np.nan, welfare['income'])
# 결측치 확인
welfare['income'].isna().sum()
[Do it! 실습] 성별에 따른 월급 차이 분석하기
1. 성별 월급 평균표 만들기
두 변수의 전처리 작업을 완료했으니 변수 간 관계를 분석할 차례임. 성별 월급 평균표를 만들어 월급 평균이 성별에 따라 차이가 있는지 비교해 보겠음.
# 성별 월급 평균표 만들기
sex_income = welfare.dropna(subset = ['income']) \
.groupby('sex', as_index = False) \
.agg(mean_income = ('income', 'mean'))
sex_income
출력 결과를 보면 월급 평균이 남자는 349만 원, 여자는 186만 원으로, 남성이 여성보다 163만 원 더 많음.
2. 그래프 만들기
분석 결과를 쉽게 이해할 수 있도록 앞에서 만든 성별 월급 평균표를 이용해 막대 그래프를 만들겠음. 출력된 그래프를 보면 남성의 월급이 여성의 두 배 가까울 정도로 많음.
# 막대 그래프 만들기
sns.barplot(data = sex_income, x = 'sex', y = 'mean_income')
09-3 나이와 월급의 관계 – 몇 살 때 월급을 가장 많이 받을까?
비정규직이 많아지면서 안정된 직장에 취업하는 것도 어려워졌지만, 젊은 세대를 더욱 힘들게 하는 것은 세대 간 소득 격차가 심해서 사회가 불평등하게 느껴진다는 점임. 데이터를 분석해서 나이에 따라 월급이 얼마나 다른지 알아보겠음.
나이 변수를 검토하고 전처리한 다음 나이와 월급의 관계를 분석하겠음. 월급 변수 전처리는 앞에서 완료했으니 생략하겠음.
분석 절차
[Do it! 실습] 나이 변수 검토 및 전처리하기
1. 변수 검토하기
나이와 월급의 관계를 분석하려면 나이를 나타낸 변수가 있어야 함. 그런데 한국복지패널 데이터에는 나이 변수는 없고 태어난 연도 변수만 있음. 따라서 태어난 연도 변수를 이용해 나이 변수를 만들어야 함. 먼저 태어난 연도 변수를 검토한 다음 나이 변수를 만들겠음.
welfare['birth'].dtypes # 변수 타입 출력
# output: dtype('float64')
welfare['birth'].describe() # 요약 통계량 구하기
sns.histplot(data = welfare, x = 'birth') # 히스토그램 만들기
2. 전처리 하기
코드북을 보면 태어난 연도는 '모름/무응답'일 경우 9999로 코딩되어 있음. 이 정보를 바탕으로 전처리 작업을 하겠음.
welfare['birth'].describe() # 이상치 확인
welfare['birth'].isna().sum() # 결측치 확인
# output: 0
출력 결과를 보면 이상치와 결측치가 없으므로 파생변수를 만드는 단계로 넘어가겠음. 만약 이상치가 발견되면 다음과 같이 전처리한 다음 분석을 진행해야 함.
# 이상치 결측 처리
welfare['birth'] = np.where(welfare['birth'] == 9999,
np.nan, welfare['birth'])
# 결측치 확인
welfare['birth'].isna().sum()
3. 파생변수 만들기 - 나이
태어난 연도 변수를 이용해 나이 변수를 만들겠음. 2019년에 조사가 진행됐으니 2019에서 태어난 연도를 뺀 다음 1을 더해 나이를 구하면 됨. 변수를 만들고 df.describe(), sns.histplot()을 이용해 특징을 살펴보겠음.
welfare = welfare.assign(age = 2019 - welfare['birth'] + 1) # 나이 변수 만들기
welfare['age'].describe() # 요약 통계량 구하기
sns.histplot(data = welfare, x = 'age') # 히스토그램 만들기
[Do it! 실습] 나이와 월급의 관계 알아보기
월급 변수 전처리는 09-2절에서 완료했음. 나이 변수와 월급 변수의 전처리 작업을 마쳤으니 이제 나이에 따른 월급을 분석할 차례임.
1. 나이에 따른 월급 평균표 만들기
나이별 월급 평균표를 만들겠음.
# 나이별 월급 평균표 만들기
age_income = welfare.dropna(subset = ['income']) \
.groupby('age', as_index = False) \
.agg(mean_income = ('income', 'mean'))
age_income.head()
2. 그래프 만들기
앞에서 만든 요약표를 이용해 그래프를 만들겠음. x축을 나이, y축을 월급으로 지정하고 나이에 따른 월급의 변화를 나타낸 선 그래프를 만들겠음.
# 선 그래프 만들기
sns.lineplot(data = age_income, x = 'age', y = 'mean_income')
출력된 그래프를 보면 20대 초반에 월급을 150만 원가량 받고 이후 지속해서 증가하는 추세를 보임. 40대에 350만 원가량으로 가장 많이 받고 지속해서 감소하다가 60대 후반부터는 20대보다 낮은 월급을 받음.
09-4 연령대에 따른 월급 차이 – 어떤 연령대의 월급이 가장 많을까?
앞에서는 나이별 월급 평균을 분석했음. 이번에는 나이를 연령대별로 분류한 다음 월급을 비교해 보겠음.
분석 절차
[Do it! 실습] 연령대 변수 검토 및 전처리하기
파생변수 만들기 – 연령대
앞에서 만든 나이 변수를 이용해 연령대 변수를 만들겠음. 표의 기준에 따라 연령대 변수를 만든 다음 각 범주에 몇 명이 있는지 살펴보겠음.
범주 | 기준 |
초년층 | 30세 미만 |
중년층 | 30~59세 |
노년층 | 60세 이상 |
# 나이 변수 살펴보기
welfare['age'].head()
# 연령대 변수 만들기
welfare = welfare.assign(ageg = np.where(welfare['age'] < 30, 'young',
np.where(welfare['age'] <= 59, 'middle',
'old')))
# 빈도 구하기
welfare['ageg'].value_counts()
# 빈도 막대 그래프 만들기
sns.countplot(data = welfare, x = 'ageg')
[Do it! 실습] 연령대에 따른 월급 차이 분석하기
월급 변수 전처리는 09-2절에서 완료했으니 생략하고 연령대에 따른 월급 차이를 분석하겠음.
1. 연령대별 월급 평균표 만들기
연령대별로 월급 평균이 다른지 알아보기 위해 연령대별 월급 평균표를 만들겠음. 출력 결과를 보면 초년층 195만 원, 중년층 329만 원, 노년층 140만 원으로 연령대별로 차이가 있음.
# 연령대별 월급 평균표 만들기
ageg_income = welfare.dropna(subset = ['income']) \
.groupby('ageg', as_index = False) \
.agg(mean_income = ('income', 'mean'))
ageg_income
2. 그래프 만들기
앞에서 만든 요약표를 이용해 그래프를 만들겠음.
# 막대 그래프 만들기
sns.barplot(data = ageg_income, x = 'ageg', y = 'mean_income')
마지막 정렬 순서는 그래프를 만드는데 사용한 데이터 프레임의 행 순서에 따라 만들어짐. 초년, 중년, 노년층 순으로 막대를 정렬하도록 order에 범주 순서를 지정하겠음.
# 막대 정렬하기
sns.barplot(data = ageg_income, x = 'ageg', y = 'mean_income',
order = ['young', 'middle', 'old'])
출력된 표와 그래프를 보면 중년층이 330만 원 정도로 가장 많은 월급을 받음. 노년층의 월급은 140만 원으로, 초년층이 받는 195만 원보다 적음.
09-5 연령대에 및 성별 월급 차이 – 성별 월급 차이는 연령대별로 다를까?
09-2절에서는 성별에 따라 월급 차이가 있는지 분석했음. 그런데 성별 월급 차이는 연령대에 따라 다른 양상을 보일 수 있음. 이번에는 성별 월급 차이가 연령대에 따라 어떻게 다른지 분석해 보겠음. 연령대, 성별, 월급 변수 모두 앞에서 전처리 작업을 완료했으므로 전처리 작업은 생략하고 연령대에 따른 성별 월급 차이를 분석하겠음.
분석 절차
[Do it! 실습] 연령대 및 성별 월급 차이 분석하기
1. 연령대 및 성별 월급 평균표 만들기
성별 월급 차이가 연령대별로 다른지 알기 위해 '연령대 및 성별에 따른 월급 평균표'를 만들겠음.
# 연령대 및 성별 평균표 만들기
sex_income = \
welfare.dropna(subset = ['income']) \
.groupby(['ageg', 'sex'], as_index = False) \
.agg(mean_income = ('income', 'mean'))
sex_income
2. 그래프 만들기
앞에서 만든 요약표를 이용해 그래프를 만들겠음. 막대가 연령대별로 나열되도록 x축에 ageg를 지정하고, 막대 색깔은 성별에 따라 지정되도록 hue에 sex를 지정함. 축 순서는 order를 이용해 연령대 순으로 설정함.
sns.barplot(data = sex_income, x = 'ageg', y = 'mean_income', hue = 'sex',
order = ['young', 'middle', 'old'])
출력된 표와 그래프를 보면 성별 월급 차이의 양상이 연령대별로 다름. 초년에는 월급 차이가 크지 않다가 중년에 크게 벌어져 남성이 179만 원가량 더 많음. 노년에는 차이가 줄어들지만 여전히 남성이 114만 원가량 더 많음.
앞 절에서 연령대별 월급을 분석할 때 노년층이 초년층보다 월급을 적게 받는 것으로 나타났음. 그런데 연령대와 성별로 나눈 이번 분석 결과를 보면 노년층이 초년층보다 월급을 적게 받는 현상은 여성에서만 나타남. 남성은 노년층과 초년층의 월급이 비슷함. 또한 중년층이 초년층보다 월급을 더 많이 받는 현상도 주로 남성에서 나타나고, 여성은 차이가 크지 않음.
[Do it! 실습] 나이 및 성별 월급 차이 분석하기
이번에는 연령대별로 구분하지 않고 '나이 및 성별 월급 평균표'를 만들어 선 그래프로 표현하겠음. 그래프에서 성별에 따라 선 색깔이 다르도록 hue에 sex를 입력하겠음.
# 나이 및 성별 월급 평균표 만들기
sex_age = \
welfare.dropna(subset = ['income']) \
.groupby(['age', 'sex'], as_index = False) \
.agg(mean_income = ('income', 'mean'))
sex_age.head()
# 선 그래프 만들기
sns.lineplot(data = sex_age, x = 'age', y = 'mean_income', hue = 'sex')
09-6 직업별 월급 차이 – 어떤 직업이 월급을 가장 많이 받을까?
어떤 직업이 월급을 가장 많이 받을까? 직업별 월급을 분석해 보겠음. 먼저 직업 변수를 검토하고 전처리하겠음. 월급 변수 전처리는 앞에서 완료했으니 생략하고 직업별 월급 차이를 분석하겠음.
분석 절차
[Do it! 실습] 직업 변수 검토 및 전처리하기
1. 변수 검토하기
먼저 직업을 나타낸 code_job 변수를 살펴보겠음.
welfare['code_job'].dtypes # 변수 타입 출력
# output: dtype('float64')
welfare['code_job'].value_counts() # 빈도 구하기
code_job 변수의 값은 직업 코드를 의미함. 복지패널 데이터에서 직업은 이름이 아니라 직업분류코드로 입력되어 있음. 지금 상태로는 코드가 어떤 직업을 의미하는지 알 수 없으므로 직업분류코드 목록(한국표준직업분류 제 7차 개정)을 보면 코드가 어떤 직업을 의미하는지 알 수 있음.
2. 전처리하기
코드북의 직업분류코드 목록을 이용해 직업 이름을 나타낸 변수를 만들겠음. 먼저 Koweps_Codebook_2019.xlsx 파일의 '직종코드' 시트에 있는 직업분류코드 목록을 불러온 다음 살펴보겠음. 출력 결과를 보면 직업분류코드 목록은 코드와 직업명 두 변수로 구성되고, 직업이 156개로 분류되어 있음.
list_job = pd.read_excel('Koweps_Codebook_2019.xlsx', sheet_name = '직종코드')
list_job.head()
list_job.shape # 행, 열 개수 출력
# output: (156, 2)
df.merge()를 이용해 list_job을 welfare에 결합하겠음. welfare와 list_job에 공통으로 들어 있는 code_job 변수를 기준으로 결합하면 됨.
# welfaer에 list_job 결합하기
welfare = welfare.merge(list_job, how = 'left', on = 'code_job')
welfare의 code_job, job 변수 일부를 출력해 잘 결합했는지 확인하겠음. 출력할 때 직업이 결측치인 행은 제외하겠음.
# code_job 결측치 제거하고 code_job, job 출력
welfare.dropna(subset = ['code_job'])[['code_job', 'job']].head()
출력 결과를 보면 welfare에 직업 이름으로 된 job 변수가 결합된 것을 확인할 수 있음. 이제 이 변수를 이용해 직업별 월급 차이를 분석하겠음.
[Do it! 실습] 직업별 월급 차이 분석하기
월급 변수 전처리는 09-2절에서 완료했으니 생략하고 직업별 월급 차이를 분석하겠음.
1. 직업별 월급 평균표 만들기
직업별 월급 평균표를 만들겠음. 직업이 없거나 월급을 받지 않는 사람은 분석 대상이 아니므로 제외함.
# 직업별 월급 평균표 만들기
job_income = welfare.dropna(subset = ['job', 'income']) \
.groupby('job', as_index = False) \
.agg(mean_income = ('income', 'mean'))
job_income.head()
2. 그래프 만들기
(1) 월급이 많은 직업
어떤 직업이 월급을 많이 받는지 알아보겠음. 앞에서 만든 요약표를 월급 기준으로 내림차순 정렬하고 상위 10개를 추출함.
# 상위 10위 추출
top10 = job_income.sort_values('mean_income', ascending = False).head(10)
top10
앞에서 만든 요약표를 이용해 그래프를 만들겠음. 우선 한글로 된 직업이 그래프에 잘 출력되도록 폰트를 설정하겠음.
# 맑은 고딕 폰트 설정
import matplotlib.pyplot as plt
plt.rcParams.update({'font.family' : 'Malgun Gothic'})
tip) macOS 사용자는 'Malgun Gothic' 대신 'AppleGothic'을 입력하면 됨.
직업 이름이 길기 때문에 x축에 직업 이름을 지정하면 서로 겹쳐 알아볼 수 없음. 직업 이름을 y축, 월급 평균을 x축에 지정해 그래프를 만들겠음.
# 막대 그래프 만들기
sns.barplot(data = top10, x = 'mean_income', y = 'job')
출력된 표와 그래프를 보면 '의료 진료 전문자'의 월급이 평균 781만 원으로 가장 많고, 그 뒤로는 '법률 전문가', '행정 및 경영 지원 관리자', '보험 및 금융 관리자' 순으로 월급이 많음.
(2) 월급이 적은 직업
이번에는 월급이 적은 직업을 알아보겠음. 직업별 월급 평균표를 월급 기준으로 오름차순 정렬하고 상위 10개를 추출함.
# 하위 10위 추출
bottom10 = job_income.sort_values('mean_income').head(10)
bottom10
요약표를 이용해 그래프를 만들겠음. 앞에서 만든 월급 상위 10위 그래프와 비교할 수 있도록 그래프의 x축을 0~800으로 제한하겠음.
# 막대 그래프 만들기
sns.barplot(data = bottom10, x = 'mean_income', y = 'job') \
.set(xlim = (0, 800))
출력된 표와 그래프를 보면 '기타 돌봄·보건 및 개인 생활 서비스 종사자'의 월급이 평균 73만 원으로 가장 적고, 그 뒤로는 '기타 서비스 관련 단순 종사자', '청소원 및 환경미화원', '가사 및 육아 도우미' 순으로 적음.
월급이 가장 많은 직업과 가장 적은 직업을 비교하면 '의료 진료 전문자'는 평균 781만 원, '기타 돌봄·보건 및 개인 생활 서비스 종사자'는 평균 73만 원을 받음. 따라서 '의료 진료 전문자'는 '기타 돌봄·보건 및 개인 생활 서비스 종사자'의 열 배가 넘는 월급을 받음.
09-7 성별 직업 빈도 – 성별로 어떤 직업이 가장 많을까?
성 평등이 상식인 세상이지만 여전히 성별에 따라 다른 직업을 선택하는 경향이 있음. 성별에 따라 어떤 직업이 많은지 분석해 보겠음. 성별, 직업 변수 전처리 작업은 앞에서 완료했으니 생략하고 바로 성별과 직업의 관계를 분석하겠음.
분석 절차
[Do it! 실습] 성별 직업 빈도 분석하기
성별 변수는 09-2절, 직업 변수는 09-6절에서 전처리 작업을 완료했으니 생략하고 성별과 직업의 관계를 분석하겠음.
# 남성 직업 빈도 상위 10위 추출
job_male = welfare.dropna(subset = ['job']) \
.query('sex == "male"') \
.groupby('job', as_index = False) \
.agg(n_job = ('job', 'count')) \
.sort_values('n_job', ascending = False) \
.head(10)
job_male
# 여성 직업 빈도 상위 10위 추출
job_female = welfare.dropna(subset = ['job']) \
.query('sex == "female"') \
.groupby('job', as_index = False) \
.agg(n_job = ('job', 'count')) \
.sort_values('n_job', ascending = False) \
.head(10)
job_female
2. 그래프 만들기
앞에서 만든 직업 빈도표를 이용해 그래프를 만들겠음. 두 그래프를 비교하기 위해 x축의 범위를 0~500으로 통일하겠음.
# 남성 직업 빈도 막대 그래프 만들기
sns.barplot(data = job_male, x = 'n_job', y = 'job').set(xlim = (0, 500))
# 여성 직업 빈도 막대 그래프 만들기
sns.barplot(data = job_female, x = 'n_job', y = 'job').set(xlim = (0, 500))
출력된 표와 그래프를 보면 남녀 모두 '작물 재배 종사자'가 가장 많지만, 그 뒤로는 순위가 다름. 남성은 '자동차 운전원', '경영 관련 사무원', '매장 판매 종사자' 순으로 많은 반면 여성은 '청소원 및 환경미화원', '매장 판매 종사자', '회계 및 경리 사무원' 순으로 많음.
09-8 종교 유무에 따른 이혼율 – 종교가 있으면 이혼을 덜 할까?
종교가 있으면 이혼을 덜 할까? 종교가 있는 사람이 종교가 없는 사람보다 이혼을 덜 하는지 분석해 보겠음. 먼저 종교, 혼인 상태 두 변수를 검토하고 전처리한 다음 종교와 이혼율의 관계를 분석하겠음.
분석 절차
[Do it! 실습] 종교 변수 검토 및 전처리하기
종교 유무를 나타낸 religion을 검토하고 전처리하겠음.
1. 변수 검토하기
welfare['religion'].dtypes # 변수 타입 출력
# output: dtype('float64')
welfare['religion'].value_counts() # 빈도 구하기
2. 전처리하기
앞의 출력 결과를 보면 1과 2 외에 다른 값이 없으므로 이상치를 결측 처리하는 작업은 생략하겠음. 값의 의미를 이해할 수 있도록 코드북의 종교 변수 항목을 참고해 종교 유무를 나타낸 문자를 부여하겠음. 다음 코드의 출력 결과를 보면 종교가 있는 사람이 6,603명, 없는 사람이 7,815명임.
# 종교 유무 이름 부여
welfare['religion'] = np.where(welfare['religion'] == 1, 'yes', 'no')
# 빈도 구하기
welfare['religion'].value_counts()
# 막대 빈도 그래프 만들기
sns.countplot(data = welfare, x = 'religion')
[Do it! 실습] 혼인 상태 변수 검토 및 전처리하기
혼인 상태를 나타낸 marrige_type을 검토하고 전처리하겠음.
1. 변수 검토하기
welfare['marriage_type'].dtypes # 변수 타입 출력
# output: dtype('float64')
welfare['marriage_type'].value_counts() # 빈도 구하기
2. 파생변수 만들기 – 이혼 여부
코드북의 '혼인 상태' 변수 항목을 보면 배우자가 있으면 1, 이혼했으면 3으로 입력되어 있음. 이 값을 이용해 이혼 여부를 나타낸 변수를 만들겠음.
# 이혼 여부 변수 만들기
welfare['marriage'] = np.where(welfare['marriage_type'] == 1, 'marriage',
np.where(welfare['marriage_type'] == 3, 'divorce',
'etc'))
# 이혼 여부별 빈도
n_divorce = welfare.groupby('marriage', as_index = False) \
.agg(n = ('marriage', 'count'))
n_divorce
# 막대 그래프 만들기
sns.barplot(data = n_divorce, x = 'marriage', y = 'n')
출력한 표와 그래프를 보면 결혼 상태인 사람은 7,190명, 이혼한 사람은 689명임. 둘 중 어디에도 속하지 않아 'etc'로 분류된 사람은 6,539명임. 이들은 분석 대상이 아니므로 이후 작업에서 제외하겠음.
[Do it! 실습] 종교 유무에 따른 이혼율 분석하기
앞에서 전처리한 종교 유무 변수와 이혼 여부 변수를 이용해 분석하겠음.
1. 종교 유무에 따른 이혼율 표 만들기
종교 유무에 따른 이혼율 표를 만들겠음. 먼저 marriage가 'etc'인 경우를 제외하고 '종교 유무 및 이혼 여부별 비율'을 구하겠음. value_counts()에 normalize = True를 입력하면 비율을 구함.
rel_div = welfare.query('marriage != "etc"') \
.groupby('religion', as_index = False) \
['marriage'] \
.value_counts(normalize = True)
rel_div
2. 그래프 만들기
이혼에 해당하는 값만 추출한 다음 proportion을 백분율로 바꾸고 소수점 첫째 자리까지 반올림하겠음. round()는 값을 반올림하는 기능을 함. round()에 출력할 자릿수를 입력하여 사용함.
# divorce 추출
# 백분율 바꾸기
# 반올림
rel_div = rel_div.query('marriage == "divorce"') \
.assign(proportion = rel_div['proportion'] * 100) \
.round(1)
rel_div
앞에서 만든 rel_div를 이용해 막대 그래프를 만들겠음.
# 막대 그래프 만들기
sns.barplot(data = rel_div, x = 'religion', y = 'proportion')
출력된 표와 그래프를 보면 이혼율은 종교가 있으면 8.0%, 종교가 없으면 9.5% 임. 따라서 종교가 있는 사람이 이혼을 덜 한다고 볼 수 있음.
[Do it! 실습] 연령대 및 종교 유무에 따른 이혼율 분석하기
앞에서는 전체를 대상으로 종교 유무에 따른 이혼율을 분석했음. 이번에는 연령대별로 나누어 종교 유무에 따른 이혼율 차이가 연령대별로 어떻게 다른지 알아보겠음.
1. 연령대별 이혼율 표 만들기
우선 이혼율이 연령대별로 다른지 알아보겠음. 연령대 및 이혼 여부별 비율을 구하겠음.
age_div = welfare.query('marriage != "etc"') \
.groupby('ageg', as_index = False) \
['marriage'] \
.value_counts(normalize = True)
age_div
출력 결과를 보면 연령대별로 이혼율이 다름. 중년층은 8.9%로 가장 높고 그 뒤로는 노년층 8.5%, 초년층 5% 순으로 높음. 그런데 빈도를 구해보면 초년층은 결혼, 이혼 모두 다른 연령대에 비해 사례가 적음. 초년층은 사례가 부족해 다른 연령대와 비교하기에 적합하지 않으므로 이후 분석 작업에서 제외하겠음.
# 연령대 및 이혼 여부별 빈도
welfare.query('marriage != "etc"') \
.groupby('ageg', as_index = False) \
['marriage'] \
.value_counts()
2. 연령대별 이혼율 그래프 만들기
앞에서 만든 age_div에서 초년층을 제외하고 이혼에 해당하는 값만 추출하겠음. 그런 다음 proportion을 백분율로 바꾸고 소수점 첫째 자리까지 반올림하겠음.
# 초년 제외, 이혼 추출
# 백분율 바꾸기
# 반올림
age_div = age_div.query('ageg != "young" & marriage == "divorce"') \
.assign(proportion = age_div['proportion'] * 100) \
.round(1)
age_div
앞에서 만든 age_div를 이용해 막대 그래프를 만들겠음.
# 막대 그래프 만들기
sns.barplot(data = age_div, x = 'ageg', y = 'proportion')
3. 연령대 및 종교 유무에 따른 이혼율 표 만들기
종교 유무에 따른 이혼율 차이가 연령대별로 다른지 알아보겠음. 먼저 '연령대, 종교 유무, 이혼 여부별 비율'을 구하겠음. 분석에서 초년층은 제외하겠음.
# etc 제외, 초년층 제외
# ageg, religion별 분리
# marriage 추출
# 비율 구하기
age_rel_div = welfare.query('marriage != "etc" & ageg != "young"') \
.groupby(['ageg', 'religion'], as_index = False) \
['marriage'] \
.value_counts(normalize = True)
age_rel_div
4. 연령대 및 종교 유무에 따른 이혼율 그래프 만들기
이혼에 해당하는 값만 추출한 다음 proportion을 백분율로 바꾸고 소수점 첫째 자리까지 반올림하겠음.
# divorce 추출
# 백분율 구하기
# 반올림
age_rel_div = \
age_rel_div.query('marriage == "divorce"') \
.assign(proportion = age_rel_div['proportion'] * 100) \
.round(1)
age_rel_div
앞에서 만든 age_rel_div을 이용해 막대 그래프를 만들겠음. 종교 유무에 따라 막대 색깔을 다르게 표현하기 위해 hue에 religion을 지정하겠음.
# 막대 그래프 만들기
sns.barplot(data = age_rel_div, x = 'ageg', y = 'proportion',
hue = 'religion')
출력된 표와 그래프를 보면 중년과 노년 모두 종교가 없는 사람의 이혼율이 더 높음. 중년은 1.3%, 노년은 1.8% 정도로 종교가 없는 사람의 이혼율이 더 높음.
09-9 지역별 연령대 비율 – 어느 지역에 노년층이 많을까?
고령 사회가 되면서 노인을 위한 시설을 마련하는 일이 점점 더 중요해지고 있음. 이를 준비하려면 어느 지역에 노년층이 많이 살고 있는지 알아야 함. 지역별 연령대 비율을 분석해 어느 지역에 노년층이 많이 사는지 알아보겠음.
먼저 지역 변수를 검토하고 전처리하겠음. 연령대 변수 전처리는 앞에서 완료했으므로 건너뛰고 지역과 연령대의 관계를 분석하겠음.
분석 절차
[Do it! 실습] 지역 변수 검토 및 전처리하기
지역을 나타낸 code_region을 검토하고 전처리하겠음.
1. 변수 검토하기
welfare['code_region'].dtypes # 변수 타입 출력
# output: dtype('float64')
welfare['code_region'].value_counts() # 빈도 구하기
2. 전처리하기
code_region 변수의 값은 7개 권역을 의미하는 지역 코드임. 먼저 코드북을 참고해 지역 코드 목록을 만들겠음. 그런 다음 코드 목록과 welfare에 동시에 들어있는 code_region 변수를 이용해 welfare에 지역명을 나타낸 변수를 추가하겠음.
# 지역 코드 목록 만들기
list_region = pd.DataFrame({'code_region' : [1, 2, 3, 4, 5, 6, 7],
'region' : ['서울',
'수도권(인천/경기)',
'부산/경남/울산',
'대구/경북',
'대전/충남',
'강원/충북',
'광주/전남/전북/제주도']})
list_region
# 지역별 변수 추가
welfare = welfare.merge(list_region, how = 'left', on = 'code_region')
welfare[['code_region', 'region']].head()
[Do it! 실습] 지역별 연령대 비율 구하기
연령대 변수 전처리는 앞에서 완료했으므로 생략하고 지역과 연령대의 관계를 알아보겠음.
1. 지역별 연령대 비율표 만들기
먼저 '지역 및 연령대별 비율표'를 만들겠음.
region_ageg = welfare.groupby('region', as_index = False) \
['ageg'] \
.value_counts(normalize = True)
region_ageg.head()
2. 그래프 만들기
region_ageg의 proportion을 백분율로 바꾸고 소수점 첫째 자리까지 반올림하겠음.
# 백분율로 바꾸기
# 반올림
region_ageg = \
region_ageg.assign(proportion = region_ageg['proportion'] * 100)\
.round(1) \
region_ageg.head()
앞에서 만든 region_ageg를 이용해 지역별 연령대 비율을 나타낸 그래프를 만들겠음. 지역명이 겹치지 않도록 y축에 region, x축에 proportion을 지정해 가로 막대 그래프를 만들겠음. 연령대별로 막대 색깔을 다르게 표현하도록 hue에 ageg를 지정하겠음.
# 막대 그래프 만들기
sns.barplot(data = region_ageg, x = 'proportion', y = 'region', hue = 'ageg')
3. 누적 비율 막대 그래프 만들기
앞에서 만든 그래프는 각 지역의 연령대별 비율이 서로 다른 막대 그래프로 표현되어 있어서 지역끼리 비교하기 어려움. 지역끼리 비교하기 쉽도록 연령대별 막대를 누적한 '누적 비율 막대 그래프'를 만들겠음.
(1) 피벗하기
행과 열을 회전해 표의 구성을 바꾸는 작업을 피벗(pivot)이라고 함. 누적 막대 그래프를 만드는 데 적합하도록 데이터 프레임의 행과 열을 회전해 구성을 바꾸겠음.
먼저 그래프를 만드는 데 사용할 지역, 연령대, 비율 변수만 추출한 다음 df.pivot()을 이용해 데이터 프레임을 피벗함.
- 지역을 기준으로 회전하도록 index = 'region'을 입력함.
- 연령대별로 열을 구성하도록 columns = 'ageg'를 입력함.
- 각 항목 값을 비율로 채우도록 values = 'proportion'을 입력함.
다음 코드의 출력 결과를 보면 행은 지역, 열은 연령대로 구성하여 지역 및 연령대별 비율을 나타내고 있음.
# 피벗
pivot_df = \
region_ageg[['region', 'ageg', 'proportion']].pivot(index = 'region',
columns = 'ageg',
values = 'proportion')
pivot_df.head()
(2) 그래프 만들기
누적 비율 막대 그래프를 만들겠음. 가로 막대 그래프를 만들도록 df.plot.barh()를 이용하고, 막대를 누적하도록 stacked = True을 입력함.
# 가로 막대 그래프 만들기
pivot_df.plot.barh(stacked = True)
(3) 막대 정렬하기
막대 순서를 정렬하기 위해 pivot_df의 행과 열 순서를 바꾸겠음.
- 노년층 비율이 놓은 순으로 막대 정렬하기: 앞에서 만든 그래프의 막대는 밑에서부터 지역명 가나다순으로 정렬되어 있음. 막대가 노년층 비율이 높은 순으로 정렬되도록 pivot_df의 행을 old 기준으로 정렬하겠음.
- 연령대 순으로 막대 색깔 나열하기: 앞에서 만든 그래프는 막대 색깔이 middle(중년), old(노년), young(초년) 순으로 나열되어 있음. 막대 색깔이 초년, 중년, 노년 순으로 나열되도록 pivot_df의 변수 순서를 바꾸겠음.
# 노년층 비율 기준 정렬, 변수 순서 바꾸기
reorder_df = pivot_df.sort_values('old')[['young', 'middle', 'old']]
reorder_df
이제 그래프를 만드는 코드를 실행하면 막대가 노년층 비율이 높은 순으로 정렬되고, 막대 색깔도 연령대 순으로 나열되어 지역끼리 쉽게 비교할 수 있음.
# 누적 가로 막대 그래프 만들기
reorder_df.plot.barh(stacked = True)
출력된 그래프를 보면 '대구/경북'의 노년층 비율이 가장 높고, 그 뒤로는 '강원/충북', '광주/전남/전북/제주도', '부산/경남/울산' 순으로 높음.
{알아 두면 좋아요} 자신만의 데이터 분석 프로젝트를 수행해 보자!
이제 코드를 따라하는 단계를 넘어 자신만의 데이터 분석 프로젝트를 진행할 차례임. '한국복지패널 데이터'에는 앞에서 다룬 변수 외에도 싱체 건강, 정신 건강, 가족 간의 관계, 주거 환경, 교육 수준 등 천여개 변수가 있음. 코드북으이 변수 설명을 보면서 흥미로운 주제를 찾아 자신만의 데이터 분석 프로젝트를 수행해 보자.
tip) 코드북(조사설계서)은 한국복지패널 사이트 또는 저자의 깃허브에서 다운로드할 수 있음.
- 9장 END -
'데이터 분석(Data Analysis) > 쉽게 배우는 파이썬 데이터 분석' 카테고리의 다른 글
[넷째마당_다양한 데이터 분석의 세계] 11 지도 시각화 (0) | 2023.12.31 |
---|---|
[넷째마당_다양한 데이터 분석의 세계] 10 텍스트 마이닝 (0) | 2023.12.30 |
[둘째마당_본격실습! 데이터 갖고 놀기] 08 그래프 만들기 (0) | 2023.12.25 |
[둘째마당_본격실습! 데이터 갖고 놀기] 07 데이터 정제 - 빠진 데이터, 이상한 데이터 제거하기 (0) | 2023.12.23 |
[둘째마당_본격실습! 데이터 갖고 놀기] 06 자유자재로 데이터 가공하기 (0) | 2023.12.18 |