본문 바로가기
python

파이썬 정형 데이터 분석 & 데이터 시각화 (with Jupyter Notebook, Pandas)(2)

by eun2ng 2024. 11. 30.

전 게시물에 이어서 서울시 범죄현황 통계자료를 가지고 어느 구가 범죄로부터 안전하고 또 위험한지 데이터 분석 및 시각화 실습을 진행해보겠습니다. 이번 게시물은 데이터 탐색부터 데이터 시각화까지입니다. 이전 데이터 분석 실습내용이 궁금하시다면 아래 게시물을 참고해주세요.

2024.11.25 - [python] - 파이썬 정형 데이터 분석 & 데이터 시각화 (with Jupyter Notebook, Pandas)(1)

 

파이썬 정형 데이터 분석 & 데이터 시각화 (with Jupyter Notebook, Pandas)(1)

이번 게시물에서는 서울시 관서별 5대 범죄 발생 & 검거 현황 데이터를 이용해 "어떤 구가 범죄율이 낮고 살기 좋은지" 알아 보기 위해 데이터를 분석하고 시각화하는 실습을 해보려고 합니다. 

eunduun.tistory.com

 

데이터 탐색 (Data Exploration)

  • 검거율 기준으로 오름차순 정렬하기
gu_df.sort_value(by='검거율', ascending=True, inplace=True)

ascending=False : 내림차순

inplace=True : 덮어쓰기

  • 범죄별 발생 건수 정규화하기

5대 범죄별 수치를 해당 범죄별 최대값으로 나눠줘서 정규화를 진행해줍니다.
범죄별로 가장 많이 발생한 구가 1 == 100% 입니다.

weight_col = gu_df[['강간', '강도', '살인', '절도', '폭력']].max()
crime_count_norm = gu_df[['강간', '강도', '살인', '절도', '폭력']] / weight_col

하지만 인구 수가 고려되지 않은 최대 발생 건수를 기준으로 했기 때문에 잘 알 수 없습니다. 이후에 인구 수를 고려해서 살펴보겠습니다.

좌 : weight_col / 우 : crime_count_norm

인구 대비 범죄 발생 비율 알아보기

인구 데이터 merge하기

정확한 위험도를 알기 위해 단순히 범죄건수만 보지 않고 인구수로 나눠서 인구대비 발생비율을 살펴보겠습니다.

 

1. 인구 데이터를 가져온 후 gu_df와 구별 index를 기준으로 merge 하기 위해 index를 세팅해줍니다.

popul_df = pd.read_csv('pop_kor.csv', encoding='utf-8', index_col='구별')
popul_df.head()

아래와 같이 먼저 read_csv()로 읽어들이고 .set_index()를 적용할 수도 있습니다.

popul_df = pd.read_csv('pop_kor.csv', encoding='utf-8').set_index('구별')

 

2. join()을 사용해 gu_df에 popul_df를 merge 해줍니다.

gu_df = gu_df.join(popul_df)

 

3. 구별 인구 수를 반영한 비율을 구한다.

범죄 수 비율을 구별 인구 수로 나눠준 후 인구 수 단위인 10만을 곱해줍니다.

행(구)별 범죄 수 비율 / 구별 인구 수 * 100000

crime_ratio = crime_count_norm.div(gu_df['인구수'], axis=0) * 100000 
crime_ratio.head()

 

seaborn의 heatmap을 이용해 시각화하기

한글 데이터 시각화를 위해서 다음과 같은 코드를 넣어줍니다.

# jupyter notebook 내에 figure를 보여주기
%matplotlib inline 

# matplotlib의 한글문제를 해결
font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
# font_name
rc('font', family=font_name)

 

  • 구별 살인 발생 순위 살펴보기
sns.heatmap(crime_count_norm.sort_values(by='살인', ascending=False)) # ascending=False : 내림차순

위의 코드는 crime_count_norm을 '살인' 열을 기준으로 내림차순 정렬하라는 뜻입니다.

코드를 실행해보면 다음과 같은 그래프가 나옵니다. 하지만 이 그래프에는 문제가 있습니다.

첫 번째로는 이 그래프를 보고는 정확한 수치를 알기 힘들다는 것이고, 수치가 작을 수록 짙은 색을 띄고 있어서 오해하기 쉽다는 것입니다. 

두번째로는 항목들끼리 너무 다닥다닥 붙어있어서 잘 알아보기 힘듭니다.

따라서 이 문제를 해결하기 위해서 몇 가지 옵션을 주어서 수정해주도록 하겠습니다.

 

  • 확인이 편리하도록 옵션 수정해주기
plt.figure(figsize = (10,10))

