본문 바로가기

Theory/DataScience

인구 소멸 위기 지역 파악해보기~~~

제 일과는 취미생활처럼 데이터 가지고 있는척(^^) 놀기와 다시 공부를 시작한 ROS입니다. ROS를 이용해서 괜찮은 성과를 얻을 수 있지 않을까 기대를 하고 있거든요... 데이터 데리고 즐겁게 놀기 프로젝트는 이제 당분간은 업무적 영역에서 데이터 프로세싱은 안하다보니 오히려 취미가 되는 듯 합니다.^^. 그래서 좀 즐겁구요... 덕분에 아가 미바뤼를 올리는게 좀 느려졌네요~ 곧 아가 미바뤼도 업로드 하도록 하겠습니다.^^ 오늘은 인구 소멸 위기 지역에 대해 이야기를 해볼려고 합니다. 인구 소멸 위기 지역에 대한 정의는 정확히 누가 어떻게 내린 것인진 모르지만, 구글이나 네이버에서 검색해보면 "65세 이상 인구 대비 20~39세 여성 인구의 비중이 0.5 이하이면 인구 소멸 위기 지역"이라는 정의를 찾을 수 있더군요. 이제 저도 그 이야기를 그려볼려고 합니다.^^

데이터 얻기

국가 통계 포털 KOSIS[바로가기]에 가면 가지고 놀만한 데이터들이 꽤 있습니다.^^. 이 중에 인구관련 데이터를 얻으로 가죠~

네.. 저기서 최대한 내가 얻고 싶은 것 위주로 얻도록 하겠습니다.~~

엑셀에서 읽어본 데이터는 위와 같네요... 흠.. 뭔가 시각화하기 위해서는 꽤~ 다듬어야할 듯 합니다... 그 과정이 즐거움이지만요^^ 저기 얻은 데이터는....

population_raw_data.xlsx

입니다.

데이터 다듬기

이제... 최대한 얻고 싶은 형태로 얻은 엑셀 데이터를 원하는 방향으로 슬쩍 데리고 놀아야겠습니다.^^

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
import seaborn as sns

import platform

from matplotlib import font_manager, rc

if platform.system() == 'Darwin':
    rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
    font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
    rc('font', family=font_name)
else:
    print('Unknown system... sorry~~~~') 
    
%matplotlib inline

심플하게 필요한 모듈을 import하고...

population = pd.read_excel('data/population_raw_data.xlsx', header=1)
population.fillna(method='pad', inplace=True)

population.rename(columns = {'행정구역(동읍면)별(1)':'광역시도', 
                             '행정구역(동읍면)별(2)':'시도', 
                             '계':'인구수'}, inplace=True)

population = population[population['광역시도'] != population['시도']]
population = population[(population['시도'] != '소계') | (population['광역시도'] == '세종특별자치시')]
population.loc[population['광역시도'] == '세종특별자치시', '시도'] = '세종시'

condition = lambda sido, gu: (sido[-1] == '도')&(gu[-1] == '구')
population = population[[not condition(population['광역시도'][n], population['시도'][n]) for n in population.index]]

population.head()

엑셀 데이터를 읽어와서 약간 다듬었습니다. 엑셀 데이터를 읽는 과정에 대한 이야기는 [바로가기]에서 했었구요. 엑셀파일의 내용과 저 코드의 결과를 비교해보시면 왜 저런 과정을 거쳤는지는 알 수 있을 겁니다. 저장된 엑셀파일이 오늘 이야기할 내용에 비춰보면 불필요한 데이터가 몇 줄 있거든요. 그걸 빼고 읽을려구요^^

그 결과는 위와 같습니다.^^

이제 잘 다듬어서 unique조사를 해보면 큰 이상이 없어 보입니다.^^..

tmp = []

for index, row in population.iterrows():
    if row['항목'] == '총인구수 (명)':
        tmp.append('합계')
    elif row['항목'] == '남자인구수 (명)':
        tmp.append('남자')
    elif row['항목'] == '여자인구수 (명)':
        tmp.append('여자')
        
population['구분'] = tmp

