티스토리 뷰

1. 판다스를 이용하여 데이터 정리하기

 

 

구글에서 '서울시 관서별 5대 범죄 발생 검거 현황'이라는 검색어로 구글에서 검색한 결과를 보면 data.go.kr이라는 주소가 나타난다. 공공데이터 포럼에 가서 원하는 데이터를 다운 받을 수 잇다. 2015년 자료를 가지고 하겠다. 자료의 이름을

crime_in_Seoul로 바꾸고 파이썬 폴더에 저장하자.

pandas를 이용해 데이터 정리하기

 

In:

 

import numpy as np
import pandas as pd

먼저 numpy와 pandas는 항상 import하는 모듈이라고 생각하면 된다. 이제 다운받은 데이터를 pandas로 읽어보겠다. crime_anal_police라는 변수를 저장한다. 그 내용을 보면 서울시 경찰서별로 살인, 강도, 강간, 절도 , 폭력 이라는 5대 범죄

에 대해 발생 건수와 검거 건수를 가지고 있다.

 

In:

 

 

crime_anal_police = pd.read_csv('C:/myPyCode/data/02. crime_in_Seoul.csv', thousands=',', encoding='euc-kr')
crime_anal_police.head()

Out:

 

 

 

 

 

 

 

 

 

 

 

관서명 살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거
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구가 안전한지를 확인하려는 것인데 데이터가 관서별로 되어 있다. 서울시에는 한 구에 하나 혹은 두 군데의 경찰서가 위치해 있고, 구 이름과 다른 경찰서도 있다. 이 경찰서 목록을 소속 구별로 변경하고 싶다. 그러기 위해서는 먼저 경찰서 먼저 경찰서 이름으로 구 정보를 알아야 한다. 양이 많지 않으니 직접 입력해도 되지만 우리는 프로그램으로 접근하겠다.

 

 

 

2. google맵에서 지도 정보를 얻기

 

위치에 대한 검색 결과 중 주소와 위도, 경도 정보를 제공하는 서비스가 구글에 있다. 바로 Google Maps API이다. Google Maps API 홈페이지에 접속해서 Google Maps Geocoding API에 접근하자. Googld Maps는 다양한 API가 있다. 그 중에 주소 검색과 위도, 경도 정보 정도를 얻을 수 있는 GeoCodingAPI를 선택해서 '키 가져오기'로 키를 가져오면 된다.

 

이제 googlemaps를 import해야 한다. 그리고 googlemap.Client로 키를 입력하면 된다.

 

In:

import googlemaps

In:

gmaps_key = " 자신의 키 적기 "
gmaps = googlemaps.Client(key=gmaps_key)

In:

