관리 메뉴

log.Sehee

[데이터 취업 스쿨 스터디 노트] Folium 지도 시각화 / 서울시 범죄 검거 현황 4 - 6 본문

Zerobase DS School

[데이터 취업 스쿨 스터디 노트] Folium 지도 시각화 / 서울시 범죄 검거 현황 4 - 6

Sehe_e 2024. 8. 4. 17:07

 


 

서울시 범죄현황 데이터 시각화

 

기본 설정

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

plt.rcParams['axes.unicode_minus'] = False
rc('font', family='Arial Unicode MS')
get_ipython().run_line_magic('matplotlib', 'inline')

 

상관관계 확인

# pairplot 강도, 살인, 폭력에 대한 상관관계 확인
# kind : scatter, kde, hist, reg

sns.pairplot(data=crime_anal_norm, vars = ['살인', '강도', '폭력'], kind='reg', height=3);

 

# 인구수, CCTV와 살인, 강도의 상관관계 확인

def drawGraph():
    sns.pairplot(
        data = crime_anal_norm, 
        x_vars = ['인구수', 'CCTV'],
        y_vars = ['살인', '강도'],
        kind = 'reg',
        height = 4,
    )
    plt.show()

drawGraph()

 

검거율 heatmap

# 검거율 heatmap
# 검거 column 기준으로 정렬

def drawGraph():
    # 데이터 프레임 생성
    target_col = ['강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율', '검거']
    crime_anal_norm_sort = crime_anal_norm.sort_values(by='검거', ascending=False)

    # 그래프 설정
    plt.figure(figsize = (10, 10))
    sns.heatmap(
        data = crime_anal_norm_sort[target_col],
        annot = True,
        fmt = 'f',
        linewidths = 0.5,    # 간격 설정
        cmap = 'RdPu'
    )
    plt.title('범죄 검거 비율(정규화된 검거의 합으로 정렬)'),
    plt.show()
drawGraph()

 

범죄 발생 건수 heatmap

# 범죄 발생 건수 heatmap
# '범죄' column을 기준으로 정렬

def drawGraph():
    # 데이터 프레임 생성
    target_col = ['강간', '강도', '살인', '절도', '폭력', '범죄']
    crime_anal_norm_sort = crime_anal_norm.sort_values(by='범죄', ascending=False)

    # 그래프 설정
    plt.figure(figsize = (10, 10))
    sns.heatmap(
        data = crime_anal_norm_sort[target_col],
        annot = True,
        fmt = 'f',
        linewidths = 0.5,    # 간격 설정
        cmap = 'RdPu'
    )
    plt.title('범죄 발생 비율(정규화된 발생 건수으로 정렬)'),
    plt.show()
drawGraph()

 

# 데이터 저장

crime_anal_norm.to_csv('../data/02. crime_in_Seoul_final.csv', sep=',', encoding='utf-8')

 

 


 

folium

 

folium 설치

# folium 설치
pip install folium

 

import folium, json, pandas as pd

# zoom_start = 지도 확대 정도 , 0 ~ 18 권장
m = folium.Map(location=[37.4850084, 126.8779291], zoom_start = 18)
m

 

save() : 시각화 한 지도를 지정 파일 형식으로 저장

m.save('./folium.html')

!ls

 

tiles : 지도 디자인을 변경해준다.

m = folium.Map(
    location=[37.4850084, 126.8779291], 
    zoom_start = 18,
    tiles='Stamen Toner'
)
m

 

강의를 만들 때엔 Stamen Toner 옵션이 존재했던 것 같은데 기본 옵션에서 없어졌나보다.

Stamen Toner 옵션을 사용하려면 링크와 인증정보를 따로 기재해주어야 했다.

tiles = "https://tiles.stadiamaps.com/tiles/stamen_toner/{z}/{x}/{y}{r}.png?api_key=인증키"
attr = "toner"

m = folium.Map(
    location=[37.4850084, 126.8779291], 
    zoom_start = 15,
    tiles = tiles,
    attr = attr
)
m

 

folium.Marker()

  • 위도, 경도로 마커 위치 설정
  • popup : 마우스로 클릭했을 때 정보가 나온다. html 문법으로 하이퍼링크, 볼드체, 이탤릭체 등 사용 가능
  • tooltip : 마우스로 hover했을 때 정보가 나온다. 위와 같이 html 문법 적용 가능
  • folium.Icon() : 아이콘 디자인을 변경
    • color : 마커 색 변경
    • icon_color : 마커 아이콘 색 변경
    • icon : 아이콘 변경 / fontawesome 사이트에서 찾아서 설정 가능
    • angel : 마커 크기 변경
    • prefix : 기울기 조정
m = folium.Map(
    location=[37.5696765, 126.976177], 
    zoom_start = 15
)

# 광화문역
folium.Marker(
    (37.5696765, 126.976177), 
    icon = folium.Icon(color='black')
).add_to(m)

# 을지로역
folium.Marker(
    (37.5660286, 126.9954924), 
    popup = '<b>Subway</b>', 
    tooltip = '<i>을지로역</i>',
    icon = folium.Icon(
        color='purple',
        icon_color = 'pink',
        icon = 'cloud'
    )
).add_to(m)

# 을지로2가
folium.Marker(
    (37.5658833, 126.9859321), 
    popup = '<a href="https://maps.app.goo.gl/mrt6y86vHp7unoUNA" target=_"blink">을지로2가</a>', 
    tooltip = '<i>을지로2가역</i>',
    icon = folium.Icon(
        color='red',
        icon_color = 'beige',
        icon = 'cat',
        angel = 50,    # 마커 크기
        prefix = 'fa'    # 기울기 / 조정해야만 나오는 아이콘들이 있다.
    )
).add_to(m)

