Date:     Updated:

카테고리:

태그: ,


folium은 지도 위에 데이터를 interactive하게 표현해주는 라이브러리이다. 원래 맛집 추천 시스템을 만들려다가, 우연찮게 알게 된 라이브러리라 기록을 해둘 겸 포스팅한다. 나는 원하는 장소에 핀을 찍는 작업을 해볼 것이다.

우선 필요한 라이브러리 설치는 아래와 같다.

!pip3 install folium
Collecting folium
  Downloading folium-0.14.0-py2.py3-none-any.whl (102 kB)
Collecting branca>=0.6.0
  Downloading branca-0.6.0-py3-none-any.whl (24 kB)
Requirement already satisfied: requests in c:\anaconda\lib\site-packages (from folium) (2.24.0)
Requirement already satisfied: numpy in c:\anaconda\lib\site-packages (from folium) (1.20.3)
Requirement already satisfied: jinja2>=2.9 in c:\anaconda\lib\site-packages (from folium) (2.11.2)
Requirement already satisfied: idna<3,>=2.5 in c:\anaconda\lib\site-packages (from requests->folium) (2.10)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in c:\anaconda\lib\site-packages (from requests->folium) (1.25.11)
Requirement already satisfied: chardet<4,>=3.0.2 in c:\anaconda\lib\site-packages (from requests->folium) (3.0.4)
Requirement already satisfied: certifi>=2017.4.17 in c:\anaconda\lib\site-packages (from requests->folium) (2022.9.24)
Requirement already satisfied: MarkupSafe>=0.23 in c:\anaconda\lib\site-packages (from jinja2>=2.9->folium) (1.1.1)
Installing collected packages: branca, folium
Successfully installed branca-0.6.0 folium-0.14.0
import random
import pandas as pd
import numpy as np

import folium
from folium.plugins import MarkerCluster, MiniMap
C:\anaconda\lib\site-packages\pandas\core\computation\expressions.py:20: UserWarning: Pandas requires version '2.7.3' or newer of 'numexpr' (version '2.7.1' currently installed).
  from pandas.core.computation.check import NUMEXPR_INSTALLED


내가 사용할 데이터는 공공데이터포털에서 제공하는 소상공인시장진흥공단_상가 데이터이다. 이 데이터에는 전국의 상권 정보가 들어가 있다.

그 중에서도 제주도의 데이터만을 사용하여 원하는 지역을 입력하면 그 지역의 식당 정보를 핀으로 찍어주는 함수를 구현해볼 것이다. 말은 거창하지만 아주 간략히 만들어볼 것이다.

우선 csv 파일을 불러온다.

df = pd.read_csv('Downloads/소상공인/소상공인시장진흥공단_상가(상권)정보_제주_202209.csv')

이 데이터가 가진 컬럼은 아래와 같다. 여기서 극히 일부만 사용할 것이다.

df.columns
Index(['상가업소번호', '상호명', '지점명', '상권업종대분류코드', '상권업종대분류명', '상권업종중분류코드',
       '상권업종중분류명', '상권업종소분류코드', '상권업종소분류명', '표준산업분류코드', '표준산업분류명', '시도코드',
       '시도명', '시군구코드', '시군구명', '행정동코드', '행정동명', '법정동코드', '법정동명', '지번코드',
       '대지구분코드', '대지구분명', '지번본번지', '지번부번지', '지번주소', '도로명코드', '도로명', '건물본번지',
       '건물부번지', '건물관리번호', '건물명', '도로명주소', '구우편번호', '신우편번호', '동정보', '층정보',
       '호정보', '경도', '위도'],
      dtype='object')


전체 코드는 아래와 같고, 조금 상세히 살펴보도록 하자.

def res_map(location):

    df_ = df[df['도로명주소'].str.contains(location, na = False)]
    df_ = df_[df_["상권업종대분류명"]=="음식"]
    col = ['상호명','위도', '경도', '상권업종중분류명']
    df_ = df_.loc[:,col]
    data = df_.values.tolist()
    
    
    category = df_['상권업종중분류명'].unique()
    color_list = ['red', 'blue', 'green', 'purple', 'orange', 'darkred', 'lightred', 'beige', 'darkblue', 'darkgreen', 'white', 'pink', 'gray', 'black', 'lightgray']

    color_dict = {}
    for k in zip(category, color_list):
        color_dict[k[0]] = k[1]
    
    map_ = folium.Map((33.511504, 126.491179), zoom_start=13)
    for i in range(len(data)):
        folium.Marker([data[i][1],data[i][2]], 
                      popup = f'[{data[i][3]}]{data[i][0]}',
                     icon = folium.Icon(color=color_dict[data[i][3]])).add_to(map_)
    return map_
    


