데이터 사이언스 02 검증- 강남3구의 안전도

게시: by Creative Commons Licence

1. 주장과 가설 - '부자 동네' … 서울 강남 3구 체감안전도 높아

1) 관련 기사

2) 가설 : 체감대로 실제 강남 3구는 안전한가?

  • 가설에 대한 검증 설계

(1) 서울의 자치구 별 범죄율에 대한 데이터 획득하기

(2) 강남 3구와 비교하기 위해 자치구 별로 분류하기

(3) 비교 가능한 지표를 만들기

(4) 지도 시각화 - Seabon, Google Maps, Folium


2. 데이터 얻기

1) 서울특별시 관서별 5대 범죄 현황

import numpy as np
import pandas as pd
police_station = pd.read_csv(
    './data_science/02. crime_in_Seoul.csv', 
    thousands=',',
    encoding='euc-kr'
                            )
police_station.head()
관서명 살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거
0 중부서 2 2 3 2 105 65 1395 477 1355 1170
1 종로서 3 3 6 5 115 98 1070 413 1278 1070
2 남대문서 1 0 6 4 65 46 1153 382 869 794
3 서대문서 2 2 5 4 154 124 1812 738 2056 1711
4 혜화서 3 2 5 4 96 63 1114 424 1015 861

2) 경찰서별 소속 자치구 확인하기

  • 웹 서비스 > Google Maps Geocoding API > API Key:

  • googlemaps

    $ pip install googlemaps
    
import googlemaps
gmaps = googlemaps.Client(key="AIzaSyC58AWrye6Z-2vAdoEVv_Xsf6sk6Vsg_90")
  • 자료 1개의 데이터 구조 파악 : 서울중부경찰서
gmaps.geocode('서울중부경찰서', language='ko')
[{'address_components': [{'long_name': '27',
    'short_name': '27',
    'types': ['political', 'premise']},
   {'long_name': '수표로',
    'short_name': '수표로',
    'types': ['political', 'sublocality', 'sublocality_level_4']},
   {'long_name': '을지로동',
    'short_name': '을지로동',
    'types': ['political', 'sublocality', 'sublocality_level_2']},
   {'long_name': '중구',
    'short_name': '중구',
    'types': ['political', 'sublocality', 'sublocality_level_1']},
   {'long_name': '서울특별시',
    'short_name': '서울특별시',
    'types': ['administrative_area_level_1', 'political']},
   {'long_name': '대한민국',
    'short_name': 'KR',
    'types': ['country', 'political']},
   {'long_name': '100-032',
    'short_name': '100-032',
    'types': ['postal_code']}],
  'formatted_address': '대한민국 서울특별시 중구 을지로동 수표로 27',
  'geometry': {'location': {'lat': 37.5636465, 'lng': 126.9895796},
   'location_type': 'ROOFTOP',
   'viewport': {'northeast': {'lat': 37.56499548029149,
     'lng': 126.9909285802915},
    'southwest': {'lat': 37.56229751970849, 'lng': 126.9882306197085}}},
  'place_id': 'ChIJc-9q5uSifDURLhQmr5wkXmc',
  'types': ['establishment', 'point_of_interest', 'police']}]
# 자료 형태 파악 : 리스트 [ 딕셔너리 { key : value } ] 
tmp = gmaps.geocode('서울종로경찰서', language='ko')[0].get('formatted_address')
tmp.split()
['대한민국', '서울특별시', '종로구', '종로1.2.3.4가동', '율곡로', '46']
# 구 이름 얻기 
[ name for name in tmp.split() if name[-1] == '구'  ]
['종로구']
  • 경찰서 검색어 완성하기
station_name=[]
for name in police_station['관서명']:
    station_name.append( '서울' + str(name[:-1]) + '경찰서' )
station_name[0:5]    
['서울중부경찰서', '서울종로경찰서', '서울남대문경찰서', '서울서대문경찰서', '서울혜화경찰서']
  • 경찰서 검색어로 경찰서 소재 자치구 이름 얻기
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']['lng'])    
station_address[0:5]
['대한민국 서울특별시 중구 을지로동 수표로 27',
 '대한민국 서울특별시 종로구 종로1.2.3.4가동 율곡로 46',
 '대한민국 서울특별시 중구 남대문로5가 한강대로 410',
 '대한민국 서울특별시 서대문구 미근동 통일로 113',
 '대한민국 서울특별시 종로구 종로1.2.3.4가동 창경궁로 112-16']
