[제로베이스] 서울시 범죄 분석 3. Folium 등

허재훈·2023년 4월 18일
0

EDA

목록 보기
13/14
post-thumbnail

1. Document, 공식예제

Folium 0.14.0 documentation

Folium 공식예제

2. 설치

Windows, mac(intel, m1)
!pip install folium

Windows
!pip install charset
!pip install charset-normalizer

3. import

import folium
import pandas as pd
import json

4. Map()

  • location: tuple or list, default None Latitude and Longitude of Map (Northing, Easting).
    (튜플 또는 리스트 형태)

location

  • location = [위도, 경도]

zoom_start

  • zoom_start : 줌 크기, 0 ~ 18
# location = [위도, 경도]
# zoom_start : 줌 크기, 0 ~ 18 
m = folium.Map(location=[37.544564958079896, 127.05582307754338], zoom_start=15)
m

save("path")

  • 웹페이지로 저장
m.save("./folium.html_test")

tiles 옵션

  • 지도가 나오는 스타일을 나타내는 옵션
  • "OpenStreetMap"
  • "Mapbox Bright" (Limited levels of zoom for free tiles)
  • "Mapbox Control Room" (Limited levels of zoom for free tiles)
  • "Stamen" (Terrain, Toner, and Watercolor)
  • "Cloudmade" (Must pass API key)
  • "Mapbox" (Must pass API key)
  • "CartoDB" (positron and dark_matter)
m = folium.Map(
    location=[37.544564958079896, 127.05582307754338], 
    zoom_start=14,
    tiles="OpenStreetMap"
) # 0 ~ 18 
m

folium.Marker() 마커

  • 지도에 마커 생성

tooltip 마우스 대면 표시됨

  • None 넣으면 위도, 경도가 표시됨

target 새 창으로 열기

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

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

# 성수역 
# popup : 마우스로 클릭하면 표시됨, None 넣으면 위도, 경도가 표시됨

folium.Marker(
    location=[37.544564958079896, 127.05582307754338],
    popup="<b>Subway</b>" # html 문법 적용가능
).add_to(m)

# 성수역 
# tooltip : 마우스 대면 표시됨 
folium.Marker(
    location=[37.544564958079896, 127.05582307754338],
    popup="<b>Subway</b>",
    tooltip="<i>성수역</i>"
).add_to(m)

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

m



folium.Icon() 아이콘

https://fontawesome.com/v5.15/icons?d=gallery&p=2&m=free
https://getbootstrap.com/docs/3.3/components/

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

# icon basic  
folium.Marker(
    (37.54712311308356, 127.04721916917774), 
    icon=folium.Icon(color="black", icon='info-sign')
).add_to(m)

# icon icon_color
folium.Marker(
    location=[37.544564958079896, 127.05582307754338],
    popup="<b>Subway</b>",
    tooltip="icon color",
    icon=folium.Icon(
        color="red",
        icon_color="blue",
        icon="cloud")
).add_to(m)

# Icon custom 
folium.Marker(
    location=[37.54035903907497, 127.06913328776446], # 건대입구역
    popup="건대입구역",
    tooltip="Icon custom",
    icon=folium.Icon(
        color="purple",
        icon_color="white",
        icon="glyphicon glyphicon-cloud",
        angle=50,
        prefix="glyphicon") #  glyphicon, fa 구글에 사이트 검색
).add_to(m)

m

folium.ClickForMarker()

  • 지도위에 마우스로 클릭했을 때 마커를 생성
m = folium.Map(
    location=[37.544564958079896, 127.05582307754338], # 성수역 
    zoom_start=14,
    tiles="OpenStreetMap"
) # 0 ~ 18 

m.add_child(folium.ClickForMarker(popup="ClickForMarker"))

folium.LatLngPopup()

  • 지도를 마우스로 클릭했을 때 위도 경도 정보를 반환해줍니다
m = folium.Map(
    location=[37.544564958079896, 127.05582307754338], # 성수역 
    zoom_start=14,
    tiles="OpenStreetMap"
) # 0 ~ 18 

m.add_child(folium.LatLngPopup())

folium.Circle()

folium.CircleMarker()

  • 둘 다 지도에서 원을 표시.
m = folium.Map(
    location=[37.55068861733562, 127.04420997492151], 
    zoom_start=14,
    tiles="OpenStreetMap"
) # 0 ~ 18 


# Circle 
folium.Circle(
    location=[37.555243442409406, 127.04370422643919], # 한양대학교
    radius=100, # 반지름
    fill=True, # True : 원 안을 채움, False : 원 안을 비움
    color="#eb9e34",
    fill_color="red", 
    popup="Circle Popup",
    tooltip="Circle Tooltip"
).add_to(m)

