데이터분석
[23.07.03] 데이터 시각화(인구 소멸) - 22(2)
gmwoo
2023. 7. 3. 17:31
우리나라 인구 소명 위기 지역 분석¶
인구 소멸 위기 지역:¶
‘한국의 ‘지방소멸' 65세 이상 노인 인구와 20∼39세 여성 인구를 비교해 젊은 여성 인구가 노인 인구의 절반에 미달할 경우 ‘소멸 위험 지역’으로 분류하는 방식
- 국가통계포털(KOSIS) https://kosis.kr/index/index.do
인구데이터 확보하고 정리하기¶
In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import platform
import warnings
warnings.filterwarnings(action='ignore')
path = "C:/Windows/Fonts/malgun.ttf"
from matplotlib import font_manager, rc
if platform.system() == 'Darwin':
rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
font_name = font_manager.FontProperties(fname=path).get_name()
rc('font', family=font_name)
else:
print('Unknown system... sorry~~~~')
In [2]:
population = pd.read_excel('../../data/05. population_raw_data.xlsx', header=1)
population
Out[2]:
행정구역(동읍면)별(1) | 행정구역(동읍면)별(2) | 항목 | 계 | 20 - 24세 | 25 - 29세 | 30 - 34세 | 35 - 39세 | 65 - 69세 | 70 - 74세 | 75 - 79세 | 80 - 84세 | 85 - 89세 | 90 - 94세 | 95 - 99세 | 100+ | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 전국 | 소계 | 총인구수 (명) | 51696216.0 | 3541061.0 | 3217367.0 | 3517868 | 4016272.0 | 2237345.0 | 1781229.0 | 1457890 | 909130.0 | 416164.0 | 141488.0 | 34844 | 17562.0 |
1 | NaN | NaN | 남자인구수 (명) | 25827594.0 | 1877127.0 | 1682988.0 | 1806754 | 2045265.0 | 1072395.0 | 806680.0 | 600607 | 319391.0 | 113221.0 | 32695.0 | 7658 | 4137.0 |
2 | NaN | NaN | 여자인구수 (명) | 25868622.0 | 1663934.0 | 1534379.0 | 1711114 | 1971007.0 | 1164950.0 | 974549.0 | 857283 | 589739.0 | 302943.0 | 108793.0 | 27186 | 13425.0 |
3 | 서울특별시 | 소계 | 총인구수 (명) | 9930616.0 | 690728.0 | 751973.0 | 803507 | 817467.0 | 448956.0 | 350580.0 | 251961 | 141649.0 | 66067.0 | 24153.0 | 7058 | 5475.0 |
4 | NaN | NaN | 남자인구수 (명) | 4876789.0 | 347534.0 | 372249.0 | 402358 | 410076.0 | 211568.0 | 163766.0 | 112076 | 54033.0 | 19595.0 | 6146.0 | 1900 | 1406.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
841 | NaN | NaN | 남자인구수 (명) | 235977.0 | 17377.0 | 13118.0 | 15084 | 18350.0 | 8474.0 | 6782.0 | 4941 | 2737.0 | 854.0 | 226.0 | 53 | 17.0 |
842 | NaN | NaN | 여자인구수 (명) | 234688.0 | 15261.0 | 12245.0 | 14687 | 18062.0 | 9265.0 | 7877.0 | 7178 | 5649.0 | 3122.0 | 1387.0 | 460 | 137.0 |
843 | NaN | 서귀포시 | 총인구수 (명) | 170932.0 | 10505.0 | 8067.0 | 9120 | 11606.0 | 8686.0 | 7460.0 | 6456 | 4521.0 | 1855.0 | 733.0 | 242 | 77.0 |
844 | NaN | NaN | 남자인구수 (명) | 86568.0 | 5600.0 | 4247.0 | 4693 | 6082.0 | 4237.0 | 3441.0 | 2611 | 1494.0 | 370.0 | 103.0 | 29 | 9.0 |
845 | NaN | NaN | 여자인구수 (명) | 84364.0 | 4905.0 | 3820.0 | 4427 | 5524.0 | 4449.0 | 4019.0 | 3845 | 3027.0 | 1485.0 | 630.0 | 213 | 68.0 |
846 rows × 16 columns
In [3]:
population.rename(columns = {'항목':'구분'}, inplace=True)
population.loc[population['구분'] == '총인구수 (명)', '구분'] = '합계'
population.loc[population['구분'] == '남자인구수 (명)', '구분'] = '남자'
population.loc[population['구분'] == '여자인구수 (명)', '구분'] = '여자'
population
Out[3]:
행정구역(동읍면)별(1) | 행정구역(동읍면)별(2) | 구분 | 계 | 20 - 24세 | 25 - 29세 | 30 - 34세 | 35 - 39세 | 65 - 69세 | 70 - 74세 | 75 - 79세 | 80 - 84세 | 85 - 89세 | 90 - 94세 | 95 - 99세 | 100+ | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 전국 | 소계 | 합계 | 51696216.0 | 3541061.0 | 3217367.0 | 3517868 | 4016272.0 | 2237345.0 | 1781229.0 | 1457890 | 909130.0 | 416164.0 | 141488.0 | 34844 | 17562.0 |
1 | NaN | NaN | 남자 | 25827594.0 | 1877127.0 | 1682988.0 | 1806754 | 2045265.0 | 1072395.0 | 806680.0 | 600607 | 319391.0 | 113221.0 | 32695.0 | 7658 | 4137.0 |
2 | NaN | NaN | 여자 | 25868622.0 | 1663934.0 | 1534379.0 | 1711114 | 1971007.0 | 1164950.0 | 974549.0 | 857283 | 589739.0 | 302943.0 | 108793.0 | 27186 | 13425.0 |
3 | 서울특별시 | 소계 | 합계 | 9930616.0 | 690728.0 | 751973.0 | 803507 | 817467.0 | 448956.0 | 350580.0 | 251961 | 141649.0 | 66067.0 | 24153.0 | 7058 | 5475.0 |
4 | NaN | NaN | 남자 | 4876789.0 | 347534.0 | 372249.0 | 402358 | 410076.0 | 211568.0 | 163766.0 | 112076 | 54033.0 | 19595.0 | 6146.0 | 1900 | 1406.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
841 | NaN | NaN | 남자 | 235977.0 | 17377.0 | 13118.0 | 15084 | 18350.0 | 8474.0 | 6782.0 | 4941 | 2737.0 | 854.0 | 226.0 | 53 | 17.0 |
842 | NaN | NaN | 여자 | 234688.0 | 15261.0 | 12245.0 | 14687 | 18062.0 | 9265.0 | 7877.0 | 7178 | 5649.0 | 3122.0 | 1387.0 | 460 | 137.0 |
843 | NaN | 서귀포시 | 합계 | 170932.0 | 10505.0 | 8067.0 | 9120 | 11606.0 | 8686.0 | 7460.0 | 6456 | 4521.0 | 1855.0 | 733.0 | 242 | 77.0 |
844 | NaN | NaN | 남자 | 86568.0 | 5600.0 | 4247.0 | 4693 | 6082.0 | 4237.0 | 3441.0 | 2611 | 1494.0 | 370.0 | 103.0 | 29 | 9.0 |
845 | NaN | NaN | 여자 | 84364.0 | 4905.0 | 3820.0 | 4427 | 5524.0 | 4449.0 | 4019.0 | 3845 | 3027.0 | 1485.0 | 630.0 | 213 | 68.0 |
846 rows × 16 columns
In [4]:
# 결측값을 앞 방향 혹은 뒷 방향으로 채우기
# fillna(method='ffill' or 'pad'), fillna(method='bfill' or 'backfill')
population.fillna(method='pad', inplace=True)
population.rename(columns = {'행정구역(동읍면)별(1)':'광역시도',
'행정구역(동읍면)별(2)':'시도',
'계':'인구수'}, inplace=True)
population = population[(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
- 위 표를 보면 항목이라는 컬럼의 내용이 각 행정구역 마다 총인구수, 남자인구수, 여자인구수로 나눠있는 것을 알 수 있음
- 이를 지금 정리하기 위해 간단히 반복문(for)으로 합계, 남자, 여자로 변경하고 구분이라는 컬럼으로 저장함
- 그리고, 항목을 지우기로 함
인구 소멸 위기 지역 계산하고 데이터 정리하기¶
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()
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 |
- pivot_table을 이용하여 광역시도,시도를 index로 두고, 구분으로 세로를 첫 번째 컬럼을 잡고, value에 인구수, 20~39세, 65세 이상으로 정리해 둔다.
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
- 소멸비율이라는 컬럼에 인구소멸위기지역을 계산하기 위한 식을 적용한다
- 이 비율이 1보다 작으면 인구소멸위기지역으로 볼 수 있다.
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 |
- 소멸위기지역인지를 boolean으로 지정해 둔다
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 |
In [9]:
#다중색인
#df.index.get_level_values('name_index')
pop[pop['소멸위기지역']==True].index.get_level_values(0)
Out[9]:
Index(['강원도', '강원도', '강원도', '강원도', '강원도', '강원도', '강원도', '강원도', '경기도', '경기도',
'경기도', '경상남도', '경상남도', '경상남도', '경상남도', '경상남도', '경상남도', '경상남도', '경상남도',
'경상남도', '경상남도', '경상남도', '경상북도', '경상북도', '경상북도', '경상북도', '경상북도', '경상북도',
'경상북도', '경상북도', '경상북도', '경상북도', '경상북도', '경상북도', '경상북도', '경상북도', '경상북도',
'경상북도', '부산광역시', '부산광역시', '인천광역시', '인천광역시', '전라남도', '전라남도', '전라남도',
'전라남도', '전라남도', '전라남도', '전라남도', '전라남도', '전라남도', '전라남도', '전라남도', '전라남도',
'전라남도', '전라남도', '전라남도', '전라남도', '전라북도', '전라북도', '전라북도', '전라북도', '전라북도',
'전라북도', '전라북도', '전라북도', '전라북도', '전라북도', '충청남도', '충청남도', '충청남도', '충청남도',
'충청남도', '충청남도', '충청남도', '충청남도', '충청남도', '충청남도', '충청북도', '충청북도', '충청북도',
'충청북도', '충청북도'],
dtype='object', name='광역시도')
In [10]:
pop[pop['소멸위기지역']==True].index.get_level_values(1)
Out[10]:
Index(['고성군', '삼척시', '양양군', '영월군', '정선군', '평창군', '홍천군', '횡성군', '가평군', '양평군',
'연천군', '거창군', '고성군', '남해군', '밀양시', '산청군', '의령군', '창녕군', '하동군', '함안군',
'함양군', '합천군', '고령군', '군위군', '문경시', '봉화군', '상주시', '성주군', '영덕군', '영양군',
'영주시', '영천시', '예천군', '울릉군', '울진군', '의성군', '청도군', '청송군', '동구', '영도구',
'강화군', '옹진군', '강진군', '고흥군', '곡성군', '구례군', '담양군', '보성군', '신안군', '영광군',
'영암군', '완도군', '장성군', '장흥군', '진도군', '함평군', '해남군', '화순군', '고창군', '김제시',
'남원시', '무주군', '부안군', '순창군', '임실군', '장수군', '정읍시', '진안군', '공주시', '금산군',
'논산시', '보령시', '부여군', '서천군', '예산군', '청양군', '태안군', '홍성군', '괴산군', '단양군',
'보은군', '영동군', '옥천군'],
dtype='object', name='시도')
- pivot_table로 잘정리가 된 상태에서 .reset_index로 pivot_table의 reset 속성을 다시 설정한다.
In [11]:
pop.reset_index(inplace=True)
pop.head()
Out[11]:
광역시도 | 시도 | 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 |
In [12]:
print(pop.columns.get_level_values(0))
print(pop.columns.get_level_values(1))
Index(['광역시도', '시도', '20-39세', '20-39세', '20-39세', '65세이상', '65세이상', '65세이상',
'인구수', '인구수', '인구수', '소멸비율', '소멸위기지역'],
dtype='object')
Index(['', '', '남자', '여자', '합계', '남자', '여자', '합계', '남자', '여자', '합계', '', ''], dtype='object', name='구분')
- 이중 columns을 해제하기 위해 두컬럼 제목을 합쳐 다시 지정한다.
In [13]:
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[13]:
광역시도 | 시도 | 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 |
지도 시각화를 위해 지역별 고유 ID 만들기¶
In [14]:
pop['시도'].unique()
Out[14]:
array(['강릉시', '고성군', '동해시', '삼척시', '속초시', '양구군', '양양군', '영월군', '원주시',
'인제군', '정선군', '철원군', '춘천시', '태백시', '평창군', '홍천군', '화천군', '횡성군',
'가평군', '고양시', '과천시', '광명시', '광주시', '구리시', '군포시', '권선구', '기흥구',
'김포시', '남양주시', '단원구', '덕양구', '동두천시', '동안구', '만안구', '부천시', '분당구',
'상록구', '성남시', '소사구', '수원시', '수정구', '수지구', '시흥시', '안산시', '안성시',
'안양시', '양주시', '양평군', '여주시', '연천군', '영통구', '오산시', '오정구', '용인시',
'원미구', '의왕시', '의정부시', '이천시', '일산동구', '일산서구', '장안구', '중원구', '처인구',
'파주시', '팔달구', '평택시', '포천시', '하남시', '화성시', '거제시', '거창군', '김해시',
'남해군', '마산합포구', '마산회원구', '밀양시', '사천시', '산청군', '성산구', '양산시', '의령군',
'의창구', '진주시', '진해구', '창녕군', '창원시', '통영시', '하동군', '함안군', '함양군',
'합천군', '경산시', '경주시', '고령군', '구미시', '군위군', '김천시', '남구', '문경시',
'봉화군', '북구', '상주시', '성주군', '안동시', '영덕군', '영양군', '영주시', '영천시',
'예천군', '울릉군', '울진군', '의성군', '청도군', '청송군', '칠곡군', '포항시', '광산구',
'동구', '서구', '달서구', '달성군', '수성구', '중구', '대덕구', '유성구', '강서구', '금정구',
'기장군', '동래구', '부산진구', '사상구', '사하구', '수영구', '연제구', '영도구', '해운대구',
'강남구', '강동구', '강북구', '관악구', '광진구', '구로구', '금천구', '노원구', '도봉구',
'동대문구', '동작구', '마포구', '서대문구', '서초구', '성동구', '성북구', '송파구', '양천구',
'영등포구', '용산구', '은평구', '종로구', '중랑구', '세종특별자치시', '울주군', '강화군', '계양구',
'남동구', '부평구', '연수구', '옹진군', '강진군', '고흥군', '곡성군', '광양시', '구례군',
'나주시', '담양군', '목포시', '무안군', '보성군', '순천시', '신안군', '여수시', '영광군',
'영암군', '완도군', '장성군', '장흥군', '진도군', '함평군', '해남군', '화순군', '고창군',
'군산시', '김제시', '남원시', '덕진구', '무주군', '부안군', '순창군', '완산구', '완주군',
'익산시', '임실군', '장수군', '전주시', '정읍시', '진안군', '서귀포시', '제주시', '계룡시',
'공주시', '금산군', '논산시', '당진시', '동남구', '보령시', '부여군', '서북구', '서산시',
'서천군', '아산시', '예산군', '천안시', '청양군', '태안군', '홍성군', '괴산군', '단양군',
'보은군', '상당구', '서원구', '영동군', '옥천군', '음성군', '제천시', '증평군', '진천군',
'청원구', '청주시', '충주시', '흥덕구'], dtype=object)
In [15]:
# 인천, 부산등 광역시 > 자치구
# 안양시, 수원시 > 행정구
pop[pop['시도'] == '분당구']
Out[15]:
광역시도 | 시도 | 20-39세남자 | 20-39세여자 | 20-39세합계 | 65세이상남자 | 65세이상여자 | 65세이상합계 | 인구수남자 | 인구수여자 | 인구수합계 | 소멸비율 | 소멸위기지역 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
35 | 경기도 | 분당구 | 72407.0 | 77174.0 | 149581.0 | 21745.0 | 29118.0 | 50863.0 | 244502.0 | 259328.0 | 503830.0 | 3.034583 | False |
In [16]:
si_name = [None] * len(pop)
tmp_gu_dict = {'수원':['장안구', '권선구', '팔달구', '영통구'],
'성남':['수정구', '중원구', '분당구'],
'안양':['만안구', '동안구'],
'안산':['상록구', '단원구'],
'고양':['덕양구', '일산동구', '일산서구'],
'용인':['처인구', '기흥구', '수지구'],
'청주':['상당구', '서원구', '흥덕구', '청원구'],
'천안':['동남구', '서북구'],
'전주':['완산구', '덕진구'],
'포항':['남구', '북구'],
'창원':['의창구', '성산구', '진해구', '마산합포구', '마산회원구'],
'부천':['오정구', '원미구', '소사구']}
In [17]:
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: #keys 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 [18]:
si_name
Out[18]:
['강릉',
'고성(강원)',
'동해',
'삼척',
'속초',
'양구',
'양양',
'영월',
'원주',
'인제',
'정선',
'철원',
'춘천',
'태백',
'평창',
'홍천',
'화천',
'횡성',
'가평',
'고양',
'과천',
'광명',
'광주',
'구리',
'군포',
'수원 권선',
'용인 기흥',
'김포',
'남양주',
'안산 단원',
'고양 덕양',
'동두천',
'안양 동안',
'안양 만안',
'부천',
'성남 분당',
'안산 상록',
'성남',
'부천 소사',
'수원',
'성남 수정',
'용인 수지',
'시흥',
'안산',
'안성',
'안양',
'양주',
'양평',
'여주',
'연천',
'수원 영통',
'오산',
'부천 오정',
'용인',
'부천 원미',
'의왕',
'의정부',
'이천',
'고양 일산동',
'고양 일산서',
'수원 장안',
'성남 중원',
'용인 처인',
'파주',
'수원 팔달',
'평택',
'포천',
'하남',
'화성',
'거제',
'거창',
'고성(경남)',
'김해',
'남해',
'창원 합포',
'창원 회원',
'밀양',
'사천',
'산청',
'창원 성산',
'양산',
'의령',
'창원 의창',
'진주',
'창원 진해',
'창녕',
'창원',
'통영',
'하동',
'함안',
'함양',
'합천',
'경산',
'경주',
'고령',
'구미',
'군위',
'김천',
'포항 남구',
'문경',
'봉화',
'포항 북구',
'상주',
'성주',
'안동',
'영덕',
'영양',
'영주',
'영천',
'예천',
'울릉',
'울진',
'의성',
'청도',
'청송',
'칠곡',
'포항',
'광주 광산',
'광주 남구',
'광주 동구',
'광주 북구',
'광주 서구',
'대구 남구',
'대구 달서',
'대구 달성',
'대구 동구',
'대구 북구',
'대구 서구',
'대구 수성',
'대구 중구',
'대전 대덕',
'대전 동구',
'대전 서구',
'대전 유성',
'대전 중구',
'부산 강서',
'부산 금정',
'부산 기장',
'부산 남구',
'부산 동구',
'부산 동래',
'부산 부산진',
'부산 북구',
'부산 사상',
'부산 사하',
'부산 서구',
'부산 수영',
'부산 연제',
'부산 영도',
'부산 중구',
'부산 해운대',
'서울 강남',
'서울 강동',
'서울 강북',
'서울 강서',
'서울 관악',
'서울 광진',
'서울 구로',
'서울 금천',
'서울 노원',
'서울 도봉',
'서울 동대문',
'서울 동작',
'서울 마포',
'서울 서대문',
'서울 서초',
'서울 성동',
'서울 성북',
'서울 송파',
'서울 양천',
'서울 영등포',
'서울 용산',
'서울 은평',
'서울 종로',
'서울 중구',
'서울 중랑',
'세종',
'울산 남구',
'울산 동구',
'울산 북구',
'울산 울주',
'울산 중구',
'인천 강화',
'인천 계양',
'인천 남구',
'인천 남동',
'인천 동구',
'인천 부평',
'인천 서구',
'인천 연수',
'인천 옹진',
'인천 중구',
'강진',
'고흥',
'곡성',
'광양',
'구례',
'나주',
'담양',
'목포',
'무안',
'보성',
'순천',
'신안',
'여수',
'영광',
'영암',
'완도',
'장성',
'장흥',
'진도',
'함평',
'해남',
'화순',
'고창',
'군산',
'김제',
'남원',
'전주 덕진',
'무주',
'부안',
'순창',
'전주 완산',
'완주',
'익산',
'임실',
'장수',
'전주',
'정읍',
'진안',
'서귀포',
'제주',
'계룡',
'공주',
'금산',
'논산',
'당진',
'천안 동남',
'보령',
'부여',
'천안 서북',
'서산',
'서천',
'아산',
'예산',
'천안',
'청양',
'태안',
'홍성',
'괴산',
'단양',
'보은',
'청주 상당',
'청주 서원',
'영동',
'옥천',
'음성',
'제천',
'증평',
'진천',
'청주 청원',
'청주',
'충주',
'청주 흥덕']
- 지도 시각화에 사용하기 위해 위과정에서 만들어진 행정구역의 고유한 이름을 ID로 지정한다
In [19]:
pop['ID'] = si_name
In [20]:
del pop['20-39세남자']
del pop['65세이상남자']
del pop['65세이상여자']
pop.head()
Out[20]:
광역시도 | 시도 | 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 | 속초 |
Cartogram으로 우리나라 지도 만들기¶
In [21]:
draw_korea_raw = pd.read_excel('../../data/05. draw_korea_raw.xlsx')
draw_korea_raw
Out[21]:
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 |
- 이제 각 행정구역의 화면상 좌표를 얻기위해 pivot_table의 반대개념으로 .stack() 명령을 사용한다.
In [22]:
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[22]:
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
- 다시 인덱스를 재설정하고
- 컬럼의 이름을 다시 설정해 준다.
In [23]:
draw_korea = draw_korea_raw_stacked
- 먼저 ID 컬럼에서 시도에 표기할때 시 이름 구 이름으로 줄을 나누기 위해 분리한다
In [24]:
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 [25]:
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()
- 인구에 대한 분석 결과인 pop과 지도를 그리기 위한 draw_korea의 데이터를 합칠 때 사용할 key인 ID컬럼의 내용이 문제가 없는지 확인하자
In [26]:
set(draw_korea['ID'].unique()) - set(pop['ID'].unique())
Out[26]:
set()
In [27]:
set(pop['ID'].unique()) - set(draw_korea['ID'].unique())
Out[27]:
{'고양', '부천', '성남', '수원', '안산', '안양', '용인', '전주', '창원', '천안', '청주', '포항'}
- 위 결과에 따르면, pop에 행정구를 가진 시들의 데이터가 더 있다는 것을 알 수 있다.
- 어차피 지도에서는 표시되지 못하니 삭제한다.
In [28]:
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의 ID컬럼이 일치했다고 보고, ID를 key로 merge를 시키도록한다.
In [29]:
pop = pd.merge(pop, draw_korea, how='left', on=['ID'])
pop.head()
Out[29]:
광역시도 | 시도 | 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 |
In [30]:
mapdata = pop.pivot_table(index='y', columns='x', values='인구수합계')
masked_mapdata = np.ma.masked_where(np.isnan(mapdata), mapdata)
In [31]:
mapdata
Out[31]:
x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
y | ||||||||||||||
0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 48013.0 | 26264.0 | 24010.0 | 30114.0 | NaN | NaN | NaN |
1 | NaN | NaN | NaN | 205513.0 | 98277.0 | 45907.0 | 154763.0 | 438457.0 | 32720.0 | 280707.0 | 81793.0 | NaN | NaN | NaN |
2 | NaN | NaN | NaN | 446233.0 | 292612.0 | 348220.0 | 567581.0 | 662154.0 | 70076.0 | 45991.0 | 27218.0 | NaN | NaN | NaN |
3 | NaN | NaN | 430781.000000 | 300839.0 | 363443.0 | 327195.0 | 450355.0 | 62448.0 | 193763.0 | 211101.0 | 38718.0 | 213846.0 | NaN | NaN |
4 | NaN | NaN | 283793.333333 | 252353.0 | 339484.0 | 314194.0 | 152737.0 | 355069.0 | 411005.0 | 111367.0 | 47070.0 | 93297.0 | NaN | NaN |
5 | NaN | 68010.0 | 283793.333333 | 345061.0 | 491476.0 | 379892.0 | 125249.0 | 299259.0 | 444168.0 | 111563.0 | 337979.0 | 69599.0 | NaN | NaN |
6 | NaN | 510733.0 | 283793.333333 | 402888.0 | 595485.0 | 400997.0 | 230241.0 | 357215.0 | 657831.0 | 210359.0 | 43318.0 | 51738.0 | NaN | NaN |
7 | NaN | 71014.0 | 330284.000000 | 375857.0 | 477739.0 | 506851.0 | 447192.0 | 237909.0 | 63778.0 | 327723.0 | 40073.0 | 39052.0 | NaN | NaN |
8 | NaN | NaN | 549716.000000 | 314002.0 | 370613.0 | 235386.0 | 567115.0 | 503830.0 | 232841.0 | 347833.0 | 74702.0 | 33539.0 | NaN | 10001.0 |
9 | NaN | 115249.0 | 417103.000000 | 640890.0 | 417551.0 | 284890.0 | 156763.0 | 340654.0 | 417163.0 | 226130.0 | 168798.0 | 17713.0 | NaN | NaN |
10 | 21351.0 | 328627.0 | 530982.000000 | 208656.0 | 182896.0 | 358393.0 | 296479.0 | 136517.0 | 46166.0 | 109247.0 | 419891.0 | 26301.0 | 272027.0 | NaN |
11 | 63900.0 | 302929.0 | 258919.000000 | 359036.0 | 470832.0 | 97787.0 | 198515.0 | 30503.0 | 101799.0 | 142256.0 | 24171.0 | 54014.0 | 244748.0 | NaN |
12 | NaN | 166630.0 | 99971.000000 | 81339.0 | 109931.0 | 69950.0 | 208350.0 | 253563.0 | 38973.0 | 123199.0 | 100521.0 | 258037.0 | 259452.0 | NaN |
13 | NaN | 170788.0 | 103873.000000 | 32753.0 | 243048.0 | 192688.0 | 37308.0 | 190813.0 | 34221.0 | 34257.0 | 43564.0 | 45205.0 | 195285.0 | NaN |
14 | NaN | NaN | 70187.000000 | 123213.0 | 42634.0 | 234959.0 | 173701.0 | 217120.0 | 440383.0 | 79712.0 | 447011.0 | 219255.0 | 174514.0 | NaN |
15 | NaN | NaN | 56012.000000 | 54612.0 | 343222.0 | 252490.0 | 52267.0 | 50552.0 | 199507.0 | 156433.0 | 351352.0 | 242536.0 | 340714.0 | NaN |
16 | NaN | NaN | 277551.000000 | 300479.0 | 491011.0 | 24949.0 | 63308.0 | 48026.0 | 591891.0 | 218268.0 | 244624.0 | 272745.0 | 158527.0 | NaN |
17 | NaN | NaN | 57005.000000 | 87782.0 | 95480.0 | 23628.0 | 40241.0 | 63982.0 | 108354.0 | 310202.0 | 376526.0 | 207268.0 | 419853.0 | NaN |
18 | NaN | 60597.0 | 115173.000000 | 289899.0 | 26069.0 | 84188.0 | 346739.0 | 28111.0 | 108909.0 | 232800.0 | 89826.0 | 45208.0 | NaN | NaN |
19 | NaN | 55618.0 | 46104.000000 | 361845.0 | 30197.0 | 36098.0 | 68937.0 | 317037.0 | 181797.0 | 112973.0 | 334603.0 | 278779.0 | NaN | NaN |
20 | NaN | 34397.0 | 47229.000000 | 29949.0 | 27412.0 | 49622.0 | 252435.0 | 231571.0 | 187313.0 | 529422.0 | 126362.0 | 179324.0 | NaN | NaN |
21 | 42652.0 | 82109.0 | 403049.000000 | 30400.0 | 65303.0 | 155580.0 | 114912.0 | 210791.0 | 138160.0 | NaN | NaN | NaN | NaN | NaN |
22 | 237739.0 | 104376.0 | 309579.000000 | 441066.0 | 278548.0 | 67656.0 | 45129.0 | 54703.0 | 257183.0 | NaN | NaN | NaN | NaN | NaN |
23 | 75121.0 | 57045.0 | 219729.000000 | 95791.0 | 288988.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
24 | 32078.0 | 37753.0 | 40669.000000 | 44469.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
25 | NaN | NaN | 52668.000000 | NaN | NaN | 470665.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
26 | NaN | NaN | NaN | NaN | NaN | 170932.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
In [32]:
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 인구 현황 및 인구 소멸 지역 확인하기¶
- 인구 소멸 위기 지역에 대한 표현
In [33]:
drawKorea('인구수합계', pop, 'Blues')
In [34]:
pop['소멸위기지역'] = [1 if con else 0 for con in pop['소멸위기지역']]
drawKorea('소멸위기지역', pop, 'Reds')
5-8 인구 현황에서¶
In [35]:
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 [36]:
pop.head()
Out[36]:
광역시도 | 시도 | 20-39세여자 | 20-39세합계 | 65세이상합계 | 인구수남자 | 인구수여자 | 인구수합계 | 소멸비율 | 소멸위기지역 | ID | y | x | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 강원도 | 강릉시 | 23098.0 | 49384.0 | 37679.0 | 106231.0 | 107615.0 | 213846.0 | 1.226041 | 0 | 강릉 | 3 | 11 |
1 | 강원도 | 고성군 | 2529.0 | 7023.0 | 7151.0 | 15899.0 | 14215.0 | 30114.0 | 0.707314 | 1 | 고성(강원) | 0 | 10 |
2 | 강원도 | 동해시 | 9753.0 | 21264.0 | 15124.0 | 47166.0 | 46131.0 | 93297.0 | 1.289738 | 0 | 동해 | 4 | 11 |
3 | 강원도 | 삼척시 | 7115.0 | 15823.0 | 14610.0 | 35253.0 | 34346.0 | 69599.0 | 0.973990 | 1 | 삼척 | 5 | 11 |
4 | 강원도 | 속초시 | 8752.0 | 18708.0 | 12752.0 | 40288.0 | 41505.0 | 81793.0 | 1.372647 | 0 | 속초 | 1 | 10 |
In [37]:
pop['여성비'] = (pop['인구수여자']/pop['인구수합계']-0.5)*100
drawKorea('여성비', pop, 'RdBu')
In [38]:
pop['2030여성비'] = (pop['20-39세여자']/pop['20-39세합계']-0.5)*100
drawKorea('2030여성비', pop, 'RdBu')
In [46]:
pop_folium = pop.set_index('ID')
pop_folium
Out[46]:
광역시도 | 시도 | 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 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
증평 | 충청북도 | 증평군 | 4554.0 | 10085.0 | 5323.0 | 19110.0 | 18198.0 | 37308.0 | 1.711065 | 0 | 13 | 6 | -1.222258 | -4.843827 |
진천 | 충청북도 | 진천군 | 7622.0 | 17013.0 | 11306.0 | 36387.0 | 33563.0 | 69950.0 | 1.348311 | 0 | 12 | 5 | -2.018585 | -5.198965 |
청주 청원 | 충청북도 | 청원구 | 27805.0 | 60021.0 | 20331.0 | 97006.0 | 93807.0 | 190813.0 | 2.735232 | 0 | 13 | 7 | -0.838255 | -3.674547 |
충주 | 충청북도 | 충주시 | 22757.0 | 49357.0 | 34790.0 | 104877.0 | 103473.0 | 208350.0 | 1.308249 | 0 | 12 | 6 | -0.336933 | -3.893065 |
청주 흥덕 | 충청북도 | 흥덕구 | 37675.0 | 78608.0 | 23459.0 | 127647.0 | 125916.0 | 253563.0 | 3.211987 | 0 | 12 | 7 | -0.341335 | -2.072308 |
252 rows × 14 columns
In [39]:
import folium
import json
import warnings
warnings.filterwarnings(action='ignore')
In [49]:
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',
key_on='feature.id')
map
Out[49]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [50]:
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 = 'PuRd',
key_on='feature.id')
map
Out[50]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [51]:
draw_korea.to_csv("../../data/05. draw_korea.csv", encoding='utf-8', sep=',')
반응형