(1) 데이터 정제
import numpy as np
import pandas as pd
population = pd.read_excel("../data/07_population_raw_data.xlsx")
population.head()
Excel 데이터 cell 병합으로 인해 컬럼이 2중으로 되어 있음

population = pd.read_excel("../data/07_population_raw_data.xlsx", header=1)
population.fillna(method="pad", inplace=True)
population


(2) 인구 소멸 위험 지역 조사
65세 고령 인구 대비 20-39세 여성인구 비중으로 비율이 < 0.5 이면 30년 내 소멸 가능성이 크다고 봄
소멸 위험지수 = (2-19세 여성인구수) / (65세 이상 인구수)
소멸위험지수가 < 0.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+"]
)
popul_pivot = pd.pivot_table(
data=population,
index=["광역시도", "시도"],
columns=["구분"],
values=["인구수", "20-39세", "65세이상"]
)
popul_pivot["소멸지수"]
= popul_pivot["20-39세", "여자"] / popul_pivot["65세이상", "합계"]
popul_pivot["소멸위기지역"] = popul_pivot["소멸지수"] < 0.5

# index 재정렬
popul_pivot.reset_index(inplace=True)
popul_pivot.head()
# column 추가
tmp_cols = [
popul_pivot.columns.get_level_values(0)[n]
+ popul_pivot.columns.get_level_values(1)[n]
for n in range(len(popul_pivot.columns.get_level_values(0)))
]
popul_pivot.columns = tmp_cols

(3) 지역별 ID 만들기
admin_gu = {
"수원": ["장안구", "권선구", "팔달구", "영통구"],
"성남": ["수정구", "중원구", "분당구"],
"안양": ["만안구", "동안구"],
"안산": ["상록구", "단원구"],
"고양": ["덕양구", "일산동구", "일산서구"],
"용인": ["처인구", "기흥구", "수지구"],
"청주": ["상당구", "서원구", "흥덕구", "청원구"],
"천안": ["동남구", "서북구"],
"전주": ["완산구", "덕진구"],
"포항": ["남구", "북구"],
"창원": ["의창구", "성산구", "진해구", "마산합포구", "마산회원구"],
"부천": ["오정구", "원미구", "소사구"],
}
일반 시
- 일반 도에 포함된 일반 시나 군의 지역 ID는 "시"를 뺀 지역명으로 함
세종특별자치시는 유일함
일반 구
- 특별시, 광역시의 구는 해당 시의 앞 두글자[:2]와 구 이름을 합침
행정구
- 일반시(12개)의 행정구도 해상 시의 앞 두글자와 구이름을 합침
def find_key_by_value(dictionary, value):
for key, val in dictionary.items():
if value in val:
return key
return None // 값이 없는 경우
for idx, row in popul_pivot[["광역시도", "시도"]].iterrows():
if row["광역시도"][-3:] not in ["광역시", "특별시", "자치시"]:
# 행정구
if row["시도"][-1:] == "구":
key = find_key_by_value(admin_gu, row["시도"])
if len(key) > 0:
if len(row["시도"]) == 2:
sidoID[idx] = key + " " + row["시도"]
elif row["시도"] in ["마산합포구", "마산회원구"]:
sidoID[idx] = key + " " + row["시도"][2:-1]
else:
sidoID[idx] = key + " " + row["시도"][:-1]
# 고성군
elif row["시도"] == "고성군":
if row["광역시도"] == "강원도":
sidoID[idx] = "고성(강원)"
else: sidoID[idx] = "고성(경남)"
# 일반시
else:
sidoID[idx] = row["시도"][:-1]
# 세종특별자치시
elif row["광역시도"] == "세종특별자치시":
sidoID[idx] = "세종"
# 특별시/광역시 구
else:
if len(row["시도"]) == 2: // 서구, 중구, 북구, ...
sidoID[idx] = row["광역시도"][:2] + " " + row["시도"]
else:
sidoID[idx] = row["광역시도"][:2] + " " + row["시도"][:-1]

# 한글화 작업
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import set_matplotlib_hangul
import warnings
warnings.filterwarnings(action="ignore")
%matplotlib inline
(1) 카토그램 구조
draw_korea_raw = pd.read_excel("../data/07_draw_korea_raw.xlsx")

