🖥️ IT, 컴퓨터/🐍 Python

[Python] 생활인구 데이터 파이썬 코드 템플릿

김 홍시 2025. 3. 9. 16:28
반응형

1. 기본 전처리

#0.1. 파일 업로드
import pandas as pd
df = pd.read_csv("당신의경로/LOCAL_PEOPLE_20240406.csv", encoding="euc-kr") 
df = df.rename(columns={'?"기준일ID"': "date", '시간대구분':'time', '행정동코드':'통계청dong_cd',"집계구코드":'jgg_cd',
                         "남자0세부터9세생활인구수":'man00-09', "남자10세부터14세생활인구수":'man10-14', "남자15세부터19세생활인구수": "man15-19", "남자20세부터24세생활인구수": "man20-24",
    "남자25세부터29세생활인구수": "man25-29", "남자30세부터34세생활인구수": "man30-34",
    "남자35세부터39세생활인구수": "man35-39", "남자40세부터44세생활인구수": "man40-44",
    "남자45세부터49세생활인구수": "man45-49", "남자50세부터54세생활인구수": "man50-54",
    "남자55세부터59세생활인구수": "man55-59", "남자60세부터64세생활인구수": "man60-64",
    "남자65세부터69세생활인구수": "man65-69", "남자70세이상생활인구수": "man70+",
    "여자0세부터9세생활인구수": "woman00-09", "여자10세부터14세생활인구수": "woman10-14",
    "여자15세부터19세생활인구수": "woman15-19", "여자20세부터24세생활인구수": "woman20-24",
    "여자25세부터29세생활인구수": "woman25-29", "여자30세부터34세생활인구수": "woman30-34",
    "여자35세부터39세생활인구수": "woman35-39", "여자40세부터44세생활인구수": "woman40-44",
    "여자45세부터49세생활인구수": "woman45-49", "여자50세부터54세생활인구수": "woman50-54",
    "여자55세부터59세생활인구수": "woman55-59", "여자60세부터64세생활인구수": "woman60-64",
    "여자65세부터69세생활인구수": "woman65-69", "여자70세이상생활인구수": "woman70+"})
df = df.replace('*', 0)
df.head(10)

 

 

길이가 긴 열 이름을 변경하고,

*으로 나오는 값을 0으로 변경한 결과

 

 

# 0.2. 집계구코드_동정보 join
df_kt = pd.read_csv(r"당신의경로/KT생활인구집계구.csv")
# jgg_cd를 기준으로 df에 df_kt를 왼쪽 조인 (df 기준)
df_merged = df.merge(df_kt, on='jgg_cd', how='left')
df_merged = df_merged.rename(columns={"emd_cd": "행안부emd_cd",})
column_order = ['date', 'time', '통계청dong_cd', 'emd_nm', '행안부emd_cd', 'jgg_cd', '총생활인구수',  'man00-09', 'man10-14',
       'man15-19', 'man20-24', 'man25-29', 'man30-34', 'man35-39', 'man40-44',
       'man45-49', 'man50-54', 'man55-59', 'man60-64', 'man65-69', 'man70+',
       'woman00-09', 'woman10-14', 'woman15-19', 'woman20-24', 'woman25-29',
       'woman30-34', 'woman35-39', 'woman40-44', 'woman45-49', 'woman50-54',
       'woman55-59', 'woman60-64', 'woman65-69', 'woman70+'  ]
# df_merged의 열 순서 변경
df_merged = df_merged[column_order]
df_merged.head(10)

 

KT생활인구집계구.csv
0.62MB

 

집계구 파일 다운 받아 위와 같이 코드 입력하면

해당 집계구가 무슨 동에 해당하는지 알 수 있음

 

 

 

#0.3 folium 지도 세팅

import folium
import geopandas as gpd
from IPython.core.display import HTML

