python(29) 서울시 범죄현황 분석 및 시각화

hyukstory 혁스토리·2020년 9월 4일
1

python

목록 보기
35/35

서울시 범죄 현황 분석 (2015년 기준)
Q. 과연 강남 3구의 체감 안전도가 높은 만큼 실제 안전도도 높을까?
범죄율과 검거율을 가지고 시각화 해보자

1. 공공 데이터 포털에서 서울시 5대 범죄 현황 다운로드 (2015년)

2. pandas 이용하여 데이터 정리하기

import numpy as np
import pandas as pd
crime_anal_police = pd.read_csv('C:/Users/student/Desktop/python/P_4week/02.crime_in_Seoul.csv', 
                                thousands=',', 
                                encoding='euc-kr')
crime_anal_police.head()
crime_anal_police.info()
crime_anal_police.values
crime_anal_police.describe()
crime_anal_police.columns
### 데이터가 관서별로 되어있으므로 데이터 수정 필요
### 보통 한 구에 하나 혹은 두 군데의 경찰서가 위치
### 또는 구 이름과 다른 경찰서도 있음
#### 그러므로, 경찰서 이름 기준으로 구 정보를 알아야 한다.

3. 지도 정보를 얻을 수 있는 Google Maps

## Google MAPS API -> Geocoding API 

4. Google Maps 이용해서 주소와 위도, 경도 정보 얻기

## pip install googplemaps 필요
import googlemaps

gmaps_key = "AIzaSyBDWRx1UZjT1i4nVReN0blTNkQU_Q4C8Aw"  # 이 키는 불변
gmaps = googlemaps.Client(key=gmaps_key)


gmaps.geocode('서울중부경찰서',language='ko')  # 서울중부경찰서의 주소 정보



## 구글 검색에서 주소 정보 제대로 얻기 위해 관서명을 풀네임으로 변경
station_name =[]

for name in crime_anal_police['관서명']:
    
    station_name.append('서울'+str(name[:-1])+'경찰서')

station_name


## 풀네임으로 바꾼 관서명 이용해 구글에서 주소 받아오기
station_address = []
station_lat = []
station_lng = []

for name in station_name:
    tmp = gmaps.geocode(name,language='ko')
    station_address.append(tmp[0].get('formatted_address'))
    tmp_loc = tmp[0].get('geometry')
    station_lat.append(tmp_loc['location']['lat'])
    station_lng.append(tmp_loc['location']['lat'])
    print(name+'-->' +tmp[0].get('formatted_address'))

station_address
station_lat
station_lng


## 구별 이름 추출
gu_name = []

for name in station_address:
    tmp = name.split()      # 저장한 주소를 공백(띄어쓰기) 기준으로 나누고
    tmp_gu = [gu for gu in tmp if gu[-1]=='구'][0] 
    # tmp를 하나씩 받아오는 gu 중에서 마지막 글자가 '구'로 끝나는 것들만 tmp_gud에 넣기
    gu_name.append(tmp_gu)
    
crime_anal_police['구별'] = gu_name   # 구별이라는 새 칼럼으로 저장
crime_anal_police



# 다른 이름으로 저장
crime_anal_police.to_csv('C:/Users/student/Desktop/python/P_4week/02.crime_in_Seoul_include_gu_name.csv',
                         sep=',', encoding='utf-8')


5. 앞선 구별 범죄 현황 데이터에 pivot table 적용


crime_anal_raw = pd.read_csv('C:/Users/student/Desktop/python/P_4week/02.crime_in_Seoul_include_gu_name.csv', 
                             encoding='utf-8')
crime_anal_raw.head()
# 맨 앞에 숫자 컬럼 생겻으므로 제거 필요
crime_anal_raw = pd.read_csv('C:/Users/student/Desktop/python/P_4week/02.crime_in_Seoul_include_gu_name.csv', 
                             encoding='utf-8', index_col=0)