(2) 시각화할 좌표 값 지정
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)] # 제주도
]
draw_kr_carto = pd.DataFrame(draw_korea_raw.stack())
draw_kr_carto.reset_index(inplace=True)
draw_kr_carto.rename(
columns={"level_0": "y", "level_1": "x", 0: "ID"},
inplace=True
)
(3) Sample 카토그램

(4) 데이터 병합

set(draw_kr_carto["ID"].unique()) - set(population["ID"].unique())
//==> set()
tmp_list = list(
set(population["ID"].unique()) - set(draw_kr_carto["ID"].unique())
)
#==> ['수원', '전주', '청주', '천안', '성남', '부천',
# '고양', '포항', '안양', '용인', '창원', '안산']
for item in tmp_list:
population.drop(
population[population["ID"] == item].index,
inplace=True
)
set(population["ID"].unique()) - set(draw_kr_carto["ID"].unique())
//==> set()
population = pd.merge(
population, draw_kr_carto, how="left", on="ID"
)
(5) 카토그램 그리기
def get_data_info(targetData, blockedMap):
vmin = min(blockedMap[targetData])
vmax = max(blockedMap[targetData])
white_label_min = (vmax - vmin) * 0.25 + vmin
map_data = blockedMap.pivot_table(
index="y", columns="x", values=targetData
)
return map_data, vmin, vmax, white_label_min
def get_data_info_for_zero_center(targetData, blockedMap):
white_label_min = 5
tmp_max = max(
[np.abs(min(blockedMap[targetData])),
np.abs(max(blockedMap[targetData]))]
)
vmin, vmax = -tmp_max, tmp_max
map_data = blockedMap.pivot_table(
index="y", columns="x", values=targetData
)
return map_data, vmin, vmax, white_label_min
def plot_text(targetData, blockedMap, white_label_min):
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]) > white_label_min 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 drawKoreaCartogram(targetData, blockedMap, cmapName, zeroCenter=False):
if zeroCenter:
masked_mapdata, vmin, vmax, white_label_min
= get_data_info_for_zero_center(targetData, blockedMap)
else:
masked_mapdata, vmin, vmax, white_label_min
= get_data_info(targetData, blockedMap)
plt.figure(figsize=(9, 11))
plt.pcolor(
masked_mapdata,
vmin=vmin,
vmax=vmax,
cmap=cmapName,
edgecolor="#aaaaaa",
linewidth=0.5
)
plot_text(targetData, blockedMap, white_label_min)
for coord in BORDER_LINES:
ys, xs = zip(*coord)
plt.plot(xs, ys, c="black", lw=1.5)
plt.gca().invert_yaxis()
plt.axis("off")
plt.tight_layout()
cb = plt.colorbar(shrink=0.1, aspect=10)
cb.set_label(targetData)
plt.show()
(6) 인구수 합계
drawKoreaCartogram(
targetData="인구수합계", blockedMap=population, cmapName="Blues"
)

(7) 소멸 위기 지역
population["소멸위기지역"] = [
1 if val else 0 for val in population["소멸위기지역"]
]
drawKoreaCartogram("소멸위기지역", population, "Reds")

import pandas as pd
import folium
import json
geo_path = "../data/07_skorea_municipalities_geo_simple.json"
geo_json = json.load(open(geo_path, encoding="utf-8"))
(1) 인구수 합계
popmap = folium.Map(location=[36.3002, 127.554], zoom_start=7)
folium.Choropleth(
geo_data=geo_json,
data=popul_folium["인구수합계"],
key_on="feature.id",
columns=[popul_folium.index, popul_folium["인구수합계"]],
fill_color="YlGnBu"
).add_to(popmap)
popmap

(2) 소멸 위기 지역
popmap = folium.Map(location=[36.3002, 127.554], zoom_start=7)
folium.Choropleth(
geo_data=geo_json,
data=popul_folium["인구수합계"],
key_on="feature.id",
columns=[popul_folium.index, popul_folium["인구수합계"]],
fill_color="YlGnBu"
).add_to(popmap)
popmap


