티스토리 뷰
5장 - 우리나라 인구 소멸 위기 지역 분석¶
이번에는 우리나라의 인구 소멸 위기 지역에 대해 조사하겠다. 인구 소멸 위기 지역을 시각화 해서 위기감을 일으키는 것이 목적이다. 그러기 위해서는 이전에 했던 서울시 지도가 아니라 대한민국 지도가 그려져야 한다.
5-1 목표 명확히 하기¶
‘한국의 지방소멸에 관한 7가지 분석’보고서를 쓴 이상호 한국고용정보원 연구원의 분석 방법을 이용하여 65세 이상 노인 인구와 20∼39세 여성 인구를 비교해 젊은 여성 인구가 노인 인구의 절반에 미달할 경우 ‘소멸 위험 지역’으로 분류하는 방식이다.
5-2 인구 데이터 확보하고 정리하기¶
데이터 확보를 위해 국가 통계포털에 접속하자. 포털에 받기에는 너무 과정이 복잡하고 시간을 단축하기 위해 (데이터 출처: https://github.com/PinkWink/DataScience) 이곳에서 05. population_raw_data.xlsx를 다운받아 data 폴더에 복사하자.
import pandas as pd
import numpy as np
import platform
import matplotlib.pyplot as plt
from matplotlib import font_manager, rc
plt.rcParams['axes.unicode_minus'] = False
if platform.system() == 'Darwin':
rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
path = "c:/Windows/Fonts/malgun.ttf"
font_name = font_manager.FontProperties(fname=path).get_name()
rc('font', family=font_name)
elif platform.system() == 'Linux':
path = "/usr/share/fonts/NanumGothic.ttf"
font_name = font_manager.FontProperties(fname=path).get_name()
plt.rc('font', family=font_name)
else:
print('Unknown system... sorry~~~~')
이제 각 장이 진행 될수록 앞에 코드가 익숙해 질 것이다.
population = pd.read_excel('/home/jaeyoon89/DataScience/data/05. population_raw_data.xlsx',engine='openpyxl', header=1)
population.fillna(method='pad', inplace=True)
population.rename(columns = {'행정구역(동읍면)별(1)':'광역시도',
'행정구역(동읍면)별(2)':'시도',
'계':'인구수'}, inplace=True)
population = population[(population['시도'] != '소계')]
population
광역시도 | 시도 | 항목 | 인구수 | 20 - 24세 | 25 - 29세 | 30 - 34세 | 35 - 39세 | 65 - 69세 | 70 - 74세 | 75 - 79세 | 80 - 84세 | 85 - 89세 | 90 - 94세 | 95 - 99세 | 100+ | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
6 | 서울특별시 | 종로구 | 총인구수 (명) | 152737.0 | 11379.0 | 11891.0 | 10684 | 10379.0 | 7411.0 | 6636.0 | 5263 | 3104.0 | 1480.0 | 602.0 | 234 | 220.0 |
7 | 서울특별시 | 종로구 | 남자인구수 (명) | 75201.0 | 5620.0 | 6181.0 | 5387 | 5034.0 | 3411.0 | 3009.0 | 2311 | 1289.0 | 506.0 | 207.0 | 89 | 73.0 |
8 | 서울특별시 | 종로구 | 여자인구수 (명) | 77536.0 | 5759.0 | 5710.0 | 5297 | 5345.0 | 4000.0 | 3627.0 | 2952 | 1815.0 | 974.0 | 395.0 | 145 | 147.0 |
9 | 서울특별시 | 중구 | 총인구수 (명) | 125249.0 | 8216.0 | 9529.0 | 10332 | 10107.0 | 6399.0 | 5313.0 | 4127 | 2502.0 | 1260.0 | 469.0 | 158 | 160.0 |
10 | 서울특별시 | 중구 | 남자인구수 (명) | 62204.0 | 4142.0 | 4792.0 | 5192 | 5221.0 | 3113.0 | 2405.0 | 1752 | 929.0 | 414.0 | 132.0 | 56 | 51.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
841 | 제주특별자치도 | 제주시 | 남자인구수 (명) | 235977.0 | 17377.0 | 13118.0 | 15084 | 18350.0 | 8474.0 | 6782.0 | 4941 | 2737.0 | 854.0 | 226.0 | 53 | 17.0 |
842 | 제주특별자치도 | 제주시 | 여자인구수 (명) | 234688.0 | 15261.0 | 12245.0 | 14687 | 18062.0 | 9265.0 | 7877.0 | 7178 | 5649.0 | 3122.0 | 1387.0 | 460 | 137.0 |
843 | 제주특별자치도 | 서귀포시 | 총인구수 (명) | 170932.0 | 10505.0 | 8067.0 | 9120 | 11606.0 | 8686.0 | 7460.0 | 6456 | 4521.0 | 1855.0 | 733.0 | 242 | 77.0 |
844 | 제주특별자치도 | 서귀포시 | 남자인구수 (명) | 86568.0 | 5600.0 | 4247.0 | 4693 | 6082.0 | 4237.0 | 3441.0 | 2611 | 1494.0 | 370.0 | 103.0 | 29 | 9.0 |
845 | 제주특별자치도 | 서귀포시 | 여자인구수 (명) | 84364.0 | 4905.0 | 3820.0 | 4427 | 5524.0 | 4449.0 | 4019.0 | 3845 | 3027.0 | 1485.0 | 630.0 | 213 | 68.0 |
792 rows × 16 columns
엑셀 파일이 좀 복잡하기 때문에 설정을 해야 할 것이 몇개 있다. 먼저 두번째 줄부터 읽어야하고, 빈 셀에 대해 NaN 처리를 하지 않고 그 앞 내용으로 채우도록 한다. 그리고 적절히 컬럼의 이름을 바꾸고 중간중간에 있는 '소계'라는 항목도 삭제했다.
population.is_copy =False
population.rename(columns = {'항목':'구분'}, inplace=True)
population.loc[population['구분'] == '총인구수 (명)', '구분'] = '합계'
population.loc[population['구분'] == '남자인구수 (명)', '구분'] = '남자'
population.loc[population['구분'] == '여자인구수 (명)', '구분'] = '여자'
population
광역시도 | 시도 | 구분 | 인구수 | 20 - 24세 | 25 - 29세 | 30 - 34세 | 35 - 39세 | 65 - 69세 | 70 - 74세 | 75 - 79세 | 80 - 84세 | 85 - 89세 | 90 - 94세 | 95 - 99세 | 100+ | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
6 | 서울특별시 | 종로구 | 합계 | 152737.0 | 11379.0 | 11891.0 | 10684 | 10379.0 | 7411.0 | 6636.0 | 5263 | 3104.0 | 1480.0 | 602.0 | 234 | 220.0 |
7 | 서울특별시 | 종로구 | 남자 | 75201.0 | 5620.0 | 6181.0 | 5387 | 5034.0 | 3411.0 | 3009.0 | 2311 | 1289.0 | 506.0 | 207.0 | 89 | 73.0 |
8 | 서울특별시 | 종로구 | 여자 | 77536.0 | 5759.0 | 5710.0 | 5297 | 5345.0 | 4000.0 | 3627.0 | 2952 | 1815.0 | 974.0 | 395.0 | 145 | 147.0 |
9 | 서울특별시 | 중구 | 합계 | 125249.0 | 8216.0 | 9529.0 | 10332 | 10107.0 | 6399.0 | 5313.0 | 4127 | 2502.0 | 1260.0 | 469.0 | 158 | 160.0 |
10 | 서울특별시 | 중구 | 남자 | 62204.0 | 4142.0 | 4792.0 | 5192 | 5221.0 | 3113.0 | 2405.0 | 1752 | 929.0 | 414.0 | 132.0 | 56 | 51.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
841 | 제주특별자치도 | 제주시 | 남자 | 235977.0 | 17377.0 | 13118.0 | 15084 | 18350.0 | 8474.0 | 6782.0 | 4941 | 2737.0 | 854.0 | 226.0 | 53 | 17.0 |
842 | 제주특별자치도 | 제주시 | 여자 | 234688.0 | 15261.0 | 12245.0 | 14687 | 18062.0 | 9265.0 | 7877.0 | 7178 | 5649.0 | 3122.0 | 1387.0 | 460 | 137.0 |
843 | 제주특별자치도 | 서귀포시 | 합계 | 170932.0 | 10505.0 | 8067.0 | 9120 | 11606.0 | 8686.0 | 7460.0 | 6456 | 4521.0 | 1855.0 | 733.0 | 242 | 77.0 |
844 | 제주특별자치도 | 서귀포시 | 남자 | 86568.0 | 5600.0 | 4247.0 | 4693 | 6082.0 | 4237.0 | 3441.0 | 2611 | 1494.0 | 370.0 | 103.0 | 29 | 9.0 |
845 | 제주특별자치도 | 서귀포시 | 여자 | 84364.0 | 4905.0 | 3820.0 | 4427 | 5524.0 | 4449.0 | 4019.0 | 3845 | 3027.0 | 1485.0 | 630.0 | 213 | 68.0 |
792 rows × 16 columns
컬럼 이름은잘 정리가 되었다.
5-3 인구 소멸 위기 지역 계산하고 데이터 정리하기¶
앞서 얘기한 것처럼 인구 소멸 위기 지역을 알기 위해선 먼저 20-30대의 인구를 알아야 한다. 그리고 65세 이상 인구수도 알아야 한다.
population['20-39세'] = population['20 - 24세'] + population['25 - 29세'] + \
population['30 - 34세'] + population['35 - 39세']
population['65세이상'] = population['65 - 69세'] + population['70 - 74세'] + \
population['75 - 79세'] + population['80 - 84세'] + \
population['85 - 89세'] + population['90 - 94세'] + \
population['95 - 99세'] + population['100+']
population.head(10)
광역시도 | 시도 | 구분 | 인구수 | 20 - 24세 | 25 - 29세 | 30 - 34세 | 35 - 39세 | 65 - 69세 | 70 - 74세 | 75 - 79세 | 80 - 84세 | 85 - 89세 | 90 - 94세 | 95 - 99세 | 100+ | 20-39세 | 65세이상 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
6 | 서울특별시 | 종로구 | 합계 | 152737.0 | 11379.0 | 11891.0 | 10684 | 10379.0 | 7411.0 | 6636.0 | 5263 | 3104.0 | 1480.0 | 602.0 | 234 | 220.0 | 44333.0 | 24950.0 |
7 | 서울특별시 | 종로구 | 남자 | 75201.0 | 5620.0 | 6181.0 | 5387 | 5034.0 | 3411.0 | 3009.0 | 2311 | 1289.0 | 506.0 | 207.0 | 89 | 73.0 | 22222.0 | 10895.0 |
8 | 서울특별시 | 종로구 | 여자 | 77536.0 | 5759.0 | 5710.0 | 5297 | 5345.0 | 4000.0 | 3627.0 | 2952 | 1815.0 | 974.0 | 395.0 | 145 | 147.0 | 22111.0 | 14055.0 |
9 | 서울특별시 | 중구 | 합계 | 125249.0 | 8216.0 | 9529.0 | 10332 | 10107.0 | 6399.0 | 5313.0 | 4127 | 2502.0 | 1260.0 | 469.0 | 158 | 160.0 | 38184.0 | 20388.0 |
10 | 서울특별시 | 중구 | 남자 | 62204.0 | 4142.0 | 4792.0 | 5192 | 5221.0 | 3113.0 | 2405.0 | 1752 | 929.0 | 414.0 | 132.0 | 56 | 51.0 | 19347.0 | 8852.0 |
11 | 서울특별시 | 중구 | 여자 | 63045.0 | 4074.0 | 4737.0 | 5140 | 4886.0 | 3286.0 | 2908.0 | 2375 | 1573.0 | 846.0 | 337.0 | 102 | 109.0 | 18837.0 | 11536.0 |
12 | 서울특별시 | 용산구 | 합계 | 230241.0 | 14317.0 | 16972.0 | 19032 | 19127.0 | 10675.0 | 9093.0 | 7477 | 4553.0 | 2254.0 | 916.0 | 264 | 315.0 | 69448.0 | 35547.0 |
13 | 서울특별시 | 용산구 | 남자 | 111601.0 | 6937.0 | 8373.0 | 9455 | 9434.0 | 4834.0 | 3975.0 | 3094 | 1739.0 | 750.0 | 284.0 | 102 | 88.0 | 34199.0 | 14866.0 |
14 | 서울특별시 | 용산구 | 여자 | 118640.0 | 7380.0 | 8599.0 | 9577 | 9693.0 | 5841.0 | 5118.0 | 4383 | 2814.0 | 1504.0 | 632.0 | 162 | 227.0 | 35249.0 | 20681.0 |
15 | 서울특별시 | 성동구 | 합계 | 299259.0 | 20813.0 | 23383.0 | 25507 | 25979.0 | 12938.0 | 10734.0 | 7989 | 4450.0 | 1944.0 | 678.0 | 209 | 198.0 | 95682.0 | 39140.0 |
아주 손쉽게 계산할 수 있다. 위 처럼 결과도 쉽게 얻었다. 이제 이 많은 컬럼 중 일부만 선택해야 한다. 그리고 결정적으로 '합계','남자','여자'로 되어있는 구분도 정리해야 한다. 이럴 때 사용하는 마법 키워드를 우리는 알고 있다.
pop = pd.pivot_table(population,
index = ['광역시도', '시도'],
columns = ['구분'],
values = ['인구수', '20-39세', '65세이상'])
pop
20-39세 | 65세이상 | 인구수 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
구분 | 남자 | 여자 | 합계 | 남자 | 여자 | 합계 | 남자 | 여자 | 합계 | |
광역시도 | 시도 | |||||||||
강원도 | 강릉시 | 26286.0 | 23098.0 | 49384.0 | 15767.0 | 21912.0 | 37679.0 | 106231.0 | 107615.0 | 213846.0 |
고성군 | 4494.0 | 2529.0 | 7023.0 | 2900.0 | 4251.0 | 7151.0 | 15899.0 | 14215.0 | 30114.0 | |
동해시 | 11511.0 | 9753.0 | 21264.0 | 6392.0 | 8732.0 | 15124.0 | 47166.0 | 46131.0 | 93297.0 | |
삼척시 | 8708.0 | 7115.0 | 15823.0 | 5892.0 | 8718.0 | 14610.0 | 35253.0 | 34346.0 | 69599.0 | |
속초시 | 9956.0 | 8752.0 | 18708.0 | 5139.0 | 7613.0 | 12752.0 | 40288.0 | 41505.0 | 81793.0 | |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
충청북도 | 진천군 | 9391.0 | 7622.0 | 17013.0 | 4731.0 | 6575.0 | 11306.0 | 36387.0 | 33563.0 | 69950.0 |
청원구 | 32216.0 | 27805.0 | 60021.0 | 8417.0 | 11914.0 | 20331.0 | 97006.0 | 93807.0 | 190813.0 | |
청주시 | 128318.0 | 115719.0 | 244037.0 | 37882.0 | 53671.0 | 91553.0 | 419323.0 | 415874.0 | 835197.0 | |
충주시 | 26600.0 | 22757.0 | 49357.0 | 14407.0 | 20383.0 | 34790.0 | 104877.0 | 103473.0 | 208350.0 | |
흥덕구 | 40933.0 | 37675.0 | 78608.0 | 9788.0 | 13671.0 | 23459.0 | 127647.0 | 125916.0 | 253563.0 |
264 rows × 9 columns
바로 pivot_table 이다. 광역시도와 시도를 index로 잡고, 인구수와 20-39세, 65세 이상만 데이터로 가져오도록 한다. 위 결과가 그 중 일부이다. 이제 인구 소멸 비율을 계산할 수 있다.
pop['소멸비율'] = pop['20-39세','여자'] / (pop['65세이상','합계'] / 2)
pop.head()
20-39세 | 65세이상 | 인구수 | 소멸비율 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
구분 | 남자 | 여자 | 합계 | 남자 | 여자 | 합계 | 남자 | 여자 | 합계 | ||
광역시도 | 시도 | ||||||||||
강원도 | 강릉시 | 26286.0 | 23098.0 | 49384.0 | 15767.0 | 21912.0 | 37679.0 | 106231.0 | 107615.0 | 213846.0 | 1.226041 |
고성군 | 4494.0 | 2529.0 | 7023.0 | 2900.0 | 4251.0 | 7151.0 | 15899.0 | 14215.0 | 30114.0 | 0.707314 | |
동해시 | 11511.0 | 9753.0 | 21264.0 | 6392.0 | 8732.0 | 15124.0 | 47166.0 | 46131.0 | 93297.0 | 1.289738 | |
삼척시 | 8708.0 | 7115.0 | 15823.0 | 5892.0 | 8718.0 | 14610.0 | 35253.0 | 34346.0 | 69599.0 | 0.973990 | |
속초시 | 9956.0 | 8752.0 | 18708.0 | 5139.0 | 7613.0 | 12752.0 | 40288.0 | 41505.0 | 81793.0 | 1.372647 |
pop['소멸위기지역'] = pop['소멸비율'] < 1.0
pop.head()
20-39세 | 65세이상 | 인구수 | 소멸비율 | 소멸위기지역 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
구분 | 남자 | 여자 | 합계 | 남자 | 여자 | 합계 | 남자 | 여자 | 합계 | |||
광역시도 | 시도 | |||||||||||
강원도 | 강릉시 | 26286.0 | 23098.0 | 49384.0 | 15767.0 | 21912.0 | 37679.0 | 106231.0 | 107615.0 | 213846.0 | 1.226041 | False |
고성군 | 4494.0 | 2529.0 | 7023.0 | 2900.0 | 4251.0 | 7151.0 | 15899.0 | 14215.0 | 30114.0 | 0.707314 | True | |
동해시 | 11511.0 | 9753.0 | 21264.0 | 6392.0 | 8732.0 | 15124.0 | 47166.0 | 46131.0 | 93297.0 | 1.289738 | False | |
삼척시 | 8708.0 | 7115.0 | 15823.0 | 5892.0 | 8718.0 | 14610.0 | 35253.0 | 34346.0 | 69599.0 | 0.973990 | True | |
속초시 | 9956.0 | 8752.0 | 18708.0 | 5139.0 | 7613.0 | 12752.0 | 40288.0 | 41505.0 | 81793.0 | 1.372647 | False |
앞서 확인한 인구 소멸 위기 지역에 대한 정의에 의해 위에서 계산한 소멸 비율이 1 이하면 소멸 위기 지역이라고 부른다.
pop[pop['소멸위기지역']==True].index.get_level_values(1)
Index(['고성군', '삼척시', '양양군', '영월군', '정선군', '평창군', '홍천군', '횡성군', '가평군', '양평군', '연천군', '거창군', '고성군', '남해군', '밀양시', '산청군', '의령군', '창녕군', '하동군', '함안군', '함양군', '합천군', '고령군', '군위군', '문경시', '봉화군', '상주시', '성주군', '영덕군', '영양군', '영주시', '영천시', '예천군', '울릉군', '울진군', '의성군', '청도군', '청송군', '동구', '영도구', '강화군', '옹진군', '강진군', '고흥군', '곡성군', '구례군', '담양군', '보성군', '신안군', '영광군', '영암군', '완도군', '장성군', '장흥군', '진도군', '함평군', '해남군', '화순군', '고창군', '김제시', '남원시', '무주군', '부안군', '순창군', '임실군', '장수군', '정읍시', '진안군', '공주시', '금산군', '논산시', '보령시', '부여군', '서천군', '예산군', '청양군', '태안군', '홍성군', '괴산군', '단양군', '보은군', '영동군', '옥천군'], dtype='object', name='시도')
위처럼 해당 지역의 리스트를 뽑았다. 총 83개 지자체이다. 이렇게 이름만 나열하는 것은 뭔가 부족해 보인다. 지도로 시각화하기 위해 조금 더 작업하자.
pop.reset_index(inplace=True)
pop.head()
광역시도 | 시도 | 20-39세 | 65세이상 | 인구수 | 소멸비율 | 소멸위기지역 | |||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
구분 | 남자 | 여자 | 합계 | 남자 | 여자 | 합계 | 남자 | 여자 | 합계 | ||||
0 | 강원도 | 강릉시 | 26286.0 | 23098.0 | 49384.0 | 15767.0 | 21912.0 | 37679.0 | 106231.0 | 107615.0 | 213846.0 | 1.226041 | False |
1 | 강원도 | 고성군 | 4494.0 | 2529.0 | 7023.0 | 2900.0 | 4251.0 | 7151.0 | 15899.0 | 14215.0 | 30114.0 | 0.707314 | True |
2 | 강원도 | 동해시 | 11511.0 | 9753.0 | 21264.0 | 6392.0 | 8732.0 | 15124.0 | 47166.0 | 46131.0 | 93297.0 | 1.289738 | False |
3 | 강원도 | 삼척시 | 8708.0 | 7115.0 | 15823.0 | 5892.0 | 8718.0 | 14610.0 | 35253.0 | 34346.0 | 69599.0 | 0.973990 | True |
4 | 강원도 | 속초시 | 9956.0 | 8752.0 | 18708.0 | 5139.0 | 7613.0 | 12752.0 | 40288.0 | 41505.0 | 81793.0 | 1.372647 | False |
먼저 pivot_table에 의해 다단으로 구성된 index를 다시 초기화 한다.
tmp_columns = [pop.columns.get_level_values(0)[n] + \
pop.columns.get_level_values(1)[n]
for n in range(0,len(pop.columns.get_level_values(0)))]
pop.columns = tmp_columns
pop.head()
광역시도 | 시도 | 20-39세남자 | 20-39세여자 | 20-39세합계 | 65세이상남자 | 65세이상여자 | 65세이상합계 | 인구수남자 | 인구수여자 | 인구수합계 | 소멸비율 | 소멸위기지역 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 강원도 | 강릉시 | 26286.0 | 23098.0 | 49384.0 | 15767.0 | 21912.0 | 37679.0 | 106231.0 | 107615.0 | 213846.0 | 1.226041 | False |
1 | 강원도 | 고성군 | 4494.0 | 2529.0 | 7023.0 | 2900.0 | 4251.0 | 7151.0 | 15899.0 | 14215.0 | 30114.0 | 0.707314 | True |
2 | 강원도 | 동해시 | 11511.0 | 9753.0 | 21264.0 | 6392.0 | 8732.0 | 15124.0 | 47166.0 | 46131.0 | 93297.0 | 1.289738 | False |
3 | 강원도 | 삼척시 | 8708.0 | 7115.0 | 15823.0 | 5892.0 | 8718.0 | 14610.0 | 35253.0 | 34346.0 | 69599.0 | 0.973990 | True |
4 | 강원도 | 속초시 | 9956.0 | 8752.0 | 18708.0 | 5139.0 | 7613.0 | 12752.0 | 40288.0 | 41505.0 | 81793.0 | 1.372647 | False |
역시 다단으로 표시된 컬럼을 하나로 합친다. 이제 세로축에 지역, 가로축에 연령대별 혹은 성별 인구수가 정리 되었다.
pop.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 264 entries, 0 to 263 Data columns (total 13 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 광역시도 264 non-null object 1 시도 264 non-null object 2 20-39세남자 264 non-null float64 3 20-39세여자 264 non-null float64 4 20-39세합계 264 non-null float64 5 65세이상남자 264 non-null float64 6 65세이상여자 264 non-null float64 7 65세이상합계 264 non-null float64 8 인구수남자 264 non-null float64 9 인구수여자 264 non-null float64 10 인구수합계 264 non-null float64 11 소멸비율 264 non-null float64 12 소멸위기지역 264 non-null bool dtypes: bool(1), float64(10), object(2) memory usage: 25.1+ KB
요약해보니 총 264개 항목에 인구수는 숫자형으로 잡혔고, 소멸위기지역은 bool형으로 잡혀있다.
5-4 대한민국 지도 그리는 방법에 대한 소개¶
우리는 이미 folium은 사용할 줄 안다. 그러나 folium에도 접근하기 어려운 부분이 있다. 바로 경계선을 그려주는 json 파일을 구하는 것이다.
pop['시도'].unique()
array(['강릉시', '고성군', '동해시', '삼척시', '속초시', '양구군', '양양군', '영월군', '원주시', '인제군', '정선군', '철원군', '춘천시', '태백시', '평창군', '홍천군', '화천군', '횡성군', '가평군', '고양시', '과천시', '광명시', '광주시', '구리시', '군포시', '권선구', '기흥구', '김포시', '남양주시', '단원구', '덕양구', '동두천시', '동안구', '만안구', '부천시', '분당구', '상록구', '성남시', '소사구', '수원시', '수정구', '수지구', '시흥시', '안산시', '안성시', '안양시', '양주시', '양평군', '여주시', '연천군', '영통구', '오산시', '오정구', '용인시', '원미구', '의왕시', '의정부시', '이천시', '일산동구', '일산서구', '장안구', '중원구', '처인구', '파주시', '팔달구', '평택시', '포천시', '하남시', '화성시', '거제시', '거창군', '김해시', '남해군', '마산합포구', '마산회원구', '밀양시', '사천시', '산청군', '성산구', '양산시', '의령군', '의창구', '진주시', '진해구', '창녕군', '창원시', '통영시', '하동군', '함안군', '함양군', '합천군', '경산시', '경주시', '고령군', '구미시', '군위군', '김천시', '남구', '문경시', '봉화군', '북구', '상주시', '성주군', '안동시', '영덕군', '영양군', '영주시', '영천시', '예천군', '울릉군', '울진군', '의성군', '청도군', '청송군', '칠곡군', '포항시', '광산구', '동구', '서구', '달서구', '달성군', '수성구', '중구', '대덕구', '유성구', '강서구', '금정구', '기장군', '동래구', '부산진구', '사상구', '사하구', '수영구', '연제구', '영도구', '해운대구', '강남구', '강동구', '강북구', '관악구', '광진구', '구로구', '금천구', '노원구', '도봉구', '동대문구', '동작구', '마포구', '서대문구', '서초구', '성동구', '성북구', '송파구', '양천구', '영등포구', '용산구', '은평구', '종로구', '중랑구', '세종특별자치시', '울주군', '강화군', '계양구', '남동구', '부평구', '연수구', '옹진군', '강진군', '고흥군', '곡성군', '광양시', '구례군', '나주시', '담양군', '목포시', '무안군', '보성군', '순천시', '신안군', '여수시', '영광군', '영암군', '완도군', '장성군', '장흥군', '진도군', '함평군', '해남군', '화순군', '고창군', '군산시', '김제시', '남원시', '덕진구', '무주군', '부안군', '순창군', '완산구', '완주군', '익산시', '임실군', '장수군', '전주시', '정읍시', '진안군', '서귀포시', '제주시', '계룡시', '공주시', '금산군', '논산시', '당진시', '동남구', '보령시', '부여군', '서북구', '서산시', '서천군', '아산시', '예산군', '천안시', '청양군', '태안군', '홍성군', '괴산군', '단양군', '보은군', '상당구', '서원구', '영동군', '옥천군', '음성군', '제천시', '증평군', '진천군', '청원구', '청주시', '충주시', '흥덕구'], dtype=object)
우리에게 필요한 것은 모두 지역에 따른 고유 ID가 필요하다는 것이다. 우선 위처럼 pop['시도']에 대해 unique를 조사하자. 조사된 결과 인천,부산 등 광역시의 구도 있지만, 안양시나 수원시에 있는 구도 있다. 일단 우리는 고유 아이디를 '광역시도'의 값과 '시도'의 값을 합치면 될 것 같다. 물론 지도 표기 및 ID로서의 간결함을 유지하기 위해 서울 강남과 같은 형태를 원칙으로 한다.
si_name = [None] * len(pop)
tmp_gu_dict = {'수원':['장안구', '권선구', '팔달구', '영통구'],
'성남':['수정구', '중원구', '분당구'],
'안양':['만안구', '동안구'],
'안산':['상록구', '단원구'],
'고양':['덕양구', '일산동구', '일산서구'],
'용인':['처인구', '기흥구', '수지구'],
'청주':['상당구', '서원구', '흥덕구', '청원구'],
'천안':['동남구', '서북구'],
'전주':['완산구', '덕진구'],
'포항':['남구', '북구'],
'창원':['의창구', '성산구', '진해구', '마산합포구', '마산회원구'],
'부천':['오정구', '원미구', '소사구']}
먼저 광역시가 아니면서 구를 가지고 있는 시와 그 행정구를 파이썬의 dict형으로 선언한다. 먼저 광역시도에 있는 이름의 끝 세 글자가 광역시, 특별시, 자치시로 끝나지 않으면 일반 시 혹은 군으로 본다. 그 속에서 강원도와 경상남도에는 동일한 이름을 가진 고성군이 있어서 그것을 처리한다. 그리고 세종특별자치시를 그냥 세종으로 처리하고 나머지는 광역시도에서 앞 두 글자와 시도에서 두 글자인 경우 모두, 아니면 앞 두글자만 선택하면서 고유 ID를 만든다.
for n in pop.index:
if pop['광역시도'][n][-3:] not in ['광역시', '특별시', '자치시']:
if pop['시도'][n][:-1]=='고성' and pop['광역시도'][n]=='강원도':
si_name[n] = '고성(강원)'
elif pop['시도'][n][:-1]=='고성' and pop['광역시도'][n]=='경상남도':
si_name[n] = '고성(경남)'
else:
si_name[n] = pop['시도'][n][:-1]
for keys, values in tmp_gu_dict.items():
if pop['시도'][n] in values:
if len(pop['시도'][n])==2:
si_name[n] = keys + ' ' + pop['시도'][n]
elif pop['시도'][n] in ['마산합포구','마산회원구']:
si_name[n] = keys + ' ' + pop['시도'][n][2:-1]
else:
si_name[n] = keys + ' ' + pop['시도'][n][:-1]
elif pop['광역시도'][n] == '세종특별자치시':
si_name[n] = '세종'
else:
if len(pop['시도'][n])==2:
si_name[n] = pop['광역시도'][n][:2] + ' ' + pop['시도'][n]
else:
si_name[n] = pop['광역시도'][n][:2] + ' ' + pop['시도'][n][:-1]
si_name
['강릉', '고성(강원)', '동해', '삼척', '속초', '양구', '양양', '영월', '원주', '인제', '정선', '철원', '춘천', '태백', '평창', '홍천', '화천', '횡성', '가평', '고양', '과천', '광명', '광주', '구리', '군포', '수원 권선', '용인 기흥', '김포', '남양주', '안산 단원', '고양 덕양', '동두천', '안양 동안', '안양 만안', '부천', '성남 분당', '안산 상록', '성남', '부천 소사', '수원', '성남 수정', '용인 수지', '시흥', '안산', '안성', '안양', '양주', '양평', '여주', '연천', '수원 영통', '오산', '부천 오정', '용인', '부천 원미', '의왕', '의정부', '이천', '고양 일산동', '고양 일산서', '수원 장안', '성남 중원', '용인 처인', '파주', '수원 팔달', '평택', '포천', '하남', '화성', '거제', '거창', '고성(경남)', '김해', '남해', '창원 합포', '창원 회원', '밀양', '사천', '산청', '창원 성산', '양산', '의령', '창원 의창', '진주', '창원 진해', '창녕', '창원', '통영', '하동', '함안', '함양', '합천', '경산', '경주', '고령', '구미', '군위', '김천', '포항 남구', '문경', '봉화', '포항 북구', '상주', '성주', '안동', '영덕', '영양', '영주', '영천', '예천', '울릉', '울진', '의성', '청도', '청송', '칠곡', '포항', '광주 광산', '광주 남구', '광주 동구', '광주 북구', '광주 서구', '대구 남구', '대구 달서', '대구 달성', '대구 동구', '대구 북구', '대구 서구', '대구 수성', '대구 중구', '대전 대덕', '대전 동구', '대전 서구', '대전 유성', '대전 중구', '부산 강서', '부산 금정', '부산 기장', '부산 남구', '부산 동구', '부산 동래', '부산 부산진', '부산 북구', '부산 사상', '부산 사하', '부산 서구', '부산 수영', '부산 연제', '부산 영도', '부산 중구', '#39;부산 해운대', '서울 강남', '서울 강동', '서울 강북', '서울 강서', '서울 관악', '서울 광진', '서울 구로', '서울 금천', '서울 노원', '서울 도봉', '서울 동대문', '서울 동작', '서울 마포', '서울 서대문', '서울 서초', '서울 성동', '서울 성북', '서울 송파', '서울 양천', '서울 영등포', '서울 용산', '서울 은평', '서울 종로', '서울 중구', '서울 중랑', '세종', '울산 남구', '울산 동구', '울산 북구', '울산 울주', '울산 중구', '인천 강화', '인천 계양', '인천 남구', '인천 남동', '인천 동구', '인천 부평', '인천 서구', '인천 연수', '인천 옹진', '인천 중구', '강진', '고흥', '곡성', '광양', '구례', '나주', '담양', '목포', '무안', '보성', '순천', '신안', '여수', '영광', '영암', '완도', '장성', '장흥', '진도', '함평', '해남', '화순', '고창', '군산', '김제', '남원', '전주 덕진', '무주', '부안', '순창', '전주 완산', '완주', '익산', '임실', '장수', '전주', '정읍', '진안', '서귀포', '제주', '계룡', '공주', '금산', '논산', '당진', '천안 동남', '보령', '부여', '천안 서북', '서산', '서천', '아산', '예산', '천안', '청양', '태안', '홍성', '괴산', '단양', '보은', '청주 상당', '청주 서원', '영동', '옥천', '음성', '제천', '증평', '진천', '청주 청원', '청주', '충주', '청주 흥덕']
결과는 위와 같다.
pop['ID'] = si_name
결과를 pop에 포함시키고 이제 큰 의미가 없는 몇몇 컬럼을 제거한다.
del pop['20-39세남자']
del pop['65세이상남자']
del pop['65세이상여자']
pop.head()
광역시도 | 시도 | 20-39세여자 | 20-39세합계 | 65세이상합계 | 인구수남자 | 인구수여자 | 인구수합계 | 소멸비율 | 소멸위기지역 | ID | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 강원도 | 강릉시 | 23098.0 | 49384.0 | 37679.0 | 106231.0 | 107615.0 | 213846.0 | 1.226041 | False | 강릉 |
1 | 강원도 | 고성군 | 2529.0 | 7023.0 | 7151.0 | 15899.0 | 14215.0 | 30114.0 | 0.707314 | True | 고성(강원) |
2 | 강원도 | 동해시 | 9753.0 | 21264.0 | 15124.0 | 47166.0 | 46131.0 | 93297.0 | 1.289738 | False | 동해 |
3 | 강원도 | 삼척시 | 7115.0 | 15823.0 | 14610.0 | 35253.0 | 34346.0 | 69599.0 | 0.973990 | True | 삼척 |
4 | 강원도 | 속초시 | 8752.0 | 18708.0 | 12752.0 | 40288.0 | 41505.0 | 81793.0 | 1.372647 | False | 속초 |
5-5 Cartogram으로 우리나라 지도 만들기¶
엑셀 파일 출처 : (https://github.com/PinkWink/DataScience)
draw_korea_raw = pd.read_csv('/home/jaeyoon89/DataScience/data/05. draw_korea_raw.csv')
draw_korea_raw
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 철원 | 화천 | 양구 | 고성(강원) | NaN | NaN | NaN |
1 | NaN | NaN | NaN | 양주 | 동두천 | 연천 | 포천 | 의정부 | 인제 | 춘천 | 속초 | NaN | NaN | NaN |
2 | NaN | NaN | NaN | 고양 덕양 | 고양 일산동 | 서울 도봉 | 서울 노원 | 남양주 | 홍천 | 횡성 | 양양 | NaN | NaN | NaN |
3 | NaN | NaN | 파주 | 고양 일산서 | 김포 | 서울 강북 | 서울 성북 | 가평 | 구리 | 하남 | 정선 | 강릉 | NaN | NaN |
4 | NaN | NaN | 부천 소사 | 안양 만안 | 광명 | 서울 서대문 | 서울 종로 | 서울 동대문 | 서울 중랑 | 양평 | 태백 | 동해 | NaN | NaN |
5 | NaN | 인천 강화 | 부천 원미 | 안양 동안 | 서울 은평 | 서울 마포 | 서울 중구 | 서울 성동 | 서울 강동 | 여주 | 원주 | 삼척 | NaN | NaN |
6 | NaN | 인천 서구 | 부천 오정 | 시흥 | 서울 강서 | 서울 동작 | 서울 용산 | 서울 광진 | 서울 송파 | 이천 | 평창 | 울진 | NaN | NaN |
7 | NaN | 인천 동구 | 인천 계양 | 안산 상록 | 서울 양천 | 서울 관악 | 서울 서초 | 성남 중원 | 과천 | 광주 | 영월 | 영덕 | NaN | NaN |
8 | NaN | NaN | 인천 부평 | 안산 단원 | 서울 영등포 | 서울 금천 | 서울 강남 | 성남 분당 | 성남 수정 | 용인 수지 | 문경 | 봉화 | NaN | 울릉 |
9 | NaN | 인천 중구 | 인천 남구 | 화성 | 서울 구로 | 군포 | 의왕 | 수원 영통 | 용인 기흥 | 용인 처인 | 안동 | 영양 | NaN | NaN |
10 | 인천 옹진 | 인천 연수 | 인천 남동 | 오산 | 안성 | 수원 권선 | 수원 장안 | 제천 | 예천 | 영주 | 구미 | 청송 | 포항 북구 | NaN |
11 | 태안 | 아산 | 천안 동남 | 천안 서북 | 평택 | 음성 | 수원 팔달 | 단양 | 상주 | 김천 | 군위 | 의성 | 포항 남구 | NaN |
12 | NaN | 당진 | 홍성 | 예산 | 공주 | 진천 | 충주 | 청주 흥덕 | 괴산 | 칠곡 | 영천 | 경산 | 경주 | NaN |
13 | NaN | 서산 | 보령 | 청양 | 세종 | 대전 대덕 | 증평 | 청주 청원 | 보은 | 고령 | 청도 | 성주 | 울산 북구 | NaN |
14 | NaN | NaN | 부여 | 논산 | 계룡 | 대전 동구 | 청주 상당 | 청주 서원 | 대구 북구 | 대구 중구 | 대구 수성 | 울산 울주 | 울산 동구 | NaN |
15 | NaN | NaN | 서천 | 금산 | 대전 유성 | 대전 중구 | 옥천 | 영동 | 대구 서구 | 대구 남구 | 대구 동구 | 울산 중구 | 울산 남구 | NaN |
16 | NaN | NaN | 군산 | 익산 | 대전 서구 | 무주 | 거창 | 합천 | 대구 달서 | 대구 달성 | 부산 금정 | 부산 동래 | 부산 기장 | NaN |
17 | NaN | NaN | 부안 | 김제 | 완주 | 장수 | 함양 | 창녕 | 밀양 | 부산 북구 | 부산 부산진 | 부산 연제 | 부산 해운대 | NaN |
18 | NaN | 고창 | 정읍 | 전주 덕진 | 진안 | 남원 | 진주 | 의령 | 부산 강서 | 부산 사상 | 부산 동구 | 부산 중구 | NaN | NaN |
19 | NaN | 영광 | 장성 | 전주 완산 | 임실 | 산청 | 함안 | 양산 | 창원 합포 | 부산 서구 | 부산 사하 | 부산 남구 | NaN | NaN |
20 | NaN | 함평 | 담양 | 순창 | 구례 | 하동 | 창원 의창 | 창원 성산 | 창원 진해 | 김해 | 부산 영도 | 부산 수영 | NaN | NaN |
21 | 신안 | 무안 | 광주 광산 | 곡성 | 화순 | 광양 | 사천 | 창원 회원 | 통영 | NaN | NaN | NaN | NaN | NaN |
22 | 목포 | 나주 | 광주 서구 | 광주 북구 | 순천 | 고흥 | 남해 | 고성(경남) | 거제 | NaN | NaN | NaN | NaN | NaN |
23 | 해남 | 영암 | 광주 남구 | 광주 동구 | 여수 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
24 | 진도 | 강진 | 장흥 | 보성 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
25 | NaN | NaN | 완도 | NaN | NaN | 제주 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
26 | NaN | NaN | NaN | NaN | NaN | 서귀포 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
27 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
draw_korea_raw_stacked = pd.DataFrame(draw_korea_raw.stack())
draw_korea_raw_stacked.reset_index(inplace=True)
draw_korea_raw_stacked.rename(columns={'level_0':'y', 'level_1':'x', 0:'ID'},
inplace=True)
draw_korea_raw_stacked
y | x | ID | |
---|---|---|---|
0 | 0 | 7 | 철원 |
1 | 0 | 8 | 화천 |
2 | 0 | 9 | 양구 |
3 | 0 | 10 | 고성(강원) |
4 | 1 | 3 | 양주 |
... | ... | ... | ... |
247 | 24 | 2 | 장흥 |
248 | 24 | 3 | 보성 |
249 | 25 | 2 | 완도 |
250 | 25 | 5 | 제주 |
251 | 26 | 5 | 서귀포 |
252 rows × 3 columns
우리에게 필요한 것은 각 지역별 x,y 좌표이다.
그래서 stack()으로 풀고 인덱스를 재설정했다. 그리고 다시 컬럼이름을 바꾸었다.
이제 각 지역에 대한 좌표를 얻었다. 철원은 (7,0)지점이다.
draw_korea = draw_korea_raw_stacked
변수 이름을 변경하자.
BORDER_LINES = [
[(5, 1), (5,2), (7,2), (7,3), (11,3), (11,0)], # 인천
[(5,4), (5,5), (2,5), (2,7), (4,7), (4,9), (7,9),
(7,7), (9,7), (9,5), (10,5), (10,4), (5,4)], # 서울
[(1,7), (1,8), (3,8), (3,10), (10,10), (10,7),
(12,7), (12,6), (11,6), (11,5), (12, 5), (12,4),
(11,4), (11,3)], # 경기도
[(8,10), (8,11), (6,11), (6,12)], # 강원도
[(12,5), (13,5), (13,4), (14,4), (14,5), (15,5),
(15,4), (16,4), (16,2)], # 충청북도
[(16,4), (17,4), (17,5), (16,5), (16,6), (19,6),
(19,5), (20,5), (20,4), (21,4), (21,3), (19,3), (19,1)], # 전라북도
[(13,5), (13,6), (16,6)], # 대전시
[(13,5), (14,5)], #세종시
[(21,2), (21,3), (22,3), (22,4), (24,4), (24,2), (21,2)], #광주
[(20,5), (21,5), (21,6), (23,6)], #전라남도
[(10,8), (12,8), (12,9), (14,9), (14,8), (16,8), (16,6)], #충청북도
[(14,9), (14,11), (14,12), (13,12), (13,13)], #경상북도
[(15,8), (17,8), (17,10), (16,10), (16,11), (14,11)], #대구
[(17,9), (18,9), (18,8), (19,8), (19,9), (20,9), (20,10), (21,10)], #부산
[(16,11), (16,13)], #울산
# [(9,14), (9,15)],
[(27,5), (27,6), (25,6)],
]
광역시도를 구분하는 경계선도 직접 입력하였다.
plt.figure(figsize=(8, 11))
# 지역 이름 표시
for idx, row in draw_korea.iterrows():
# 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시한다.
# (중구, 서구)
if len(row['ID'].split())==2:
dispname = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
elif row['ID'][:2]=='고성':
dispname = '고성'
else:
dispname = row['ID']
row['x'] = int(row['x'])
# 서대문구, 서귀포시 같이 이름이 3자 이상인 경우에 작은 글자로 표시한다.
if len(dispname.splitlines()[-1]) >= 3:
fontsize, linespacing = 9.5, 1.5
else:
fontsize, linespacing = 11, 1.2
plt.annotate(dispname, (row['x']+0.5, row['y']+0.5), weight='bold',
fontsize=fontsize, ha='center', va='center',
linespacing=linespacing)
# 시도 경계 그린다.
for path in BORDER_LINES:
ys, xs = zip(*path)
plt.plot(xs, ys, c='black', lw=1.5)
plt.gca().invert_yaxis()
#plt.gca().set_aspect(1)
plt.axis('off')
plt.tight_layout()
plt.show()
이 코드를 실행하면 위와 같이 경계선과 지역 이름만 나타난다. 반복문 안에서 대다수 코드는 이름을 표기하기 위한 코드이다. 그리고 경계선을 그린다. 마지막 부분에 inver_yaxis()라고 되어 있는 이유는 y축이 엑셀에서 0번이 시작하는 것과 matblotlib가 0이라고 인식하는 좌표가 서로 반대이기 때문이다.
set(draw_korea['ID'].unique()) - set(pop['ID'].unique())
set()
set(pop['ID'].unique()) - set(draw_korea['ID'].unique())
set()
tmp_list = list(set(pop['ID'].unique()) - set(draw_korea['ID'].unique()))
for tmp in tmp_list:
pop = pop.drop(pop[pop['ID']==tmp].index)
print(set(pop['ID'].unique()) - set(draw_korea['ID'].unique()))
set()
원래 인구 현황을 가지고 있던 pop 변수와 엑셀에서 출발해서 지도를 그리기 위해 만든 draw_korea 변수에서 일반 행정구를 가진 시(성남,수원)이 합계 정보를 삭제한다.
pop = pd.merge(pop, draw_korea, how='left', on=['ID'])
pop.head()
광역시도 | 시도 | 20-39세여자 | 20-39세합계 | 65세이상합계 | 인구수남자 | 인구수여자 | 인구수합계 | 소멸비율 | 소멸위기지역 | ... | y_x | x_x | y_y | x_y | y_x | x_x | y_y | x_y | y | x | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 강원도 | 강릉시 | 23098.0 | 49384.0 | 37679.0 | 106231.0 | 107615.0 | 213846.0 | 1.226041 | 0 | ... | 3 | 11 | 3 | 11 | 3 | 11 | 3 | 11 | 3 | 11 |
1 | 강원도 | 고성군 | 2529.0 | 7023.0 | 7151.0 | 15899.0 | 14215.0 | 30114.0 | 0.707314 | 1 | ... | 0 | 10 | 0 | 10 | 0 | 10 | 0 | 10 | 0 | 10 |
2 | 강원도 | 동해시 | 9753.0 | 21264.0 | 15124.0 | 47166.0 | 46131.0 | 93297.0 | 1.289738 | 0 | ... | 4 | 11 | 4 | 11 | 4 | 11 | 4 | 11 | 4 | 11 |
3 | 강원도 | 삼척시 | 7115.0 | 15823.0 | 14610.0 | 35253.0 | 34346.0 | 69599.0 | 0.973990 | 1 | ... | 5 | 11 | 5 | 11 | 5 | 11 | 5 | 11 | 5 | 11 |
4 | 강원도 | 속초시 | 8752.0 | 18708.0 | 12752.0 | 40288.0 | 41505.0 | 81793.0 | 1.372647 | 0 | ... | 1 | 10 | 1 | 10 | 1 | 10 | 1 | 10 | 1 | 10 |
5 rows × 42 columns
그리고 위 처럼 두 변수(pop,draw_korea)를 merge 한다.
이제 ID 도 마련하고 각 지역별 좌표도 확보했다. 이제 지도 그릴 준비가 데이터 입장에서는 다 된것 같다.
def drawKorea(targetData, blockedMap, cmapname):
gamma = .75
whitelabelmin = (max(blockedMap[targetData]) - min(blockedMap[targetData]))*0.25 + min(blockedMap[targetData])
datalabel = targetData
vmin = min(blockedMap[targetData])
vmax = max(blockedMap[targetData])
mapdata = blockedMap.pivot_table(index='y', columns = 'x', values = targetData)
masked_mapdata = np.ma.masked_where(np.isnan(mapdata), mapdata)
plt.figure(figsize = (6, 8))
plt.pcolor(masked_mapdata, vmin=vmin, vmax=vmax, cmap=cmapname, edgecolor='#aaaaaa', linewidth=0.5)
for idx, row in blockedMap.iterrows():
if len(row['ID'].split())==2:
dispname = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
elif row['ID'][:2] =='고성':
dispname = '고성'
else:
dispname = row['ID']
if len(dispname.splitlines()[-1]) >= 3:
fontsize, linespacing = 10.0, 1.1
else:
fontsize, linespacing = 11, 1.
row['x'] = int(row['x'])
annocolor = 'white' if row[targetData] > whitelabelmin else 'black'
plt.annotate(dispname, (row['x']+0.5, row['y']+0.5), weight='bold', fontsize= fontsize, ha='center', va='center', linespacing=linespacing)
for path in BORDER_LINES:
ys, xs = zip(*path)
plt.plot(xs, ys, c='black', lw=2)
plt.gca().invert_yaxis()
plt.axis('off')
cb =plt.colorbar(shrink=.1, aspect=10)
cb.set_label(datalabel)
plt.tight_layout()
plt.show()
5-6 인구 현황 및 인구 소멸 지역 확인하기¶
앞에서 만든 함수 drawKorea를 사용해서 인구수합계를 그려보자.
drawKorea('인구수합계', pop, 'Blues')
확실히 서울 송파, 남양주, 화성은 인구가 많다는 것이 보인다. 이렇게 확인 했더니 사선으로 갈라지면서 인구가 거의 없는 곳이 보인다.
pop['소멸위기지역'] = [1 if con else 0 for con in pop['소멸위기지역']]
drawKorea('소멸위기지역', pop, 'Reds')
그리고 원래 우리의 목적 '인구소멸위기지역'을 확인해야 한다. 결과는 위와 같다. 특히 광역시 중에서 부산은 동구와 영도가 위험한 지역에 포함되어 있다. 바꿔 이야기하면 두 지역 외에 인구 밀집 현상이 심하다고 할 수 있다.
5-7 인구 현황에서 여성 인구 비율 확인하기¶
이제는 여성 인구 비율도 확인해 보자. 한때 인구 소멸 위기 지역 이야기가 나오기 전에는 여성 인구가 너무 줄어들어 남녀 비율의 불균형이 뉴스에 자주 등장했는데 지금은 어떤지 확인해 보자.
def drawKorea(targetData, blockedMap, cmapname):
gamma = 0.75
whitelabelmin = 20.
datalabel = targetData
tmp_max = max([ np.abs(min(blockedMap[targetData])),
np.abs(max(blockedMap[targetData]))])
vmin, vmax = -tmp_max, tmp_max
mapdata = blockedMap.pivot_table(index='y', columns='x', values=targetData)
masked_mapdata = np.ma.masked_where(np.isnan(mapdata), mapdata)
plt.figure(figsize=(9, 11))
plt.pcolor(masked_mapdata, vmin=vmin, vmax=vmax, cmap=cmapname,
edgecolor='#aaaaaa', linewidth=0.5)
# 지역 이름 표시
for idx, row in blockedMap.iterrows():
# 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시한다.
#(중구, 서구)
row['x'] = int(row['x'])
if len(row['ID'].split())==2:
dispname = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
elif row['ID'][:2]=='고성':
dispname = '고성'
else:
dispname = row['ID']
# 서대문구, 서귀포시 같이 이름이 3자 이상인 경우에 작은 글자로 표시한다.
if len(dispname.splitlines()[-1]) >= 3:
fontsize, linespacing = 10.0, 1.1
else:
fontsize, linespacing = 11, 1.
annocolor = 'white' if np.abs(row[targetData]) > whitelabelmin else 'black'
plt.annotate(dispname, (row['x']+0.5, row['y']+0.5), weight='bold',
fontsize=fontsize, ha='center', va='center', color=annocolor,
linespacing=linespacing)
# 시도 경계 그린다.
for path in BORDER_LINES:
ys, xs = zip(*path)
plt.plot(xs, ys, c='black', lw=2)
plt.gca().invert_yaxis()
plt.axis('off')
cb = plt.colorbar(shrink=.1, aspect=10)
cb.set_label(datalabel)
plt.tight_layout()
plt.show()
pop['여성비'] = (pop['인구수여자']/pop['인구수합계'] - 0.5)* 100
drawKorea('여성비', pop, 'RdBu')
여성 인구수와 합계를 알고 있어서 나눈 다음 0.5를 빼는 작업을 했다.그래서 0이면 여성 인구수가 50%인 것이다. 이렇게 한 이유는 drawKorea에 색상 팔레트를 지정할 때 RdBu 같은 설정을 사용하면 0을 기준으로 좌우가 다른 색상을 갖도록 할 수 있기 때문이다.
pop['2030여성비'] = (pop['20-39세여자']/pop['20-39세합계'] - 0.5) *100
drawKorea('2030여성비', pop, 'RdBu')
이번엔 20-30대 여성의 20-30대 전체 인구에 대한 비율을 확인해 보았다.
5-8 Folium에서 인구 소멸 위기 지역 표현하기¶
pop_folium = pop.set_index('ID')
pop.head()
광역시도 | 시도 | 20-39세여자 | 20-39세합계 | 65세이상합계 | 인구수남자 | 인구수여자 | 인구수합계 | 소멸비율 | 소멸위기지역 | ... | x_x | y_y | x_y | y_x | x_x | y_y | x_y | y | x | 2030여성비 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 강원도 | 강릉시 | 23098.0 | 49384.0 | 37679.0 | 106231.0 | 107615.0 | 213846.0 | 1.226041 | 0 | ... | 11 | 3 | 11 | 3 | 11 | 3 | 11 | 3 | 11 | -3.227766 |
1 | 강원도 | 고성군 | 2529.0 | 7023.0 | 7151.0 | 15899.0 | 14215.0 | 30114.0 | 0.707314 | 1 | ... | 10 | 0 | 10 | 0 | 10 | 0 | 10 | 0 | 10 | -13.989748 |
2 | 강원도 | 동해시 | 9753.0 | 21264.0 | 15124.0 | 47166.0 | 46131.0 | 93297.0 | 1.289738 | 0 | ... | 11 | 4 | 11 | 4 | 11 | 4 | 11 | 4 | 11 | -4.133747 |
3 | 강원도 | 삼척시 | 7115.0 | 15823.0 | 14610.0 | 35253.0 | 34346.0 | 69599.0 | 0.973990 | 1 | ... | 11 | 5 | 11 | 5 | 11 | 5 | 11 | 5 | 11 | -5.033812 |
4 | 강원도 | 속초시 | 8752.0 | 18708.0 | 12752.0 | 40288.0 | 41505.0 | 81793.0 | 1.372647 | 0 | ... | 10 | 1 | 10 | 1 | 10 | 1 | 10 | 1 | 10 | -3.217875 |
5 rows × 43 columns
원래 pop 데이터에서 ID 컬럼을 index로 설정한다. 그래야 Folium에서 쉽게 인식한다.
import folium
import json
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
geo_path = '/home/jaeyoon89/DataScience/data/05. skorea_municipalities_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))
map = folium.Map(location=[36.2002, 127.054], zoom_start=7)
map.choropleth(geo_data = geo_str,
data = pop_folium['인구수합계'],
columns = [pop_folium.index, pop_folium['인구수합계']],
fill_color = 'YlGnBu', #PuRd, YlGnBu
key_on = 'feature.id')
map
이렇게 지도에서 보니 우리나라의 인구 밀집 현상을 쉽게 확인할 수 있다.
map = folium.Map(location=[36.2002, 127.054], zoom_start=7)
map.choropleth(geo_data = geo_str,
data = pop_folium['소멸위기지역'],
columns = [pop_folium.index, pop_folium['소멸위기지역']],
fill_color = 'PuRd', #PuRd, YlGnBu
key_on = 'feature.id')
map
출처 : 파이썬으로 데이터 주무르기
'파이썬으로 데이터 주무르기' 카테고리의 다른 글
파이썬으로 데이터 주무르기 ch.7 (0) | 2021.04.06 |
---|---|
파이썬으로 데이터 주무르기 ch.6 (0) | 2021.04.05 |
파이썬으로 데이터 주무르기 ch.4 (0) | 2021.04.03 |
파이썬으로 데이터 주무르기 ch.3-2 (0) | 2021.04.02 |
파이썬으로 데이터 주무르기 ch.3-1 (0) | 2021.04.01 |