인구 분석 (2)

Jungmin·2022년 10월 28일
1

데이터 분석

목록 보기
18/24

지도 그리기 (카르토그램)

draw_korea_raw = pd.read_excel("../data/07_draw_korea_raw.xlsx")
draw_korea_raw
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 NaN NaN NaN NaN NaN NaN NaN 철원 화천 양구 고성(강원) NaN NaN NaN
1 NaN NaN NaN 양주 동두천 연천 포천 의정부 인제 춘천 속초 NaN NaN NaN
2 NaN NaN NaN 고양 덕양 고양 일산동 서울 도봉 서울 노원 남양주 홍천 횡성 양양 NaN NaN NaN
3 NaN NaN 파주 고양 일산서 김포 서울 강북 서울 성북 가평 구리 하남 정선 강릉 NaN NaN
4 NaN NaN 부천 소사 안양 만안 광명 서울 서대문 서울 종로 서울 동대문 서울 중랑 양평 태백 동해 NaN NaN
5 NaN 인천 강화 부천 원미 안양 동안 서울 은평 서울 마포 서울 중구 서울 성동 서울 강동 여주 원주 삼척 NaN NaN
6 NaN 인천 서구 부천 오정 시흥 서울 강서 서울 동작 서울 용산 서울 광진 서울 송파 이천 평창 울진 NaN NaN
7 NaN 인천 동구 인천 계양 안산 상록 서울 양천 서울 관악 서울 서초 성남 중원 과천 광주 영월 영덕 NaN NaN
8 NaN NaN 인천 부평 안산 단원 서울 영등포 서울 금천 서울 강남 성남 분당 성남 수정 용인 수지 문경 봉화 NaN 울릉
9 NaN 인천 중구 인천 남구 화성 서울 구로 군포 의왕 수원 영통 용인 기흥 용인 처인 안동 영양 NaN NaN
10 인천 옹진 인천 연수 인천 남동 오산 안성 수원 권선 수원 장안 제천 예천 영주 구미 청송 포항 북구 NaN
11 태안 아산 천안 동남 천안 서북 평택 음성 수원 팔달 단양 상주 김천 군위 의성 포항 남구 NaN
12 NaN 당진 홍성 예산 공주 진천 충주 청주 흥덕 괴산 칠곡 영천 경산 경주 NaN
13 NaN 서산 보령 청양 세종 대전 대덕 증평 청주 청원 보은 고령 청도 성주 울산 북구 NaN
14 NaN NaN 부여 논산 계룡 대전 동구 청주 상당 청주 서원 대구 북구 대구 중구 대구 수성 울산 울주 울산 동구 NaN
15 NaN NaN 서천 금산 대전 유성 대전 중구 옥천 영동 대구 서구 대구 남구 대구 동구 울산 중구 울산 남구 NaN
16 NaN NaN 군산 익산 대전 서구 무주 거창 합천 대구 달서 대구 달성 부산 금정 부산 동래 부산 기장 NaN
17 NaN NaN 부안 김제 완주 장수 함양 창녕 밀양 부산 북구 부산 부산진 부산 연제 부산 해운대 NaN
18 NaN 고창 정읍 전주 덕진 진안 남원 진주 의령 부산 강서 부산 사상 부산 동구 부산 중구 NaN NaN
19 NaN 영광 장성 전주 완산 임실 산청 함안 양산 창원 합포 부산 서구 부산 사하 부산 남구 NaN NaN
20 NaN 함평 담양 순창 구례 하동 창원 의창 창원 성산 창원 진해 김해 부산 영도 부산 수영 NaN NaN
21 신안 무안 광주 광산 곡성 화순 광양 사천 창원 회원 통영 NaN NaN NaN NaN NaN
22 목포 나주 광주 서구 광주 북구 순천 고흥 남해 고성(경남) 거제 NaN NaN NaN NaN NaN
23 해남 영암 광주 남구 광주 동구 여수 NaN NaN NaN NaN NaN NaN NaN NaN NaN
24 진도 강진 장흥 보성 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
25 NaN NaN 완도 NaN NaN 제주 NaN NaN NaN NaN NaN NaN NaN NaN
26 NaN NaN NaN NaN NaN 서귀포 NaN NaN NaN NaN NaN NaN NaN NaN
draw_korea_raw.stack()
0   7         철원
    8         화천
    9         양구
    10    고성(강원)
1   3         양주
           ...  
24  2         장흥
    3         보성
25  2         완도
    5         제주