del population['항목']
population.head()

이제 항목에서 좀 긴 내용을 위 코드로 살짝꿍 줄이구요~~

이렇게 만들었구요...

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()

필요한 연령대 데이터를 만들었습니다.

^^ 이제.. 제가 이야기한 적이 있는 pivot_table[바로가기]로 한 방에 정리를 하죠^^...

pop = pd.pivot_table(population, 
                     index = ['광역시도', '시도'], 
                     columns = ['구분'],
                     values = ['인구수', '20-39세', '65세이상'])
pop

를 실행하면...

어떤가요~~ 잘 정리가 되었네요^^ 이 표만해도 사실... 보기 편하죠^^

원하는 데이터 만들기

이제 원래 다룰려고 했던 인구 소멸 지역에 대한 정보를 만들어 보도록 하겠습니다.^^

pop['소멸비율'] = pop['20-39세','여자'] / (pop['65세이상','합계'] / 2)
pop.head()

인구 소멸 비율을 처음 이야기한데로 20~39세 여성의 인구와 65세 이상 인구의 절반과의 비율로 보고

이렇게 만들었구요...

pop['소멸위기지역'] = pop['소멸비율'] < 1.0
pop.head()

다음은 그 비율이 1.0보다 작으면 소멸 위기 지역으로 보는거죠~

이렇게 되네요^^

정렬을 해보니.. 경상북도 의성군, 전라남도 고흥군, 경상북도 군위군, 경상남도 남해, 합천군이 소멸 비율이 낮으면서 큰 위기 지역이네요. 저런...

데이터 시각화하기

이제 위 데이터를 지난 번에 소개해 드린 한국지도 그리는 법[바로가기]에 따라 한국 지도로 한 번 시각화를 해볼까합니다. 먼저 지도를 그리는데 필요한 데이터와 병합할 때 key로 사용할 데이터를 만들기 위해...

shortName = []

for gysido, sido in pop.index:
    if (gysido[-3:] == '특별시')|(gysido[-3:] == '광역시'):
        if len(sido[:-1]) == 1:
            shortName.append(gysido[:2] + sido)
        else:
            shortName.append(gysido[:2] + sido[:-1])
            
    elif gysido=='세종특별자치시':
        shortName.append('세종')
        
    else:
        if sido[:-1]=='고성':
            if gysido[:2]=='강원':
                shortName.append('고성(강원)')
            else:
                shortName.append('고성(경남)')
        else:
            shortName.append(sido[:-1])
        
pop['shortName'] = shortName
pop.head()

shortName이라는 아이를

만들었습니다.~ 그리고 pivot_table로 만든 데이터와 지도 그리는데 필요한 데이터를 병합하기 위해 multi-index를 변환할 필요가 있습니다.

pop.reset_index(inplace=True)  

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()

그러면...

이렇게 됩니다. 이제~

data_draw_korea.csv

위 데이터를 읽어서 오늘은 필요없는 데이터를 제거하고 ..

draw_korea = pd.read_csv('data/data_draw_korea.csv', index_col=0, encoding='UTF-8')

del draw_korea['인구수']
del draw_korea['면적']
del draw_korea['광역시도']
del draw_korea['행정구역']

draw_korea.head()

이걸 얻으면 됩니다. x, y좌표가 필요하니까요^^ [바로가기]에서 소개한 Hyeshik님의 한국 지도 그리기에 적용해 보죠^^

pop = pd.merge(pop, draw_korea, how='left', on=['shortName'])

pop.head()

ㅎㅎ 간단하게 merge명령 하나로 합쳤네요^^

