🔥알림🔥
① 테디노트 유튜브 -
구경하러 가기!
② LangChain 한국어 튜토리얼
바로가기 👀
③ 랭체인 노트 무료 전자책(wikidocs)
바로가기 🙌
④ RAG 비법노트 LangChain 강의오픈
바로가기 🙌
⑤ 서울대 PyTorch 딥러닝 강의
바로가기 🙌
지도 시각화 도구 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)
상가업소번호 | 상호명 | 지점명 | 상권업종대분류코드 | 상권업종대분류명 | 상권업종중분류코드 | 상권업종중분류명 | 상권업종소분류코드 | 상권업종소분류명 | 표준산업분류코드 | ... | 구우편번호 | 신우편번호 | 동정보 | 층정보 | 호정보 | 전화번호 | 경도 | 위도 | 상권번호 | 데이터기준일자 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 20713599 | 눈높이러닝센타 | NaN | R | 학문/교육 | R13 | 학문교육기타 | R13A01 | 학습지보급 | P85503 | ... | 423010.0 | 14221.0 | NaN | NaN | 2 | 02-2066-9109 | 126.858147 | 37.478530 | NaN | 2016-01-26 |
1 | 20642964 | 유니베라 | 구성대리점 | D | 소매 | D16 | 화장품소매 | D16A01 | 화장품판매점 | G47813 | ... | 446525.0 | 16919.0 | NaN | 2 | NaN | 031-8005-7071 | 127.116575 | 37.294690 | NaN | 2021-10-22 |
2 | 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)
상가업소번호 | 상호명 | 지점명 | 상권업종대분류코드 | 상권업종대분류명 | 상권업종중분류코드 | 상권업종중분류명 | 상권업종소분류코드 | 상권업종소분류명 | 표준산업분류코드 | ... | 구우편번호 | 신우편번호 | 동정보 | 층정보 | 호정보 | 전화번호 | 경도 | 위도 | 상권번호 | 데이터기준일자 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
35 | 24527550 | 코리아세븐분당 | 정자3호점 | D | 소매 | D03 | 종합소매점 | D03A01 | 편의점 | G47122 | ... | 463834.0 | 13612.0 | NaN | 1 | NaN | 031-718-9733 | 127.113508 | 37.362807 | NaN | 2016-01-27 |
102 | 28523933 | 처가방 | NaN | Q | 음식 | Q10 | 별식/퓨전요리 | Q10A02 | 샤브샤브전문 | I56111 | ... | 463420.0 | 13529.0 | NaN | NaN | NaN | 031-5170-1908 | 127.112071 | 37.392785 | NaN | 2016-11-17 |
244 | 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[['위도', '경도', '상호명']]
위도 | 경도 | 상호명 | |
---|---|---|---|
35 | 37.362807 | 127.113508 | 코리아세븐분당 |
102 | 37.392785 | 127.112071 | 처가방 |
244 | 37.389333 | 127.110756 | 백현 |
284 | 37.401265 | 127.108639 | 야쿤커피앤토스트판교점 |
678 | 37.368770 | 127.112015 | 해동검도 |
... | ... | ... | ... |
514805 | 37.397736 | 127.111414 | 에세이스튜디오 |
515501 | 37.362357 | 127.113513 | 아몽옷고치는전문집 |
515824 | 37.384859 | 127.111408 | 티랩 |
516332 | 37.392951 | 127.118687 | 우디크리빙 |
516457 | 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
시군구명 | 상권업종대분류명 | 상권업종중분류명 | 위도 | 경도 | |
---|---|---|---|---|---|
0 | 송파구 | 소매 | 의복의류 | 37.493054 | 127.147321 |
1 | 영등포구 | 소매 | 건강/미용식품 | 37.520613 | 126.907168 |
2 | 성동구 | 소매 | 취미/오락관련소매 | 37.566857 | 127.049018 |
3 | 동작구 | 음식 | 한식 | 37.487105 | 126.980952 |
4 | 종로구 | 음식 | 한식 | 37.572387 | 126.981794 |
... | ... | ... | ... | ... | ... |
325875 | 마포구 | 부동산 | 부동산중개 | 37.557971 | 126.907290 |
325876 | 은평구 | 소매 | 시계/귀금속소매 | 37.604195 | 126.936049 |
325877 | 은평구 | 소매 | 애견/애완/동물 | 37.596790 | 126.905613 |
325878 | 광진구 | 음식 | 커피점/카페 | 37.556004 | 127.085023 |
325879 | 강북구 | 생활서비스 | 이/미용/건강 | 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
시군구명 | 상권업종대분류명 | 상권업종중분류명 | 위도 | 경도 | |
---|---|---|---|---|---|
29 | 종로구 | 음식 | 커피점/카페 | 37.583149 | 127.000171 |
109 | 도봉구 | 음식 | 커피점/카페 | 37.658728 | 127.034746 |
190 | 마포구 | 음식 | 커피점/카페 | 37.554599 | 126.929692 |
200 | 강서구 | 음식 | 커피점/카페 | 37.580941 | 126.813358 |
206 | 양천구 | 음식 | 커피점/카페 | 37.522055 | 126.842935 |
... | ... | ... | ... | ... | ... |
325715 | 서대문구 | 음식 | 커피점/카페 | 37.557293 | 126.950705 |
325754 | 금천구 | 음식 | 커피점/카페 | 37.468602 | 126.902047 |
325788 | 마포구 | 음식 | 커피점/카페 | 37.540241 | 126.948240 |
325791 | 강서구 | 음식 | 커피점/카페 | 37.532109 | 126.839080 |
325878 | 광진구 | 음식 | 커피점/카페 | 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 파일로 저장할 수 있습니다.
m.save('map.html')
댓글남기기