station_lat[0:5]
[37.5636465, 37.5755578, 37.5547584, 37.5647848, 37.5718401]
station_lng[0:5]
[126.9895796, 126.9848674, 126.9734981, 126.9667762, 126.9988562]
# 구 이름 얻기
gu_name = []
for name in station_address:
    tmp = name.split()
    tmp_gu = [ gu for gu in tmp if gu[-1] == '구'][0]
    gu_name.append(tmp_gu)
    
gu_name[20:-1]    
['강동구', '성북구', '구로구', '서초구', '양천구', '송파구', '노원구', '서초구', '은평구', '도봉구']
# 구 이름을 붙이기
police_station['구별'] = gu_name
police_station.head()
관서명 살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거 구별
0 중부서 2 2 3 2 105 65 1395 477 1355 1170 중구
1 종로서 3 3 6 5 115 98 1070 413 1278 1070 종로구
2 남대문서 1 0 6 4 65 46 1153 382 869 794 중구
3 서대문서 2 2 5 4 154 124 1812 738 2056 1711 서대문구
4 혜화서 3 2 5 4 96 63 1114 424 1015 861 종로구

3. 데이터 가공

1) 자치구 기준으로 범죄 발생/검거 데이터 정리하기

# 중간 데이터 저장
police_station.to_csv(
    './data_ouput/02.crime_in_Seoul_including_gu_name.csv',
    sep=',',
    encoding='utf-8'
)
  • 관서명기준에서 구별기준으로 바꾸기 : Pandaspivot_table기능 이용하기
gu_criminal_raw = pd.read_csv(
    './data_ouput/02.crime_in_Seoul_including_gu_name.csv',
    encoding='utf-8',
    index_col=0
)
gu_criminal_raw.head()
관서명 살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거 구별
0 중부서 2 2 3 2 105 65 1395 477 1355 1170 중구
1 종로서 3 3 6 5 115 98 1070 413 1278 1070 종로구
2 남대문서 1 0 6 4 65 46 1153 382 869 794 중구
3 서대문서 2 2 5 4 154 124 1812 738 2056 1711 서대문구
4 혜화서 3 2 5 4 96 63 1114 424 1015 861 종로구
gu_criminal = pd.pivot_table(
    gu_criminal_raw,
    index='구별',
    aggfunc=np.sum
)
gu_criminal.head()
강간 검거 강간 발생 강도 검거 강도 발생 살인 검거 살인 발생 절도 검거 절도 발생 폭력 검거 폭력 발생
구별
강남구 349 449 18 21 10 13 1650 3850 3705 4284
강동구 123 156 8 6 3 4 789 2366 2248 2712
강북구 126 153 13 14 8 7 618 1434 2348 2649
관악구 343 471 20 18 12 12 1715 4273 4418 5352
광진구 220 240 26 14 4 4 1277 3026 2180 2625

2) 위 데이터의 문제점

  • 살인절도가 서로 비교할 수 있는 범죄인가?
  • 치안(검거)의 관점에서 살인 건수와 절도 건수의 격차 (100배에서 1,000배 차이) 를 어떻게 처리할 것인가?

3) 검거율

gu_criminal['강간검거율'] = gu_criminal['강간 검거']/gu_criminal['강간 발생'] * 100
gu_criminal['강도검거율'] = gu_criminal['강도 검거']/gu_criminal['강도 발생'] * 100
gu_criminal['살인검거율'] = gu_criminal['살인 검거']/gu_criminal['살인 발생'] * 100
gu_criminal['절도검거율'] = gu_criminal['절도 검거']/gu_criminal['절도 발생'] * 100
gu_criminal['폭력검거율'] = gu_criminal['폭력 검거']/gu_criminal['폭력 발생'] * 100