26  5        서귀포
Length: 252, dtype: object
draw_korea_raw_stacked = pd.DataFrame(draw_korea_raw.stack())
draw_korea_raw_stacked.reset_index(inplace=True)
draw_korea_raw_stacked
level_0 level_1 0
0 0 7 철원
1 0 8 화천
2 0 9 양구
3 0 10 고성(강원)
4 1 3 양주
... ... ... ...
247 24 2 장흥
248 24 3 보성
249 25 2 완도
250 25 5 제주
251 26 5 서귀포

252 rows × 3 columns

# 컬럼 이름 변경
draw_korea_raw_stacked.rename(
    columns={
        "level_0" : "y",
        "level_1" : "x",
        0 : "ID"
    }, inplace=True)
draw_korea_raw_stacked
y x ID
0 0 7 철원
1 0 8 화천
2 0 9 양구
3 0 10 고성(강원)
4 1 3 양주
... ... ... ...
247 24 2 장흥
248 24 3 보성
249 25 2 완도
250 25 5 제주
251 26 5 서귀포

252 rows × 3 columns

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)]
]
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(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(# matplotlib 주석 다는 기능
            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()
simpleDraw(draw_korea)

pop.head()
광역시도 시도 20-39세여자 20-39세합계 65세 이상합계 인구수남자 인구수여자 인구수합계 소멸 비율 소멸 위기 지역 ID
0 강원도 강릉시 23098.0 49384.0 37679.0 106231.0 107615.0 213846.0 1.226041 False 강릉
1 강원도 고성군 2529.0 7023.0 7151.0 15899.0 14215.0 30114.0 0.707314 True 고성(강원)
2 강원도 동해시 9753.0 21264.0 15124.0 47166.0 46131.0 93297.0 1.289738 False 동해
3 강원도 삼척시 7115.0 15823.0 14610.0 35253.0 34346.0 69599.0 0.973990 True 삼척
4 강원도 속초시 8752.0 18708.0 12752.0 40288.0 41505.0 81793.0 1.372647 False 속초
draw_korea.head()  # pop 과 draw_korea에 공통된 "ID"
y x ID
0 0 7 철원
1 0 8 화천
2 0 9 양구
3 0 10 고성(강원)
4 1 3 양주

검증작업

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()))
set()
tmp2_list = list(set(draw_korea["ID"].unique()) - set(pop["ID"].unique()))

for tmp in tmp2_list:
    draw_korea = draw_korea.drop(draw_korea[draw_korea["ID"]== tmp].index)
print(set(draw_korea["ID"].unique()) - set(pop["ID"].unique()))
set()

공통 컬럼 "ID"로 merge`

pop = pd.merge(pop,draw_korea, how="left", on="ID")
pop.head()
광역시도 시도 20-39세여자 20-39세합계 65세 이상합계 인구수남자 인구수여자 인구수합계 소멸 비율 소멸 위기 지역 ID y x
0 강원도 강릉시 23098.0 49384.0 37679.0 106231.0 107615.0 213846.0 1.226041 False 강릉 3 11
1 강원도 고성군 2529.0 7023.0 7151.0 15899.0 14215.0 30114.0 0.707314 True 고성(강원) 0 10
2 강원도 동해시 9753.0 21264.0 15124.0 47166.0 46131.0 93297.0 1.289738 False 동해 4 11
3 강원도 삼척시 7115.0 15823.0 14610.0 35253.0 34346.0 69599.0 0.973990 True 삼척 5 11
4 강원도 속초시 8752.0 18708.0 12752.0 40288.0 41505.0 81793.0 1.372647 False 속초 1 10

그림을 그리기 위한 데이터를 계산하는 함수

  • 색상을 만들 때, 최소값을 흰색으로
  • blockedMap : 인구현황 (pop)
  • targetData : 그리고싶은 컬럼
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()
    
drawKorea("인구수합계", pop, "Blues")

pop["소멸 위기 지역"]

pop["소멸 위기 지역"] = [1 if con else 0 for con in pop["소멸 위기 지역"]]  # True/False 로 된 데이터 --> 0 /1 
drawKorea("소멸 위기 지역", pop, "Reds")

# 여성 비 컬럼 만들기 
pop["여성비"] = (pop["인구수여자"] / pop["인구수합계"] - 0.5 ) * 100
drawKorea("여성비", pop, "RdBu", zeroCenter=True)

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

folium으로 지도 시각화

import folium
import json 

pop_folium = pop.set_index("ID")  #지도 그릴 때 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

# 소멸위기 지역 시각화

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

# 데이터 저장 

draw_korea.to_csv("../data/07_draw_korea2.csv", encoding="utf-8", sep=",")
profile
데이터분석 스터디노트🧐✍️

0개의 댓글