34.EDA-11

SOWA·2023년 4월 12일
0

EDA

목록 보기
11/11

🧷 인구소멸위기지역 파악하기

🖇️ 인구현황데이터 정리& 소멸위기지역 파악

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import set_matplotlib_hangul

get_ipython().run_line_magic('matplotlib', 'inline')
population = pd.read_excel('../data/07_population_raw_data.xlsx', header=1)
population.fillna(method='pad', inplace=True)
population.head()



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



population = population[(population['시도'] != '소계')]
population.head()


population.is_copy = False
population.rename(columns={'항목':'구분'}, inplace=True)

population.loc[population['구분'] == '총인구수 (명)', '구분'] = '합계'
population.loc[population['구분'] == '남자인구수 (명)', '구분'] = '남자'
population.loc[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(10)



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



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



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



pop[pop['소멸위기지역'] == True].index.get_level_values(1)

Index(['고성군', '삼척시', '양양군', '영월군', '정선군', '평창군', '홍천군', '횡성군', '가평군', '양평군',
       '연천군', '거창군', '고성군', '남해군', '밀양시', '산청군', '의령군', '창녕군', '하동군', '함안군',
       '함양군', '합천군', '고령군', '군위군', '문경시', '봉화군', '상주시', '성주군', '영덕군', '영양군',
       '영주시', '영천시', '예천군', '울릉군', '울진군', '의성군', '청도군', '청송군', '동구', '영도구',
       '강화군', '옹진군', '강진군', '고흥군', '곡성군', '구례군', '담양군', '보성군', '신안군', '영광군',
       '영암군', '완도군', '장성군', '장흥군', '진도군', '함평군', '해남군', '화순군', '고창군', '김제시',
       '남원시', '무주군', '부안군', '순창군', '임실군', '장수군', '정읍시', '진안군', '공주시', '금산군',
       '논산시', '보령시', '부여군', '서천군', '예산군', '청양군', '태안군', '홍성군', '괴산군', '단양군',
       '보은군', '영동군', '옥천군'],

pop.reset_index(inplace=True)
pop.head()



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


🖇️ 지도 시각화를 위한 지역별 ID 만들기

  • 만들어야 할 ID
    • 서울 중구
    • 서울 서초
    • 통영
    • 남양주
    • 포항 북구
    • 인천 남동
    • 안양 만안
    • 안양 동안
    • 안산 단원
si_name = [None] * len(pop)

tmp_gu_dict = {
    '수원':['장안구', '권선구', '팔달구', '영통구'],
    '성남':['수정구', '중원구', '분당구'],
    '안양':['만안구', '동안구'],
    '안산':['상록구', '단원구'],
    '고양':['덕양구', '일산동구', '일산서구'],
    '용인':['처인구', '기흥구', '수지구'],
    '청주':['상당구', '서원구', '흥덕구', '청원구'],
    '천안':['동남구', '서북구'],
    '전주':['완산구', '덕진구'],
    '포항':['남구', '북구'],
    '창원':['의창구', '성산구', '진해구', '마산합포구', '마산회원구'],
    '부천':['오정구', '원미구', '소사구']
}

ㄴ 행정구를 가진 시 정리


for idx, row in pop.iterrows():
    if row['광역시도'][-3:] not in ['광역시', '특별시', '자치시']:
        si_name[idx] = row['시도'][:-1]
        
    elif row['광역시도'] == '세종특별자치시':
        si_name[idx] = '세종'
        
    else:
        if len(row['시도']) == 2:
            si_name[idx] = row['광역시도'][:2] + '' + row['시도']
        else:
            si_name[idx] = row['광역시도'][:2] + '' + row['시도'][:-1]

ㄴ 일반 시의 이름과 세종시, 광역시도의 일반구 정리


for idx, row in pop.iterrows():
    if row['광역시도'][-3:] not in ['광역시', '특별시', '자치시']:  #광역시나 특별시,자치시가 아닌 경우의 팽정구에 대해서만 적용
        for keys, values in tmp_gu_dict.items():
            if row['시도'] in values: #행정구를 지정한 dict형 자료에 있는 지역인지 검색
                if len(row['시도']) == 2:  ## 분당구-> 분당, 특별히 너무 긴 이름의 구는 짧게 처리
                    si_name[idx] = keys + ' ' + row['시도']
                elif row['시도'] in ['마산합포구', '마산회원구']:
                    si_name[idx] = keys + ' ' + row['시도'][2:-1]
                else:
                    si_name[idx] = keys + ' ' + row['시도'][:-1]

ㄴ 행정구에대해 다시 계산


 for idx, row in pop.iterrows():
    if row['광역시도'][-3:] not in ['광역시', '특별시', '자치시']:
        if row['시도'][:-1] == '고성' and row['광역시도'] == '강원도':
            si_name[idx] = '고성(강원)'
        elif row['시도'][:-1] == '고성' and row['광역시도'] == '경상남도':
            si_name[idx] = '고성(경남)'

ㄴ 고성이 두 군데이므로 강원에 있는 고성과 경남에 있는 고성을 구분


pop['ID'] = si_name

ㄴ pop 데이터 프레임의 ID 컬럼에 si_name 할당


del pop['20-39세남자']
del pop['65세이상남자']
del pop['65세이상여자']

pop.head()




🖇️ 카르토그램으로 인구현황 시각화

draw_korea_raw = pd.read_excel('../data/07_draw_korea_raw.xlsx')
draw_korea_raw


ㄴ 엑셀로 그린 지도 모양 불러오기


draw_korea_raw_stacked = pd.DataFrame(draw_korea_raw.stack())
draw_korea_raw_stacked


  • stack(): pivot_table의 반대. 데이터가 존재하는 index만 보여줌

draw_korea_raw_stacked.reset_index(inplace=True)
draw_korea_raw_stacked


ㄴ 인덱스로 나타난 좌표를 데이터로 사용하기 위해 reset_index


draw_korea_raw_stacked.rename(
    columns={'level_0':'y', 'level_1':'x', 0:'ID'}, inplace=True
)
draw_korea_raw_stacked



ㄴ 이름변경


```pyhon 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)],
    [(27, 5), (27, 6), (25, 6)]
]

ㄴ 경계선 그리기


def plot_text_simple(draw_korea):
    for idx, row in draw_korea.iterrows():
        if len(ros['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 = 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
        )

ㄴ 시도 이름을 표현하는 함수



def plot_text_simple(draw_korea):
    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']
            
        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
        )
def simpleDraw(draw_korea):
    plt.figure(figsize=(8, 11))
    
    plot_text_simple(draw_korea)
    
    for path in BORDER_LINES:
        ys, xs = zip(*path)
        plt.plot(xs, ys, c='black', lw=1.5)
        
    plt.gca().invert_yaxis()
    plt.axis('off')
    plt.tight_layout()
    plt.show()
    
simpleDraw(draw_korea)



  • 검증 작업
set(draw_korea['ID'].unique()) - set(pop['ID'].unique())


set()


set(pop['ID'].unique()) - set(draw_korea['ID'].unique())

{'고양', '부천', '성남', '수원', '안산', '안양', '용인', '전주', '창원', '천안', '청주', '포항'}

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 = pd.merge(pop, draw_korea, how='left', on='ID')
pop.head()



  • 그림을 그리기 위한 데이터를 계산하는 함수
    • 색상을 만들때, 최솟값을 흰색
    • blockedMap: 인구현황(pop)
    • targetData: 그리고 싶은 컬럼
def get_data_info(targetData, blockedMap):
    whitelavelmin = (
        max(blockedMap[targetData]) - min(blockedMap[targetData])
    ) * 0.25 + min(blockedMap[targetData])
    vmin =  min(blockedMap[targetData])
    vmax = max(blockedMap[targetData])
    
    mapdata = blockedMap.pivot_table(index='y', columns='x', values=targetData)
    
    return mapdata, vmax, vmin, whitelavelmin
def get_data_info_for_zero_center(targetData, blockedMap):
    whitelabelmin = 5
    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)
    return mapdata, vmax, vmin, whitelabelmin

def plot_text(targetData, blockedMap, whitelabelmin):
    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 = 9.5, 1.5
        else:
            fontsize, linespacing = 11, 1.2


        annocolor = "white" if np.abs(row[targetData]) > whitelabelmin else "black"
        plt.annotate(
            dispname,
            (row["x"] + 0.5, row["y"] + 0.5),
            weight="bold",
            color=annocolor,
            fontsize=fontsize,
            linespacing=linespacing,
            ha="center", # 수평 정렬
            va="center", # 수직 정렬 
        )
def drawKorea(targetData, blockedMap, cmapname, zeroCenter=False):
    if zeroCenter:
        masked_mapdata, vmax, vmin, whitelabelmin = get_data_info_for_zero_center(targetData, blockedMap)
        
    if not zeroCenter:
        masked_mapdata, vmax, vmin, whitelabelmin = get_data_info(targetData, blockedMap)
        
    plt.figure(figsize=(8, 11))
    plt.pcolor(masked_mapdata, vmin=vmin, vmax=vmax, cmap=cmapname, edgecolor='#aaaaaa', linewidth=0.5)
    
    plot_text(targetData, blockedMap, whitelabelmin)
    
    for path in BORDER_LINES:
        ys, xs = zip(*path)
        plt.plot(xs, ys, c='black', lw=1.5)
        
    plt.gca().invert_yaxis() #y축 반전
    plt.axis('off')  
    plt.tight_layout()
    cb = plt.colorbar(shrink=0, aspect=10)
    cb.set_label(targetData)
    plt.show()
drawKorea('인구수합계', pop, 'Blues')




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



pop['여성비'] = (pop['인구수여자']/pop['인구수합계'] - 0.5) * 100
drawKorea('여성비', pop, 'RdBu', zeroCenter=True)




  • folium으로 지도 시각화
import folium
import json

pop_folium = pop.set_index('ID')
pop_folium.head()
geo_path = '../data/07_skorea_municipalities_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='UTF-8'))
mymap = folium.Map(location=[36.2002, 127.054], zoom_start=7)
mymap.choropleth(
    geo_data=geo_str,
    data=pop_folium['인구수합계'],
    key_on='feature.id',
    columns=[pop_folium.index, pop_folium['인구수합계']],
    fill_color='YlGnBu'
)
mymap



  • 소멸위기지역 지도 시각화
geo_path = '../data/07_skorea_municipalities_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='UTF-8'))
mymap = folium.Map(location=[36.2002, 127.054], zoom_start=7)
mymap.choropleth(
    geo_data=geo_str,
    data=pop_folium['소멸위기지역'],
    key_on='feature.id',
    columns=[pop_folium.index, pop_folium['소멸위기지역']],
    fill_color='PuRd'
)
mymap




from.제로베이스 데이터 취업스쿨 강의

0개의 댓글