인구데이터 지도 시각화, folium, choropieth map,
대한민국의 시군구별 인구 데이터를 folium 라이브러리를 사용하여 단계구분도로 시각화하는 예제
단계 구분도(choropieth map) : 지역별 통계를 색으로 구분한 지도 *folium 외부 라이브러리 필요
목적 :
지역별 인구수 분포를 시각적으로 표현해 공간적 특성 이해
지역별 인구 밀도의 편차, 지역 간 비교 분석
<필수 라이브러리>
webbrowser, json, pandas, folium
browser_open 함수 : 생성된 지도를 html 파일로 저장하고 웹 브라우저에서 자동 열기
< 필요 데이터>
대한민국 시군구별 경계좌표 데이터 geojson = 지형 데이터)
시군구별 인구데이터
서울시 동별 경계 지도 데이터
동별 외국인 인구 데이터
구 경계선 데이터
< 파이참에서 웹브라우져를 통해 지도 표시 >
import webbrowser
def browser_open (f_map, path) :
html_page = f'{path}'
f_map.save(html_page)
webbrowser.open(html_page)
<전체 과정의 흐름 >
1. GeoJSON 파일 (지도 경계 좌표) 로딩 (json 라이브러리 사용)
2. 시군구 인구 데이터 (csv) 로딩 및 전처리 (pandas 라이브러리 사용)
3. 지도에 데이터 시각화 (folium)
4. 지도 웹 브라우져로 보기 (webbrowser)
# geovisulalization1.py
# 대한민국 시군구별 인구데이터 지도시각화
# pycharm에서 웹브라우져를 통해 지도 표시 함수
import webbrowser
def browser_open(f_map, path):
html_page = f'{path}'
f_map.save(html_page)
webbrowser.open(html_page)
# 대한민국 시군구 경계 좌표 데이터
import json
geo = json.load(open('assets/SIG.geojson', encoding="utf-8"))
#print(geo)
#print(type(geo)) # dictionary
# 행정구역 코드
print(geo['features'][0]['properties']['SIG_CD'])
# 위도, 경도 좌표
print(geo['features'][0]['geometry']['coordinates'])
# 시군구별 인구데이터
import pandas as pd
df_pop = pd.read_csv('assets/Population_SIG.csv')
df_pop.info()
# code를 int64타입에서 문자타입으로 변환
df_pop['code'] = df_pop['code'].astype(str)
# 단계구분도 생성
import folium
# 지도 형태
map_sig = folium.Map(
location = [35.95, 127.7], # 중심 위/경도
zoom_start = 8, # 확대 레벨
tiles = 'cartodbpositron' # 지도의 타일
)
# 지도 데이터
folium.Choropleth(
geo_data = geo, # 지도데이터
data = df_pop, # 인구데이터
columns = ('code', 'pop'), # 행정구역코드, 인구수
key_on = 'feature.properties.SIG_CD' # geo 행정구역코드를 키로 사용
).add_to(map_sig) # 지도에 데이터 추가
# 지도를 웹브라우져에 표시
browser_open(map_sig, 'geo1.html')
# 계급구간 정하기
bins = list(df_pop['pop'].quantile([0, 0.2, 0.4, 0.6, 0.8, 1]))
#print(bins)
folium.Choropleth(
geo_data = geo, # 지도데이터
data = df_pop, # 인구데이터
columns = ('code', 'pop'), # 행정구역코드, 인구수
key_on = 'feature.properties.SIG_CD', # geo 행정구역코드를 키로 사용
fill_color = 'YlGnBu', # 채움 색상
fill_opacity = 0.5, # 투명도
bins = bins # 계급구간
).add_to(map_sig) # 지도에 데이터 추가
browser_open(map_sig, 'geo2.html')
< 코드 뜯어보기 >
import webbrowser
def browser_open(f_map, path):
html_page = f'{path}'
f_map.save(html_page)
webbrowser.open(html_page)
- 지도를 html로 저장하고, 브라우저에서 자동으로 열어줄 때, 반복적으로 지도를 확인할 때 편리하게 쓰인다.
import json
geo = json.load(open('assets/SIG.geojson', encoding="utf-8"))
지도 경계(시군구 단위) 데이터를 JSON 형태로 불러옴 (각 시군구는 좌표와 코드(SIG_CD) 를 가지고 있음)
print(geo['features'][0]['properties']['SIG_CD']) # 행정구역 코드 확인
print(geo['features'][0]['geometry']['coordinates']) # 경계 좌표 확인
+)
geo의 구조 확인하고 이해하기
geo 데이터의 "features" (key)의 첫 번째 인덱스를 가져오고 [0], "properties"를 가져온 뒤 SIG_CD를 뽑은 것
객체의 객체의 객체 구조이므로 확실하게 필요함.
import pandas as pd
df_pop = pd.read_csv('assets/Population_SIG.csv')
df_pop.info()
데이터 처리+분석을 위한 필수 라이브러리 pandas 임포트
'Population_SIG.csv"를 읽어 pandas dataframe(df_pop)으로 저장 => 각 시군구별 행정구역 코드와 인구수가 있음
info() : 데이터 프레임의 정보(컬럼명, 타입, 결측값 등) 확인
df_pop['code'] = df_pop['code'].astype(str)
code 컬럼(행정구역 코드)을 숫자형 -> 문자형 으로 변환
GeoJSON과 연결할 때 문자형으로 통일하는 게 중요
import folium
map_sig = folium.Map(
location=[35.95, 127.7],
zoom_start=8,
tiles='cartodbpositron'
)
지도 시각화에 자주 사용되는 folium 라이브러리 불러오기
map_sig : 지도 객체 생성
중심 좌표 : 대한민국 중심부
초기 줌 레벨 : 8 -> 전국 시군구 범위 표현에 적합
타일 유형 : cartodbpositron : 깔끔하고 가독성 높은 스타일
folium.Choropleth(
geo_data=geo,
data=df_pop,
columns=('code', 'pop'),
key_on='feature.properties.SIG_CD'
).add_to(map_sig)
단계구분도 지도 추가
시군구 경계를 기반으로 인구수 데이터를 지도 위에 색상으로 표현
geo_data = geo : 지리적 경계 정보를 담은 GeoJSON 데이터
data = df_pop : 분석할 실제 데이터 (인구수 데이터)
columns = ('code', 'pop') : 데이터 프레임에서 사용할 컬럼 (지역코드, 인구수)
key_on='feature.properties.SIG_CD'. : GeoJSON 데이터와 연결할 키로 사용할 행정쿠역 코드 설정
.add_to(map_sig) : 지도 (map_sig)에 이 데이터 추가
browser_open(map_sig, 'geo1.html')
지금까지 생성한 지도를 'geo1.html' 파일로 저장하고, 웹브라우저에서 표시하는 함수 호출
bins = list(df_pop['pop'].quantile([0, 0.2, 0.4, 0.6, 0.8, 1]))
folium.Choropleth(
geo_data=geo,
data=df_pop,
columns=('code', 'pop'),
key_on='feature.properties.SIG_CD',
fill_color='YlGnBu',
fill_opacity=0.5,
bins=bins
).add_to(map_sig)
인구 데이터에서 0% ~ 100% 백분위수 값을 계급으로 설정해 리스트로 만듦 => 데이터 분포를 계급으로 나눠 표현해 보다 직관적인 인사이트 도출 가능
bins(계급 구간)을 활용한 단계 구분도 재 생성
추가된 옵션 :
fill_color = "YIGnBu" : 색상 팔레트 : 노랑 -> 초록 -> 파랑
fill_opacity=0.5 : 지도 색상의 투명도 조정 (지도 가시성 높임)
bins = bins =: 미리 정한 계급 구간을 적용 -> 데이터의 시각적 명확성 향상
browser_open(map_sig, 'geo2.html')
계급 구간이 적용된 최종 지도를 html 파일로 저장하고 웹 브라우저에서 바로 표시
< 인사이트 >
인구 밀집 지역 파악 : 인구가 집중된 지역과 인구가 적은 지역을 시각적으로 즉각 식별 가능
지역간 인구 불균형 정도 평가 : 특정 계급 간 급격한 색상 변화로 인구수 격차를 직관적으로 확인
추가 분석 연결 가능 : 지역별 인구를 기반으로 한 인구수 격차를 직관적으로 확인 가능
< 추가 >
1. GeoJSON과 데이터 연결 키 (SIG_CD) 처리 : 데이터 타입을 문자형으로 일치 시키기
df_pop['code'] = df_pop['code'].astype(str)
2. 지도 스타일링 및 UX 개선
* 지도 색상 팔레트 (fill_color)는 데이터 특성에 따라 변경
- 인구수처럼 연속적인 변수 -> Sequential 색상 팔레트 (YIFnBu, PuBu 등)
- 양극성 데이터 -> Diverging 색상 팔레트 활용 (RdBu 등)
* 계급(bins)을 설정할 때 데이터 분포에 따라 수정
- 균등 분할 외에도 Jenks Natural Breaks 등 통계적 접근도 활용.
3. 인터랙티브 지도 정보 추가
Folium의 GeoJsonTooltip 활용해서 지역 이름과 인구스를 마우스 오버로 제공하면 편의성+활용성 up
folium.GeoJson(
geo,
tooltip=folium.GeoJsonTooltip(fields=['SIG_KOR_NM', 'SIG_CD'])
).add_to(map_sig)
< 향후 개선 방향 및 확장성 >
1. 시계열 데이터 적용 : 특정 연도별 변화 추이를 비교 시각화 (시간적 인사이트 제공)
2. 다양한 데이터 병합 활용 : 경제 지표, 연령층 분포, 인프라 수준 등을 병합하여 복합적 분석 수행 가능
3. 웹 기반 서비스로 확장 : 대시보드 (Ploty Dash 등)로 확장하여 실시간으로 관리하고 전달 가능