gu_criminal.head()
강간 검거 강간 발생 강도 검거 강도 발생 살인 검거 살인 발생 절도 검거 절도 발생 폭력 검거 폭력 발생 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 349 449 18 21 10 13 1650 3850 3705 4284 77.728285 85.714286 76.923077 42.857143 86.484594
강동구 123 156 8 6 3 4 789 2366 2248 2712 78.846154 133.333333 75.000000 33.347422 82.890855
강북구 126 153 13 14 8 7 618 1434 2348 2649 82.352941 92.857143 114.285714 43.096234 88.637222
관악구 343 471 20 18 12 12 1715 4273 4418 5352 72.823779 111.111111 100.000000 40.135736 82.548580
광진구 220 240 26 14 4 4 1277 3026 2180 2625 91.666667 185.714286 100.000000 42.200925 83.047619
  • 검거율이 100%를 넘는 데이터가 있다.
gu_criminal[ gu_criminal[[ '강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율' ]] > 100 ] = 100
gu_criminal.head()
강간 검거 강간 발생 강도 검거 강도 발생 살인 검거 살인 발생 절도 검거 절도 발생 폭력 검거 폭력 발생 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 349 449 18 21 10 13 1650 3850 3705 4284 77.728285 85.714286 76.923077 42.857143 86.484594
강동구 123 156 8 6 3 4 789 2366 2248 2712 78.846154 100.000000 75.000000 33.347422 82.890855
강북구 126 153 13 14 8 7 618 1434 2348 2649 82.352941 92.857143 100.000000 43.096234 88.637222
관악구 343 471 20 18 12 12 1715 4273 4418 5352 72.823779 100.000000 100.000000 40.135736 82.548580
광진구 220 240 26 14 4 4 1277 3026 2180 2625 91.666667 100.000000 100.000000 42.200925 83.047619
del gu_criminal['강간 검거']
del gu_criminal['강도 검거']
del gu_criminal['살인 검거']
del gu_criminal['절도 검거']
del gu_criminal['폭력 검거'] 
gu_criminal.head()
강간 발생 강도 발생 살인 발생 절도 발생 폭력 발생 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 449 21 13 3850 4284 77.728285 85.714286 76.923077 42.857143 86.484594
강동구 156 6 4 2366 2712 78.846154 100.000000 75.000000 33.347422 82.890855
강북구 153 14 7 1434 2649 82.352941 92.857143 100.000000 43.096234 88.637222
관악구 471 18 12 4273 5352 72.823779 100.000000 100.000000 40.135736 82.548580
광진구 240 14 4 3026 2625 91.666667 100.000000 100.000000 42.200925 83.047619
gu_criminal.rename( columns={'강간 발생' : '강간',
                             '강도 발생' : '강도',
                             '살인 발생' : '살인',
                             '절도 발생' : '절도',
                             '폭력 발생' : '폭력' },
                   inplace=True
                  )
gu_criminal.head()
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 449 21 13 3850 4284 77.728285 85.714286 76.923077 42.857143 86.484594
강동구 156 6 4 2366 2712 78.846154 100.000000 75.000000 33.347422 82.890855
강북구 153 14 7 1434 2649 82.352941 92.857143 100.000000 43.096234 88.637222
관악구 471 18 12 4273 5352 72.823779 100.000000 100.000000 40.135736 82.548580
광진구 240 14 4 3026 2625 91.666667 100.000000 100.000000 42.200925 83.047619

4) 데이터 normalization

gu_criminal_norm = gu_criminal[['강간', '강도', '살인', '절도', '폭력' ]]/gu_criminal[['강간', '강도', '살인', '절도', '폭력' ]].max()
gu_criminal_norm[[ '강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율'  ]]  = \
                                                            gu_criminal[[ '강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율'  ]] 
gu_criminal_norm.head()    
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 0.953291 0.954545 0.928571 0.901006 0.749475 77.728285 85.714286 76.923077 42.857143 86.484594
강동구 0.331210 0.272727 0.285714 0.553709 0.474458 78.846154 100.000000 75.000000 33.347422 82.890855
강북구 0.324841 0.636364 0.500000 0.335596 0.463436 82.352941 92.857143 100.000000 43.096234 88.637222
관악구 1.000000 0.818182 0.857143 1.000000 0.936319 72.823779 100.000000 100.000000 40.135736 82.548580
광진구 0.509554 0.636364 0.285714 0.708168 0.459237 91.666667 100.000000 100.000000 42.200925 83.047619

5) 인구 및 cctv 현황 데이터와 결합하기

# 인구 및 cctv 현황 데이터 읽기 
result_CCTV = pd.read_csv(
                         './data_ouput/01.CCTV_result.csv', 
                          encoding='utf-8',
                          index_col='구별',
                         )
