도찐개찐
[Python] folium(지도시각화) 본문
지도위에 데이터를 interactive하게 표현해 주는 대표적인 파이썬 지도 시각화 라이브러리 folium에 대해서 알아보고 그 사용법을 파헤쳐보도록 하겠습니다.
folium 개요
folium은 leaflet.js 기반으로 만들어진 Python 지도 시각화 라이브러리 입니다.
folium을 사용하여 인터랙티브한 지도를 생성하고 마커를 추가하여 시각화하거나 원으로 범위를 표기하고 html 파일로 내보내기 등을 수행할 수 있습니다.
folium 설치
pip install folium으로 라이브러리를 설치할 수 있습니다.
!pip install folium
모듈 import
import folium
기본 좌표 설정
location에 위도, 경도 정보를 입력하여 입력한 위,경도 좌표를 기준으로 지도를 그릴 수 있습니다.
이때 zoom_start 정보를 지정하여 확대의 정도를 지정할 수 있습니다.
- 참고: zoom_start의 범위는 최대 18 입니다.
# 위도
latitude = 37.394946
# 경도
longitude = 127.111104
m = folium.Map(location=[latitude, longitude],
zoom_start=17,
width=750,
height=500
)
m
마커 추가
- location: 마커를 추가할 위도/경도 좌표를 입력 후
- popup: 표기할 팝업 문구 지정 (마우스 클릭시 표기되는 문구)
- tooltip: 표기할 툴팁 지정 (마우스 오버시 표기되는 문구)
마커를 생성 후 기존에 생성된 지도 m에 추가합니다.
folium.Marker([latitude, longitude],
popup="판교역",
tooltip="판교역 입구").add_to(m)
m
마커에 대한 스타일 변경도 가능합니다. 스타일 변경시 icon 파라미터에 folium.Icon(color=?, icon=?)을 지정합니다.
folium.Marker([latitude, longitude],
popup="판교역",
tooltip="판교역 입구",
icon=folium.Icon('red', icon='star'),
).add_to(m)
m
popup이나 tooltip에 다음과 같이 html 코드를 삽입하여 이미지를 표기하거나 심지어 YouTube 영상도 삽입할 수 있습니다.
folium.Marker([latitude, longitude],
popup='<iframe width="560" height="315" src="https://www.youtube.com/embed/dpwTOQri42s" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
tooltip="판교역 입구").add_to(m)
m
folium.CircleMarker([latitude, longitude],
color='tomato',
radius = 50,
tooltip='판교역 상권').add_to(m)
m
import pandas as pd
df = pd.read_csv('소상공인시장진흥공단_상가(상권)정보_경기_202109.csv')
df.head(3)
20713599 | 눈높이러닝센타 | NaN | R | 학문/교육 | R13 | 학문교육기타 | R13A01 | 학습지보급 | P85503 | ... | 423010.0 | 14221.0 | NaN | NaN | 2 | 02-2066-9109 | 126.858147 | 37.478530 | NaN | 2016-01-26 |
20642964 | 유니베라 | 구성대리점 | D | 소매 | D16 | 화장품소매 | D16A01 | 화장품판매점 | G47813 | ... | 446525.0 | 16919.0 | NaN | 2 | NaN | 031-8005-7071 | 127.116575 | 37.294690 | NaN | 2021-10-22 |
24510829 | 엠아이케이21 | NaN | D | 소매 | D14 | 운동/경기용품소매 | D14A01 | 운동/경기용품 | G47631 | ... | 463937.0 | 13622.0 | 901 | NaN | 90 | 070-8699-8912 | 127.123291 | 37.340903 | NaN | 2016-01-26 |
3 rows × 42 columns
pd.Series(df.columns)
0 상가업소번호
1 상호명
2 지점명
3 상권업종대분류코드
4 상권업종대분류명
5 상권업종중분류코드
6 상권업종중분류명
7 상권업종소분류코드
8 상권업종소분류명
9 표준산업분류코드
10 표준산업분류명
11 시도코드
12 시도명
13 시군구코드
14 시군구명
15 행정동코드
16 행정동명
17 법정동코드
18 법정동명
19 PNU코드
20 대지구분코드
21 대지구분명
22 지번본번지
23 지번부번지
24 지번주소
25 도로명코드
26 도로명
27 건물본번지
28 건물부번지
29 건물관리번호
30 건물명
31 도로명주소
32 구우편번호
33 신우편번호
34 동정보
35 층정보
36 호정보
37 전화번호
38 경도
39 위도
40 상권번호
41 데이터기준일자
dtype: object
sub_df = df.loc[df['행정동명'].isin(['백현동', '정자동', '삼평동'])]
sub_df.head(3)
24527550 | 코리아세븐분당 | 정자3호점 | D | 소매 | D03 | 종합소매점 | D03A01 | 편의점 | G47122 | ... | 463834.0 | 13612.0 | NaN | 1 | NaN | 031-718-9733 | 127.113508 | 37.362807 | NaN | 2016-01-27 |
28523933 | 처가방 | NaN | Q | 음식 | Q10 | 별식/퓨전요리 | Q10A02 | 샤브샤브전문 | I56111 | ... | 463420.0 | 13529.0 | NaN | NaN | NaN | 031-5170-1908 | 127.112071 | 37.392785 | NaN | 2016-11-17 |
20786763 | 백현 | NaN | L | 부동산 | L01 | 부동산중개 | L01A01 | 부동산중개 | L68221 | ... | 463887.0 | 13532.0 | NaN | 1 | 1 | 031-8016-8100 | 127.110756 | 37.389333 | NaN | 2017-10-02 |
3 rows × 42 columns
sub_df[['위도', '경도', '상호명']]
37.362807 | 127.113508 | 코리아세븐분당 |
37.392785 | 127.112071 | 처가방 |
37.389333 | 127.110756 | 백현 |
37.401265 | 127.108639 | 야쿤커피앤토스트판교점 |
37.368770 | 127.112015 | 해동검도 |
... | ... | ... |
37.397736 | 127.111414 | 에세이스튜디오 |
37.362357 | 127.113513 | 아몽옷고치는전문집 |
37.384859 | 127.111408 | 티랩 |
37.392951 | 127.118687 | 우디크리빙 |
37.395832 | 127.113503 | 써브웨이 |
3322 rows × 3 columns
from folium.plugins import MarkerCluster
m = folium.Map(
location=[latitude, longitude],
zoom_start=15
)
coords = sub_df[['위도', '경도']]
marker_cluster = MarkerCluster().add_to(m)
for lat, long in zip(coords['위도'], coords['경도']):
folium.Marker([lat, long], icon = folium.Icon(color="green")).add_to(marker_cluster)
m
서울 지도에서 행정 구역별 표시
import requests
import json
# 서울 행정구역 json raw파일(githubcontent)
r = requests.get('https://raw.githubusercontent.com/southkorea/seoul-maps/master/kostat/2013/json/seoul_municipalities_geo_simple.json')
c = r.content
seoul_geo = json.loads(c)
서울 지역의 구별 boundary 시각화
m = folium.Map(
location=[37.559819, 126.963895],
zoom_start=11,
)
folium.GeoJson(
seoul_geo,
name='지역구'
).add_to(m)
m
tiles 옵션 변경을 통해 지도의 테마 변경
지도의 기본 테마(tiles)는 OpenStreetMap으로 설정되어 있는데, 이를 변경하여 다른 지도 테마를 적용할 수 있다.
Stamen Toner 적용시
m = folium.Map(
location=[37.559819, 126.963895],
zoom_start=11,
tiles='Stamen Toner'
)
folium.GeoJson(
seoul_geo,
name='지역구'
).add_to(m)
m
cartodbpositron 적용시
m = folium.Map(
location=[37.559819, 126.963895],
zoom_start=11,
tiles='cartodbpositron'
)
folium.GeoJson(
seoul_geo,
name='지역구'
).add_to(m)
m
서울시 자치구별 상권정보 시각화
seoul 데이터프레임에 소상공인시장진흥공단에서 제공하는 서울시 상권정보 csv 파일을 로드합니다.
seoul = pd.read_csv('소상공인시장진흥공단_상가(상권)정보_서울_202109.csv')
# 필요한 컬럼 정보만 가져옵니다
seoul = seoul[['시군구명', '상권업종대분류명', '상권업종중분류명', '위도', '경도']]
seoul
송파구 | 소매 | 의복의류 | 37.493054 | 127.147321 |
영등포구 | 소매 | 건강/미용식품 | 37.520613 | 126.907168 |
성동구 | 소매 | 취미/오락관련소매 | 37.566857 | 127.049018 |
동작구 | 음식 | 한식 | 37.487105 | 126.980952 |
종로구 | 음식 | 한식 | 37.572387 | 126.981794 |
... | ... | ... | ... | ... |
마포구 | 부동산 | 부동산중개 | 37.557971 | 126.907290 |
은평구 | 소매 | 시계/귀금속소매 | 37.604195 | 126.936049 |
은평구 | 소매 | 애견/애완/동물 | 37.596790 | 126.905613 |
광진구 | 음식 | 커피점/카페 | 37.556004 | 127.085023 |
강북구 | 생활서비스 | 이/미용/건강 | 37.629348 | 127.017622 |
325880 rows × 5 columns
시각화 모듈을 import 하고 서울시의 업종별 개수를 시각화합니다.
import matplotlib.pyplot as plt
import seaborn as sns
# 한글 폰트 설정
plt.rcParams['font.family'] = 'NanumGothic'
plt.figure(figsize=(12, 30))
sns.countplot(y=seoul['상권업종중분류명'], order=seoul['상권업종중분류명'].value_counts().index)
plt.yticks(fontsize=12)
plt.title('서울시 업종별 개수')
plt.show()
한식 업종이 가장 많은 개수를 차지합니다. 커피점/카페 업종이 가장 많은 업종일 줄 알았는데요. 한식, 이미용/건강, 종합소매업 다음 4위를 차지하였습니다.
그럼 커피점/카페 업종은 자치구별로 어느 곳에 가장 많이 분포해 있는지 시각화해 보겠습니다.
plt.figure(figsize=(12, 10))
seoul.loc[seoul['상권업종중분류명'] == '커피점/카페'].groupby('시군구명')['상권업종대분류명'].count()\
.sort_values().plot(kind='barh', color='royalblue')
plt.yticks(fontsize=12)
plt.title('서울시 자치구별 커피점/카페 업종수')
plt.show()
강남구가 가장 많은 수를 차지하고 강북구는 가장 적은 수를 차지하였습니다.
이제 이를 folium 위에 시각화를 해보겠습니다.
seoul_coffee = seoul.loc[seoul['상권업종중분류명'] == '커피점/카페']
seoul_coffee
종로구 | 음식 | 커피점/카페 | 37.583149 | 127.000171 |
도봉구 | 음식 | 커피점/카페 | 37.658728 | 127.034746 |
마포구 | 음식 | 커피점/카페 | 37.554599 | 126.929692 |
강서구 | 음식 | 커피점/카페 | 37.580941 | 126.813358 |
양천구 | 음식 | 커피점/카페 | 37.522055 | 126.842935 |
... | ... | ... | ... | ... |
서대문구 | 음식 | 커피점/카페 | 37.557293 | 126.950705 |
금천구 | 음식 | 커피점/카페 | 37.468602 | 126.902047 |
마포구 | 음식 | 커피점/카페 | 37.540241 | 126.948240 |
강서구 | 음식 | 커피점/카페 | 37.532109 | 126.839080 |
광진구 | 음식 | 커피점/카페 | 37.556004 | 127.085023 |
19703 rows × 5 columns
m = folium.Map(
location=[37.559819, 126.963895],
zoom_start=12,
tiles='cartodbpositron'
)
folium.GeoJson(
seoul_geo,
name='지역구'
).add_to(m)
marker_cluster = MarkerCluster().add_to(m)
for lat, long in zip(seoul_coffee['위도'], seoul_coffee['경도']):
folium.Marker([lat, long], icon = folium.Icon(color="green")).add_to(marker_cluster)
m
seoul_group_data = seoul.loc[seoul['상권업종중분류명'] == '커피점/카페'].groupby('시군구명')['상권업종중분류명'].count()
seoul_group_data
시군구명
강남구 2253
강동구 763
강북구 330
강서구 1048
관악구 593
광진구 771
구로구 596
금천구 444
노원구 534
도봉구 373
동대문구 499
동작구 526
마포구 1427
서대문구 704
서초구 1277
성동구 640
성북구 668
송파구 1125
양천구 543
영등포구 859
용산구 730
은평구 575
종로구 1082
중구 895
중랑구 448
Name: 상권업종중분류명, dtype: int64
choropleth 를 사용하여 시각화를 하면 업종 별 개수에 따른 색상의 차이를 두어 시각화를 해줍니다.
m = folium.Map(
location=[37.559819, 126.963895],
zoom_start=11,
tiles='cartodbpositron'
)
folium.GeoJson(
seoul_geo,
name='지역구'
).add_to(m)
m.choropleth(geo_data=seoul_geo,
data=seoul_group_data,
fill_color='YlOrRd', # 색상 변경도 가능하다
fill_opacity=0.5,
line_opacity=0.2,
key_on='properties.name',
legend_name="지역구별 커피 업종 수"
)
m
bins를 만들어 1/4, 2/4, 3/4분위수별로 구간을 나누어 시각화할 수 있습니다.
bins = list(seoul_group_data.quantile([0, 0.25, 0.5, 0.75, 1]))
m = folium.Map(
location=[37.559819, 126.963895],
zoom_start=11,
tiles='cartodbpositron'
)
folium.GeoJson(
seoul_geo,
name='지역구'
).add_to(m)
m.choropleth(geo_data=seoul_geo,
data=seoul_group_data,
fill_color='YlOrRd', # 색상 변경도 가능하다
fill_opacity=0.5,
line_opacity=0.2,
key_on='properties.name',
legend_name="지역구별 커피 업종 수",
bins=bins
)
m
Html 파일로 저장
저장은 save() 함수로 쉽게 html 파일로 저장할 수 있습니다.
'PYTHON > 데이터분석' 카테고리의 다른 글
[Python] 선형회귀 (2) | 2022.12.26 |
---|---|
인공지능 개념 (0) | 2022.12.26 |
[Python] plot 한글 사용 (0) | 2022.12.26 |
[Python] 스파크(spark) (0) | 2022.12.26 |
[Python] 판다스(pandas) (0) | 2022.12.26 |