[021] folium 라이브러리를 이용한 범죄 데이터 지도 시각화

이연희·2023년 9월 4일

Chapter
1. Folium 살펴보기

(1) 지도 출력하기
(2) 지도에 좌표 찍기
(3) Choropleth
2. 범죄 데이터 지도 시각화
(1) 서울시 범죄 현황에 대한 지도 시각화
(2) 서울시 경찰서별 범죄 검거 실적 지도 시각화
(3) 서울시 장소별 범죄 발생 현황 지도 시각화

1. Folium 살펴보기

Folium 패키지를 이용하면 원하는 좌표를 찍어서 지도를 그릴 수가 있다. 이번 시간에는 지도를 그려서 원하는 좌표에 마크를 찍거나 원을 그려내고, 더 나아가 지도 위에 데이터 별로 구획을 나누어서 수치별로 색을 입혀보자.

먼저 Folium을 설치하고, 라이브러리를 불러냈다.

!pip install folium
import folium
import pandas as pd
import json

(1) 지도 출력하기

이제 지도를 출력해보자. google map으로 원하는 장소의 위도,경도를 찾아서 Map()매서드를 사용하여 지도를 출력한다. 이때 zoom_start 옵션은 지도 확대 정도를 0-18 사이로 조절할 수 있다. 성수역 지도를 출력했다.

m = folium.Map(location=[37.544564958079896, 127.05582307754338], zoom_start=15)   #0-18
m

이때, tiles 옵션으로 지도의 종류를 바꿔줄 수 있다.

# 'Stamen Toner'로 그려보기
m = folium.Map(location=[37.544564958079896, 127.05582307754338], 
               zoom_start=14,
               tiles="Stamen Toner"   
               )
m

# 'Stamen Terrain'으로 그려보기
m = folium.Map(location=[37.544564958079896, 127.05582307754338], 
               zoom_start=14,
               tiles="Stamen Terrain"   
               )
m


등이 있다.

(2) 지도에 좌표 찍기

1) Marker()

다음은 지도에 마커를 그려보자. 특정 좌표에 마커를 찍어서 원하는 위치를 더 명확하게 보일 수 있다.

m = folium.Map(location=[37.544564958079896, 127.05582307754338],  #성수역
               zoom_start=14,
               tiles="OpenStreetMap"   
               )

#뚝섬역
folium.Marker((37.54712311308356, 127.04721916917774)).add_to(m)


#성수역
folium.Marker(location = [37.544564958079896, 127.05582307754338],
              popup = "<b>Subway</b>"  #<b>진하게
             ).add_to(m)

m

뚝섬역과 성수역에 마커를 찍었다. popup을 넣어주면 마커를 클릭했을 때, 원하는 문구 등을 출력할 수 있다. 또한 여기에 html 문법을 응용하면 문구를 커스텀할 수 있다.

다음은 tootip을 넣어보자. tooltip은 마커에 마우스 커서를 갖다대면 원하는 문구를 출력할 수 있다.

m = folium.Map(location=[37.544564958079896, 127.05582307754338],  #성수역
               zoom_start=14,
               tiles="OpenStreetMap"   
               )

#뚝섬역
folium.Marker((37.54712311308356, 127.04721916917774)).add_to(m)


#성수역
folium.Marker(location = [37.544564958079896, 127.05582307754338],
              popup = "<b>Subway</b>",
              tooltip = "<i>성수역</i>"    #기울기
             ).add_to(m)

m

html문법을 이용해서 마커에 링크를 걸 수도 있다.

m = folium.Map(location=[37.544564958079896, 127.05582307754338],  #성수역
               zoom_start=14,
               tiles="OpenStreetMap"   
               )

#뚝섬역
folium.Marker((37.54712311308356, 127.04721916917774)).add_to(m)


#성수역
folium.Marker(location = [37.544564958079896, 127.05582307754338],
              popup = "<b>Subway</b>",
              tooltip = "<i>성수역</i>"
             ).add_to(m)


# html
folium.Marker(location = [37.54558642069953, 127.05729705810472],
              popup = "<a href='http://zero-base.co.kr'  target=_'blink'>제로베이스</a>",   #링크걸기
              tooltip = "<i>Zerobase</i>"
             ).add_to(m)
m

2) Icon()