result_CCTV.head()
소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율 CCTV비율 오차
구별
강남구 3238 150.619195 565731.0 560827.0 4904.0 64579.0 0.866843 11.415143 0.572357 1543.390613
양천구 2482 34.671731 476627.0 472730.0 3897.0 54598.0 0.817620 11.455079 0.520743 887.616126
강서구 911 134.793814 607877.0 601391.0 6486.0 75046.0 1.066992 12.345590 0.149866 831.015839
용산구 2096 53.216374 243922.0 228960.0 14962.0 36727.0 6.133928 15.056862 0.859291 763.366194
서초구 2297 63.371266 447177.0 442833.0 4344.0 52738.0 0.971427 11.793540 0.513667 735.741927
# 구별 범죄 데이터에 인구 및 CCTV 현황 데이터 중 '인구수'/'소계' 붙이기
gu_criminal_norm[['인구수', 'CCTV']] = result_CCTV[['인구수', '소계']]
gu_criminal_norm.head()
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV
구별
강남구 0.953291 0.954545 0.928571 0.901006 0.749475 77.728285 85.714286 76.923077 42.857143 86.484594 565731.0 3238
강동구 0.331210 0.272727 0.285714 0.553709 0.474458 78.846154 100.000000 75.000000 33.347422 82.890855 446760.0 1010
강북구 0.324841 0.636364 0.500000 0.335596 0.463436 82.352941 92.857143 100.000000 43.096234 88.637222 329042.0 831
관악구 1.000000 0.818182 0.857143 1.000000 0.936319 72.823779 100.000000 100.000000 40.135736 82.548580 522849.0 2109
광진구 0.509554 0.636364 0.285714 0.708168 0.459237 91.666667 100.000000 100.000000 42.200925 83.047619 372414.0 878

4. 데이터 분석 - 표

1) 살인 사건이 많이 발생하는 구

gu_criminal_norm.sort_values(
    by='살인',
    ascending=False
).head()
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV
구별
영등포구 0.626327 1.000000 1.000000 0.693658 0.624913 62.033898 90.909091 85.714286 32.995951 82.894737 401908.0 1277
강남구 0.953291 0.954545 0.928571 0.901006 0.749475 77.728285 85.714286 76.923077 42.857143 86.484594 565731.0 3238
중랑구 0.397028 0.500000 0.928571 0.499649 0.498076 79.144385 81.818182 92.307692 38.829040 84.545135 414554.0 916
관악구 1.000000 0.818182 0.857143 1.000000 0.936319 72.823779 100.000000 100.000000 40.135736 82.548580 522849.0 2109
송파구 0.467091 0.590909 0.785714 0.758015 0.576452 80.909091 76.923077 90.909091 34.856437 84.552352 668366.0 1081

2) 강도 사건이 많이 발생하는 구

gu_criminal_norm.sort_values(
    by='강도',
    ascending=False
).head()
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV
구별
영등포구 0.626327 1.000000 1.000000 0.693658 0.624913 62.033898 90.909091 85.714286 32.995951 82.894737 401908.0 1277
강남구 0.953291 0.954545 0.928571 0.901006 0.749475 77.728285 85.714286 76.923077 42.857143 86.484594 565731.0 3238
양천구 0.811040 0.863636 0.714286 0.932834 1.000000 77.486911 84.210526 100.000000 48.469644 83.065080 476627.0 2482
관악구 1.000000 0.818182 0.857143 1.000000 0.936319 72.823779 100.000000 100.000000 40.135736 82.548580 522849.0 2109
구로구 0.596603 0.681818 0.571429 0.546454 0.526067 58.362989 73.333333 75.000000 38.072805 80.877951 443288.0 1884

3) 5대 범죄가 많이 발생하는 구

