티스토리 뷰

2단원 서울시 범죄 현황 분석 - 복사본 (2)

제 2장 서울시 범죄 현황 분석

구성 및 블로깅 진행과정

2-1 데이터 획득하기
2-2 pandas를 이용하여 데이터 정리하기
2-3 지도 정보를 얻을 수 있는 Google Maps
2-4 Google Maps를 이용해서 주소와 위도, 경도 정보 얻기
-------------------------------------------------------
2-5 pandas의 pivot_table 학습하기
2-6 pivot_table을 이용해서 데이터 정리하기
2-7 데이터 표현을 위해 다듬기
-------------------------------------------------------
2-8 좀 더 편리한 시각화 도구 - Seaborn
2-9 범죄 데이터 시각화하기
-------------------------------------------------------
2-10 지도 시각화 도구 - folium
2-11 서울시 범죄율에 대한 지도 시각화
2-12 서울시 경찰서별 검거율과 구별 범죄 발생율을 동시에 시각화하기

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

2-5 pandas의 pivot_table 학습하기

pandas 관련 예제가 많은 chris1610님 공개 데이터 인용 https://github.com/chris1610/pbpython/tree/master/data

In [15]:
import pandas as pd
import numpy as np
In [16]:
# 데이터 불러오기
df= pd.read_excel('pydata/salesfunnel.xlsx')
df.head()
Out[16]:
Account Name Rep Manager Product Quantity Price Status
0 714466 Trantow-Barrows Craig Booker Debra Henley CPU 1 30000 presented
1 714466 Trantow-Barrows Craig Booker Debra Henley Software 1 10000 presented
2 714466 Trantow-Barrows Craig Booker Debra Henley Maintenance 2 5000 pending
3 737550 Fritsch, Russel and Anderson Craig Booker Debra Henley CPU 1 35000 declined
4 146832 Kiehn-Spinka Daniel Hilton Debra Henley CPU 2 65000 won
In [17]:
#Name 항목으로만 정렬하고 싶을때 pivot_table을 사용한다.
pd.pivot_table(df,index=['Name'])
Out[17]:
Account Price Quantity
Name
Barton LLC 740150 35000 1.000000
Fritsch, Russel and Anderson 737550 35000 1.000000
Herman LLC 141962 65000 2.000000
Jerde-Hilpert 412290 5000 2.000000
Kassulke, Ondricka and Metz 307599 7000 3.000000
Keeling LLC 688981 100000 5.000000
Kiehn-Spinka 146832 65000 2.000000
Koepp Ltd 729833 35000 2.000000
Kulas Inc 218895 25000 1.500000
Purdy-Kunde 163416 30000 1.000000
Stokes LLC 239344 7500 1.000000
Trantow-Barrows 714466 15000 1.333333
In [18]:
# Index를 여러 개 지정할 수 있다.
pd.pivot_table(df,index=['Name','Rep','Manager'])
Out[18]:
Account Price Quantity
Name Rep Manager
Barton LLC John Smith Debra Henley 740150 35000 1.000000
Fritsch, Russel and Anderson Craig Booker Debra Henley 737550 35000 1.000000
Herman LLC Cedric Moss Fred Anderson 141962 65000 2.000000
Jerde-Hilpert John Smith Debra Henley 412290 5000 2.000000
Kassulke, Ondricka and Metz Wendy Yule Fred Anderson 307599 7000 3.000000
Keeling LLC Wendy Yule Fred Anderson 688981 100000 5.000000
Kiehn-Spinka Daniel Hilton Debra Henley 146832 65000 2.000000
Koepp Ltd Wendy Yule Fred Anderson 729833 35000 2.000000
Kulas Inc Daniel Hilton Debra Henley 218895 25000 1.500000
Purdy-Kunde Cedric Moss Fred Anderson 163416 30000 1.000000
Stokes LLC Cedric Moss Fred Anderson 239344 7500 1.000000
Trantow-Barrows Craig Booker Debra Henley 714466 15000 1.333333
In [19]:
# 특정 value만 지정해서 나타나도록 할 수도 있다.
pd.pivot_table(df,index=['Manager','Rep'], values=['Price'])
Out[19]:
Price
Manager Rep
Debra Henley Craig Booker 20000.000000
Daniel Hilton 38333.333333
John Smith 20000.000000
Fred Anderson Cedric Moss 27500.000000
Wendy Yule 44250.000000

pivot_table 인덱스에 대해서 value 값이 여러개 있을 경우 평균치가 값으로 지정된다.

