- 인구 소멸 위기 지역 파악
- 인구 소멸 위기 지역의 지도 표현
- 지도 표현에 대한 카르토그램 표현
선언
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import platform
from matplotlib import font_manager, rc
import warnings
warnings.filterwarnings(action="ignore")
%matplotlib inline
path = "C://Windows/Fonts/malgun.ttf"
if platform.system() == "Darwin":
rc("font",family="Arial Unicode MS")
elif platform.system() == "Windows":
font_name = font_manager.FontProperties(fname=path).get_name()
rc("font",family=font_name)
else:
print("Unknown system. sorry")
fillna(method="")
빈 값 채워주는 함수
A열 - C열까지의 빈 값이 있다고 가정을 해보자 그럴때 fillna()함수로이렇게 채워줄 수가 있따.
method : 'backfill', 'bfill','pad','ffill', None
pad/ffill : 앞에 값으로 빈값 채움
backfill/bfill : 다음 값으로 빈값 채움population = pd.read_excel("../../data/07.population/07_population_raw_data.xlsx", header=1) population.fillna(method="pad", inplace=True) population
[출력]
header로 첫번째 날려서 읽고, fill_na로 빈 값은 빈 값전에 데이터로 채워준다.
컬럼 이름 변경
population.rename(
columns={
"행정구역(동읍면)별(1)" : "광역시도",
"행정구역(동읍면)별(2)" : "시도",
"계" : "인구수"
}, inplace=True
)
population.tail()
변경 전
변경 후
필요없는 소계 데이터 삭제
population = population[population["시도"] != "소계"]
population
항목 컬럼명 변경
population.is_copy = False
population.rename(
columns={"항목":"구분"},
inplace=True
)
데이터 내용 변경
population.loc[population["구분"] == "총인구수 (명)", "구분"] = "합계" population.loc[population["구분"] == "남자인구수 (명)", "구분"] = "남자" population.loc[population["구분"] == "여자인구수 (명)", "구분"] = "여자" population
변경 전
변경 후
소멸지역을 조사하기 위한 데이터
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
인구분석을 위한 중요한 데이터인 20-39세와 65세이상 데이터를 합산해줌.
데이터 맨 뒤에 합쳐져있다.
pop = pd.pivot_table( data = population, index = ["광역시도","시도"], columns=["구분"], values=["인구수","20~39세","65세이상"] ) pop
소멸 비율 계산
pop["소멸비율"] = pop["20~39세","여자"]/ (pop["65세이상","합계"]/2)
소멸위기 지역 컬럼 생성
pop["소멸위기지역"] = pop["소멸비율"] < 1.0
pop
소멸 위기 지역 조회
pop[pop["소멸위기지역"] == True].index.get_level_values(1)
인덱스 재정렬
pop.reset_index(inplace=True) pop.head()
2줄짜리 인덱스 한줄로 만들기
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.info()
pop['시도'].unique
ID 값으로 쓸만한 데이터 확인.
행정시와 구로 나누기 위해 데이터 만듦
1)
si_name = [None] * len(pop) # 264 길이 si_name
pop길이 만큼의 None값의 변수 만들어줌
2)
tmp_gu_dict = { "수원": ["장안구", "권선구", "팔달구", "영통구"], "성남": ["수정구", "중원구", "분당구"], "안양": ["만안구", "동안구"], "안산": ["상록구", "단원구"], "고양": ["덕양구", "일산동구", "일산서구"], "용인": ["처인구", "기흥구", "수지구"], "청주": ["상당구", "서원구", "흥덕구", "청원구"], "천안": ["동남구", "서북구"], "전주": ["완산구", "덕진구"], "포항": ["남구", "북구"], "창원": ["의창구", "성산구", "진해구", "마산합포구", "마산회원구"], "부천": ["오정구", "원미구", "소사구"], }
추가데이터는 수작업
3)
- 만들고자 하는 ID의 형태
- 서울 중구
- 서울 서초
- 통영
- 남양주
- 포항 북구
- 인천 남동
- 안양 만안
- 안양 동안
- 안산 단원
...(1) 일반 시 이름과 세종시, 광역시도 일반 구 정리
만들기 전 test
행정구 1차 나누기 작업
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]
행정구 데이터 추가2 (수작업)
for idx, row in pop.iterrows(): if row["광역시도"][-3:] not in ["광역시","특별시","자치시"]: for keys, values in tmp_gu_dict.items(): if row["시도"] in values: 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]
행정구 데이터 추가3 (고성)
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
필요없는 데이터 열 제거
del pop["20~39세남자"] del pop["65세이상남자"] del pop["65세이상여자"] pop.head()
민형기 담임교수님이 한땀 한땀 만든 엑셀파일 불러오기
draw_korea_raw = pd.read_excel("../../data/07.population/07_draw_korea_raw.xlsx")
draw_korea_raw
.stack()
위에 컬럼을 인덱스로 보내주기draw_korea_raw_stacked = pd.DataFrame(draw_korea_raw.stack()) draw_korea_raw_stacked
.stack() 적용 전
.stack() 적용 후
지도의 X,Y축을 .stack()을 이용해 인덱스로 정렬해 옆으로 보냄↔ 반대의 개념으로 .unstack()은 인덱스를 컬럼으로 보낼때 쓰임!
draw_korea_raw_stacked.reset_index(inplace=True)
draw_korea_raw_stacked
리셋 인덱스로 다시 열을 재정렬해줌
draw_korea_raw_stacked.rename(
columns={
"level_0" : "y",
"level_1" : "x",
0 : "ID"
},
inplace=True
)
draw_korea_raw_stacked
컬럼명 이름 바꾸기
draw_korea = draw_korea_raw_stacked
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 = 7.5,0.9
else:
fontsize, linespacing = 8, 0.7
plt.annotate(
dispname,
(row["x"]+0.5, row["y"]+0.5),
weight = "bold",
fontsize = fontsize,
linespacing = linespacing,
ha="center", # 수평정렬
va="center", # 수직정렬
)
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()
이 두 함수로 윤곽선과 글자 배치조정
def get_data_info (targetData, blockedMap): whitelabelmin = ( max(blockedMap[targetData]) - min(blockedMap[targetData]) ) * 0.25 + min(blockedMap[targetData]) vmin = min(blockedMap[targetData]) vmax = max(blockedMap[targetData]) mapdata = blockedMap.pivot(index="y",columns="x",values=targetData) return mapdata, vmax, vmin, whitelabelmin
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(index="y",columns="x",values=targetData) return mapdata, vmax, vmin, whitelabelmin
def plot_text_simple(targetData, blockedMap, Whitelablemin): 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 = 7.5,0.9 else: fontsize, linespacing = 8, 0.7 annocolor = "white" if np.abs(row[targetData]) > Whitelablemin 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_simple(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() # 지도방향 바꾸기 (상하) plt.axis("off") plt.tight_layout() cb = plt.colorbar(shrink= 0.1 , aspect=10) cb.set_label(targetData) plt.show()