gu_criminal_norm['범죄'] = np.sum( gu_criminal_norm[['강간', '강도', '살인', '절도', '폭력'  ]], axis=1 )
gu_criminal_norm.sort_values(
    by='범죄',
    ascending=False
).head()
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV 범죄
구별
관악구 1.000000 0.818182 0.857143 1.000000 0.936319 72.823779 100.000000 100.000000 40.135736 82.548580 522849.0 2109 4.611644
강남구 0.953291 0.954545 0.928571 0.901006 0.749475 77.728285 85.714286 76.923077 42.857143 86.484594 565731.0 3238 4.486889
양천구 0.811040 0.863636 0.714286 0.932834 1.000000 77.486911 84.210526 100.000000 48.469644 83.065080 476627.0 2482 4.321796
영등포구 0.626327 1.000000 1.000000 0.693658 0.624913 62.033898 90.909091 85.714286 32.995951 82.894737 401908.0 1277 3.944897
송파구 0.467091 0.590909 0.785714 0.758015 0.576452 80.909091 76.923077 90.909091 34.856437 84.552352 668366.0 1081 3.178182

4) 검거율이 높은 구

gu_criminal_norm['검거'] = np.sum( gu_criminal_norm[['강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율'  ]], axis=1 )
gu_criminal_norm.sort_values(
    by='검거',
    ascending=False
).head()
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV 범죄 검거
구별
도봉구 0.216561 0.409091 0.214286 0.248771 0.260147 100.000000 100.000000 100.0 44.967074 87.626093 347338.0 825 1.348855 432.593167
광진구 0.509554 0.636364 0.285714 0.708168 0.459237 91.666667 100.000000 100.0 42.200925 83.047619 372414.0 878 2.599037 416.915211
동대문구 0.367304 0.590909 0.357143 0.463609 0.445766 84.393064 100.000000 100.0 41.090358 87.401884 367769.0 1870 2.224731 412.885306
용산구 0.411890 0.636364 0.357143 0.364381 0.358642 89.175258 100.000000 100.0 37.700706 83.121951 243922.0 2096 2.128419 409.997915
성동구 0.267516 0.409091 0.285714 0.376082 0.282015 94.444444 88.888889 100.0 37.149969 86.538462 312933.0 1327 1.620419 407.021764

5) 데이터 간 상관관계

np.corrcoef( gu_criminal_norm['범죄'], gu_criminal_norm['인구수'] )
array([[ 1.        ,  0.49350266],
       [ 0.49350266,  1.        ]])
np.corrcoef( gu_criminal_norm['검거'], gu_criminal_norm['인구수'] )
array([[ 1.        , -0.01502092],
       [-0.01502092,  1.        ]])
np.corrcoef( gu_criminal_norm['살인'], gu_criminal_norm['폭력'] )
array([[ 1.        ,  0.69507356],
       [ 0.69507356,  1.        ]])

5. 시각화 1 - Heatmap

1) 표 데이터 분석의 문제점

  • 한 눈에 데이터의 경향과 상관관계를 **알 수 없다. **

2) SEABORN 설치

 ➯ pip install seaborn 

Collecting seaborn
  Downloading seaborn-0.8.1.tar.gz (178kB)
    100% |████████████████████████████████| 184kB 1.1MB/s 
Collecting scipy (from seaborn)
  Downloading scipy-1.0.0-cp36-cp36m-manylinux1_x86_64.whl (50.0MB)
    100% |████████████████████████████████| 50.0MB 19kB/s 
Requirement already satisfied: numpy>=1.8.2 in /home/learn/.pyenv/versions/3.6.2/envs/Jupyter/lib/python3.6/site-packages (from scipy->seaborn)
Installing collected packages: scipy, seaborn
  Running setup.py install for seaborn ... done
Successfully installed scipy-1.0.0 seaborn-0.8.1
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline

# 한글 문제 
import matplotlib.font_manager as fm
# 폰트 적용
font_location = '/usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf'
font_name = fm.FontProperties(fname=font_location).get_name()

from matplotlib import rc
rc('font', family=font_name)

3) pairplot으로 데이터를 한 눈에 보기

  • 강도, 살인, 폭력은 서로 상관관계가 있다.
sns.pairplot(
    gu_criminal_norm,
    vars=['강도', '살인', '폭력' ],
    kind='reg',
    size=3
)
plt.show()

png

4) CCTV와의 관계

  • CCTV숫자가 늘어날수록 살인, 강도가 낮아지고 살인검거율/강도검거율이 높아져야 하는데 실제로는,

❶ 살인, 강도가 높아지는 경향이 있으며,

➁ 살인검거율, 강도검거율이 높아지지 않는다.

  • 그러나 절도검거율은 CCTV의 숫자에 비례하여 높아지는 경향이 있다.
