-
chapter-5 우리나라 인구 소멸 위기 지역 분석파이썬으로 데이터 주무르기 2021. 10. 25. 17:29
5장 우리나라 인구 소멸 위기 지역 분석¶
이번에는 우리나라의 인구 소멸 위기 지역에 대해 조사하겠습니다. 인구 소멸 위기 지역을 시각화해서 위기감을 일으키는 것이 목접입니다. 그러기 위해서는 이전에 했던 서울시 지도가 아니라 대한민국 지도가 그려져야 합니다. 이번 절에서는 대한민국 지도를 그리고 그 위에 인구 소멸 위기 지역에 대해 매핑해서 시각화하는 것을 최종 목표로 합니다.
5- 목표 명확히 하기¶
이상호 한국고용정보원 연구원의 <한국의 지방 소멸에 관한 7가지 분석>이라는 보고서에서 사용한 방법으로, 인구 소멸 지역의 정의를 65세 이상 노인 인구와 20 ~ 39세 여성 인구를 비교해서 젊은 여성인구가 노인 인구의 절반에 미달할 경우 인구 소멸 위험 지역으로 분류하는 방법입니다.
이 방식에 따라 먼저 각 지역별 20 ~ 30대 여성 인구수를 파악해야 하며, 또65세 이상 노인 인구수를 파악해야 합니다. 또한 인구 소멸 위기 지역인지 파악해야 합니다. 그리고 한 단계 더 나아가 한국 지도에 시각화하기 위해 한국 지도를 그리는 법을 확보해야 합니다. 한국 지도 그리는 법은 앞서 사용한 Folium을 이용한 방법과 또 다른 방법 모두 확인해보겠습니다.5-2 인구 데이터 확보하고 정리하기¶
In [1]:import pandas as pd import numpy as np import platform import matplotlib.pyplot as plt %matplotlib inline paht = "c:/Windows/Fonts/malgun.ttf"
In [2]:from matplotlib import font_manager, rc plt.rcParams['axes.unicode_minus'] = False if platform.system() == 'Darwin': rc('font', family='AppleGothic') print('Mac version') elif platform.system() == 'Windows': path = "c:/Windows/Fonts/malgun.ttf" font_name = font_manager.FontProperties(fname=path).get_name() rc('font', family=font_name) print('Windows version') elif platform.system() == 'Linux': path = "/usr/share/fonts/NanumFont/NanumGothicBold.ttf" font_name = font_manager.FontProperties(fname=path).get_name() plt.rc('font', family=font_name) print('Linux version') else: print('Unknown system... sorry~~~~')
Linux version
In [3]:population = pd.read_excel('data/05. population_raw_data.xlsx', header=1) population.fillna(method='pad', inplace = True) population.rename(columns = {'행정구역(동읍면)별(1)':'광역시도', '행정구역(동읍면)별(2)':'시도', '계':'인구수'}, inplace=True) population = population[(population['시도'] != '소계')] population
Out[3]:광역시도 시도 항목 인구수 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 처리를 하지 않고 그 앞 내용으로 채우도록 합니다. 그리고 적절히 컬럼의 이름을 바꾸고 중간중간에 있는'소계'라는 항목도 삭제했습니다.
그렇게 얻은 결과 중 일부입니다. 웹 브라우저에서는 스크롤해야 할 정도의 양이 될 것입니다.In [4]:population.is_copy = False population.rename(columns = {'항목':'구분'}, inplace=True) population.loc[population['구분'] == '총인구수 (명)', '구분'] = '합계' population.loc[population['구분'] == '남자인구수 (명)', '구분'] = '남자' population.loc[population['구분'] == '여자인구수 (명)', '구분'] = '여자' population
Out[4]:광역시도 시도 구분 인구수 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 인구 소멸 위기 지역 계산하고 데이터 정리하기¶
5-1절에서 이야기한 것처럼 인구 소멸 위기 지역을 알기 위해서는 먼저 20-30대의 인구를 알아야합니다. 그리고 65세 이상 인구수도 알아야 합니다.
In [5]: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)
Out[5]:광역시도 시도 구분 인구수 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 아주 손쉽게 계산할 수 있습니다.
그 결과는 쉽게 얻을 수 있습니다. 이제 이 많은 컬럼들 중에서 일부만 선택해야 합니다. 그리고 결정적으로'합계','남자','여자'로 되어 있는 구분도 정리해야 합니다. 이럴 때 사용하는 마법 키워드를 우리는 하나 알고 있습니다.In [6]:pop = pd.pivot_table(population, index = ['광역시도','시도'], columns = ['구분'], values = ['인구수','20-39세', '65세이상']) pop
Out[6]: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세 이상만 데이터로 가져오도록 합니다.
그 결과 중 일부입니다. 이제 대망의 인구 소멸 비율을 계산할 수 있습니다. 20-39세 여성 인구와 65세 이상 인구를 지역별로 알게 되었기 때문입니다.In [7]:pop['소멸비율'] = pop['20-39세','여자'] / (pop['65세이상','합계'] /2) pop.head()
Out[7]: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 In [8]:pop['소멸위기지역'] = pop['소멸비율'] < 1.0 pop.head()
Out[8]: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 5-1절에서 확인한 인구 소멸 위기 지역에 대한 정의에 의해 코드[6]에서 계산한 소멸비율이 1이하면 소멸위기지역이라고 기록합니다
In [9]:pop[pop['소멸위기지역']==True].index.get_level_values(1)
Out[9]:Index(['고성군', '삼척시', '양양군', '영월군', '정선군', '평창군', '홍천군', '횡성군', '가평군', '양평군', '연천군', '거창군', '고성군', '남해군', '밀양시', '산청군', '의령군', '창녕군', '하동군', '함안군', '함양군', '합천군', '고령군', '군위군', '문경시', '봉화군', '상주시', '성주군', '영덕군', '영양군', '영주시', '영천시', '예천군', '울릉군', '울진군', '의성군', '청도군', '청송군', '동구', '영도구', '강화군', '옹진군', '강진군', '고흥군', '곡성군', '구례군', '담양군', '보성군', '신안군', '영광군', '영암군', '완도군', '장성군', '장흥군', '진도군', '함평군', '해남군', '화순군', '고창군', '김제시', '남원시', '무주군', '부안군', '순창군', '임실군', '장수군', '정읍시', '진안군', '공주시', '금산군', '논산시', '보령시', '부여군', '서천군', '예산군', '청양군', '태안군', '홍성군', '괴산군', '단양군', '보은군', '영동군', '옥천군'], dtype='object', name='시도')
그리고 해당 지역의 리스트를 뽑았습니다. 대한민국에서 인구 소멸 위기 지역으로 분류된 곳은 다음과 같습니다.
총 83개 지자체입니다. 이렇게 이름만 나열하는 것은 뭔가 부족해 보입니다. 지도로 시각화하기 위해 조금 더 작업을 진행하겠습니다.In [10]:pop.reset_index(inplace=True) pop.head()
Out[10]:광역시도 시도 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를 다시 초기화합니다.
In [11]:tmp_coloumns = [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_coloumns pop.head()
Out[11]:광역시도 시도 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 역시 다단으로 표시된 컬럼(column)을 하나로 합칩니다.
이제 세로축에 지역, 가로축에 연령대별 혹은 성별 인구수가 정리되었습니다.In [12]: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 파일을 구하는 것입니다.
일단 Lucy Park이라는 이름으로 활동하는 github 사이트가 있습니다. Lucy Park님은 인터넷 상에서 참으로 좋아하고 존경하는 분입니다. Github에서는 e9t라는 아이디로 활동합니다. https://goo.gl/xi5pKD로 접속하면 대한민국 시군구 단위로 구분된 json파일을 얻을 수 있습니다.
5-5 지도 시각화를 위해 지역별 고유 ID 만들기¶
일단 우리에게 필요한 것은 두 방법 모두 지역에 따른 고유ID가 필요하다는 것입니다. 우선 pop['시도']에 대해 unique를 조사하도록 하겠습니다.
In [13]:pop['시도'].unique()
Out[13]:array(['강릉시', '고성군', '동해시', '삼척시', '속초시', '양구군', '양양군', '영월군', '원주시', '인제군', '정선군', '철원군', '춘천시', '태백시', '평창군', '홍천군', '화천군', '횡성군', '가평군', '고양시', '과천시', '광명시', '광주시', '구리시', '군포시', '권선구', '기흥구', '김포시', '남양주시', '단원구', '덕양구', '동두천시', '동안구', '만안구', '부천시', '분당구', '상록구', '성남시', '소사구', '수원시', '수정구', '수지구', '시흥시', '안산시', '안성시', '안양시', '양주시', '양평군', '여주시', '연천군', '영통구', '오산시', '오정구', '용인시', '원미구', '의왕시', '의정부시', '이천시', '일산동구', '일산서구', '장안구', '중원구', '처인구', '파주시', '팔달구', '평택시', '포천시', '하남시', '화성시', '거제시', '거창군', '김해시', '남해군', '마산합포구', '마산회원구', '밀양시', '사천시', '산청군', '성산구', '양산시', '의령군', '의창구', '진주시', '진해구', '창녕군', '창원시', '통영시', '하동군', '함안군', '함양군', '합천군', '경산시', '경주시', '고령군', '구미시', '군위군', '김천시', '남구', '문경시', '봉화군', '북구', '상주시', '성주군', '안동시', '영덕군', '영양군', '영주시', '영천시', '예천군', '울릉군', '울진군', '의성군', '청도군', '청송군', '칠곡군', '포항시', '광산구', '동구', '서구', '달서구', '달성군', '수성구', '중구', '대덕구', '유성구', '강서구', '금정구', '기장군', '동래구', '부산진구', '사상구', '사하구', '수영구', '연제구', '영도구', '해운대구', '강남구', '강동구', '강북구', '관악구', '광진구', '구로구', '금천구', '노원구', '도봉구', '동대문구', '동작구', '마포구', '서대문구', '서초구', '성동구', '성북구', '송파구', '양천구', '영등포구', '용산구', '은평구', '종로구', '중랑구', '세종특별자치시', '울주군', '강화군', '계양구', '남동구', '부평구', '연수구', '옹진군', '강진군', '고흥군', '곡성군', '광양시', '구례군', '나주시', '담양군', '목포시', '무안군', '보성군', '순천시', '신안군', '여수시', '영광군', '영암군', '완도군', '장성군', '장흥군', '진도군', '함평군', '해남군', '화순군', '고창군', '군산시', '김제시', '남원시', '덕진구', '무주군', '부안군', '순창군', '완산구', '완주군', '익산시', '임실군', '장수군', '전주시', '정읍시', '진안군', '서귀포시', '제주시', '계룡시', '공주시', '금산군', '논산시', '당진시', '동남구', '보령시', '부여군', '서북구', '서산시', '서천군', '아산시', '예산군', '천안시', '청양군', '태안군', '홍성군', '괴산군', '단양군', '보은군', '상당구', '서원구', '영동군', '옥천군', '음성군', '제천시', '증평군', '진천군', '청원구', '청주시', '충주시', '흥덕구'], dtype=object)
조사된 결과를 유심히 보면 인천,부산 등 광역시의 구(이런 구를 자치구라고 부릅니다)도 있지만, 안양시나 수원시에 있는 구(이런 구를 행정구라고 합니다.)도 있습니다. 일단 우리는 고유 아이디를 '과역시도'의 값과'시도'의 값을 합치면 될 듯합니다. 물론 지도 표기 및 ID로서의 간결함을 유지하기 위해 서울 강남과 같은 형태를 원칙으로 합니다. 특별히 구 이름이 두 글자인 경우는'서울 중구'로 두면 됩니다. 그럼 일반 자치시의 경우는'충북 제천'과 같이 표기하면 좋겠습니다. 그런데 우리가 받은 엑셀 파일의 규칙을 다시 보면서 단순히 pop['광역시도']와 pop['시도']를 합친다고 하면 광역시도의 자치구는 서울 중구와 같이 만들 수 있지만, 행정구의 경우는 안양시 동안구가 되지 않고'경기도 동안구'가 됩니다. 그래서 이 부분에 대한 고민이 필요합니다.
In [14]:si_name = [None] * len(pop) tmp_gu_dict = {'수원':['장안구','권선구','팔달구','영통구'], '성남':['수정구','중원구','분당구'], '안양':['만안구','동안구'], '안산':['상록구','단원구'], '고양':['덕양구','일산동구','일산서구'], '용인':['처인구','기흥구','수지구'], '청주':['상당구','서원구','흥덕구','청원구'], '천안':['동남구','서북구'], '전주':['완산구','덕진구'], '포항':['남구','북구'], '창원':['의창구','성산구','진해구','마산합포구','마산회원구'], '부천':['오정구','원미구','소사구']}
먼저 광역시가 아니면서 구를 가지고 있는 시와 그 행정구를 파이썬의 dict형으로 선언합니다. 그리고 코드[14]와 같이 작성합니다. 먼저 광역시도에 잇는 이름의 끝 세글자가'광역시','득별시','자치시'로 끝나지 않으면 일반 시 혹은 군으로 봅니다. 그 속에서 강원도와 경상남도에는 동일한 이름을 가진'고성군'이 있어서 그것을 처리합니다. 그리고 방금 이야기한 일반 시인데 구를 가지는 경우에 대해 대응합니다. 그리고 세종특별자치시를 그냥'세종'으로 처리하고, 나머지는 광역시도에서 앞 두 글자(서울특별시)와 시도에서 두 글자인 경우 모두, 아니면 앞 두 글자만 선택하면서 고유 ID를 만듭니다.
In [15]: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]
In [16]:si_name
Out[16]:['강릉', '고성(강원)', '동해', '삼척', '속초', '양구', '양양', '영월', '원주', '인제', '정선', '철원', '춘천', '태백', '평창', '홍천', '화천', '횡성', '가평', '고양', '과천', '광명', '광주', '구리', '군포', '수원 권선', '용인 기흥', '김포', '남양주', '안산 단원', '고양 덕양', '동두천', '안양 동안', '안양 만안', '부천', '성남 분당', '안산 상록', '성남', '부천 소사', '수원', '성남 수정', '용인 수지', '시흥', '안산', '안성', '안양', '양주', '양평', '여주', '연천', '수원 영통', '오산', '부천 오정', '용인', '부천 원미', '의왕', '의정부', '이천', '고양 일산동', '고양 일산서', '수원 장안', '성남 중원', '용인 처인', '파주', '수원 팔달', '평택', '포천', '하남', '화성', '거제', '거창', '고성(경남)', '김해', '남해', '창원 합포', '창원 회원', '밀양', '사천', '산청', '창원 성산', '양산', '의령', '창원 의창', '진주', '창원 진해', '창녕', '창원', '통영', '하동', '함안', '함양', '합천', '경산', '경주', '고령', '구미', '군위', '김천', '포항 남구', '문경', '봉화', '포항 북구', '상주', '성주', '안동', '영덕', '영양', '영주', '영천', '예천', '울릉', '울진', '의성', '청도', '청송', '칠곡', '포항', '광주 광산', '광주 남구', '광주 동구', '광주 북구', '광주 서구', '대구 남구', '대구 달서', '대구 달성', '대구 동구', '대구 북구', '대구 서구', '대구 수성', '대구 중구', '대전 대덕', '대전 동구', '대전 서구', '대전 유성', '대전 중구', '부산 강서', '부산 금정', '부산 기장', '부산 남구', '부산 동구', '부산 동래', '부산 부산진', '부산 북구', '부산 사상', '부산 사하', '부산 서구', '부산 수영', '부산 연제', '부산 영도', '부산 중구', '부산 해운대', '서울 강남', '서울 강동', '서울 강북', '서울 강서', '서울 관악', '서울 광진', '서울 구로', '서울 금천', '서울 노원', '서울 도봉', '서울 동대문', '서울 동작', '서울 마포', '서울 서대문', '서울 서초', '서울 성동', '서울 성북', '서울 송파', '서울 양천', '서울 영등포', '서울 용산', '서울 은평', '서울 종로', '서울 중구', '서울 중랑', '세종', '울산 남구', '울산 동구', '울산 북구', '울산 울주', '울산 중구', '인천 강화', '인천 계양', '인천 남구', '인천 남동', '인천 동구', '인천 부평', '인천 서구', '인천 연수', '인천 옹진', '인천 중구', '강진', '고흥', '곡성', '광양', '구례', '나주', '담양', '목포', '무안', '보성', '순천', '신안', '여수', '영광', '영암', '완도', '장성', '장흥', '진도', '함평', '해남', '화순', '고창', '군산', '김제', '남원', '전주 덕진', '무주', '부안', '순창', '전주 완산', '완주', '익산', '임실', '장수', '전주', '정읍', '진안', '서귀포', '제주', '계룡', '공주', '금산', '논산', '당진', '천안 동남', '보령', '부여', '천안 서북', '서산', '서천', '아산', '예산', '천안', '청양', '태안', '홍성', '괴산', '단양', '보은', '청주 상당', '청주 서원', '영동', '옥천', '음성', '제천', '증평', '진천', '청주 청원', '청주', '충주', '청주 흥덕']
결과는 이렇게 나타납니다.
In [17]:pop['ID'] = si_name
결과를 pop에 포함시키고 이제 큰 의미가 없는 몇몇 컬럼을 제거합니다.
In [18]:del pop['20-39세남자'] del pop['65세이상남자'] del pop['65세이상여자'] pop.head()
Out[18]:광역시도 시도 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-6 Cartogram으로 우리나라 지도 만들기¶
5-4절에서 이야기했지만 혜식 님의 방식은 그 시작을 고민해야 합니다. 혜식님의 방법에서는 안양동안이나 안양 만안을 합쳐서 그냥'안양'이라고만 되어 있기 때문에 제 의도와는 약간 다른 것이 문제입니다. 그래서 처음부터 고민해야하는 것인데, 저는 그 시작 원리로 엑셀로 잡았습니다.
In [19]:draw_korea_raw = pd.read_excel('data/05. draw_korea_raw.xlsx', engine='openpyxl') draw_korea_raw
Out[19]: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 우리에게 필요한 것은 각 지역별 x,y좌표입니다.
In [20]: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
Out[20]: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
그래서 stack()으로 풀고 인덱스를 재설정(reset_index)했습니다. 그리고 다시 컬럼 이름을 바꿨습니다.
이제 각 지역에 대한 좌표를 얻었습니다. 철원은 (7,0)지점입니다In [21]:draw_korea = draw_korea_raw_stacked
변수 이름을 변경합니다.
In [22]: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)], ]
그리고 광역시도를 구분하는 경계선도 직접 입력했습니다.
In [23]: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'] # 서대문구, 서귀포시 같이 이름이 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()
이 코드는 길어서 Jupyter Notebook 화면으로 담을 수 없었습니다. 이 코드까지 실행하면 위 그림과 같이 경계선과 지역 이름만 나타납니다. 반복문 안에서 대다수 코드는 이름을 표기하기 위한 코드입니다. 그리고 경계선을 그립니다. 마지막 부분에 invert_yaxis()가 되어 있는 이유는 y축이 엑셀에서 0번이 시작하는 것과 matplotlib가 0이라고 인식하는 좌표가 서로 반대이기 때문입니다. 일단 대한민국 지도 그리기는 잘 될 듯합니다.
In [24]: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()
set()
Out[24]:set()
원래 인구 현황을 가지고 있던 pop 변수와 엑셀에서 출발해서 지도를 그리기 위해 만든 draw_korea변수에서 일반 행정구를 가진 시 (성남,수원 등등)의 합계 정보를 삭제합니다
In [25]:pop = pd.merge(pop, draw_korea, how='left', on=['ID']) pop.head()
Out[25]:광역시도 시도 20-39세여자 20-39세합계 65세이상합계 인구수남자 인구수여자 인구수합계 소멸비율 소멸위기지역 ID y x 0 강원도 강릉시 23098.0 49384.0 37679.0 106231.0 107615.0 213846.0 1.226041 False 강릉 3 11 1 강원도 고성군 2529.0 7023.0 7151.0 15899.0 14215.0 30114.0 0.707314 True 고성(강원) 0 10 2 강원도 동해시 9753.0 21264.0 15124.0 47166.0 46131.0 93297.0 1.289738 False 동해 4 11 3 강원도 삼척시 7115.0 15823.0 14610.0 35253.0 34346.0 69599.0 0.973990 True 삼척 5 11 4 강원도 속초시 8752.0 18708.0 12752.0 40288.0 41505.0 81793.0 1.372647 False 속초 1 10 이제 ID도 마련하고 각 지역별 좌표도 모두 확보했습니다. 이렇게 해서 지도 그릴 준비가 데이터 입장에서는 다 된 듯합니다.
In [26]:def drawKorea(targetData, blockedMap, cmapname): gamma = 0.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=(9, 11)) 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'] # 서대문구, 서귀포시 같이 이름이 3자 이상인 경우에 작은 글자로 표시한다. if len(dispname.splitlines()[-1]) >= 3: fontsize, linespacing = 10.0, 1.1 else: fontsize, linespacing = 11, 1. 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', 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()
위 코드는 앞서 이야기한 혜식 님의 메인 함수입니다. 이것을 그대로 사용할 수 있도록 작업한 것과 같다고 생각해도 됩니다.
5-7 인구 현황 및 인구 소멸 지역 확인하기¶
5-6절에서 만든 함수 drawKorea를 사용해서 인구수합계를 그려봅니다.
In [27]:drawKorea('인구수합계', pop, 'Blues')
확실히 서울 송파,남양주,화성은 인구가 많다는 것이 보입니다. 이렇게 확인했더니 사선으로 갈라지면서 인구가 거의 없는 곳이 보이네요.
In [28]:pop['소멸위기지역'] = [1 if con else 0 for con in pop['소멸위기지역']] drawKorea('소멸위기지역', pop, 'Reds')
그리고 원래 우리의 목적'인구소멸위기지역'을 확인해야 합니다. 그림을 그리기 위해 bool형이었던것을 1과 0으로 바꾸겠습니다. 그러면 그림에서 색이 확연히 구분됩니다.
결과는 위와 같습니다. 생각보다 심각합니다. 특히 광역시 중에서도 부산은 동구와 영도가 위험한 지역에 포함되어 있습니다. 바꿔 이야기하면 두 지역 외에 인구 밀집 현상이 심하다고 할 수도 있습니다.5-8 인구 현황에서 여성 인구 비율 확인하기¶
이미 가지고 있는 데이터이니 여성 인구 비율도 확인해보겠습니다. 한때 인구 소멸 위기 지역 이야기가 나오기 전에는 여성 인구가 너무 줄어들어 남녀 비율의 불균형이 뉴스에 자주 등장했는데 지금은 어떤지 확인해보겠습니다.
그러기 위해서는 5-7절의 drawKorea 함수의 일부 내용이 바뀌어야 합니다. 이유는 표현하고자 하는 데이터에 음(-)의 값이 있는지 여부에 따라 일부 설정이 바뀌어야 하기 때문입니다.In [29]: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(): # 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시한다. #(중구, 서구) 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()
그와 같은 설정은 당연히 평상시라면 한 함수에서 처리하도록 해야 하지만, 지금은 오히려 너무 함수코드를 어렵게 만드는 것 같아 그냥 두도록 하겠습니다.
In [30]:pop['여성비'] = (pop['인구수여자']/pop['인구수합계'] - 0.5)*100 drawKorea('여성비', pop, 'RdBu')
여성인구수와 합꼐를 알고 있어서 나눈 다음 0.5를 빼는 작업을 했습니다. 그래서 0이면 여성 인구수가 50%인 것입니다. 이렇게 한 이유는 drawKorea에 색상 팔레트를 지정할 때 RdBu 같은 설정을 사용하면 0을 기준으로 좌우가 다른 색상을 갖도록 할 수 있기 때문입니다.
일단 그 결과를 보면 파란색 으로 갈수록 여성비가 높은 것이고, 빨간색으로 갈수록 여성비가 낮은 것입니다. 극도로 남성 비율이 높은 몇몇 지역이 있지만 어림잡아 지역별로 여성비의 높낮이가 비슷해 보입니다. 그러나 이것은 여성 인구 전체를 대상으로 한 것으로 20-30대 여성으로 데이터를 바꾸면 상황은 달라집니다.In [31]:pop['2030여성비'] = (pop['20-39세여자']/pop['20-39세합계'] -0.5)*100 drawKorea('2030여성비', pop, 'RdBu')
이번에는 20-30대 여성의 20-30대 전체 인구에 대한 비율을 확인해보겠습니다. 이번에는 확실히 위기감이 들 모양새가 됩니다. 고령 여성 인구를 제외하고 나면 전국적으로 남성 인구 비율이 높다는 이야기가 되니 이것도 어쩌면 사회문제가 될지도 모르겠습니다.
5-9 Folium에서 인구 소멸 위기 지역 표현하기¶
In [32]:pop_folium = pop.set_index('ID') pop_folium.head()
Out[32]:광역시도 시도 20-39세여자 20-39세합계 65세이상합계 인구수남자 인구수여자 인구수합계 소멸비율 소멸위기지역 y x 여성비 2030여성비 ID 강릉 강원도 강릉시 23098.0 49384.0 37679.0 106231.0 107615.0 213846.0 1.226041 0 3 11 0.323597 -3.227766 고성(강원) 강원도 고성군 2529.0 7023.0 7151.0 15899.0 14215.0 30114.0 0.707314 1 0 10 -2.796042 -13.989748 동해 강원도 동해시 9753.0 21264.0 15124.0 47166.0 46131.0 93297.0 1.289738 0 4 11 -0.554680 -4.133747 삼척 강원도 삼척시 7115.0 15823.0 14610.0 35253.0 34346.0 69599.0 0.973990 1 5 11 -0.651590 -5.033812 속초 강원도 속초시 8752.0 18708.0 12752.0 40288.0 41505.0 81793.0 1.372647 0 1 10 0.743951 -3.217875 In [33]:import folium import json import warnings warnings.simplefilter(action='ignore', category=FutureWarning)
Folium과 json을 import 한다.
In [34]:geo_path = '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
Out[34]:Make this Notebook Trusted to load map: File -> Trust Notebook앞 절에서 약간 편집을 한 json파일을 연결하고 '인구수합계'를 표현했다.
In [35]: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
Out[35]:Make this Notebook Trusted to load map: File -> Trust Notebook인구 소멸 위기 지역도 확인했습니다.
출처 : "파이썬으로 데이터 주무르기"
'파이썬으로 데이터 주무르기' 카테고리의 다른 글
chapter-7 시계열 데이터를 다뤄보자 (0) 2021.10.25 chapter-3 시카고 샌드위치 맛집 분석 & 네이버 영화 평점 변화,날짜 변화 평점 확인하기 (0) 2021.10.23 chapter-2 서울시 범죄 현황 분석 (0) 2021.10.21 chapter-01 서울시 구별 CCTV 현황 분석 (0) 2021.10.21