서울시 범죄 현황 데이터 분석 4

eunbi kim·2024년 3월 15일
0
    1. 강남3구 범죄현황 데이터 개요 및 읽어오기
  • [번외1] Pandas의 pivot_table
    1. 서울시 범죄현황 데이터 정리
    1. google maps에서 구별 정보를 얻어서 데이터 정리
    1. 구별 데이터로 변경하기
    1. 서울시 범죄현황 데이터 최종 정리
  • [번외2] seaborn
    1. 범죄 현황 데이터 시각화
  • [번외3] Folium 지도 시각화 <- here
    1. 서울시 범죄 현황 지도 시각화

[번외3] Folium 지도 시각화

Folium은 지도 위에 데이터를 표현하여 시각화하는 도구이다.
지난 시간에 구글맵에서 위도, 경도 정보를 가져오는 법을 배웠으므로
이젠 그렇게 받은 위치 정보를 이용해 지도 시각화를 할 수 있다~

!pip install folium

!pip install charset
!pip install charset-normalizer

필요한 모듈 import

import folium
import pandas as pd
import json

folium.Map()

location에 위도와 경도 좌표를 넣어주고
zoom_start로 화면상 노출되는 사이즈를 조절해준다.
위도, 경도는 구글맵에서 찾을 수 있는데...
상세 위치를 클릭하여 주소창에서 보이는 숫자값들이 좌표이다(난 이렇게 눈빠지게 찾았다 왜 나만 좌표 정보가 잘 안보이는 건가)

m = folium.Map(location=[37.3925126, 126.6393808], zoom_start=17)
m

지도가 생겼다~

m.save("./folium.html")  # 현재 경로에 파일 저장

# 지도를 웹에서 확인 가능

folium.Marker()

지도에 마커를 생성해주는 기능이다.
공원 두 곳을 지정하여 마크를 표시해주었다.

popup <- 클릭하면 보이는 내용 지정
tooltip <- 마우스를 올리기만 해도 보이는 내용, 문구에는 html 문법을 적용할 수 있다.
링크 연결하는 문법도 적용 가능하다.

m = folium.Map(
    location=[37.3925126, 126.6393808], 
    zoom_start=14,
)

# 센트럴파크
folium.Marker(
    (37.3925126, 126.6393808),
    popup="<a href='https://www.insiseol.or.kr/park/songdo/facility/central.jsp' target=_'blink'>Songdo central park</a>"
).add_to(m)

# 달빛축제공원
folium.Marker(
    (37.4064278, 126.6338237),
    popup="Moonlight festival park",
    tooltip="Park"
).add_to(m)

m

folium.Icon()

마커에 다양한 아이콘으로 정보를 좀 더 줄 수 있다.
여러 옵션들로 아이콘을 커스텀할 수 있다.

https://getbootstrap.com/docs/3.3/components/
https://fontawesome.com/search?m=free&o=r

등의 웹사이트를 참고하면 좋다.

m = folium.Map(
    location=[37.3925126, 126.6393808], 
    zoom_start=14,
)

# 센트럴파크
folium.Marker(
    (37.3925126, 126.6393808),
    popup="<a href='https://www.insiseol.or.kr/park/songdo/facility/central.jsp' target=_'blink'>Songdo central park</a>",
    icon=folium.Icon(
        color="red",
        icon_color="pink",
        icon="cloud"
    )

).add_to(m)

# 달빛축제공원
folium.Marker(
    (37.4064278, 126.6338237),
    popup="Moonlight festival park",
    tooltip="Park",
    icon=folium.Icon(color="black")
).add_to(m)

m

folium.ClickForMarker()

지도 위에 마우스로 클릭했을 때 마커를 생성해주는 기능이다.
팝업 기능으로 위도와 경도 정보를 보여주었다

m = folium.Map(
    location=[37.3925126, 126.6393808], 
    zoom_start=17
)

m.add_child(folium.ClickForMarker())

-> 클릭하면 마커 생성됨

folium.LatLngPopup()

지도를 마우스로 클릭했을 때 위도, 경도 정보를 반환해주는 기능이다.

m = folium.Map(
    location=[37.3925126, 126.6393808], 
    zoom_start=17
)

m.add_child(folium.LatLngPopup())

요러케 내가 클릭한 곳에 정보가 나옴

folium.Circle(), folium.CircleMarker()

유용하게 쓰일듯한 circle 기능.

m = folium.Map(
    location=[37.3925126, 126.6393808], 
    zoom_start=17
)

# Circle
folium.Circle(
    location=[37.3925126, 126.6393808],
    radius=100,
    fill=True,
    color="red",
    fill_color="red"
).add_to(m)

m

folium.Choropleth

choropleth map은 색상을 활용하여 지도상에서 요소들의 차이를 나타내는 지도인데,
배포받은 us_unemployment 자료를 활용하여
미국 지도 위에 실업율을 그려보자

import json

state_data = pd.read_csv("../data/02. US_Unemployment_Oct2012.csv")
state_data.tail()


이러케 state, unemployment rate 두 가지의 정보가 있는 데이터프레임이다.

data로는 json파일 경로를 입력해주었다.
이 파일은 경계선 좌표값이 담긴 데이터이다.
51개의 행이 있는 데이터인데 행 하나당 state의 정보가 담겨있고, 좌표값이 있다.