def drawKorea(targetData, blockedMap, d1, d2, 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])

    BORDER_LINES = [
        [(3, 2), (5, 2), (5, 3), (9, 3), (9, 1)], # 인천
        [(2, 5), (3, 5), (3, 4), (8, 4), (8, 7), (7, 7), (7, 9), (4, 9), (4, 7), (1, 7)], # 서울
        [(1, 6), (1, 9), (3, 9), (3, 10), (8, 10), (8, 9),
         (9, 9), (9, 8), (10, 8), (10, 5), (9, 5), (9, 3)], # 경기도
        [(9, 12), (9, 10), (8, 10)], # 강원도
        [(10, 5), (11, 5), (11, 4), (12, 4), (12, 5), (13, 5),
         (13, 4), (14, 4), (14, 2)], # 충청남도
        [(11, 5), (12, 5), (12, 6), (15, 6), (15, 7), (13, 7),
         (13, 8), (11, 8), (11, 9), (10, 9), (10, 8)], # 충청북도
        [(14, 4), (15, 4), (15, 6)], # 대전시
        [(14, 7), (14, 9), (13, 9), (13, 11), (13, 13)], # 경상북도
        [(14, 8), (16, 8), (16, 10), (15, 10),
         (15, 11), (14, 11), (14, 12), (13, 12)], # 대구시
        [(15, 11), (16, 11), (16, 13)], # 울산시
        [(17, 1), (17, 3), (18, 3), (18, 6), (15, 6)], # 전라북도
        [(19, 2), (19, 4), (21, 4), (21, 3), (22, 3), (22, 2), (19, 2)], # 광주시
        [(18, 5), (20, 5), (20, 6)], # 전라남도
        [(16, 9), (18, 9), (18, 8), (19, 8), (19, 9), (20, 9), (20, 10)], # 부산시
    ]

    mapdata = blockedMap.pivot(index='y', columns='x', values=targetData)
    masked_mapdata = np.ma.masked_where(np.isnan(mapdata), mapdata)
    
    plt.figure(figsize=(8, 13))
    plt.pcolor(masked_mapdata, vmin=vmin, vmax=vmax, cmap=cmapname, edgecolor='#aaaaaa', linewidth=0.5)

    # 지역 이름 표시
    for idx, row in blockedMap.iterrows():
        annocolor = 'white' if row[targetData] > whitelabelmin else 'black'

        # 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시한다. (중구, 서구)
        if row[d1].endswith('시') and not row[d1].startswith('세종'):
            dispname = '{}\n{}'.format(row[d1][:2], row[d2][:-1])
            if len(row[d2]) <= 2:
                dispname += row[d2][-1]
        else:
            dispname = row[d2][:-1]

        # 서대문구, 서귀포시 같이 이름이 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', color=annocolor,
                     linespacing=linespacing)
        
    # 시도 경계 그린다.
    for path in BORDER_LINES:
        ys, xs = zip(*path)
        plt.plot(xs, ys, c='black', lw=4)

    plt.gca().invert_yaxis()
    #plt.gca().set_aspect(1)

    plt.axis('off')

    cb = plt.colorbar(shrink=.1, aspect=10)
    cb.set_label(datalabel)

    plt.tight_layout()
    plt.show()

이제 저 함수를 정의한 다음~~~

pop['소멸위기지역'] = [1 if con else 0 for con in pop['소멸위기지역']]
drawKorea('소멸위기지역', pop, '광역시도', '시도', 'Reds')

인구 소멸 지역을 그리면~~

이렇게 나타나네요.. 대한민국을 사선으로 가르는 선로에 인구 소멸 위기 지역이 분포해 있네요ㅠㅠ. 광역시중에서는 부산에 동구, 영도가 위험하다고 하네요 저런... 특히 수도권을 제외하면 대다수의 지방 군단위들은 모두 위험합니다.ㅠㅠ.

pop['고령인구비율'] = pop['65세이상합계']/pop['인구수합계']
drawKorea('고령인구비율', pop, '광역시도', '시도', 'Reds')

이왕 하는 김에 고령인구비율도 한 번 표현해 보았습니다.

우리나라가 고령화사회가 맞나봅니다. 우와... 이런.. 아가 미바뤼가 커서 즐겁게 지내기 위해서는 고령화 사회에 대한 해법이 필요하겠습니다.ㅠㅠ. 특히 단순히 출산율이 아니라.. 아이를 낳아 기르는 것에 부담을 주지 않는 사회가 되었으면 정말 좋겠습니다.ㅠㅠ.

반응형