# pivot_table 이용하여 원 데이터를 관서별에서 구별로 바꾸기
crime_anal = pd.pivot_table(crime_anal_raw, index='구별', aggfunc=np.sum)  #값은 합계로 
crime_anal.head()


# 각 범죄율 계산
crime_anal['강간검거율'] = crime_anal['강간 검거']/crime_anal['강간 발생']*100
crime_anal['강도검거율'] = crime_anal['강도 검거']/crime_anal['강도 발생']*100
crime_anal['살인검거율'] = crime_anal['살인 검거']/crime_anal['살인 발생']*100
crime_anal['절도검거율'] = crime_anal['절도 검거']/crime_anal['절도 발생']*100
crime_anal['폭력검거율'] = crime_anal['폭력 검거']/crime_anal['폭력 발생']*100

# 검거 건수는 검거율로 대체할 수 있어서 삭제
del crime_anal['강간 검거']
del crime_anal['강도 검거']
del crime_anal['살인 검거']
del crime_anal['절도 검거']
del crime_anal['폭력 검거']

crime_anal.head()




# 전년도 발생건수에 대한 검거도 포함되다 보니 검거율이 100이 넘는 경우가 생김
## 여기서는 그냥 간단히 100 넘는 경우는 다 100으로 처리

col_list = ['강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율']

for column in col_list:
    crime_anal.loc[crime_anal[column] > 100, column] = 100
    
crime_anal.head()


# 변수명 변경
crime_anal.rename(columns = {'강간 발생':'강간', 
                             '강도 발생':'강도', 
                             '살인 발생':'살인', 
                             '절도 발생':'절도', 
                             '폭력 발생':'폭력'}, inplace=True)
crime_anal.columns


6. 데이터 표현을 위해 다듬기

# normalize(정규화)
# 강도와 살인은 발생 건수가 1~2자리 수 인 것에 비해 절도는 4자리수
# 최소-최대 정규화를 통해 모든 값에 대해 0과 1 사이의 값으로 표현

from sklearn import preprocessing
#data 사전 전처리 패키지

col = ['강간', '강도','살인','절도','폭력']
x = crime_anal[col].values #정규화할 값들을 할당
min_max_scaler = preprocessing.MinMaxScaler()

x_scaled = min_max_scaler.fit_transform(x.astype(float))
crime_anal_norm = pd.DataFrame(x_scaled, columns = col, index = crime_anal.index)



col2 = ['강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율']
crime_anal_norm[col2] = crime_anal[col2]
crime_anal_norm.head()



# 구별 인구수와 CCTV 개수 갖고 오기
result_CCTV = pd.read_csv('C:/Users/student/Desktop/python/P_4week/01.CCTV_result.csv', encoding='UTF-8', 
                          index_col='구별')
crime_anal_norm[['인구수', 'CCTV']] = result_CCTV[['인구수', '소계']]
crime_anal_norm.head()


# 범죄 발생 건수의 합을 '범죄'라는 항목으로 두고 합함
col = ['강간','강도','살인','절도','폭력']
crime_anal_norm['범죄'] = np.sum(crime_anal_norm[col], axis=1)
crime_anal_norm.head()


# 검거율도 '검거'라는 항목으로 합함
col = ['강간검거율','강도검거율','살인검거율','절도검거율','폭력검거율']
crime_anal_norm['검거'] = np.sum(crime_anal_norm[col], axis=1)
crime_anal_norm.head()

7. 범죄 데이터 시각화하기

# 그래프에 대한 한글 폰트 문제 해결
import platform
path = "c:/Windows/Fonts/malgun.ttf"
from matplotlib import font_manager, rc
if platform.system() == 'Darwin':
    rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc('font', family=font_name)
else:
    print('Unknown system... sorry~~~~')
    
    
# pairplot으로 강도, 살인, 폭력 간의 상관관계 보기
import matplotlib.pyplot as plt
import seaborn as sns

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

# 인구수와 CCTV개수,  살인과 강도에 대해 상관관계 보기
sns.pairplot(crime_anal_norm, x_vars=["인구수", "CCTV"], 
             y_vars=["살인", "강도"], kind='reg', height=3)
