▷ 오늘 학습 계획: EDA 강의(인구분석)
인구 소멸 위기 지역 파악
인구 소멸 위기 지역의 지도 표현
지도 표현에 대한 카르토그램 표현
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import rc
rc("font", family="Malgun Gothic")
import warnings
warnings.filterwarnings(action="ignore")
%matplotlib inline
population = pd.read_excel("../data/07_population_raw_data.xlsx", header =1)
population.fillna(method="pad", inplace=True) #Nan값 없애기
fillna()
1) df
2) df.fillna(value=0)
3) df.fillna(method="ffill", axis=0)
4) df.fillna(method="ffill", axis=1)
5) df.fillna(method="backfill", axis=0)
6) df.fillna(method="backfill", axis=1)
- method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
Method to use for filling holes in reindexed Series
pad / ffill: propagate last valid observation forward to next valid
backfill / bfill: use next valid observation to fill gap.
# 컬럼 이름 변경
population.rename(
columns={
"행정구역(동읍면)별(1)" : "광역시도",
"행정구역(동읍면)별(2)" : "시도",
"계" : "인구수",
"항목" : "구분"
}, inplace = True
)
# 소계 제거
population = population[population["시도"] != "소계"]
#copy 했을 때 warning 나오지 않게 설정
population.is_copy = False
# 구분 컬럼의 "총인구수 (명)" → "합계"로 변경
population.loc[population["구분"] == "총인구수 (명)", "구분"] = "합계"
population.loc[population["구분"] == "남자인구수 (명)", "구분"] = "남자"
population.loc[population["구분"] == "여자인구수 (명)", "구분"] = "여자"
# 소멸지역을 조사하기 위한 데이터 정리
```python
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+"]
)
# pivot_table
# index만 설정하게 되면 평균값이 기본으로 들어가게 됨
pop = pd.pivot_table(
population,
index = ["광역시도", "시도"],
columns = ["구분"],
values = ["인구수", "20-39세", "65세이상"]
)
# 소멸 비율 계산
pop["소멸비율"] = pop["20-39세", "여자"] / (pop["65세이상", "합계"] / 2)
# 소멸위기지역 컬럼 생성
pop["소멸위기지역"] = pop["소멸비율"] < 1.0
# 소멸위기지역 조회
pop[pop["소멸위기지역"] == True].index.get_level_values(1)
# 인덱스 재정렬
pop.reset_index(inplace=True)
# multi-index 정리
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
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:
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
#엑셀에서 그린 지도모양 읽어오기
import pandas as pd
draw_korea_raw = pd.read_excel("../data/07_draw_korea_raw.xlsx")
#각 지역별 위치
draw_korea_raw_stacked = pd.DataFrame(draw_korea_raw.stack())
#인덱스로 나타난 좌표를 데이터로 활용하기
draw_korea_raw_stacked.reset_index(inplace=True)
draw_korea_raw_stacked.rename(columns={"level_0":"y", "level_1":"x", 0:"ID"}, inplace=True)
draw_korea = draw_korea_raw_stacked
# set(집합)으로 차집합 구하기
set(draw_korea["ID"].unique()) - set(pop["ID"].unique())
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()))
pop = pd.merge(pop, draw_korea, how="left", on="ID")
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_table(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_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()
plt.axis("off")
plt.tight_layout()
cb = plt.colorbar(shrink=0.1, aspect=10)
cb.set_label(targetData)
plt.show()
#1
drawKorea("인구수합계", pop, "Blues")
#2
pop["소멸위기지역"] = [1 if con else 0 for con in pop["소멸위기지역"]]
drawKorea("소멸위기지역", pop, "Reds")
#3
pop["여성비"] = (pop["인구수여자"]/pop["인구수합계"]-0.5) *100
drawKorea("여성비", pop, "RdBu", zeroCenter=True)
#4
pop["2030여성비"] = (pop["20-39세여자"]/pop["20-39세합계"]-0.5) *100
drawKorea("2030여성비", pop, "RdBu", zeroCenter=True)
import folium
import json
pop_folium = pop.set_index('ID')
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
# 소멸위기지역 시각화
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
📝 인구분석 프로젝트는 코드가 길어져서 흐름을 놓칠뻔했다. EDA 전체 강의는 생각보다 빨리 끝났는데 내가 과연 프로젝트를 맡으면 흐름을 잃지 않고 잘 해낼 수 있을까 라는 생각이 들었다. 아직 부족한게 많으니까 하나씩 알아가면서 공부해야겠다.
▷ 내일 학습 계획: EDA 학습과제