sns.pairplot(
    gu_criminal_norm,
    x_vars=[ '인구수', 'CCTV' ],
    y_vars=[ '살인', '강도', '살인검거율', '폭력검거율', '절도검거율' ],
    kind='reg',
    size=3
)
plt.show()

png

5) heatmap으로 데이터 한 눈에 보기

(1) 검거율 기준

  • '검거' 부분 normalize
  • heatmap으로 보기
  • 다른 범죄에 비해 절도는 검거율이 낮다
  • 동작, 서초, 송파, 강남구 모두 검거율이 낮다.
gu_criminal_norm['검거'] = gu_criminal_norm['검거']/gu_criminal_norm['검거'].max() * 100
gu_criminal_norm_sort = gu_criminal_norm.sort_values(
    by='검거',
    ascending=False
)
gu_criminal_norm_sort.head()
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV 범죄 검거
구별
도봉구 0.216561 0.409091 0.214286 0.248771 0.260147 100.000000 100.000000 100.0 44.967074 87.626093 347338.0 825 1.348855 100.00000
광진구 0.509554 0.636364 0.285714 0.708168 0.459237 91.666667 100.000000 100.0 42.200925 83.047619 372414.0 878 2.599037 96.37582
동대문구 0.367304 0.590909 0.357143 0.463609 0.445766 84.393064 100.000000 100.0 41.090358 87.401884 367769.0 1870 2.224731 95.44425
용산구 0.411890 0.636364 0.357143 0.364381 0.358642 89.175258 100.000000 100.0 37.700706 83.121951 243922.0 2096 2.128419 94.77679
성동구 0.267516 0.409091 0.285714 0.376082 0.282015 94.444444 88.888889 100.0 37.149969 86.538462 312933.0 1327 1.620419 94.08881
target_col = [ '강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율', '검거'  ]

plt.figure( figsize=(10, 10) )
sns.heatmap( gu_criminal_norm_sort[target_col], annot=True, fmt='f', linewidths=.5  )
plt.title( '범죄비율 (정규화된 검거율로 정렬)')
plt.show()

png

(2) 범죄 발생 건수 기준

  • 강남, 송파, 서초의 범죄 발생 건수가 다른 구에 비해서 높다.
gu_criminal_norm['범죄'] = gu_criminal_norm['범죄'] / 5

target_col = [ '강간', '강도', '살인', '절도', '폭력', '범죄' ]
gu_criminal_norm_sort = gu_criminal_norm.sort_values(by='범죄', ascending=False)

plt.figure( figsize=(10, 10) )
sns.heatmap( gu_criminal_norm_sort[target_col], annot=True, fmt='f', linewidths=.5  )
plt.title( '범죄비율 (정규화된 발생 건수로 정렬)')
plt.show()

png

6. 소결 및 시각화 2 - Folium

1) 강남 3구의 범죄 안전도는 높지 않다.

2) 중간 결과 저장

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

3) Folium

➯ pip install folium

Collecting folium
  Downloading folium-0.5.0.tar.gz (79kB)
    100% |████████████████████████████████| 81kB 537kB/s 
Collecting branca (from folium)
  Downloading branca-0.2.0-py3-none-any.whl
Requirement already satisfied: jinja2 in /home/learn/.pyenv/versions/3.6.2/envs/Jupyter/lib/python3.6/site-packages (from folium)
Requirement already satisfied: requests in /home/learn/.pyenv/versions/3.6.2/envs/Jupyter/lib/python3.6/site-packages (from folium)
Requirement already satisfied: six in /home/learn/.pyenv/versions/3.6.2/envs/Jupyter/lib/python3.6/site-packages (from folium)
Requirement already satisfied: MarkupSafe>=0.23 in /home/learn/.pyenv/versions/3.6.2/envs/Jupyter/lib/python3.6/site-packages (from jinja2->folium)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /home/learn/.pyenv/versions/3.6.2/envs/Jupyter/lib/python3.6/site-packages (from requests->folium)
Requirement already satisfied: certifi>=2017.4.17 in /home/learn/.pyenv/versions/3.6.2/envs/Jupyter/lib/python3.6/site-packages (from requests->folium)
Requirement already satisfied: urllib3<1.23,>=1.21.1 in /home/learn/.pyenv/versions/3.6.2/envs/Jupyter/lib/python3.6/site-packages (from requests->folium)
Requirement already satisfied: idna<2.7,>=2.5 in /home/learn/.pyenv/versions/3.6.2/envs/Jupyter/lib/python3.6/site-packages (from requests->folium)
Installing collected packages: branca, folium
  Running setup.py install for folium ... done