plt.show()
    


# 살인 및 폭력 검거율과 CCTV의 관계
sns.pairplot(crime_anal_norm, x_vars=["인구수", "CCTV"], 
             y_vars=["살인검거율", "폭력검거율"], kind='reg', height=3)
plt.show()  # 오히려 음의 상관관계!




# 검거 항목 최고값을 100을 한정하여 정규화
tmp_max = crime_anal_norm['검거'].max()
crime_anal_norm['검거'] = crime_anal_norm['검거'] / tmp_max * 100
crime_anal_norm_sort = crime_anal_norm.sort_values(by='검거', ascending=False)
crime_anal_norm_sort.head()


# 정규화된 검거의 합으로 정렬
crime_anal_norm_sort = crime_anal_norm.sort_values(by='검거', ascending=False) 

# heatmap 그려보기
target_col = ['강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율']

plt.figure(figsize = (10,10))
sns.heatmap(crime_anal_norm_sort[target_col], annot=True, fmt='f', 
                    linewidths=.5, cmap='RdPu')
plt.title('범죄 검거 비율 (정규화된 검거의 합으로 정렬)')
plt.show()   # -> 절도 검거율이 다른 검거율에 비해 낮다


# 발생 건수의 합으로 정렬하여 heatmap 
crime_anal_norm['범죄'] = crime_anal_norm['범죄'] / 5
crime_anal_norm_sort = crime_anal_norm.sort_values(by='범죄', ascending=False)


target_col = ['강간', '강도', '살인', '절도', '폭력', '범죄']

plt.figure(figsize = (10,10))
sns.heatmap(crime_anal_norm_sort[target_col], annot=True, fmt='f', linewidths=.5,
                       cmap='RdPu')
plt.title('범죄비율 (정규화된 발생 건수로 정렬)')
plt.show()   # -> 강남구, 양천구, 영등포구가 범죄 발생 건수가 높다
# 송파구, 서초구도 꽤 높다

# 다른 이름으로 저장
crime_anal_norm.to_csv('C:/Users/student/Desktop/python/P_4week/02.crime_in_Seoul_final.csv', sep=',', 
                       encoding='utf-8')

8. 서울시 범죄율에 대한 지도 시각화

# 서울시 구별 경계선 데이터 로딩
import json
geo_path = 'C:/Users/student/Desktop/python/P_4week/02.skorea_municipalities_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))



# folium 활용하여 경계선 있는 지도로 시각화
## 살인 발생 건수로 컬러맵
import folium
final_map = folium.Map(location=[37.5502, 126.982], zoom_start=11, 
                 tiles='Stamen Toner')

final_map.choropleth(geo_data = geo_str,
               data = crime_anal_norm['살인'],  
               columns = [crime_anal_norm.index, crime_anal_norm['살인']],
               fill_color = 'PuRd', #PuRd, YlGnBu
               key_on = 'feature.id')
final_map.save("C:/Users/student/Desktop/python/P_4week/final_map.html")
final_map
# -> 강남 3구 진하게 나타남



## 강간 발생 건수로 컬러맵
final_map2 = folium.Map(location=[37.5502, 126.982], zoom_start=11, 
                 tiles='Stamen Toner')

final_map2.choropleth(geo_data = geo_str,
               data = crime_anal_norm['강간'],
               columns = [crime_anal_norm.index, crime_anal_norm['강간']],
               fill_color = 'PuRd', #PuRd, YlGnBu
               key_on = 'feature.id')
final_map2.save("C:/Users/student/Desktop/python/P_4week/final_map2.html")
final_map2
# ->  강남 3구 진하게 나타남



## 범죄 발생 건수 전체로 컬러맵
final_map3 = folium.Map(location=[37.5502, 126.982], zoom_start=11, 
                 tiles='Stamen Toner')