# CircleMarker
folium.CircleMarker(
    location=[37.54347089498245, 127.04439204503049], # 한양대학교
    radius=100, 
    fill=True,
    color="#34ebc6",
    fill_color="#c634eb",
    popup="CircleMarker Popup",
    tooltip="CircleMarker Tooltip"
).add_to(m)

m

m = folium.Map(
    location=[37.55068861733562, 127.04420997492151], 
    zoom_start=14,
    tiles="OpenStreetMap"
) # 0 ~ 18 

# 원을 채우지 않으면 원 테두리에 마우스를 대거나, 클릭해야 표시됨

# Circle 
folium.Circle(
    location=[37.555243442409406, 127.04370422643919], # 한양대학교
    radius=100,  # 반지름
    fill= False, # True : 원 안을 채움, False : 원 안을 비움
    color="#eb9e34",
    # fill_color="red", # 이게 들어가 있으면 fill= False 이라도 원 안이 채워짐
    popup="Circle Popup",
    tooltip="Circle Tooltip"
).add_to(m)

m

folium.Choropleth(단계구분도)

import json 

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

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

m

5. 아파트 유형 지도 시각화

import pandas as pd 

df = pd.read_csv("../data/02. 서울특별시 동작구_주택유형별 위치 정보 및 세대수 현황_20210825.csv", encoding="cp949")
df.tail(2)

df.info()