# 📌 Shapefile 로드
shapefile1_path = "당신의경로/행정구역.shp"
shapefile2_path = "당신의경로/집계구.shp"

gdf1 = gpd.read_file(shapefile1_path)  # 행정구역
gdf2 = gpd.read_file(shapefile2_path)  # 집계구

# 📌 좌표계 확인 및 변환 (EPSG:4326)
if gdf1.crs != "EPSG:4326":
    gdf1 = gdf1.to_crs(epsg=4326)

if gdf2.crs != "EPSG:4326":
    gdf2 = gdf2.to_crs(epsg=4326)

# 📌 투영 좌표계 변환 후 중심 좌표 계산
gdf_proj = gdf1.to_crs(epsg=3857)  # EPSG:3857로 변환
centroid = gdf_proj.geometry.centroid.to_crs(epsg=4326)  # 다시 EPSG:4326으로 변환

# 📌 folium 지도 생성 (중심 좌표 설정)
m = folium.Map(
    location=[centroid.y.mean(), centroid.x.mean()],
    zoom_start=12
)

# 📌 GeoJson 변환 및 스타일 적용 (첫 번째 레이어: 행정구역)
folium.GeoJson(
    gdf1,
    name="행정구역",
    style_function=lambda x: {
        "fillColor": "grey",
        "color": "black",
        "weight": 1,
        "fillOpacity": 0.0
    }
).add_to(m)

# 📌 GeoJson 변환 및 스타일 적용 (두 번째 레이어: 집계구, 툴팁 추가)
folium.GeoJson(
    gdf2,
    name="집계구",
    style_function=lambda x: {
        "fillColor": "blue",
        "color": "darkblue",
        "weight": 0.5,
        "fillOpacity": 0.2
    },
    tooltip=folium.GeoJsonTooltip(
        fields=["TOT_REG_CD", "ADM_NM"],  # 툴팁에 표시할 필드
        aliases=["집계구코드:", "행정동:"],  # 툴팁에서 보일 레이블
        localize=True
    )
).add_to(m)

# 📌 지도에 레이어 컨트롤 추가 (켜고 끌 수 있음)
folium.LayerControl().add_to(m)

# 📌 HTML 스타일을 이용한 지도 높이 조절
map_height = 600  # 원하는 지도 높이 설정
HTML(f'<div style="width: 100%; height: {map_height}px;">{m._repr_html_()}</div>')

 

 

 

집계구 / 읍면동 함께 보이는 지도 세팅하는 코드

위 코드 실행 전에 아래 파일 받아서 압축 풀어둘 것(행정구역, 집계구 shp) 

생활인구_집계구_2016년 기준.zip
8.02MB

 

 

 

 

이렇게 마우스 오버 시, 해당 집계구가 어떤 행정동에 해당하는지 보임.

 

 

이제 기본 세팅은 끝

 

 

 

 

 

2. 총생활인구 수 기준으로 정렬하기

# 1. 총생활인구수 랭킹
df_총인구수 = df_merged[['date', 'time', '통계청dong_cd', 'emd_nm', '행안부emd_cd', 'jgg_cd', '총생활인구수']]
df_총인구수 = df_총인구수[df_총인구수['time'] == 15]  # time이 15인 데이터만 필터링
df_총인구수 = df_총인구수.sort_values(by='총생활인구수', ascending=False)
df_총인구수.head(20)

 

 

 

 

import folium
import geopandas as gpd
import pandas as pd
import random
from IPython.display import display, HTML

# 파일 로드 및 EPSG 변환 함수 정의
def load_and_transform_shapefile(path, target_crs="EPSG:4326"):
    gdf = gpd.read_file(path)
    if gdf.crs != target_crs:
        gdf = gdf.to_crs(epsg=int(target_crs.split(":")[1]))
    return gdf

# 경로 설정
shapefile1_path = "당신의경로/행정구역.shp"
shapefile2_path = "당신의경로/집계구.shp"

