데이터분석/미니프로젝트 06. 인구 분석

06-03-02. 카르토그램으로 인구현황 시각화하기(2)

책다니엘 2024. 4. 27. 01:31

1. 이제 앞서 만든 데이터(pop, 인구수와 인구소멸지수 데이터와 draw_korea, 대한민국 지역을 엑셀로 그린 데이터)를 검증한 후 merge할 것이다. 

부천 소사, 오정, 원미구는 통합되었고, 인천 남구는 미추홀구로 바뀌었다. 최신화해주자.
원본 데이터를 바꾸고
셀들을 차례로 다시 실행해주었다. 이제 차집합이 사라졌다.

 

2. 그 다음에는 반대로 차집합을 확인하여 여집합을 삭제해준다.

차집합이 있다.

 

삭제해준다.

 

3. 이제 pop와 draw_korea를 merge해준다.

 

4. 이제 색상을 표시하기 위한 함수를 만들어야 한다. 코드는 다음과 같다.

#### 그림을 그리기 위한 데이터를 계산하는 함수
- 색상을 만들 때, 최소값은 흰색
- blockedMap = 인구현황(pop)
- targetData = 그리고 싶은 컬럼

def get_data_info(targetData, blockedMap): # blockedMap은 pop데이터, targetData는 색이 입혀지는 컬럼 이름
    whitelabelmin=(  # 배경과 글자의 시인성을 위해 일정 값 이상부터는 글자 색을 바꿔주는 함수
        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) # 지역이름이 들어갈 자리에 targetData의 숫자가 들어감
    
    return mapdata, vmax, vmin, whitelabelmin
def get_data_info_for_zero_center(targetData, blockedMap): # 값이 좌우대칭일 때, 0(가운데)의 위치(범위)를 정해 주는 함수
    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):
    
        # 주석을 다는 부분은 위의 plot_text_simple함수와 똑같지만
    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

        # font의 글자색을 정해주는 부분이 추가
        annocolor = 'white' if np.abs(row[targetData]) > whitelabelmin else 'black'
        plt.annotate( # matplotlib의 주석 기능
            dispname, 
            (row['x'] + 0.5, row['y'] + 0.5),
            color = annocolor,
            weight = 'bold', 
            fontsize=fontsize,
            linespacing = linespacing,
            ha = 'center', # 수직 정렬
            va = 'center', # 수평 정렬
        )
def drawKorea(targetData, blockedMap, cmapname, zeroCenter=False): # zeroCenter 디폴트는 False
    if zeroCenter: # 값의 범위가 음수~양수를 가질 때
        masked_mapdata, vmax, vmin, whitelabelmin = get_data_info_for_zero_center(targetData, blockedMap) # masked_mapdata는 인덱스를 x좌표, column을 y좌표로 한 데이터
    if not zeroCenter: # 값의 범위가 양수만 존재할 때
        masked_mapdata, vmax, vmin, whitelabelmin = get_data_info(targetData, blockedMap)

    # 데이터별로 색을 채우는 함수
    plt.figure(figsize=(9, 11))
    plt.pcolor(
        masked_mapdata, vmax = vmax, vmin = vmin, 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=2)

    plt.gca().invert_yaxis()
    plt.axis('off')
    cb=plt.colorbar(shrink=0.1, aspect=10)
    cb.set_label(targetData)
    plt.tight_layout()
    plt.show()

 

5. 위의 함수를 한번 실행해보자. 먼저 '인구수합계'를 바탕으로 지도에 표시된 결과를 보자.

drawKorea('인구수합계', pop, 'Blues')

남양주, 서울 송파, 부천, 화성 등의 인구수가 높게 나타난다.

 

 

6. 다음에는 '인구소멸위기지역'을 표시해보자.

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

경상도, 전북, 경기, 서울 제외하고 전부 인구소멸위기지역임을 알 수 있다.

 

7. 다음은 강사님이 직접 해보신 부분이다. 전국의 '여성 비율'을 표시해보았다.

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

서울, 경상도, 전북 중심으로 여성비가 높게 나타난다. 수원은 남초 도시다.

 

8. 전반적으로 특정 몇 지역을 제외하면 크게 성비가 차이나지 않는 것처럼 보인다. 그러나 이는 착시효과다. 아래와 같이 계산식을 바꿔 보자.

pop['2030여성비'] = (pop['20~39세여자'] / pop['20~39세합계'] - 0.5) * 100
drawKorea('2030여성비', pop, 'RdBu', zeroCenter = True)

서울, 대구/부산, 광주 제외하고는 전부 빨간색이다. ㄷㄷ

9. 다음엔 folium으로 접근해보자. 인덱스를 'ID'로 잡아 준다.

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)
folium.Choropleth(
    geo_data=geo_str,
    data=pop_folium['인구수합계'],
    columns = [pop_folium.index, pop_folium['인구수합계']],
    fill_color = 'YlGnBu', # PuRd, YlGnBu
    key_on = 'feature.id'
).add_to(mymap)

mymap

서울과 서울 근교 외에는 인구수가 대체로 낮게 나타난다.

 

10. 이번엔 folium으로 인구소멸위기지역을 표시해보자.

mymap = folium.Map(location=[36.2002, 127.054], zoom_start=7)
folium.Choropleth(
    geo_data=geo_str,
    data=pop_folium['인구소멸위기지역'],
    columns = [pop_folium.index, pop_folium['인구소멸위기지역']],
    fill_color = 'PuRd', # PuRd, YlGnBu
    key_on = 'feature.id'
).add_to(mymap)

mymap

대한민국의 인구소멸위기가 좀더 시각적으로 크게 와닿는다.