m

 

마우스 hover시 이미지. tooltip 옵션

 

마커 클릭 시 이미지. a 태그로 href를 달았다.

 

folium.CilckForMarker() : 지도 위에 마우스로 클릭했을 때 마커를 생성한다.

m = folium.Map(
    location=[37.5696765, 126.976177], 
    zoom_start = 15
)

# popup : 마커의 정보. 기본적으로 위도, 경도가 나온다
m.add_child(folium.ClickForMarker(popup='Click'))

 

folium.LatLngPopup() : 지도를 마우스로 클릭했을 때 위도 경도 정보를 반환해준다.

m = folium.Map(
    location=[37.5696765, 126.976177], 
    zoom_start = 15
)

m.add_child(folium.LatLngPopup())

 

 

Circle(), CircleMarker() : 설정 범위를 동그란 구역으로 표시해준다. 큰 차이점이 없으나 radius의 크기 차이가 있다.

m = folium.Map(
    location=[37.5702, 126.9769], 
    zoom_start = 15
)

# Circle
folium.Circle(
    location = [37.5702, 126.9769],
    radius = 100,
    fill = True,
    color = 'green',
    fill_color = 'red',
    popup = 'Circle',
    tooltip = 'C'
).add_to(m)

# Circle Marker, Circle과 큰 차이가 없다.
# radius 값의 차이가 있다.
folium.CircleMarker(
    location = [37.5656, 126.9735],
    radius = 30,
    fill = True,
    color = 'blue',
    fill_color = 'yellow',
    popup = 'Circle',
    tooltip = 'C'
).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)    # 배경 지도

# json 파일 사용
folium.Choropleth(
    geo_data = '../data/02. us-states.json',    # 경계선 좌표값이 담긴 데이터
    data = state_data,    # Series or DataFrame / 실업률 데이터
    columns = ['State', 'Unemployment'],    # DataFrame columns
    key_on = 'feature.id',    # us-states.json 데이터와 state_date 묶기
    fill_color = 'BuPu',    # 색상 설정
    fill_opacity = 1,    # 배경 색상 투명도 설정
    line_opacity = 1,    # 경계선 투명도 설정
    legend_name = 'Unemployment rate (%)'
).add_to(m)

m

 


 

아파트 유형 지도 시각화

 

공공데이터포털에서 '서울특별시 동작구_주택유형별 위치 정보 및 세대수 현황' 다운로드

import pandas as pd

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

 

df.info()

 

데이터 갯수가 맞지 않아 Null 값 제거

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

 

index 초기화

# reset_index의 drop 옵션은 기본 False. 인덱스가 column으로 들어온다. True일 때 column으로 들어오지 않으면서 index가 초기화된다.
df = df.reset_index(drop=True)

 

 

columns 정보확인

df.columns

 

 

column name 전처리

# 연번, 분류 column이 '연번 ', '분류 ' 으로 공백 제거를 위한 rename 실행

df = df.rename(columns={'연번 ':'연번', '분류 ':'분류'})
del df['연번']

 

df.tail()

 

세대수의 빈도에 따라 색상을 다르게 표시해주기 위해 데이터 확인. 50%를 기준으로 색상 변경

df.describe()

 

주택 위치 정보 표시 및 세대수 빈도에 따른 정보 표시

# folium

m = folium.Map(location=[37.4988794, 126.9516345], zoom_start = 13.5)

for idx, row in df.iterrows():
    # location
    lat, lng = row.위도, row.경도

    # Marker
    folium.Marker(
        location = [lat, lng],
        popup = row.주소,
        tooltip = row.분류,
        icon = folium.Icon(
            icon = 'home',
            color = 'lightred' if row.세대수 >= 204 else 'lightblue',
            icon_color = 'darkred' if row.세대수 >= 204 else 'darkblue',
        )
    ).add_to(m)

    # Circle
    folium.Circle(
        location = [lat, lng],
        radius = row.세대수 * 0.5,
        fill = True,
        color = 'pink' if row.세대수 >= 528 else 'green',
        fill_color = 'pink' if row.세대수 >= 528 else 'green'
    ).add_to(m)
m

 


 

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

 

import json, folium, pandas as pd

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'))

crime_anal_norm.tail(2)

 

살인 발생 건수 지도 시각화

# 살인 발생 건수 지도 시각화

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

 

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

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

tmp_criminal = crime_anal_norm['범죄'] / crime_anal_norm['인구수']

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

folium.Choropleth(
    geo_data = geo_str,    # 우리나라 경계선 좌표값이 담긴 데이터
    data = tmp_criminal,
    columns = [crime_anal_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

 

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

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

crime_anal_station = pd.read_csv('../data/02. crime_in_Seoul_raw.csv', encoding='utf-8')
crime_anal_station.tail(2)

 

검거 정규화

col = ['살인검거', '강간검거', '강도검거', '절도검거', '폭력검거']
tmp = crime_anal_station[col] / crime_anal_station[col].max()    # 정규화
crime_anal_station['검거'] = np.mean(tmp, axis=1)    # numpy에서 axis = 1은 가로(행), pandas에서 axis = 1은 세로(열)
crime_anal_station.head()

 

경찰서 위치 마커 표시

# 경찰서 위치 마커 표시

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

 

 

범죄 발생률에 따른 경계선 표시와 검거율에 따른 정보 표시

# 검거에 값을 곱한 뒤 원의 넓이 적용

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 = '#3186cc',
        fill = True,
        fill_color = '#3186cc',
    ).add_to(my_map)
    
my_map

 

 


내일의 학습 목표

웹데이터 분석 1 - 3

 

 

Comments