final_map3.choropleth(geo_data = geo_str,
               data = crime_anal_norm['범죄'],
               columns = [crime_anal_norm.index, crime_anal_norm['범죄']],
               fill_color = 'PuRd', #PuRd, YlGnBu
               key_on = 'feature.id')
final_map3.save("C:/Users/student/Desktop/python/P_4week/final_map3.html")
final_map3
# ->  강남 3구 진하게 나타남
# 그러나 인구수 고려해야함. 즉, 인구 대비 범죄 발생 비율을 알아봐야함




## 인구수 대비 범죄율로 컬러맵
tmp_criminal = crime_anal_norm['범죄'] /  crime_anal_norm['인구수'] * 1000000

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

final_map4.choropleth(geo_data = geo_str,
               data = tmp_criminal,
               columns = [crime_anal.index, tmp_criminal],
               fill_color = 'PuRd', #PuRd, YlGnBu
               key_on = 'feature.id')
final_map4.save("C:/Users/student/Desktop/python/P_4week/final_map4.html")
final_map4




## 검거율로 컬러맵
final_map5 = folium.Map(location=[37.5502, 126.982], zoom_start=11, 
                 tiles='Stamen Toner')

final_map5.choropleth(geo_data = geo_str,
               data = crime_anal_norm['검거'],
               columns = [crime_anal_norm.index, crime_anal_norm['검거']],
               fill_color = 'YlGnBu', #PuRd, YlGnBu
               key_on = 'feature.id')
final_map5.save("C:/Users/student/Desktop/python/P_4week/final_map5.html")
final_map5

9. 서울시 경찰서별 검거율과 구별 범죄 발생율 동시에 시각화

# Google Maps 통해 얻어온 위도와 경도 정보를 새로움 칼럼에 추가
crime_anal_raw['lat'] = station_lat
crime_anal_raw['lng'] = station_lng

# 검거만 따로 모아두기
col = ['살인 검거', '강도 검거', '강간 검거', '절도 검거', '폭력 검거']
tmp = crime_anal_raw[col] / crime_anal_raw[col].max()
    
crime_anal_raw['검거'] = np.sum(tmp, axis=1)

crime_anal_raw.head()



# 경찰서들의 위치를 지도에 나타내어 시각화
final_map6 = folium.Map(location=[37.5502, 126.982], zoom_start=13) # 서울의 위도와 경도

for n in crime_anal_raw.index:
    folium.Marker([crime_anal_raw['lat'][n], 
                   crime_anal_raw['lng'][n]]).add_to(final_map6)
    
final_map6.save("C:/Users/student/Desktop/python/P_4week/final_map6.html")
final_map6





# 경찰서의 검거율을 원의 넓이로 표현
final_map7 = folium.Map(location=[37.5502, 126.982], zoom_start=13)

for n in crime_anal_raw.index:
    folium.CircleMarker([crime_anal_raw['lat'][n], crime_anal_raw['lng'][n]], 
                        radius = crime_anal_raw['검거'][n]*10, 
                        color='#3186cc', fill_color='#3186cc', fill=True).add_to(final_map7)
final_map7.save("C:/Users/student/Desktop/python/P_4week/final_map7.html")    
final_map7



# 붉은 색상으로 범죄 발생 건수를 추가
final_map8 = folium.Map(location=[37.5502, 126.982], zoom_start=13)

final_map8.choropleth(geo_data = geo_str,
               data = crime_anal_norm['범죄'],
               columns = [crime_anal_norm.index, crime_anal_norm['범죄']],
               fill_color = 'PuRd', #PuRd, YlGnBu
               key_on = 'feature.id')

for n in crime_anal_raw.index:
    folium.CircleMarker([crime_anal_raw['lat'][n], crime_anal_raw['lng'][n]], 
                        radius = crime_anal_raw['검거'][n]*10, 
                        color='#3186cc', fill_color='#3186cc', fill=True).add_to(final_map8)
final_map8.save("C:/Users/student/Desktop/python/P_4week/final_map8.html")    
final_map8
profile
문돌이의 고군분투 개발 공부

0개의 댓글