gmaps.geocode('서울중부경찰서', language='ko')

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[{'address_components': [{'long_name': '27',
    'short_name': '27',
    'types': ['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']}]

 

 Google Maps를 사용해서 '서울중부경찰서'라는 단어를 검색해 보자. 그러면 formatted_address 항목에 주소가 나오고 lng와 lat에서 위도 경도 정보도 확인해 볼 수 있다.

 

경찰서의 이름을 관서명이 아닌 **경찰서로 부르기 위해 다음과 같이 해보자.

 

In:

 

 

 

station_name = []

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

station_name

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

['서울중부경찰서',
 '서울종로경찰서',
 '서울남대문경찰서',
 '서울서대문경찰서',
 '서울혜화경찰서',
 '서울용산경찰서',
 '서울성북경찰서',
 '서울동대문경찰서',
 '서울마포경찰서',
 '서울영등포경찰서',
 '서울성동경찰서',
 '서울동작경찰서',
 '서울광진경찰서',
 '서울서부경찰서',
 '서울강북경찰서',
 '서울금천경찰서',
 '서울중랑경찰서',
 '서울강남경찰서',
 '서울관악경찰서',
 '서울강서경찰서',
 '서울강동경찰서',
 '서울종암경찰서',
 '서울구로경찰서',
 '서울서초경찰서',
 '서울양천경찰서',
 '서울송파경찰서',
 '서울노원경찰서',
 '서울방배경찰서',
 '서울은평경찰서',
 '서울도봉경찰서',
 '서울수서경찰서']

구글 maps에 위 코드에 만들어ㄷㄴ 경찰서 이름을 이용해 주소를 받아오자.

 

In:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

station_name = []

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

station_name

station_addreess = []
station_lat = []
station_lng = []

for name in station_name:
    tmp = gmaps.geocode(name, language='ko')
    station_addreess.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'])
   
    print(name + '-->' + tmp[0].get("formatted_address"))

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

서울중부경찰서-->대한민국 서울특별시 중구 을지로동 수표로 27
서울종로경찰서-->대한민국 서울특별시 종로구 종로1.2.3.4가동 율곡로 46
서울남대문경찰서-->대한민국 서울특별시 중구 남대문로5가 한강대로 410
서울서대문경찰서-->대한민국 서울특별시 서대문구 미근동 통일로 113
서울혜화경찰서-->대한민국 서울특별시 종로구 종로1.2.3.4가동 창경궁로 112-16
서울용산경찰서-->대한민국 서울특별시 용산구 원효로1가 25
서울성북경찰서-->대한민국 서울특별시 성북구 삼선동5가 301
서울동대문경찰서-->대한민국 서울특별시 동대문구 청량리동 약령시로21길 29
서울마포경찰서-->대한민국 서울특별시 마포구 아현동 618-1
서울영등포경찰서-->대한민국 서울특별시 영등포구 당산동3가 2-11
서울성동경찰서-->대한민국 서울특별시 성동구 행당1동 왕십리광장로 9
서울동작경찰서-->대한민국 서울특별시 동작구 노량진동 72
서울광진경찰서-->대한민국 서울특별시 광진구 구의1동 자양로 167
서울서부경찰서-->대한민국 서울특별시 은평구 대조동 통일로 757
서울강북경찰서-->대한민국 서울특별시 강북구 번1동 415-15
서울금천경찰서-->대한민국 서울특별시 관악구 신림동 544
서울중랑경찰서-->대한민국 서울특별시 중랑구 신내1동 신내역로3길 40-10
서울강남경찰서-->대한민국 서울특별시 강남구 대치동 998
서울관악경찰서-->대한민국 서울특별시 관악구 봉천동
서울강서경찰서-->대한민국 서울특별시 양천구 신월동 화곡로 73
서울강동경찰서-->대한민국 서울특별시 강동구 성내1동 성내로 57
서울종암경찰서-->대한민국 서울특별시 성북구 종암동 종암로 135
서울구로경찰서-->대한민국 서울특별시 구로구 가마산로 235
서울서초경찰서-->대한민국 서울특별시 서초구 서초3동 반포대로 179
서울양천경찰서-->대한민국 서울특별시 양천구 신정6동 목동동로 99
서울송파경찰서-->대한민국 서울특별시 송파구 가락본동 9
서울노원경찰서-->대한민국 서울특별시 노원구 하계동 노원로 283
서울방배경찰서-->대한민국 서울특별시 서초구 방배2동 방배천로 54
서울은평경찰서-->대한민국 서울특별시 은평구 불광2동 연서로 365
서울도봉경찰서-->대한민국 서울특별시 도봉구 창4동 노해로 403
서울수서경찰서-->대한민국 서울특별시 강남구 개포동 개포로 617

경찰서별 주소를 모두 얻었다.

 

주소를 저장하자.

 

In:

station_addreess

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

['대한민국 서울특별시 중구 을지로동 수표로 27',
 '대한민국 서울특별시 종로구 종로1.2.3.4가동 율곡로 46',
 '대한민국 서울특별시 중구 남대문로5가 한강대로 410',
 '대한민국 서울특별시 서대문구 미근동 통일로 113',
 '대한민국 서울특별시 종로구 종로1.2.3.4가동 창경궁로 112-16',
 '대한민국 서울특별시 용산구 원효로1가 25',
 '대한민국 서울특별시 성북구 삼선동5가 301',
 '대한민국 서울특별시 동대문구 청량리동 약령시로21길 29',
 '대한민국 서울특별시 마포구 아현동 618-1',
 '대한민국 서울특별시 영등포구 당산동3가 2-11',
 '대한민국 서울특별시 성동구 행당1동 왕십리광장로 9',
 '대한민국 서울특별시 동작구 노량진동 72',
 '대한민국 서울특별시 광진구 구의1동 자양로 167',
 '대한민국 서울특별시 은평구 대조동 통일로 757',
 '대한민국 서울특별시 강북구 번1동 415-15',
 '대한민국 서울특별시 관악구 신림동 544',
 '대한민국 서울특별시 중랑구 신내1동 신내역로3길 40-10',
 '대한민국 서울특별시 강남구 대치동 998',
 '대한민국 서울특별시 관악구 봉천동',
 '대한민국 서울특별시 양천구 신월동 화곡로 73',
 '대한민국 서울특별시 강동구 성내1동 성내로 57',
 '대한민국 서울특별시 성북구 종암동 종암로 135',
 '대한민국 서울특별시 구로구 가마산로 235',
 '대한민국 서울특별시 서초구 서초3동 반포대로 179',
 '대한민국 서울특별시 양천구 신정6동 목동동로 99',
 '대한민국 서울특별시 송파구 가락본동 9',
 '대한민국 서울특별시 노원구 하계동 노원로 283',
 '대한민국 서울특별시 서초구 방배2동 방배천로 54',
 '대한민국 서울특별시 은평구 불광2동 연서로 365',
 '대한민국 서울특별시 도봉구 창4동 노해로 403',
 '대한민국 서울특별시 강남구 개포동 개포로 617']

위도와 경도를 얻어보자.

 

In:

station_lat

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[37.5636465,
 37.5755578,
 37.5547584,
 37.5647848,
 37.5718401,
 37.538649,
 37.5897271,
 37.58506149999999,
 37.550814,
 37.5257884,
 37.5617309,
 37.5130685,
 37.542873,
 37.6128611,
 37.6373881,
 37.4814051,
 37.618692,
 37.5094352,
 37.4743789,
 37.5397827,
 37.528511,
 37.6020592,
 37.494931,
 37.4956054,
 37.5165667,
 37.5019065,
 37.6423605,
 37.4815453,
 37.6283597,
 37.6533589,
 37.49349]

 

In:

station_lng

Out:

[126.9895796,
 126.9848674,
 126.9734981,
 126.9667762,
 126.9988562,
 126.966055,
 127.0161318,
 127.0457679,
 126.954028,
 126.901006,
 127.0363806,
 126.9428078,
 127.083821,
 126.9274951,
 127.0273238,
 126.9099508,
 127.1047136,
 127.0669578,
 126.9509748,
 126.8299968,
 127.1268224,
 127.0321577,
 126.886731,
 127.0052504,
 126.8656763,
 127.1271513,
 127.0714027,
 126.9829992,
 126.9287226,
 127.052682,
 127.0772119]

저장했던 주소를 띄어쓰기, 공백으로 나두고 두 번째 단어를 선택해서 구별이라는 컬럼으로 저장하자. 이렇게 하면 관서명에서 google maps의 도움을 받아 구별 이름으로 저장할 수 있게 된다.

 

In:

 

 

 

 

 

 

 

 

 

gu_name = []

for name in station_addreess:
    tmp = name.split()
   
    tmp_gu = [gu for gu in tmp if gu[-1] == '구'][0]
   
    gu_name.append(tmp_gu)
   
crime_anal_police['구별'] = gu_name
crime_anal_police.head()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

관서명 살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거 구별
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 종로구

단, 금천경찰서는 관악구에 위치해 있기 때문에 금천서는 예외 처리를 해야 한다.

 

In:

crime_anal_police[crime_anal_police['관서명']=='금천서']

Out:


관서명
살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거 구별
15 금천서 3 4 6 6 151 122 1567 888 2054 1776 관악구

관악구로 되어 있는 것을 금천구로 변경한다.

 

In:

crime_anal_police.loc[crime_anal_police['관서명']=='금천서', ['구별']] = '금천구'
crime_anal_police[crime_anal_police['관서명']=='금천서']

Out:

관서명 살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거 구별
15 금천서 3 4 6 6 151 122 1567 888 2054 1776 금천구

인터넷에서 자료를 가져오는 경우 이 과정을 단순한 프로그램 오류가 발생할 수 있기에 적당한 이름으로 저장한다.

In:

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

 

In:

crime_anal_police.head()

Out:


관서명
살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거 구별
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. pivot_table을 이용해 데이터 정리

 

In:

 

 

crime_anal_raw = pd.read_csv('../data/02. crime_in_Seoul_include_gu_name.csv',
                             encoding='utf-8')
crime_anal_raw.head()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Unnamed: 0 관서명 살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거 구별
0 0 중부서 2 2 3 2 105 65 1395 477 1355 1170 중구
1 1 종로서 3 3 6 5 115 98 1070 413 1278 1070 종로구
2 2 남대문서 1 0 6 4 65 46 1153 382 869 794 중구
3 3 서대문서 2 2 5 4 154 124 1812 738 2056 1711 서대문구
4 4 혜화서 3 2 5 4 96 63 1114 424 1015 861 종로구

pandas의 pivot_table을 이용해 원 데이터를 관서별에서 구별로 바꾸면 다음과 같다.

 

In:

 

 

 

crime_anal_raw = pd.read_csv('../data/02. crime_in_Seoul_include_gu_name.csv',
                             encoding='utf-8', index_col=0)

crime_anal = pd.pivot_table(crime_anal_raw, index='구별', aggfunc=np.sum)
crime_anal.head()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

강간 검거 강간 발생 강도 검거 강도 발생 살인 검거 살인 발생 절도 검거 절도 발생 폭력 검거 폭력 발생
구별
강남구 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
관악구 221 320 14 12 8 9 827 2706 2642 3298
광진구 220 240 26 14 4 4 1277 3026 2180 2625

추가로 각 범죄별 검거율을 계산하고, 검거 건수는 검거율로 대체할 수 있기때문에 삭제하기로 하자. 그리고 pandas의 결과표를 한 화면에 표현하자.

In:

 

 

 

 

 

 

 

 

 

 

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

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


강간 발생 강도 발생 살인 발생 절도 발생 폭력 발생 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 449 21 13 3850 4284 77.728285 85.714286 76.923077 42.857143 86.484594
강동구 156 6 4 2366 2712 78.846154 133.333333 75.000000 33.347422 82.890855
강북구 153 14 7 1434 2649 82.352941 92.857143 114.285714 43.096234 88.637222
관악구 320 12 9 2706 3298 69.062500 116.666667 88.888889 30.561715 80.109157
광진구 240 14 4 3026 2625 91.666667 185.714286 100.000000 42.200925 83.047619

그런데 검거율이 100이 넘는 숫자들이 보인다. 그 이유는 전년도에 발생한 건수에 대한 검거가 포함되기 때문이다. 그냥 이 숫자들은 100으로 처리해 주자.

 

In:

 

 

 

 

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

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

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

강간 발생 강도 발생 살인 발생 절도 발생 폭력 발생 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 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
관악구 320 12 9 2706 3298 69.062500 100.000000 88.888889 30.561715 80.109157
광진구 240 14 4 3026 2625 91.666667 100.000000 100.000000 42.200925 83.047619

발생이라는 단어를 삭제해 보자.

 

In:

 

 

 

 

 

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

Out:

 

 

 

 

 

 

 

 

 

 

강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 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
관악구 320 12 9 2706 3298 69.062500 100.000000 88.888889 30.561715 80.109157
광진구 240 14 4 3026 2625 91.666667 100.000000 100.000000 42.200925 83.047619

 

 

3. 데이터 표현을 다듬기

 

 

위 표를 보면 강도, 살인 사건은 두 자릿수인데, 절도와 폭력은 네 자릿수이다. 물론 숫자 그 자체로도 중요하지만 각각을 비슷한 범위에 놓고 비교하는 것이 편리할 수 잇다. 개념적으로는 살인 1건과 절도 1천 건이 같은 비중은 아니겠지만, 각 항목의 최대값을 1로 두면 추후 범죄 발생 건수를 종합적으로 비교할 때 변리할 것이다. 그래서 강간, 강도, 살인, 절도, 폭력에 대해 각 컬럼별로 '정규화'하도록 하겠다.

 

In:

 

 

 

 

 

 

 

 

from sklearn import preprocessing

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

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 1.000000 0.941176 0.916667 0.953472 0.661386 77.728285 85.714286 76.923077 42.857143 86.484594
강동구 0.155620 0.058824 0.166667 0.445775 0.289667 78.846154 100.000000 75.000000 33.347422 82.890855
강북구 0.146974 0.529412 0.416667 0.126924 0.274769 82.352941 92.857143 100.000000 43.096234 88.637222
관악구 0.628242 0.411765 0.583333 0.562094 0.428234 69.062500 100.000000 88.888889 30.561715 80.109157
광진구 0.397695 0.529412 0.166667 0.671570 0.269094 91.666667 100.000000 100.000000 42.200925 83.047619

다양한 시각화에서 편리해지도록 각 발생건수를 정규화 시켰다.

 

In:

 

 

 

result_CCTV = pd.read_csv('../data/01. CCTV_result.csv', encoding='UTF-8',
                          index_col='구별')
crime_anal_norm[['인구수', 'CCTV']] = result_CCTV[['인구수', '소계']]
crime_anal_norm.head()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV
구별
강남구 1.000000 0.941176 0.916667 0.953472 0.661386 77.728285 85.714286 76.923077 42.857143 86.484594 570500.0 2780
강동구 0.155620 0.058824 0.166667 0.445775 0.289667 78.846154 100.000000 75.000000 33.347422 82.890855 453233.0 773
강북구 0.146974 0.529412 0.416667 0.126924 0.274769 82.352941 92.857143 100.000000 43.096234 88.637222 330192.0 748
관악구 0.628242 0.411765 0.583333 0.562094 0.428234 69.062500 100.000000 88.888889 30.561715 80.109157 525515.0 1496
광진구 0.397695 0.529412 0.166667 0.671570 0.269094 91.666667 100.000000 100.000000 42.200925 83.047619 372164.0 707

서울시 cctv 공부할때 나왔던 결과인 CCTV_result.csv를 읽어서 그 속에서 구별 인구수와 CCTV 개수를 가지고 오겠다.

 

In:

 

 

col = ['강간','강도','살인','절도','폭력']
crime_anal_norm['범죄'] = np.sum(crime_anal_norm[col], axis=1)
crime_anal_norm.head()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV 범죄
구별
강남구 1.000000 0.941176 0.916667 0.953472 0.661386 77.728285 85.714286 76.923077 42.857143 86.484594 570500.0 2780 4.472701
강동구 0.155620 0.058824 0.166667 0.445775 0.289667 78.846154 100.000000 75.000000 33.347422 82.890855 453233.0 773 1.116551
강북구 0.146974 0.529412 0.416667 0.126924 0.274769 82.352941 92.857143 100.000000 43.096234 88.637222 330192.0 748 1.494746
관악구 0.628242 0.411765 0.583333 0.562094 0.428234 69.062500 100.000000 88.888889 30.561715 80.109157 525515.0 1496 2.613667
광진구 0.397695 0.529412 0.166667 0.671570 0.269094 91.666667 100.000000 100.000000 42.200925 83.047619 372164.0 707 2.034438

또한 발생 건수의 합을 '범죄'라는 항목으로 두고 이를 합하겠습니다. 만약 정규화하지 않았다면 몇 천 건의 절도에 수십 건의 살인의 비중이 애매했겠지만 정규화를 통해 그 부분은 유리해졌습니다. 단, 여기서는 범죄의 경중을 논하자는 것이 절대 아닙니다.

 

In:

 

 

col = ['강간검거율','강도검거율','살인검거율','절도검거율','폭력검거율']
crime_anal_norm['검거'] = np.sum(crime_anal_norm[col], axis=1)
crime_anal_norm.head()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV 범죄 검거
구별
강남구 1.000000 0.941176 0.916667 0.953472 0.661386 77.728285 85.714286 76.923077 42.857143 86.484594 570500.0 2780 4.472701 369.707384
강동구 0.155620 0.058824 0.166667 0.445775 0.289667 78.846154 100.000000 75.000000 33.347422 82.890855 453233.0 773 1.116551 370.084431
강북구 0.146974 0.529412 0.416667 0.126924 0.274769 82.352941 92.857143 100.000000 43.096234 88.637222 330192.0 748 1.494746 406.943540
관악구 0.628242 0.411765 0.583333 0.562094 0.428234 69.062500 100.000000 88.888889 30.561715 80.109157 525515.0 1496 2.613667 368.622261
광진구 0.397695 0.529412 0.166667 0.671570 0.269094 91.666667 100.000000 100.000000 42.200925 83.047619 372164.0 707 2.034438 416.915211

그리고 검거율도 해당한다.

 

In:

crime_anal_norm

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV 범죄 검거
구별
강남구 1.000000 0.941176 0.916667 0.953472 0.661386 77.728285 85.714286 76.923077 42.857143 86.484594 570500.0 2780 4.472701 369.707384
강동구 0.155620 0.058824 0.166667 0.445775 0.289667 78.846154 100.000000 75.000000 33.347422 82.890855 453233.0 773 1.116551 370.084431
강북구 0.146974 0.529412 0.416667 0.126924 0.274769 82.352941 92.857143 100.000000 43.096234 88.637222 330192.0 748 1.494746 406.943540
관악구 0.628242 0.411765 0.583333 0.562094 0.428234 69.062500 100.000000 88.888889 30.561715 80.109157 525515.0 1496 2.613667 368.622261
광진구 0.397695 0.529412 0.166667 0.671570 0.269094 91.666667 100.000000 100.000000 42.200925 83.047619 372164.0 707 2.034438 416.915211
구로구 0.515850 0.588235 0.500000 0.435169 0.359423 58.362989 73.333333 75.000000 38.072805 80.877951 447874.0 1561 2.398678 325.647079
금천구 0.141210 0.058824 0.083333 0.172426 0.134074 80.794702 100.000000 100.000000 56.668794 86.465433 255082.0 1015 0.589867 423.928929
노원구 0.273775 0.117647 0.666667 0.386589 0.292268 61.421320 100.000000 100.000000 36.525308 85.530665 569384.0 1265 1.736946 383.477292
도봉구 0.000000 0.235294 0.083333 0.000000 0.000000 100.000000 100.000000 100.000000 44.967074 87.626093 348646.0 485 0.318627 432.593167
동대문구 0.204611 0.470588 0.250000 0.314061 0.250887 84.393064 100.000000 100.000000 41.090358 87.401884 369496.0 1294 1.490147 412.885306
동작구 0.527378 0.235294 0.250000 0.274376 0.100024 48.771930 55.555556 100.000000 35.442359 83.089005 412520.0 1091 1.387071 322.858850
마포구 0.553314 0.529412 0.500000 0.510434 0.353748 84.013605 71.428571 100.000000 31.819961 84.445189 389649.0 574 2.446908 371.707327
서대문구 0.149856 0.000000 0.000000 0.256244 0.134547 80.519481 80.000000 100.000000 40.728477 83.219844 327163.0 962 0.540647 384.467802
서초구 0.838617 0.235294 0.500000 0.537804 0.215654 63.358779 66.666667 75.000000 41.404175 87.453105 450310.0 1930 2.327368 333.882725
성동구 0.069164 0.235294 0.166667 0.186110 0.029558 94.444444 88.888889 100.000000 37.149969 86.538462 311244.0 1062 0.686793 407.021764
성북구 0.138329 0.000000 0.250000 0.247007 0.170726 82.666667 80.000000 100.000000 41.512605 83.974649 461260.0 1464 0.806061 388.153921
송파구 0.340058 0.470588 0.750000 0.744441 0.427524 80.909091 76.923077 90.909091 34.856437 84.552352 667483.0 618 2.732611 368.150048
양천구 0.806916 0.823529 0.666667 1.000000 1.000000 77.486911 84.210526 100.000000 48.469644 83.065080 479978.0 2034 4.297113 393.232162
영등포구 0.556196 1.000000 1.000000 0.650359 0.493024 62.033898 90.909091 85.714286 32.995951 82.894737 402985.0 904 3.699580 354.547963
용산구 0.265130 0.529412 0.250000 0.169004 0.133128 89.175258 100.000000 100.000000 37.700706 83.121951 244203.0 1624 1.346674 409.997915
은평구 0.184438 0.235294 0.083333 0.291139 0.275715 84.939759 66.666667 100.000000 37.147335 86.920467 494388.0 1873 1.069920 375.674229
종로구 0.314121 0.352941 0.333333 0.383510 0.190589 76.303318 81.818182 83.333333 38.324176 84.212822 162820.0 1002 1.574494 363.991830
중구 0.195965 0.235294 0.083333 0.508040 0.174273 65.294118 66.666667 66.666667 33.712716 88.309353 133240.0 671 1.196905 320.649519
중랑구 0.244957 0.352941 0.916667 0.366746 0.321589 79.144385 81.818182 92.307692 38.829040 84.545135 414503.0 660 2.202900 376.644434

그럼 이 결과를 어떻게 하면 효과적으로 인식할 수 있게 시각화할 수 있을까요? 혹시 정렬하는 것을 상상하셨다면, 그건 효과적이지 않습니다.

 

 

 

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

 

 

Seaborn을 이용해보자. 일단 그래프 한글 폰트 문제를 해결한다.

 

In:

 

 

 

 

 

 

 

 

 

 

 

 

import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline

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

In:

crime_anal_norm.head()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV 범죄 검거
구별
강남구 1.000000 0.941176 0.916667 0.953472 0.661386 77.728285 85.714286 76.923077 42.857143 86.484594 570500.0 2780 4.472701 369.707384
강동구 0.155620 0.058824 0.166667 0.445775 0.289667 78.846154 100.000000 75.000000 33.347422 82.890855 453233.0 773 1.116551 370.084431
강북구 0.146974 0.529412 0.416667 0.126924 0.274769 82.352941 92.857143 100.000000 43.096234 88.637222 330192.0 748 1.494746 406.943540
관악구 0.628242 0.411765 0.583333 0.562094 0.428234 69.062500 100.000000 88.888889 30.561715 80.109157 525515.0 1496 2.613667 368.622261
광진구 0.397695 0.529412 0.166667 0.671570 0.269094 91.666667 100.000000 100.000000 42.200925 83.047619 372164.0 707 2.034438 416.915211

pairplot으로 강도, 살인, 폭력 간의 상관관계를 그래프로 보겠다.

 

In:

 

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

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


인구와 CCTV 개수, 그리고 살인과 강도에 대해 조사했다. 전체적인 상관계수는 CCTV와 살인의 관계가 낮을지 몰라도 CCTV가 없을 때 살인이 많이 일어나는 구간이 있다. 즉, CCTV 개수를 기준으로 좌측면에 살인과 강도의 높은 수를 갖는 데이터가 보인다.

 

In:

 

 

sns.pairplot(crime_anal_norm, x_vars=["인구수", "CCTV"],
             y_vars=["살인", "강도"], kind='reg', size=3)
plt.show()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

살인 및 폭력 검거율과 CCTV의 관계가 양의 상관관계가 아니다. 오히려 음의 상관계수도 보인다. 또 인구와 살인 및 폭력 검겨율도 음의 상관관계가 관찰된다.

 

In:

 

 

sns.pairplot(crime_anal_norm, x_vars=["인구수", "CCTV"],
             y_vars=["살인검거율", "폭력검거율"], kind='reg', size=3)
plt.show()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


In:

 

 

sns.pairplot(crime_anal_norm, x_vars=["인구수", "CCTV"],
             y_vars=["절도검거율", "강도검거율"], kind='reg', size=3)
plt.show()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이쯤에서 검거율의 합계인 검거 항목 최고 값을 100으로 한정하고 그 값으로 정렬한 다음, heatmap을 그려보자.

 

In:

 

 

 

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

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 CCTV 범죄 검거
구별
도봉구 0.000000 0.235294 0.083333 0.000000 0.000000 100.000000 100.0 100.0 44.967074 87.626093 348646.0 485 0.318627 100.000000
금천구 0.141210 0.058824 0.083333 0.172426 0.134074 80.794702 100.0 100.0 56.668794 86.465433 255082.0 1015 0.589867 97.997139
광진구 0.397695 0.529412 0.166667 0.671570 0.269094 91.666667 100.0 100.0 42.200925 83.047619 372164.0 707 2.034438 96.375820
동대문구 0.204611 0.470588 0.250000 0.314061 0.250887 84.393064 100.0 100.0 41.090358 87.401884 369496.0 1294 1.490147 95.444250
용산구 0.265130 0.529412 0.250000 0.169004 0.133128 89.175258 100.0 100.0 37.700706 83.121951 244203.0 1624 1.346674 94.776790

In:

 

 

 

 

 

 

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

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

결과를 보면 절도 검거율은 다른 검거율에 비해 낮다는 것을 알 수 있다. 그리고 그래프의 하단으로 갈수록 검거율이 낮은데 그 속에 강남3구 중에 '서초구'가 보인다. 전반적으로 검거율이 우수한 구는 '도봉구', '광진구', '성동구'로 보인다.

 

이번에는 발생 건수의 합으로 정렬해서 heatmap으로 관찰해보겠다.

 

In:

 

 

 

 

 

 

 

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

crime_anal_norm['범죄'] = crime_anal_norm['범죄'] / 5
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()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

발생 건수로 보니 '강남구', '양천구', '영등포구'가 범죄 발생 건수가 높다. 그리고 '송파구'와 '서초구'도 낮다고 볼 수 없다. 그렇다면 정말 강남 3구가 안전하다고 할 수 있을지 의문이 생긴다.

 

일단 여기까지 하고 저장하자.

 

In:

 

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

 

 

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

 

 

이제 지도를 다루는 법도 알았으니 우리가 열심히 다듬은 자료를 시각화해보겠다. 물론 언제나 그렇듯이 난관은 있다. 서울시 구별 경계선을 그릴 수 있는 json파일이 있어야 한다. 이건 우리가 직접 만들 수 없기 때문에 검색을 해보면 Github에서 e9t라는 아이디로 활동하는 Lucy Park 님이 있다.

 

먼저 json파일을 로딩하자.

 

In:

 

 

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

그리고 서울시의 중심의 위도와 경도 정보를 먼저 입력하고 경계선을 그리는데, 컬러맵은 살인 발생 건수로 지정한다.

 

In:

 

 

 

 

 

 

 

 

 

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

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

결과를 보면 살인 발생 건수에서 강남 3구가 안전하다고 보기는 어렵다.

 

특히 강간 발생 건수로 다시 그려보면,

 

In:

 

 

 

 

 

 

 

 

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

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

더더욱 강남 3구가 안전한지 의심이 든다.

 

이전에 만들어둔 범죄 발생 건수 전체에 대해 살펴보면,

 

In:

 

 

 

 

 

 

 

 

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

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

강남3구와 강서구 주변이 범죄 발생 건수가 높은 것으로 나타나고 있다. 그러나 인구수를 고려해야 할 것 같다. 즉 인구 대비 범죄 발생 비율을 알아보는 것이다. 그래서 범죄 전체 발생 건수에 인구수를 나누고 소수점 밑으로 가서 적절한 값을 곱하는 것으로 하겠다.

 

In:

tmp_criminal = crime_anal_norm['살인'] /  crime_anal_norm['인구수'] * 1000000

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

map.choropleth(geo_data = geo_str,
               data = tmp_criminal,
               columns = [crime_anal.index, tmp_criminal],
               fill_color = 'PuRd', #PuRd, YlGnBu
               key_on = 'feature.id')
map

결과를 다시 보면 인구 대비 범죄 발생 건수로 보면 강남 3구가 안전도가 제일 높다고 말할 수는 없을것 같다. 그런데 중구와 종로구의 범죄율이 엄청 높아졌다. 아마 거주 인구는 적고, 관광지여서 그런게 아닐지 추측해본다.

 

 

 

6. 서울시 경찰서별 검거율과 구별 범죄 발생률을 동시에 시각화하기

 

조금 더 진행해서 경찰서별 검거율과 방금 전까지 수행한 범죄 발생율을 동시에 표현하는 게 효과적일 것 같다.

 

검거만 따로 모아주자.

 

In:

 

 

 

 

 

 

 

 

mp_criminal = crime_anal_norm['범죄'] /  crime_anal_norm['인구수'] * 1000000

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

map.choropleth(geo_data = geo_str,
               data = tmp_criminal,
               columns = [crime_anal.index, tmp_criminal],
               fill_color = 'PuRd', #PuRd, YlGnBu
               key_on = 'feature.id')
map

앞서 수집해둔 각 경찰서의 위도와 경도 정보를 이용하자.

 

In:

 

 

 

 

 

 

 

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

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

경찰서의 위치만 먼저 확인했다.

 

이제 검거에 정한한 값(10)을 곱해서 원 넓이를 정하고, 경찰서의 검거율을 원의 넓이로 표현하겠다. 이러면 각 경찰서의 위치에서 넓은 원을 가지면 검거율이 높다고 보면 된다.

 

이제 색상을 붉은 색으로 해서 범죄 발생 건수를 넣어보자.

 

In:

 

 

 

 

 

 

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

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

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(map)
   
map

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

관서명 살인 발생 살인 검거 강도 발생 강도 검거 강간 발생 강간 검거 절도 발생 절도 검거 폭력 발생 폭력 검거 구별 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

 

 

 

 

 

출처: 파이썬으로 데이터 주무르기

'beginner > 파이썬 분석' 카테고리의 다른 글

단일선형회귀분석 ②  (0) 2019.01.28
단일선형회귀분석 ①  (0) 2019.01.24
서울시 구별 CCTV 현황 분석  (0) 2019.01.18
데이터 시각화  (0) 2019.01.17
데이터 분석을 위한 패키지-pandas ②  (0) 2019.01.17
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함