sns.heatmap(crime_count_norm.sort_values(by='살인', ascending=False), annot=True, fmt='f', linewidths=.5, cmap='Reds')

plt.title('범죄 발생(살인발생으로 정렬) - 각 항목별 최대값으로 나눠 정규화')
plt.show()

 

plt.figure() 함수를 이용해서 figure 사이즈를 조정해줄 수 있습니다.

다음으로 heatmap에 추가해준 옵션입니다.

annot : 셀 내에 수치 입력 여부

fmt : 셀 내에 입력될 수치의 format (f == float)

linewidth : 셀 간 이격거리(내부 테두리)

cmap : matpotlib colormap  @ https://goo.gl/YWpBES <- 이 페이지에서 colormap을 확인하실 수 있습니다.

plt.title() 함수를 이용해서 제목도 정해줍니다.

실행 결과입니다. 보기 훨씬 편해진 모습입니다.

 

(다른 열 기준으로 살펴보시고 싶으시면 by='원하는 열이름' 으로 바꿔주시면 됩니다.)

 

이제 구별 5대 범죄 발생 수치의 평균을 구해 어떤 구가 모든 범죄에서 평균적으로 안전한지 알아보겠습니다.

 

  • 구별 5대범죄 발생 수치 평균 / 순위 비교
crime_ratio['전체발생비율'] = crime_ratio.mean(axis=1)

plt.figure(figsize = (10,10))

sns.heatmap(crime_ratio.sort_values(by='전체발생비율', ascending=False), annot=True, fmt='f', linewidths=.5, cmap='Reds')
plt.title('범죄 발생(전체발생비율로 정렬) - 각 항목을 정규화한 후 인구로 나눔')
plt.show()

코드를 실행해보면 다음과 같은 실행결과가 나옵니다.

 

Folium library 을 활용한 geo-mapping

지도 시각화를 위해 파이썬에서 사용할 수 있는 Folium library를 활용하겠습니다.

Folium library를 설치해줍니다.

!pip install folium==0.5.0

 https://github.com/southkorea/southkorea-maps

 

GitHub - southkorea/southkorea-maps: South Korea administrative divisions in ESRI Shapefile, GeoJSON and TopoJSON formats.

South Korea administrative divisions in ESRI Shapefile, GeoJSON and TopoJSON formats. - southkorea/southkorea-maps

github.com

southkorea github에서 서울만 따로 추린 GeoJSON 파일을 활용합니다.

1. GeoJSON 파일 가져오기

import json

geo_path = 'skorea_municipalities_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))

 

여기서 가져온 geo_str은 dict 형식으로 아래와 같은 내용을 담고 있습니다.

 

2. Folium library를 활용해 지도 불러오기

import folium

map = folium.Map(location=[37.5502, 126.982], zoom_start=11, tiles='Cartodb Positron') 
map

tiles : 지도 타입 (default type or "Stamen Terrain" or "Stamen Toner")
location : 초기 지도 center 위치

 

인구 수 대비 5대 범죄 발생 수치 평균 구별 시각화

map = folium.Map(location=[37.5502, 126.982], zoom_start=11, tiles='Cartodb Positron')

# 전체 5대 범죄 인구당 발생비율 시각화
map.choropleth(geo_data = geo_str,
               data = crime_ratio['전체발생비율'], 
               columns = [crime_ratio.index, crime_ratio['전체발생비율']],
               fill_color = 'PuRd', #PuRd, YlGnBu
               key_on = 'feature.id')
map

 

 

 

map.choropleth()
: 지도에 데이터의 색상 차이를 시각화
하는 함수입니다.

geo_data=geo_str
: GeoJSON 데이터를 입력해야 합니다.  각 구에 대한 경계 정보를 포함하는 GeoJSON 형식의 데이터를 참조합니다.

data=gu_df['검거율']

: gu_df라는 데이터프레임에서 '검거율' 열의 데이터를 사용합니다. 

columns=[gu_df.index, gu_df['검거율']]
: gu_df의 인덱스(각 구의 이름)와 '검거율' 열을 시각화에 사용할 데이터로 설정합니다. 

key_on='feature.id'
: GeoJSON 데이터에서 각 구의 ID를 기준으로 색을 입히려는 설정입니다. 

 

다음은 시행 결과 입니다.

 

 

 

지금까지 파이썬을 이용해서 데이터를  전처리하고 탐색하고 시각화하는 실습을 진행해보았습니다.

앞으로 더 많은 데이터를 가지고 시각화해보도록 하겠습니다.

 

데이터 다루기 달인이 되는 그 날까지 화이팅입니다.