가장 먼저 folium.Map으로 밑바탕이 되는 지도를 만들어주고,
geo_data로 json형태로 경계선 좌표값 데이터가 있기 때문에 경계선을 그어주고
데이터를 불러와 실업율이 높으면 진하게, 낮으면 연하게 시각화가 되도록 값을 넣어준다.

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"],
    key_on="feature.id",
    fill_color="BuPu",
    fill_opacity=1,  # color 투명도 0-1
    line_opacity=0.5,  # line 투명도 0-1
    legend_name="Unemployment rate (%)"
).add_to(m)

m

한 눈에 실업율을 비교할 수 있는 미국 지도가 완성되었다~~


7. 서울시 범죄 현황 지도 시각화

이젠 서울 지도에다가 범죄 현황 자료를 가지고 시각화할 것이다.

우리나라 경계선 데이터에서 서울 부분만 추려 저장하여 로드해주었다.
[출처] https://github.com/southkorea/southkorea-maps

crime_gu_norm = pd.read_csv(
    "../data/02. crime_in_Seoul_final.csv", index_col=0, encoding="utf-8"
)

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

서울 지역 지도 생성해주고,

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

이제 다양한 주제에 대하여 지도 시각화를 할 것이다.

살인발생 건수 지도 시각화

folium.Choropleth(
    geo_data=geo_str,  # 우리나라 경계선 좌표값이 담긴 데이터
    data=crime_gu_norm["살인"],
    columns=[crime_gu_norm.index, crime_gu_norm["살인"]],  # index<- 구 이름
    key_on="feature.id",
    fill_color="PuRd",
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name="정규화된 살인 발생 건수"
).add_to(my_map)

my_map

영등포구가 가장 많네 ㄷㄷ

성범죄 건수 지도 시각화

folium.Choropleth(
    geo_data=geo_str,
    data=crime_gu_norm["강간"],
    columns=[crime_gu_norm.index, crime_gu_norm["강간"]], 
    key_on="feature.id",
    fill_color="PuRd",
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name="정규화된 강간 발생 건수"
).add_to(my_map)

my_map

강남3구 중 2개 구가 포함이네 ㄷㄷ

5대범죄 건수 지도 시각화

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

folium.Choropleth(
    geo_data=geo_str,
    data=crime_gu_norm["범죄"],
    columns=[crime_gu_norm.index, crime_gu_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

인구 대비 범죄 건수 지도 시각화

# 인구 대비 범죄 발생률 데이터 만들기

tmp_criminal = crime_gu_norm["범죄"] / crime_gu_norm["인구수"]

folium.Choropleth(
    geo_data=geo_str,
    data=tmp_criminal,
    columns=[crime_gu_norm.index, tmp_criminal],
    key_on="feature.id",
    fill_color="PuRd",
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name="인구 대비 범죄 발생 건수"
).add_to(my_map)

my_map

강남 범죄 발생 건수 높네.

이런 식으로 대략적인 정보를 한 눈에 파악할 수 있다!

경찰서별 정보를 범죄발생과 함께 정리

이제 경찰서 위치도 마커로 표시하여
합체해보자!!

경찰서의 능력을 보여주는 지도라 할 수 있다.
어떤 경찰서가 검거를 잘하는지??등의 분석을 할 수 있다.

먼저 경찰서 위치 정보 데이터 불러오기 - 경찰서별 위도/경도를 구글맵 이용하여 for문으로 불러왔던 자료이다.

crime_station_gu = pd.read_csv(
    "../data/02. crime_in_Seoul_1st.csv", encoding="utf-8"
)

검거 컬럼 생성,
axis=1은 행(가로) 을 뜻한다. 가로로 검거 컬럼들을 연산한 평균값을 넣어준 것이다.

numpy에서 axis=1은 행(가로), pandas에서 axis=1은 열(세로) 을 뜻함!!

col = ["살인검거", "강도검거", "강간검거", "절도검거", "폭력검거"]
tmp = crime_station_gu[col] / crime_station_gu[col].max()  # 0-1사이의 정규화
crime_station_gu["검거"] = np.mean(tmp, axis=1)  

경찰서 위치 마커는 for문 찍었다.

이 때 iterrows를 사용한다.
인덱스와 행을 받아서, 행에서 lat과 lng를 찍어서 표시!

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

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

여기에 경계선 데이터를 추가하고, 검거율에 따른 circle marker를 추가해보자.

circle marker의 크기는 radius와 검거 컬럼을 이용한다. 검거율이 높을수록 bigger circle이 되는 형식이다.
검거율에 적절한 값을 곱해주어 원이 잘 보이게 한다.

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

folium.Choropleth(
    geo_data=geo_str,
    data=crime_gu_norm["범죄"],
    columns=[crime_gu_norm.index, crime_gu_norm["범죄"]],
    key_on="feature.id",
    fill_color="PuRd",
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name="정규화된 5대 범죄 발생 건수"
).add_to(my_map)

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

Q. 강남, 송파, 서초 정말 안전한 것입니까?

-> 범죄 발생 건수가 결코 낮지 않고, 범죄 검거율 또한 높지는 않다.

안전<-이라는 것 자체에 대해서는
정말 여러가지 요소를 고려해야 하는 게 맞긴 하지만.

또한 범죄는 많은데 검거는 잘함, 범죄 없고 검거도 잘함,
범죄율에 비해 검거율 낮은 구,
...등등의 정보를 알 수 있다.

.
.
.

지도 시각화 재밌다

0개의 댓글