>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 167 entries, 0 to 166
Data columns (total 8 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   연번      167 non-null    int64  
 1   분류      167 non-null    object 
 2   건물명     167 non-null    object 
 3   행정동     167 non-null    object 
 4   주소      167 non-null    object 
 5   세대수     167 non-null    int64  
 6   위도      163 non-null    float64
 7   경도      163 non-null    float64
dtypes: float64(2), int64(2), object(4)
memory usage: 10.6+ KB

NaN 데이터 제거(데이터프레임)

# NaN 데이터 제거 
df = df.dropna()
df.info()

>>
<class 'pandas.core.frame.DataFrame'>
Int64Index: 163 entries, 0 to 166
Data columns (total 8 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   연번      163 non-null    int64  
 1   분류      163 non-null    object 
 2   건물명     163 non-null    object 
 3   행정동     163 non-null    object 
 4   주소      163 non-null    object 
 5   세대수     163 non-null    int64  
 6   위도      163 non-null    float64
 7   경도      163 non-null    float64
dtypes: float64(2), int64(2), object(4)
memory usage: 11.5+ KB

reset_index(데이터프레임)

  • 데이터프레임을 수정하는 전처리 과정에서 인덱스를 초기화하고 싶을 때 사용.
  • 행인덱스를 기본값 0부터 시작하는 순서로 초기화 된다
  • reset_index(drop=True) 옵션을 주면 기존 인덱스가 출력되지 않는다.
df = df.reset_index(drop=True)
df.tail(2)

del df["연번"]
# 에러남
df.columns
# 연번 뒤에 공백 하나 있음

>>
Index(['연번 ', '분류 ', '건물명', '행정동', '주소', '세대수', '위도', '경도'], dtype='object')

rename(데이터프레임)

# 컬럼명 변경(공백 없애기)
df = df.rename(columns={"연번 ": "연번", "분류 ": "분류"})
df.연번[:10]

>>
0     1
1     2
2     3
3     4
4     5
5     6
6     7
7     8
8     9
9    10
Name: 연번, dtype: int64
df.tail(2)

df.위도[0]
>> 37.51030426
df.describe()

folium에 조건 넣기

# folium 

m = folium.Map(location=[37.50589466533131, 126.93450729567374], zoom_start=13)

for idx, rows in df.iterrows():
    
    # location 
    lat, lng = rows.위도, rows.경도
    
    # Marker 
    folium.Marker(
        location=[lat, lng],
        popup=rows.주소,
        tooltip=rows.분류, 
        icon=folium.Icon(
            icon="home",
            color="lightred" if rows.세대수 >= 199 else "lightblue",
            icon_color="darkred" if rows.세대수 >= 199 else "darkblue",
        )
    ).add_to(m)

    # CircleMarker
    folium.Circle(
        location=[lat, lng],
        radius=rows.세대수 * 0.5, 
        fill=True, 
        color="pink" if rows.세대수 >= 518 else "green", # 조건
        fill_color="pink" if rows.세대수 >= 518 else "green", # 조건
    ).add_to(m)
    
m  

reference

https://nbviewer.jupyter.org/github/python-visualization/folium/tree/master/examples/

6. 지도시각화(아래는 서울 범죄 관련)

import json

crime_anal_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"))

(1) 살인 사건 시각화

# 2016년 서울시에서 어느정도 살인사건이 있는가?
# 영등포구에서 살인사건이 제일 많이 일어났다

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

my_map.choropleth(
    geo_data=geo_str, # 우리나라 경계선 좌표값이 담긴 데이터
    data=crime_anal_norm["살인"],
    columns=[crime_anal_norm.index, crime_anal_norm["살인"]],
    fill_color="PuRd",
    key_on="feature.id",
    fill_opacity=0.7, # 투명도
    line_opacity=0.2, # 투명도
    legend_name="정규화된 살인 발생 건수",
)

my_map

(2) 성범죄 사건 시각화

# 강남3구 중 2개 구가 포함되어 있다 

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

my_map.choropleth(
    geo_data=geo_str, # 우리나라 경계선 좌표값이 담긴 데이터
    data=crime_anal_norm["강간"],
    columns=[crime_anal_norm.index, crime_anal_norm["강간"]],
    fill_color="PuRd",
    key_on="feature.id",
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name="정규화된 강간 발생 건수",
)

my_map

(3) 5대 범죄 건수 시각화

# 강남구가 역시 포함되어 있다 

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

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

my_map

(4) 인구 대비 범죄 발생 건수 시각화

tmp_criminal = crime_anal_norm["범죄"] / crime_anal_norm["인구수"] # 인구수 대비 범죄발생 비율 

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

my_map.choropleth(
    geo_data=geo_str,
    data=tmp_criminal,
    columns=[crime_anal_norm.index, tmp_criminal],
    fill_color="PuRd",
    key_on="feature.id",
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name="정규화된 범죄 발생 건수",
)

my_map

7. 경찰서별 검거현황과 구별 범죄발생 현황을 표현하기

import numpy as np

crime_anal_station = pd.read_csv(
    "../data/02. crime_in_Seoul_1st.csv", index_col=0, encoding="utf-8"
)
col = ["살인검거", "강도검거", "강간검거", "절도검거", "폭력검거"]
tmp = crime_anal_station[col] / crime_anal_station[col].max()
crime_anal_station["검거"] = np.mean(tmp, axis=1)
crime_anal_station.head()

경찰서 위치를 지도에 표시(Marker)

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

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

my_map

검거에 적절한 값을 곱해서 원의 넓이로 사용

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

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

my_map

구별 범죄 현황과 경찰서별 검거율을 함께 표시

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

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

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

my_map

강남의 범죄 발생이 많은 것은 혹시 유흥업소의 밀집과 관련이 있지는 않을까

  • 확인을 위해 최초 받았던 발생 장소별 데이터를 읽어보자
crime_loc_raw = pd.read_csv(
    "../data/02. crime_in_Seoul_location.csv", thousands=",", encoding="euc-kr"
)
crime_loc_raw.head()


crime_loc_raw["범죄명"].unique()

>>
array(['살인', '강도', '강간.추행', '절도', '폭력'], dtype=object)

crime_loc_raw["장소"].unique()

>>
array(['아파트, 연립 다세대', '단독주택', '노상', '상점', '숙박업소, 목욕탕', '유흥 접객업소', '사무실',
       '역, 대합실', '교통수단', '유원지 ', '학교', '금융기관', '기타'], dtype=object)

피봇 테이블 생성

crime_loc = crime_loc_raw.pivot_table(
    crime_loc_raw, index=["장소"], columns=["범죄명"], aggfunc=[np.sum]
)
crime_loc.columns = crime_loc.columns.droplevel([0, 1])
crime_loc.head()


정규화

col = ["살인", "강도", "강간", "절도", "폭력"]
crime_loc_norm = crime_loc / crime_loc.max()
crime_loc_norm.head()


컬럼 추가

crime_loc_norm["종합"] = np.mean(crime_loc_norm, axis=1)
crime_loc_norm.head()


matplotlib

## 임포트 등 세팅

import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import rc

plt.rcParams["axes.unicode_minus"] = False  # 마이너스 기호 사용
rc("font", family="Malgun Gothic")  # 한글 폰트 사용
get_ipython().run_line_magic("matplotlib", "inline") # seaborn은 matplotlib과 함께 실행된다
crime_loc_norm_sort = crime_loc_norm.sort_values(by="종합", 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()

위 글은 제로베이스 데이터 취업 스쿨의 강의자료를 참고하여 작성되었습니다.

profile
허재

0개의 댓글

관련 채용 정보