아래 부분은 주석으로 설명을 갈음한다.

def res_map(location):
    # 도로명주소에 내가 원하는 location이 포함되었는지 확인하고, 포함된 행만 반환
    df_ = df[df['도로명주소'].str.contains(location, na = False)]
    # 음식점을 알아볼 것이므로 상권업종대분류명이 '음식'인 행만 반환
    df_ = df_[df_["상권업종대분류명"]=="음식"]
    
    # 사용할 컬럼은 아래 4개뿐
    col = ['상호명','위도', '경도', '상권업종중분류명']
    df_ = df_.loc[:,col]
    
    # 작업을 편리하게 하기 위해 list 형식으로 반환
    data = df_.values.tolist()


folium에서는 Marker를 설정할 수 있다. Marker란 우리가 지도 앱에서 자주 보는 핀을 의미한다. 이 때 핀의 색깔을 지정할 수 있는데, 우리는 상권업종중분류명에 따라 color를 달리 할 것이다. 그래서 중분류명과 color를 한 쌍으로 하는 딕셔너리를 만들 것이다.

color_list는 folium의 내장된 icon의 color들을 리스트로 갖고온 것이다.

    category = df_['상권업종중분류명'].unique()
    color_list = ['red', 'blue', 'green', 'purple', 'orange', 'darkred', 'lightred', 'beige', 'darkblue', 'darkgreen', 'white', 'pink', 'gray', 'black', 'lightgray']

    color_dict = {}
    for k in zip(category, color_list):
        color_dict[k[0]] = k[1]


이제 본격적으로 지도를 그려볼 것이다. 아래 부분은 지도의 초기 설정을 하는 부분이다. 초기 위도와 경도를 지정해주고, zoom_start 파라미터로 확대를 어느정도 할지 설정한다. 값이 클수록 지도는 더 확대된다. 필자는 초기 좌표를 제주공항으로 잡아주었다.

    map_ = folium.Map((33.511504, 126.491179), zoom_start=13)


이제 앞서 만든 data 리스트를 가져다 쓴다. data의 저장된 값들을 초반 10개 식당만 뽑아보면 다음과 같다. 즉, data[0][0]을 입력하면 ‘제라진’이 나올 것이고, data[0][1]을 입력하면 33.2514383512425라는 위도값이 나올 것이다.

data[:10]
[['제라진', 33.2514383512425, 126.431964272011, '한식'],
 ['시골길', 33.2517005185431, 126.42275280511, '한식'],
 ['삼정식당', 33.2538946164217, 126.42443030442, '한식'],
 ['팡팡가요주점', 33.251828906594, 126.425057561329, '유흥주점'],
 ['예원회수산', 33.2533594787683, 126.427026012877, '일식/수산물'],
 ['무궁화한식당', 33.2484066169078, 126.410472378291, '한식'],
 ['케이팝', 33.2518402302897, 126.425239777172, '유흥주점'],
 ['사랑방조림식당', 33.2517005185431, 126.42275280511, '한식'],
 ['너바나', 33.2514702890931, 126.426641376889, '유흥주점'],
 ['모모야마일식당', 33.2484066169078, 126.410472378291, '일식/수산물']]


folium.Marker를 data에 저장된 식당 수만큼 반복하여 각 식당마다 marker를 만들어줄 것이다.

처음에 오는 파라미터는 위도와 경도이다.

popup 파라미터는 marker를 클릭했을 때 보여줄 제목을 의미한다. 필자는 f string을 사용하여 [상권업종중분류명]상호명 형식으로 만들었다.

icon 파라미터에서 아이콘에 대한 설정을 할 수 있다. 우리가 앞서 만들었던 color_dict를 활용하여 중분류에 따라 색깔을 달리 지정하도록 한다.

마지막으로 add_to(map_)을 해주면 marker를 지도에 추가한다는 의미이다.

    for i in range(len(data)):
        folium.Marker([data[i][1],data[i][2]], 
                      popup = f'[{data[i][3]}]{data[i][0]}',
                     icon = folium.Icon(color=color_dict[data[i][3]])).add_to(map_)
    return map_


이제 함수를 사용하면 아래처럼 지도가 예쁘게 완성된다.

res_map("중문")