Successfully installed branca-0.2.0 folium-0.5.0
import folium
import json
# 행정구역 경계정보
geo_path = './data_science/02. skorea_municipalities_geo_simple.json'
geo_str = json.load( open(geo_path, encoding='utf-8' ) )

4) 서울시 자치구별 살인 사건 발생 상황

# 행정구역도
fmap = folium.Map( location=[37.5502, 126.982 ], zoom_start=11, tiles='Stamen Toner' )

fmap.choropleth(
    geo_data = geo_str,
    data = gu_criminal_norm['살인'],
    columns = [ gu_criminal_norm.index, gu_criminal_norm['살인'] ],
    fill_color = 'PuRd', 
    key_on = 'feature.id'
)

fmap

5) 서울시 자치구별 범죄 사건 발생 상황

fmap.choropleth(
    geo_data = geo_str,
    data = gu_criminal_norm['범죄'],
    columns = [ gu_criminal_norm.index, gu_criminal_norm['범죄'] ],
    fill_color = 'PuRd', 
    key_on = 'feature.id'
)

fmap

6) 서울시 자치구별 인구대비 전체 범죄 발생 비율

tmp_criminal = gu_criminal_norm['범죄']/gu_criminal_norm['인구수'] * 1000000

fmap.choropleth(
    geo_data = geo_str,
    data = tmp_criminal,
    columns = [ gu_criminal_norm.index, tmp_criminal ],
    fill_color = 'YlGnBu', 
    key_on = 'feature.id'
)

fmap

7) 범죄 발생과 검거율을 한 화면에 표기하기

(1) 검거 = 각 항목별 검거율의 정규화 값의 합

gu_criminal_raw['lat'] = station_lat
gu_criminal_raw['lng'] = station_lng

tmp = gu_criminal_raw[ [ '살인 검거', '강도 검거', '강간 검거', '절도 검거', '폭력 검거'] ] / \
                       gu_criminal_raw[ [ '살인 검거', '강도 검거', '강간 검거', '절도 검거', '폭력 검거'] ].max()

gu_criminal_raw['검거']   = np.sum(tmp, axis=1)
gu_criminal_raw.head()
관서명 살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거 구별 lat lng 검거
0 중부서 2 2 3 2 105 65 1395 477 1355 1170 중구 37.563646 126.989580 1.275416
1 종로서 3 3 6 5 115 98 1070 413 1278 1070 종로구 37.575558 126.984867 1.523847
2 남대문서 1 0 6 4 65 46 1153 382 869 794 중구 37.554758 126.973498 0.907372
3 서대문서 2 2 5 4 154 124 1812 738 2056 1711 서대문구 37.564785 126.966776 1.978299
4 혜화서 3 2 5 4 96 63 1114 424 1015 861 종로구 37.571840 126.998856 1.198382
  • 경찰서 별 검거 점수에 비례해서 원을 크게 그리도록 함
fmap = folium.Map( location=[37.5502, 126.982 ], zoom_start=11 )

for n in gu_criminal_raw.index:
    folium.CircleMarker(
        [ gu_criminal_raw['lat'][n],  gu_criminal_raw['lng'][n]  ],
        radius=gu_criminal_raw['검거'][n] * 10,
        color='#3186cc',
        fill_color='#3186cc'
    ).add_to(fmap)

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

fmap.choropleth(
    geo_data = geo_str,
    data = gu_criminal_norm['범죄'],
    columns = [ gu_criminal_norm.index, gu_criminal_norm['범죄'] ],
    fill_color = 'PuRd', 
    key_on = 'feature.id'
)


for n in gu_criminal_raw.index:
    folium.CircleMarker(
        [ gu_criminal_raw['lat'][n],  gu_criminal_raw['lng'][n]  ],
        radius=gu_criminal_raw['검거'][n] * 10,
        color='#3186cc',
        fill_color='#3186cc'
    ).add_to(fmap)

fmap

8) 소결

  • 강남3구는 범죄 발생 정도에서 안전하지 않음
  • 위 구의 경찰서의 검거 실적이 높지 않음