In [20]:
# value 값의 합계를 구하고 싶다면, aggfunc 옵션의 np.sum을 사용한다.
pd.pivot_table(df, index=['Manager','Rep'],values=["Price"],aggfunc=np.sum)
Out[20]:
Price
Manager Rep
Debra Henley Craig Booker 80000
Daniel Hilton 115000
John Smith 40000
Fred Anderson Cedric Moss 110000
Wendy Yule 177000
In [21]:
#Index와 value를 지정하고, 각 value에 대해 합과 평균을 표시하도록 한다.
#Nan이 나타나면 0으로 채운다.
pd.pivot_table(df, index=['Manager','Rep','Product'],
              values=['Price','Quantity'],
              aggfunc=[np.sum,np.mean],fill_value=0,margins=True)
Out[21]:
sum mean
Price Quantity Price Quantity
Manager Rep Product
Debra Henley Craig Booker CPU 65000 2 32500 1.000000
Maintenance 5000 2 5000 2.000000
Software 10000 1 10000 1.000000
Daniel Hilton CPU 105000 4 52500 2.000000
Software 10000 1 10000 1.000000
John Smith CPU 35000 1 35000 1.000000
Maintenance 5000 2 5000 2.000000
Fred Anderson Cedric Moss CPU 95000 3 47500 1.500000
Maintenance 5000 1 5000 1.000000
Software 10000 1 10000 1.000000
Wendy Yule CPU 165000 7 82500 3.500000
Maintenance 7000 3 7000 3.000000
Monitor 5000 2 5000 2.000000
All 522000 30 30705 1.764706

2-6 Pivot_tabel을 이용해서 데이터 정리하기

In [22]:
# 2-4에서 저장했던 데이터 불러오기
crime_anal_raw = pd.read_csv('pydata/02. crime_in_Seoul_include_gu_name.csv',
                             encoding='utf-8')
crime_anal_raw.head()
Out[22]:
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 종로구
In [23]:
#index_col=0을 사용하면 저장되어 있는 index를 index로 사용한다.
crime_anal_raw = pd.read_csv('pydata/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()
Out[23]:
강간 검거 강간 발생 강도 검거 강도 발생 살인 검거 살인 발생 절도 검거 절도 발생 폭력 검거 폭력 발생
구별
강남구 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
In [24]:
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[24]:
강간 발생 강도 발생 살인 발생 절도 발생 폭력 발생 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 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이 넘는 숫자는 100으로 처리하자.

In [25]:
con_list = ['강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율']
for column in con_list:
    crime_anal.loc[crime_anal[column] > 100, column] = 100
    
crime_anal.head()
Out[25]:
강간 발생 강도 발생 살인 발생 절도 발생 폭력 발생 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 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 [26]:
# 컬럼명에 발생이라는 단어를 삭제하자.
crime_anal.rename(columns = {'강간 발생':'강간',
                             '강도 발생':'강도',
                             '살인 발생':'살인',
                             '절도 발생':'절도',
                             '폭력 발생':'폭력'}, inplace=True)
crime_anal.head()
Out[26]:
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 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

2-7 데이터 표현을 위해 다듬기

위 데이터를 보면 강도, 살인 사건은 두 자릿수인데, 절도와 폭력은 네 자릿수이다. 물론 숫자 그 자체로도 중요하지만 각각을 비슷한 범위에 놓고 비교하는 것이 편리할 수 있다.
각 항목의 최대값을 1로 두면 추후 범죄 건수를 종합적으로 비교할 때 편리할 것이다. 우리는 이런 방법을 정규화 라고 한다.

In [27]:
# 발생 건수 정규화
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[27]:
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율
구별
강남구 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 [28]:
#1장 학습결과에서 구별 인구수와 CCTV 개수를 가져오자
result_CCTV = pd.read_csv('pydata/01. CCTV_result.csv', encoding='UTF-8',
                         index_col='구별')
crime_anal_norm[['인구수','CCTV']] = result_CCTV[['인구수','소계']]
crime_anal_norm.head()
Out[28]:
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 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
In [29]:
# 각 범죄 발생 건수에 대해 합을 구해 '범죄'라는 컬럼을 만들자 
col = ['강간', '강도', '살인', '절도', '폭력']
crime_anal_norm['범죄'] = np.sum(crime_anal_norm[col],axis=1)
crime_anal_norm.head()
Out[29]:
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 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 [30]:
# 위와 마찬가지로 각 검거율의 합을 구하여 '검거'라는 컬럼을 만들자
col = ['강간검거율','강도검거율','살인검거율','절도검거율','폭력검거율']
crime_anal_norm['검거'] = np.sum(crime_anal_norm[col], axis=1)
crime_anal_norm
Out[30]:
강간 강도 살인 절도 폭력 강간검거율 강도검거율 살인검거율 절도검거율 폭력검거율 인구수 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

이 데이터를 어떻게 하면 효과적으로 인식할 수 있을까??
sort 하면 된다고 생각하는 사람들도 있겠지만 그렇지 않다. 그것보다 효과 적인 방법을 공부해 보자.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
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
글 보관함