이번 글은 서울시에 있는 각 구의 범죄 현황과 인구수 대비 얼마나 치안이 좋은지 데이터 분석을 통해 알아보겠습니다.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from matplotlib import rc
rc('font', family='Arial Unicode MS')
crime_raw_data = pd.read_csv('data/02. crime_in_Seoul.csv', thousands=',', encoding = 'euc-kr')
crime_raw_data.head(3)
crime_raw_data['죄종'].unique()
array(['살인', '강도', '강간', '절도', '폭력', nan], dtype=object)
crime_raw_data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 65534 entries, 0 to 65533 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 구분 310 non-null object 1 죄종 310 non-null object 2 발생검거 310 non-null object 3 건수 310 non-null float64 dtypes: float64(1), object(3) memory usage: 2.0+ MB
# 널값 제거
crime_raw_data = crime_raw_data[crime_raw_data['죄종'].notnull()]
crime_raw_data.info()
<class 'pandas.core.frame.DataFrame'> Index: 310 entries, 0 to 309 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 구분 310 non-null object 1 죄종 310 non-null object 2 발생검거 310 non-null object 3 건수 310 non-null float64 dtypes: float64(1), object(3) memory usage: 12.1+ KB
널 값을 제거했더니 데이터의 용량이 매우 줄은 것을 확인 할 수 있습니다.
crime_station = crime_raw_data.pivot_table(
crime_raw_data, index = ['구분'], columns=['죄종','발생검거'], aggfunc=[np.sum]
)
crime_station.head()
# 필요없는 행 삭제
crime_station.columns = crime_station.columns.droplevel([0,1])
import googlemaps
gmaps_key = 'AIzaSyDaMnoVWZaz1CPPRVFb4vl8wv6Mny2Pt_o'
gmaps = googlemaps.Client(key = gmaps_key)
tmp = gmaps.geocode('서울영등포경찰서',language='ko')
print(tmp[0].get('geometry')['location']['lat'])
print(tmp[0].get('geometry')['location']['lng'])
print(tmp[0].get('formatted_address'))
37.5223245 126.9101692 대한민국 서울특별시 영등포구
# 정보를 얻을 열 미리 생성
crime_station['구별'] = np.nan
crime_station['lat'] = np.nan
crime_station['lng'] = np.nan
# 경찰서 위치 정보 채우기
count = 0
for idx, rows in crime_station.iterrows():
if str(idx) == '동작':
station_name = '서울동작구경찰서'
else:
station_name = '서울' + str(idx) + '경찰서'
tmp = gmaps.geocode(station_name, language = 'ko')
tmp[0].get('formatted_address')
tmp_gu = tmp[0].get('formatted_address')
lat = tmp[0].get('geometry')['location']['lat']
lng = tmp[0].get('geometry')['location']['lng']
crime_station.loc[idx, 'lat'] = lat
crime_station.loc[idx, 'lng'] = lng
if str(idx) == '마포':
crime_station.loc[idx, '구별'] = '마포구'
else :
crime_station.loc[idx, '구별'] = tmp_gu.split()[2]
count = count + 1
# 강서구 경찰서의 위치가 임시 위치로 표현되어 현재 위치를 찾아서 수기로 수정하겠습니다.
crime_station.loc['강서', '구별'] = '강서구'
crime_station.loc['강서', 'lat'] =37.5511433
crime_station.loc['강서', 'lng'] =126.8497936
tmp = [
crime_station.columns.get_level_values(0)[n] # 첫번째 줄 인덱스
+ crime_station.columns.get_level_values(1)[n] # 두번째 줄 인덱스 합치기
for n in range(0, len(crime_station.columns.get_level_values(0)))
]
crime_station.columns = tmp
crime_station.head()
crime_anal_gu = pd.pivot_table(crime_station, index = '구별', aggfunc = np.sum)
del crime_anal_gu['lat']
del crime_anal_gu['lng']
# 검거율 열 만들기
target = ['강간검거율','강도검거율','살인검거율','절도검거율','폭력검거율']
num = ['강간검거','강도검거','살인검거','절도검거','폭력검거']
den = ['강간발생','강도발생','살인발생','절도발생','폭력발생']
crime_anal_gu[target] = crime_anal_gu[num].div(crime_anal_gu[den].values) * 100
# 필요없는 열 삭제
del crime_anal_gu['강간검거']
del crime_anal_gu['강도검거']
crime_anal_gu.drop(['살인검거','절도검거','폭력검거'],axis = 1, inplace=True)
# 데이터에서 100을 넘는 값은 100으로 설정
crime_anal_gu[crime_anal_gu[target]>100] =100
검거율이 100프로가 넘는건 발생건수보다 검거수가 더 많아서 그렇습니다. 검거수는 작년이나 제작년에 일어난 범죄들도 해당하기에 발생건수보다 많을 수 있습니다.
crime_anal_gu.rename(
columns = {'강간발생': '강간','강도발생':'강도','살인발생':'살인','절도발생':'절도','폭력발생':'폭력'},
inplace = True
)
crime_anal_gu.head()
# 새로운 데이터 프레임 생성
col = ['살인','강도','강간','절도','폭력']
crime_anal_norm = crime_anal_gu[col] /crime_anal_gu[col].max()
col2 = ['강간검거율','강도검거율','살인검거율','절도검거율','폭력검거율']
crime_anal_norm[col2] = crime_anal_gu[col2]
crime_anal_norm.head()
# 각 구별 인구수와 CCTV 개수 불러오기
result_CCTV = pd.read_csv('data/01. CCTV_result.csv', encoding = 'UTF-8', index_col = '구별')
crime_anal_norm[['인구수','CCTV']] = result_CCTV[['인구수','소계']]
col = ['강간','강도','살인','절도','폭력']
crime_anal_norm['범죄'] = np.mean(crime_anal_norm[col],axis = 1)
crime_anal_norm.head()
col = ['강간검거율','강도검거율','살인검거율','절도검거율','폭력검거율']
crime_anal_norm['검거'] = np.mean(crime_anal_norm[col], axis=1)
crime_anal_norm
import seaborn as sns
get_ipython().run_line_magic('matplotlib','inline')
sns.pairplot(crime_anal_norm, vars =['강도','살인','폭력'],kind = 'reg', height =3);
폭력과 강도 사이의 기울기가 가파른 것으로 보아 둘의 상관관계가 높은 것을 확인할 수 있습니다.
sns.pairplot(
crime_anal_norm, x_vars=['인구수','CCTV'],
y_vars=['살인','강도'],
kind = 'reg',
height =4)
plt.show()
회귀선을 매우 벗어난 이상치들 때문에 정확한 파악이 어렵습니다.
sns.pairplot(
crime_anal_norm, x_vars=['인구수','CCTV'],
y_vars=['살인검거율','폭력검거율'],
kind = 'reg',
height =4)
plt.show()
이것 또한 이상치들 때문에 상관관계 파악이 어렵습니다.
sns.pairplot(
crime_anal_norm, x_vars=['인구수','CCTV'],
y_vars=['절도검거율','강도검거율'],
kind = 'reg',
height =4)
plt.show()
target_col = ['강간검거율','강도검거율','살인검거율','절도검거율','폭력검거율','검거']
crime_anal_norm_sort = crime_anal_norm.sort_values(by='검거', ascending = False)
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()
송파구, 서초구 처럼 치안이 매우 좋을 것 같은 동네도 검거율로만 따졌을 때는 생각보다 높지 않다는 사실을 확인할 수 있습니다.
target_col = ['강간','강도','살인','절도','폭력','범죄']
crime_anal_norm_sort = crime_anal_norm.sort_values(by='범죄', ascending = False)
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()
정규화된 발생건수로 따지면 강남구가 제일 높은것을 확인할 수 있습니다.
import json
import folium
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, tiles='cartodbpositron')
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
# 구별 범죄 총 발생 시각화
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11, tiles='cartodbpositron')
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
발생 건수로만 따지면 인구수가 많은 구가 불리할 수 있습니다. 따라서 인구수 대비 범죄 발생건수를 파악해보겠습니다.
# 구별 인구 대비 범죄 발생 건수
tmp_criminal = crime_anal_norm['범죄'] / crime_anal_norm['인구수']
my_map = folium.Map(location=[37.5502, 126.982], zoom_start = 11, tiles = 'cartodbpositron')
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
col = ['살인검거','강도검거','강간검거','절도검거','폭력검거']
tmp = crime_station[col]/crime_station[col].max()
crime_station['검거'] = np.mean(tmp, axis = 1)
crime_station.head()
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11, tiles='cartodbpositron')
# 각 구별 범죄건수 시각화
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 = '정규화된 범죄 발생 건수'
)
# 각 구별 경찰서 위치 표현 및 구석 건수 시각화
for idx, rows in crime_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['종합'] = np.mean(crime_loc_norm, axis = 1)
crime_loc_norm.head()
crime_loc_norm_sort = crime_loc_norm.sort_values(by='종합', ascending = False)
plt.figure(figsize = (10, 10))
sns.heatmap(crime_loc_norm_sort, annot = True, fmt = 'f', linewidths=0.5, cmap = 'RdPu')
plt.title('범죄와 발생 장소')
plt.show()
- 성추행,폭행은 주로 교통수단에서 빈번하게 일어나는 것을 확인할 수 있습니다.
- 강도는 상점에서 주로 일어나는 것을 확인할 수 있습니다.
- 살인은 단독주택에서 빈번하게 일어나는 것을 확인할 수 있습니다.
- 폭력은 노상에서 빈번하게 일어나는 것을 확인할 수 있습니다.