icon()매서드를 사용해서 마커의 색상이나 아이콘 모양 등을 변경할 수 있다.

m = folium.Map(location=[37.544564958079896, 127.05582307754338],  #성수역
               zoom_start=14,
               tiles="OpenStreetMap")

# icon basic 뚝섬역
folium.Marker((37.54712311308356, 127.04721916917774),
              icon = folium.Icon(color="black", icon="info-sign") # default color = "blue"
             ).add_to(m)


# icon icon-color
folium.Marker(location = [37.544564958079896, 127.05582307754338],
              popup = "<b>Subway</b>",
              tooltip = "<i>성수역</i>",
              icon = folium.Icon(color="pink", icon_color="lightblue", icon="cloud")
             ).add_to(m)


# Icon custom
folium.Marker(location = [37.54558642069953, 127.05729705810472],
              popup = "<a href='http://zero-base.co.kr'  target=_'blink'>제로베이스</a>",   #링크걸기
              tooltip = "<i>Zerobase</i>",
              icon = folium.Icon(color="lightblue",
                                 icon_color="black", 
                                 icon="hippo",    #fontawsome
                                 angle= 50,   # 기울기
                                 prefix = "fa")   # 특정 아이콘들은 'fa'를 해줘야 나타남(fontawsome)
             ).add_to(m)

m

folium.Icon( color = '마커 색상',
			icon_color = '아이콘 색상',
            icon = '아이콘 모양',
            angle = '아이콘 기울기'
            prefix = '특정 아이콘 사용할시 옵션 생성'

위와 같은 매서드 형식을 사용해서 마커를 커스텀할 수 있는데, 특정 아이콘 모양에 따라 'prefix=' 옵션을 넣어줘야 한다. 만약, icon 모양은 다양한 모양이 있는데 'Font-Awsome' 웹사이트에서 모양을 참고하면 prefix='fa'옵션을 선택해줘야 한다.

3) ClickForMarker()

지도를 출력할 때, 사용자가 유동적으로 마우스로 클릭하면 클릭지점에 마크를 생성하게 할 수도 있다.
CilckForMarker() 매서드를 사용하는 것인데, 이때 디폴트값으로 popup문구로 위도와 경도가 출력된다.
서울어린이대공원을 클릭해서 마커를 찍어보자.

m = folium.Map(location=[37.544564958079896, 127.05582307754338],  #성수역
               zoom_start=14,
               tiles="OpenStreetMap")
m.add_child(folium.ClickForMarker())  # default로 위도경도 출력

매서드 인자로 원하는 문구를 넣어주면 popup 문구로 출력할 수도 있다.

m = folium.Map(location=[37.544564958079896, 127.05582307754338],  #성수역
               zoom_start=14,
               tiles="OpenStreetMap")
m.add_child(folium.ClickForMarker(popup = "ClickForMarker"))  

4) LatLngPopup()

지도에 굳이 마커를 찍지 않고서, 원하는 지점을 클릭해서 간단하게 위도와 경도 정보만을 알아내는 방법도 있다.

m = folium.Map(location=[37.544564958079896, 127.05582307754338],  #성수역
               zoom_start=14,
               tiles="OpenStreetMap")
m.add_child(folium.LatLngPopup())   

5) Circle(), CircleMarker()

이번에는 원하는 위치에 마커가 아닌 동그라미를 그려서 좌표를 찍어보자. Circle()과 CircleMarker() 두 가지 매서드가 있지만 사실 문법의 차이는 거의 없고 출력물도 거의 동일하다. 단, 똑같은 반지름을 입력했을 때, CircleMarker()에서 원의 크키가 더 크게 출력되는 차이가 있다.

m = folium.Map(location=[37.544564958079896, 127.05582307754338],  #성수역
               zoom_start=14,
               tiles="OpenStreetMap")


# circle
folium.Circle(
    location = [37.544564958079896, 127.05582307754338], # 성수역
    radius = 100,            #반지름
    fill = True,             #색상을 채우기
    color = "#e80707",
    fill_color = "blue",
    popup = "Circle Popup",
    tooltip = "Circle Tooltip"
).add_to(m)


