EDA_인구분석

이새롬·2023년 3월 5일
0

EDA

목록 보기
7/7
post-thumbnail

목표

  1. 인구 소멸 위기 지역 파악
  2. 인구 소멸 위기 지역의 지도 표현
  3. 지도 표현에 대한 카르토그램 표현

데이터 읽고 인구 소멸 지역 계산하기

선언

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

적용 전

적용 후


지도 시각화를 위한 지역별 ID 만들기

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()


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

민형기 담임교수님이 한땀 한땀 만든 엑셀파일 불러오기

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()

이 두 함수로 윤곽선과 글자 배치조정


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

  • 색상을 만들 때 최소값을 흰색
  • blockedMap : 인구현황
  • 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(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()

0개의 댓글