gdf1 = load_and_transform_shapefile(shapefile1_path)  # 행정구역
gdf2 = load_and_transform_shapefile(shapefile2_path)  # 집계구

# 총생활인구수 데이터 로드 (df_총인구수가 주어진다고 가정)
df_총인구수 = df_총인구수[df_총인구수['time'] == 15]  # 특정 시간대(15시) 필터링

# 상위 20개 집계구 코드 추출 (1~10위, 11~20위 분리)
top_10_jgg_cd = set(df_총인구수["jgg_cd"].head(10))  # 1~10위
top_11_20_jgg_cd = set(df_총인구수["jgg_cd"].iloc[10:20])  # 11~20위

# gdf2에 총생활인구수 데이터 병합
gdf2 = gdf2.merge(df_총인구수[['jgg_cd', '총생활인구수']], on='jgg_cd', how='left')

# 지도 중심 계산
gdf_proj = gdf1.to_crs(epsg=3857)
centroids = gdf_proj.geometry.centroid.to_crs(epsg=4326)
centroid_y, centroid_x = centroids.y.mean(), centroids.x.mean()

# Folium 지도 생성
m = folium.Map(location=[centroid_y, centroid_x], zoom_start=12)

# 행정구역 GeoJson 추가
folium.GeoJson(
    gdf1,
    name="행정구역",
    style_function=lambda _: {"fillColor": "grey", "color": "black", "weight": 1, "fillOpacity": 0.0},
).add_to(m)

# 집계구 스타일 함수 (순위별 색상 적용)
def get_top_20_style(feature):
    jgg_cd = feature["properties"].get("jgg_cd")
    if jgg_cd in top_10_jgg_cd:
        color = "red"  # 1~10위: 빨간색
    elif jgg_cd in top_11_20_jgg_cd:
        color = "pink"  # 11~20위: 분홍색
    else:
        color = "blue"  # 나머지: 파란색
    return {
        "fillColor": color,
        "color": "black",
        "weight": 0.5,
        "fillOpacity": 0.7 if jgg_cd in top_10_jgg_cd or jgg_cd in top_11_20_jgg_cd else 0.1,
    }

# 집계구 GeoJson 추가 (툴팁에 '총생활인구수' 추가)
folium.GeoJson(
    gdf2,
    name="집계구",
    style_function=get_top_20_style,
    tooltip=folium.GeoJsonTooltip(
        fields=["TOT_REG_CD", "ADM_NM", "총생활인구수"],  # 총 3개 필드 포함
        aliases=["집계구코드:", "행정동:", "총생활인구수:"],  
        localize=True
    ),
).add_to(m)

# 상위 20개 집계구 마커 추가
for _, row in gdf2[gdf2["jgg_cd"].isin(top_10_jgg_cd | top_11_20_jgg_cd)].iterrows():
    centroid = row.geometry.centroid
    folium.Marker(
        location=[centroid.y + random.uniform(-0.001, 0.001), centroid.x + random.uniform(-0.001, 0.001)],
        icon=folium.DivIcon(
            html=f'<div style="font-size:10px; color:black; font-weight:bold; background-color:white; padding:2px; border-radius:3px;">{row["ADM_NM"]}</div>'
        ),
    ).add_to(m)

# 레이어 컨트롤 추가
folium.LayerControl().add_to(m)

# 지도 출력
display(HTML(f'<div style="width: 100%; height: 600px;">{m._repr_html_()}</div>'))

# 지도 저장
m.save("C:/Users/jiyun/OneDrive/바탕 화면/map.html")

 

마우스 스크롤 확대/축소 감도 조절

 

상위 1위-10위 - 빨간색,
상위 11위-20위 - 분홍색 설정되도록 조절

  

추가로 해결해야 할 과제

집계구는 울퉁불퉁하다는 문제가 있음.

어떻게 100m by 100m으로 맵핑시킬지 

 

반응형