# circleMarker
# circle과 차이점이 명확하지는 않음 다만 radius부분에서 좀 차이남
folium.CircleMarker(
    location = [37.5471, 127.0474], # 뚝섬역
    radius = 100,            #반지름
    fill = True,             #색상을 채우기
    color = "#cf17cb",
    fill_color = "#cf1798",
    popup = "CircleMarker Popup",
    tooltip = "CircleMarker Tooltip"
).add_to(m)

m

원의 둘레 생각이나, 내부 색상은 구글 검색창에 'color picker'로 색상을 일부 선택했다.

(3) Choropleth

다음은 Choropleth를 사용하여 지도시각화 해보자. Choropleth이란, 지도에 데이터를 접목시켜서 구획별 데이터를 색상이나 패턴 등으로 출력시켜 시각화하는 기법이다.

예시를 들면서 살펴보기 위해, 미국의 state별 실업률 데이터를 불러왔다.

이제 미국 위도 경도를 불러와 그곳에 시각화를 해보자.

m = folium.Map([43, -102], zoom_start=3)
m

Choropeth 시각화를 위해서는 먼저 경계선이 좌표값이 담긴 데이터가 따로 필요하다. 예를 들어 지금과 같은 데이터에서는 state별로 구획화해야하기 때문이다. 그다음 시각화할 data와 columns을 설정해주고, key_on을 통해 columns의 데이터들을 기준으로 묶어준다. 그리고 데이터별로 출력할 색상표를 fill_color를 이용해 선택하고, fill_opacity와 line_opacity를 통해 각각 색상과 라인의 진하기를 0-1 사이로 선택한다.

m = folium.Map([43, -102], zoom_start=3)

folium.Choropleth(
    geo_data="..\\data\\02. us-states.json",   #경계선 좌표값이 담긴 데이터
    data = state_data,   # series or dataframe
    columns = ["State", "Unemployment"],  # dataframe or column
    key_on = "feature.id",   #columns의 두 컬럼 데이터를 key_on을 기준으로 묶어줌
    fill_color = "BuPu",
    fill_opacity = 1,  # 0-1
    line_opacity = 1,   # 0-1
    legend_name = "Unemployment rate(%)"
    
).add_to(m)

m


.
.
.
.

2. 범죄 데이터 지도 시각화

(1) 서울시 범죄 현황에 대한 지도 시각화

이제 지난 시간에 최댓값으로 정규화한 서울시 5대 범죄 현황 데이터로 지도 시각화를 해보자.

그리고 지도 경계 좌표값이 담긴 json파일도 불러왔다.

geo_path = "..\\data\\02. skorea_municipalities_geo_simple.json"
geo_str = json.load(open(geo_path, encoding="utf-8"))
geo_str

먼저, 살인 발생 건수를 지도 시각화해보자.

my_map = folium.Map(
    location = [37.5502, 126.982],
    zoom_start = 11,
    tiles = "Stamen Toner"
)

# 살인 건수가 많을 수록 지도 구획이 더 진한 색으로 
folium.Choropleth(
    geo_data= geo_str,   #우리나라 경계선 좌표값이 담긴 데이터
    data = crime_anal_norm["살인"],
    columns= [crime_anal_norm.index, crime_anal_norm["살인"]],
    key_on = "feature.id",
    fill_color = "PuRd",
    fill_opacity = 0.7,
    line_opacity = 0.2,
    legend_name = "정규화된 살인 발생 건수"
).add_to(my_map)

my_map

이를 '살인' 컬럼으로 내림차순한 데이터와 서울시 지도와 비교해보자.

지도 시각화가 잘 이뤄졌다는 것을 알 수 있다.
(Choropleth를 사용해서 구이름을 라벨링하고 싶었는데, 안타깝게 그런 옵션은 없는 듯 보였다. 매우 아쉬움ㅜㅜ)

다음으로 5대 범죄 발생 건수의 평균을 시각화해보자.

# 5대 범죄 발생 건수 지도 시각화
my_map = folium.Map(
    location = [37.5502, 126.982],
    zoom_start = 11,
    tiles = "Stamen Toner"
)

#건수가 많을 수록 지도 구획이 더 진한 색으로 
folium.Choropleth(
    geo_data= geo_str,   #우리나라 경계선 좌표값이 담긴 데이터
    data = crime_anal_norm["범죄"],
    columns= [crime_anal_norm.index, crime_anal_norm["범죄"]],
    key_on = "feature.id",
    fill_color = "PuRd",
    fill_opacity = 0.7,
    line_opacity = 0.2,
    legend_name = "정규화된 5대 범죄 발생 건수"
).add_to(my_map)

my_map

마찬가지로 내림차순한 데이터와 비교했을 때 시각화가 잘 되었음을 알 수 있다.


.
.

(2) 서울시 경찰서별 범죄 검거 실적 지도 시각화

다음으로 경찰서별 정보를 범죄 발생화함께 시각화 해보자.
이전에 사용한 30개 자치구의 경찰서 정보를 담은 데이터를 불러왔다.

데이터의 범죄 검거 건수를 최댓값으로 정규화한 다음, "검거"컬럼을 만들어서 각 자치구별로 범죄 검거 평균을 할당하였다.

먼저 30개 경찰서의 위치를 마커로 찍어보자


my_map = folium.Map(
    location = [37.5502, 126.982], zoom_start=11
)


for idx, rows in crime_anal_station.iterrows():
    folium.Marker(
        location = [rows["lat"], rows["lng"]]
    ).add_to(my_map)
    

다음으로 경찰서별 정규화시킨 범죄 검거 횟수 평균을 비율별로 원의 크기를 잡아서 지도에 그려보았다.

my_map = folium.Map(
    location = [37.5502, 126.982], zoom_start=11
)


for idx, rows in crime_anal_station.iterrows():
    folium.CircleMarker(
        location = [rows["lat"], rows["lng"]],
        radius = rows["검거"] * 50,
        popup = rows["구분"] + " : " + "%.2f" % rows["검거"],
        color = "red",
        fill = True,
        fill_color = "red"
    ).add_to(my_map)
    
my_map


원의 크키가 가장 큰 영등포구 경찰서가 0.81로 검거실적이 가장 좋다는 것을 알 수 있다.

여기에 Choropleth을 추가하면 자치구 위치를 좀 더 명확하게 알아볼 수 있다.


my_map = folium.Map(
    location = [37.5502, 126.982], zoom_start=11
)


folium.Choropleth(
    geo_data = geo_str,
    data = crime_anal_norm["범죄"],
    columns = [crime_anal_norm.index, crime_anal_norm["범죄"]],
    key_on = "feature.id",
    fill_color = "PuRd",
    fill_opacity = 0.7,
    line_opacity = 0.2
).add_to(my_map)



for idx, rows in crime_anal_station.iterrows():
    folium.CircleMarker(
        location = [rows["lat"], rows["lng"]],
        radius = rows["검거"] * 50,
        popup = rows["구분"] + " : " + "%.2f" % rows["검거"],
        color = "black",
        fill = True,
        fill_color = "black"
    ).add_to(my_map)
    
my_map


.
.

(3) 서울시 장소별 범죄 발생 현황 지도 시각화

마지막으로 서울시 장소별 5대 범죄 발생수 데이터를 heatmap을 그려서 시각화해보자.

데이터는 범죄명 별 장소에 따른 발생 건수를 보여준다.
좀더 데이터를 한눈에 잘 살펴보기 위해서 pivot table로 커스텀해주었다.

그다음, 범죄 발생 건수의 최댓값으로 각 컬럼을 정규화해주고, 장소별 발생건수의 평균을 '종합' 컬럼에 할당하였다.

col = ["살인", "강도", "강간", "절도", "폭력"]
crime_loc_norm = crime_loc / crime_loc.max()
crime_loc_norm["종합"] = np.mean(crime_loc_norm, axis=1)
crime_loc_norm

이제 heatmap을 그려보자.

crime_loc_norm_sort = crime_loc_norm.sort_values("종합", ascending=False) #내림차순

def drawGraph():
    plt.figure(figsize = (10,10))
    sns.heatmap(
        crime_loc_norm_sort,
        annot=True,
        fmt="f",
        linewidths = 0.5,
        cmap = "RdPu")
    plt.title("범죄 발생 장소")
    plt.show
    
drawGraph()

'기타'장소를 제외한 나머지 장소들중 '노상'에서 5대 범죄가 가장 많이 발생했으며 그다음으로는 단독주택, 아파트 등의 주거공간에서 범죄가 많이 발생한다는 것을 확인할 수 있다.

profile
안녕하세요, 데이터 공부를 하고 있습니다.

0개의 댓글