San Francisco Crime Classification EDA

  • Kaggle 주소: San Francisco Crime Classification
  • 경진대회의 목표: 특정 위치 데이터를 활용해서 특정 시간대, 요일, 관할서에서 일어난 범죄를 분석해서 범죄의 구체적인 종류를 예측하는 것
import pandas as pd
import numpy as np

Load Dataset

train = pd.read_csv('./data/train.csv')
print(train.shape)
train.head()
(878049, 9)
  Dates Category Descript DayOfWeek PdDistrict Resolution Address X Y
0 2015-05-13 23:53:00 WARRANTS WARRANT ARREST Wednesday NORTHERN ARREST, BOOKED OAK ST / LAGUNA ST -122.425892 37.774599
1 2015-05-13 23:53:00 OTHER OFFENSES TRAFFIC VIOLATION ARREST Wednesday NORTHERN ARREST, BOOKED OAK ST / LAGUNA ST -122.425892 37.774599
2 2015-05-13 23:33:00 OTHER OFFENSES TRAFFIC VIOLATION ARREST Wednesday NORTHERN ARREST, BOOKED VANNESS AV / GREENWICH ST -122.424363 37.800414
3 2015-05-13 23:30:00 LARCENY/THEFT GRAND THEFT FROM LOCKED AUTO Wednesday NORTHERN NONE 1500 Block of LOMBARD ST -122.426995 37.800873
4 2015-05-13 23:30:00 LARCENY/THEFT GRAND THEFT FROM LOCKED AUTO Wednesday PARK NONE 100 Block of BRODERICK ST -122.438738 37.771541
train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 878049 entries, 0 to 878048
Data columns (total 9 columns):
Dates         878049 non-null object
Category      878049 non-null object
Descript      878049 non-null object
DayOfWeek     878049 non-null object
PdDistrict    878049 non-null object
Resolution    878049 non-null object
Address       878049 non-null object
X             878049 non-null float64
Y             878049 non-null float64
dtypes: float64(2), object(7)
memory usage: 60.3+ MB

데이터 설명

  • row의 사이즈가 매우 큰 데이터, 실전 데이터 분석에 도움이 될 듯
  • column의 설명
    • Datas: 범죄가 발생한 날짜와 시간
    • Category: 범죄의 세부종류(label colum)
    • Descript: 범죄의 세부정보(test.csv에는 없는 데이터, 참고용으로만 사용되는 데이터)
    • DayOfWeek: 범죄가 발생한 요일(월~일)
    • PdDistrict: 범죄 관할 경찰서 이름(총 10개)
    • Resolution: 범죄의 상태, 범죄 해결 여부(test.csv에는 없는 데이터, 참고용으로만 사용되는 데이터)
    • Address: 범죄가 발생한 구체적인 주소(미국의 주소체계, 샌프란시스코 주소체계에 대한 도메인 지식이 있다면 좋은 예측 모델을 나타낼 수 있음)
    • X: 범죄가 발생한 좌표 정보(경도)
    • Y: 범죄가 발생한 좌표 정보(위도)

Explore

  • 탐험적 데이터 분석(Exploratory Data Analysis, EDA)이 중요
    • 파이썬 패키지는 자유롭게 선택가능
    • 데이터 시각화: matplotlib, seaborn, ggplot, bokeh 등 사용 가능
  • Cross Validation을 활용해 머신러닝 모델의 성능을 측정, 점수로 피드백 받아 모델 개선(Try and Error 방식으로 모델을 점진적으로 개선함)
    • 단, 모델이 개선되거나 개선되지 않았을 때 이유를 명확히 파악하기 어려움
    • 모델 개선의 구체적 전략을 짜는 것이 어려움

Library loading

# matplotlib으로 시각화하는 것을 jupyter notebook에 바로 띄움
%matplotlib inline
import seaborn as sns
import matplotlib.pyplot as plt

Dates Column

  • Dates 형식
    • dtype('O'): 문자열, object
    • to_datetime()으로 변환
train["Dates"].dtypes
dtype('O')
# 문자열(string, object) 데이터 타입을 DateTime 형태로 변환
pd.to_datetime(train["Dates"]).dtypes
dtype('<M8[ns]')
train["Dates"] = pd.to_datetime(train["Dates"])
train.dtypes
Dates         datetime64[ns]
Category              object
Descript              object
DayOfWeek             object
PdDistrict            object
Resolution            object
Address               object
X                    float64
Y                    float64
dtype: object
# pandas에 Dates 컬럼을 년,월,일,시,분,초 새로운 컬럼으로 따로 추가
train["Dates-year"] = train["Dates"].dt.year
train["Dates-month"] = train["Dates"].dt.month
train["Dates-day"] = train["Dates"].dt.day
train["Dates-hour"] = train["Dates"].dt.hour
train["Dates-minute"] = train["Dates"].dt.minute
train["Dates-second"] = train["Dates"].dt.second

print(train.shape)
train[["Dates", "Dates-year", "Dates-month", "Dates-day", "Dates-hour", "Dates-minute", "Dates-second"]].head()
(878049, 15)
  Dates Dates-year Dates-month Dates-day Dates-hour Dates-minute Dates-second
0 2015-05-13 23:53:00 2015 5 13 23 53 0
1 2015-05-13 23:53:00 2015 5 13 23 53 0
2 2015-05-13 23:33:00 2015 5 13 23 33 0
3 2015-05-13 23:30:00 2015 5 13 23 30 0
4 2015-05-13 23:30:00 2015 5 13 23 30 0

시각화

  • 시각화 전 시각화의 예상 결과를 미리 상상하는 것을 추천
  • seaborn의 countplot을 이용하여 시각화
# 여러개의 시각화를 한 화면에 띄움: matplotlib의 subplots 사용
# 2*3으로 총 6개의 시각화를 한 화면에 띄움
figure, ((ax1, ax2, ax3), (ax4, ax5, ax6)) = plt.subplots(nrows=2, ncols=3)

# 시각화 전체 사이즈 지정
figure.set_size_inches(20, 8)

sns.countplot(data=train, x="Dates-year", ax=ax1) # ax1: 좌상단
sns.countplot(data=train, x="Dates-month", ax=ax2)
sns.countplot(data=train, x="Dates-day", ax=ax3)
sns.countplot(data=train, x="Dates-hour", ax=ax4)
sns.countplot(data=train, x="Dates-minute", ax=ax5)
sns.countplot(data=train, x="Dates-second", ax=ax6) # ax6: 우하단
<matplotlib.axes._subplots.AxesSubplot at 0x11dd31ef0>

  • year: 2015년만 범죄 발생수가 작음
  • month: 균등
  • day: 1일은 많고 31일은 범죄 발생수가 적음(31일이 적은 이유는 1,3,5,7,8,10,12월만 존재하기 때문에)
  • hour: 새벽에는 범죄 발생수가 적음
  • minute: 0, 30분에 범죄발생수가 많음(0,30,45,15,20분 순으로 많음)
  • second: 모두 0으로 범죄 발생에 영향 없음

feadback

  1. 초(Dates-second), 분(Dates-minute)는 정확하게 기록되어 있지 않을 가능성이 높다.
  2. 초(Dates-second)는 전부 0으로 기록되어 있기 때문에 범죄를 예측하는데 크게 중요하지 않은 것 같음, feature를 제거하는 것도 좋은 아이디어다.
  3. 시간(Dates-hour)은 범죄를 예측하는 데 큰 영향을 미칠 것으로 보인다.
  4. 분(Dates-minute)은 잘 정리하면 머신러닝 모델이 이 데이터를 이용하는데 도움이 될 것 같다.
    • 0분, 30분은 배제하고 나머지 분들을 사용
train["Dates-minute"].value_counts().head()
0     268950
30    125173
45     40229
15     37904
20     26027
Name: Dates-minute, dtype: int64
train["Dates-hour"].value_counts().tail(10)
10    37806
9     35555
8     32900
1     26173
2     22296
7     22048
3     14014
6     13133
4      9863
5      8637
Name: Dates-hour, dtype: int64

X, Y Column

  • X: 경도, Y: 위도
  • seaborn의 lmplot을 이용해 좌표 정보를 시각화
train[["X", "Y"]]
  X Y
0 -122.425892 37.774599
1 -122.425892 37.774599
2 -122.424363 37.800414
3 -122.426995 37.800873
4 -122.438738 37.771541
... ... ...
878044 -122.459033 37.714056
878045 -122.447364 37.731948
878046 -122.403390 37.780266
878047 -122.390531 37.780607
878048 -122.394926 37.738212

878049 rows × 2 columns

시각화

# seaborn의 lmplot을 사용해 좌표 데이터를 출력
# fil_reg 옵션에 False 지정 (추세선을 그리지 않기 위해 지정, 계산속도 느림)
sns.lmplot(data=train, x="X", y="Y", fit_reg=False)
<seaborn.axisgrid.FacetGrid at 0x11d29f7f0>

train["X"].max(), train["Y"].max()
(-120.5, 90.0)
# outlier 데이터를 가져옴
X_outliers = train["X"] == train["X"].max()
Y_outliers = train["Y"] == train["Y"].max()

outlier = train[X_outliers & Y_outliers]
print(outlier.shape)
outlier.head()
(67, 15)
  Dates Category Descript DayOfWeek PdDistrict Resolution Address X Y Dates-year Dates-month Dates-day Dates-hour Dates-minute Dates-second
660485 2005-12-30 17:00:00 LARCENY/THEFT GRAND THEFT FROM LOCKED AUTO Friday TENDERLOIN NONE 5THSTNORTH ST / OFARRELL ST -120.5 90.0 2005 12 30 17 0 0
660711 2005-12-30 00:34:00 ASSAULT INFLICT INJURY ON COHABITEE Friday BAYVIEW ARREST, BOOKED JAMESLICKFREEWAY HY / SILVER AV -120.5 90.0 2005 12 30 0 34 0
660712 2005-12-30 00:34:00 ASSAULT AGGRAVATED ASSAULT WITH BODILY FORCE Friday BAYVIEW ARREST, BOOKED JAMESLICKFREEWAY HY / SILVER AV -120.5 90.0 2005 12 30 0 34 0
661106 2005-12-29 00:07:00 NON-CRIMINAL AIDED CASE, MENTAL DISTURBED Thursday TENDERLOIN PSYCHOPATHIC CASE 5THSTNORTH ST / EDDY ST -120.5 90.0 2005 12 29 0 7 0
666430 2005-11-30 11:25:00 OTHER OFFENSES TRAFFIC VIOLATION Wednesday TENDERLOIN ARREST, CITED 5THSTNORTH ST / ELLIS ST -120.5 90.0 2005 11 30 11 25 0

아웃라이어를 제외하고 시각화

  • 샌프란시스코 지도 모양으로 시각화됨
# outlier가 아닌(~) 데이터만 색인으로 추출
non_outliers = train[~(X_outliers & Y_outliers)]

# 시각화
sns.lmplot(data=non_outliers, x="X", y="Y", fit_reg=False)
<seaborn.axisgrid.FacetGrid at 0x11d8ab320>

sns.scatterplot(x="X", y="Y", data=non_outliers)
<matplotlib.axes._subplots.AxesSubplot at 0x11d805c18>

feadback

  1. 아웃라이어가 전체 데이터에 0.01%도 되지 않기 때문에 이 아웃라이어를 해결한다고 모델의 성능이 크게 좋아지지는 않는다.
  2. 범죄의 종류마다 좌표 데이터가 차이가 있다면(특정 지역에서 많이 발생하는 범죄 등이 있다면) 이 데이터를 머신러닝 모델에 집어넣으면 성능을 좋게 만들 수 있을 것이다.
  3. 아웃라이어가 존재한다면, 이를 정상적인 값으로 고치거나 아예 아웃라이어를 배제하면 머신러닝 알고리즘이 아웃라이어에 편향되는 현상이 사라질 것이다.

DayOfWeek Column

non_outliers["DayOfWeek"].value_counts()
Friday       133723
Wednesday    129200
Saturday     126804
Thursday     125027
Tuesday      124954
Monday       121573
Sunday       116701
Name: DayOfWeek, dtype: int64

시각화

  • 요일마다 범죄 발생률의 차이가 크지 않으나 금요일(Friday)이 비교적 높고 일요일(Sunday)이 비교적 낮다.
plt.figure(figsize=(12,4))
# seaborn의 경우 자동정렬이 되지 않으므로, 명시적으로 정렬 순서를 정해줌
dwofweek_list = ["Monday", "Tuesday", "Wednesday", 
                 "Thursday", "Friday", "Saturday", "Sunday"]

sns.countplot(x="DayOfWeek", order=dwofweek_list, data=non_outliers)
<matplotlib.axes._subplots.AxesSubplot at 0x11db76e80>

Category Column

  • LARCENY/THEFT: 절도(경범죄)
  • OTHER OFFENSES: 기타
  • NON-CRIMINAL: 범죄가 아님?
  • ASSAULT: 폭행
  • DRUG/NARCOTIC: 마약
  • VEHICLE THEFT: 차량 절도
  • VANDALISM: 기물 파손
  • WARRANTS: 구속
  • BURGLARY: 절도(중범죄)
  • SUSPICIOUS OCC: 의심스러운 행위
  • MISSING PERSON: 실종
  • ROBBERY: 강도
  • FRAUD: 사기
  • FORGERY/COUNTERFEITING: 위조
  • SECONDARY CODES: 2차적 배경들이 있는 범죄
  • WEAPON LAWS: 총기 사고
  • PROSTITUTION: 매춘
  • TRESPASS: 무단 침입
  • STOLEN PROPERTY: 도난
  • SEX OFFENSES FORCIBLE: 강제적 성범죄
  • DISORDERLY CONDUCT: 문란 행위
  • DRUNKENNESS: 과음
  • RECOVERED VEHICLE: 차량 탈취
  • KIDNAPPING: 납치, 유괴
  • DRIVING UNDER THE INFLUENCE: 음주 운전
  • RUNAWAY: 가출
  • LIQUOR LAWS: 음주 및 기타 주류 유통 위반
  • ARSON: 방화
  • LOITERING: 의심스러운 배회 및 방황
  • EMBEZZLEMENT: 횡령
  • SUICIDE: 자살
  • FAMILY OFFENSES: 가정 범죄
  • BAD CHECKS: 부도 수표
  • BRIBERY: 뇌물 수수
  • EXTORTION: 갈취
  • SEX OFFENSES NON FORCIBLE: 비강제적 성범죄
  • GAMBLING: 도박
  • PORNOGRAPHY/OBSCENE MAT: 음란행위, 외설행위
  • TREA: 무단 침입
non_outliers["Category"].value_counts().index
Index(['LARCENY/THEFT', 'OTHER OFFENSES', 'NON-CRIMINAL', 'ASSAULT',
       'DRUG/NARCOTIC', 'VEHICLE THEFT', 'VANDALISM', 'WARRANTS', 'BURGLARY',
       'SUSPICIOUS OCC', 'MISSING PERSON', 'ROBBERY', 'FRAUD',
       'FORGERY/COUNTERFEITING', 'SECONDARY CODES', 'WEAPON LAWS',
       'PROSTITUTION', 'TRESPASS', 'STOLEN PROPERTY', 'SEX OFFENSES FORCIBLE',
       'DISORDERLY CONDUCT', 'DRUNKENNESS', 'RECOVERED VEHICLE', 'KIDNAPPING',
       'DRIVING UNDER THE INFLUENCE', 'RUNAWAY', 'LIQUOR LAWS', 'ARSON',
       'LOITERING', 'EMBEZZLEMENT', 'SUICIDE', 'FAMILY OFFENSES', 'BAD CHECKS',
       'BRIBERY', 'EXTORTION', 'SEX OFFENSES NON FORCIBLE', 'GAMBLING',
       'PORNOGRAPHY/OBSCENE MAT', 'TREA'],
      dtype='object')
non_outliers["Category"].value_counts()
LARCENY/THEFT                  174885
OTHER OFFENSES                 126165
NON-CRIMINAL                    92300
ASSAULT                         76872
DRUG/NARCOTIC                   53971
VEHICLE THEFT                   53772
VANDALISM                       44724
WARRANTS                        42206
BURGLARY                        36754
SUSPICIOUS OCC                  31412
MISSING PERSON                  25989
ROBBERY                         22999
FRAUD                           16679
FORGERY/COUNTERFEITING          10609
SECONDARY CODES                  9985
WEAPON LAWS                      8555
PROSTITUTION                     7484
TRESPASS                         7325
STOLEN PROPERTY                  4539
SEX OFFENSES FORCIBLE            4387
DISORDERLY CONDUCT               4318
DRUNKENNESS                      4280
RECOVERED VEHICLE                3138
KIDNAPPING                       2341
DRIVING UNDER THE INFLUENCE      2268
RUNAWAY                          1946
LIQUOR LAWS                      1903
ARSON                            1513
LOITERING                        1225
EMBEZZLEMENT                     1166
SUICIDE                           508
FAMILY OFFENSES                   491
BAD CHECKS                        406
BRIBERY                           289
EXTORTION                         256
SEX OFFENSES NON FORCIBLE         148
GAMBLING                          146
PORNOGRAPHY/OBSCENE MAT            22
TREA                                6
Name: Category, dtype: int64

시각화

  • LARCENY/THEFT가 제일 많음
# 상위 20개 범죄 종류만 시각화
sns.countplot(y="Category", data=non_outliers, 
              order=non_outliers["Category"].value_counts().index[:20])
<matplotlib.axes._subplots.AxesSubplot at 0x123a44358>

DayOfWeek와 Category관계 확인

  • DayOfWeek 시각화에서 큰 차이를 볼 수 없어서 Category 컬럼도 같이 시각화

1. 첫번째 방법

# 다수개의 차트를 같이 표현하는 방법
figure, axes = plt.subplots(nrows=10, ncols=4)

# 각 차트의 사이즈 지정
figure.set_size_inches(30, 48)

# seaborn의 경우 자동정렬이 되지 않으므로, 명시적으로 정렬 순서를 정해줌
dwofweek_list = ["Monday", "Tuesday", "Wednesday", 
                 "Thursday", "Friday", "Saturday", "Sunday"]

category_list = non_outliers["Category"].value_counts().index

# 반복문 수행
for row in range(10):
    for col in range(4):
        index = row * 4 + col #category의 개수가 39개

        if index < len(category_list):
            ax = axes[row][col]
            category = category_list[index]
            #print(row, col, index, category)

            target = non_outliers[non_outliers["Category"] == category]
            sns.countplot(x="DayOfWeek", order=dwofweek_list, data=target, ax=ax)
            ax.set(xlabel = category)

feadback

  1. 몇몇 특정 범죄에 한해 주중이나 주말일 때 범죄 발생에 차이가 있다.
    • 주말에 많이 발생: 특히 음주운전(DRIVING UNDER THE INFLUENCE), 과음(DRUNKENNESS), 무단침입(TREA)
    • 주중에 많이 발생: 마약(DRUG/NARCOTIC), 절도(BURGLARY), 위조(FORGERY/COUNTERFEITING)
  2. 주말의 경우도 금,토 또는 토,일에 따라 범죄 발생률이 다르다.
  3. 요일(DayOfWeek)가 범죄를 예측하는데 중요한 영향을 끼친다는 것을 알 수 있으므로 예측 모델의 성능을 개선하는데 효과가 있을 것 같다.
  4. 범죄마다 중요한 요일의 타입이 다르며, 대부분 주중과 주말로 나뉘며, 주말도 금,토 또는 토,일에 따라 다르다.
  5. 예외적으로 특정 요일에만 발생하는 범죄가 있다.
    • 무단침입(TREA): 토요일
    • 실종(MISSING PERSON): 금요일

2. 두번째 방법

groups = non_outliers.groupby(['Category', 'DayOfWeek'])['DayOfWeek'].count()
groups
Category     DayOfWeek
ARSON        Friday        220
             Monday        228
             Saturday      220
             Sunday        211
             Thursday      199
                          ... 
WEAPON LAWS  Saturday     1232
             Sunday       1128
             Thursday     1282
             Tuesday      1176
             Wednesday    1252
Name: DayOfWeek, Length: 271, dtype: int64
# Group으로 묶여진 데이터를 matrix형태로 전환
dw_category = groups.unstack(fill_value=0.0)
dw_category
DayOfWeek Friday Monday Saturday Sunday Thursday Tuesday Wednesday
Category              
ARSON 220.0 228.0 220.0 211.0 199.0 235.0 200.0
ASSAULT 11157.0 10559.0 11995.0 12082.0 10246.0 10280.0 10553.0
BAD CHECKS 62.0 66.0 45.0 20.0 66.0 76.0 71.0
BRIBERY 49.0 41.0 42.0 41.0 39.0 37.0 40.0
BURGLARY 6326.0 5262.0 4754.0 4231.0 5350.0 5374.0 5457.0
DISORDERLY CONDUCT 541.0 608.0 624.0 586.0 643.0 657.0 659.0
DRIVING UNDER THE INFLUENCE 352.0 263.0 457.0 442.0 282.0 251.0 221.0
DRUG/NARCOTIC 7420.0 7823.0 6390.0 6143.0 8454.0 8474.0 9267.0
DRUNKENNESS 622.0 513.0 833.0 813.0 496.0 461.0 542.0
EMBEZZLEMENT 211.0 222.0 137.0 108.0 165.0 156.0 167.0
EXTORTION 35.0 30.0 32.0 39.0 40.0 39.0 41.0
FAMILY OFFENSES 82.0 69.0 59.0 54.0 63.0 85.0 79.0
FORGERY/COUNTERFEITING 1757.0 1704.0 1178.0 901.0 1610.0 1752.0 1707.0
FRAUD 2641.0 2533.0 2256.0 1874.0 2351.0 2506.0 2518.0
GAMBLING 35.0 16.0 21.0 12.0 20.0 12.0 30.0
KIDNAPPING 385.0 340.0 355.0 374.0 289.0 306.0 292.0
LARCENY/THEFT 27102.0 23568.0 27214.0 24147.0 24415.0 23955.0 24484.0
LIQUOR LAWS 291.0 188.0 297.0 222.0 248.0 323.0 334.0
LOITERING 139.0 193.0 140.0 155.0 186.0 252.0 160.0
MISSING PERSON 4663.0 3592.0 3752.0 3061.0 3680.0 3655.0 3586.0
NON-CRIMINAL 13983.0 12854.0 14007.0 12972.0 12818.0 12738.0 12928.0
OTHER OFFENSES 18587.0 17783.0 17128.0 15456.0 18459.0 18806.0 19946.0
PORNOGRAPHY/OBSCENE MAT 4.0 3.0 1.0 3.0 5.0 3.0 3.0
PROSTITUTION 1158.0 409.0 850.0 620.0 1547.0 1421.0 1479.0
RECOVERED VEHICLE 494.0 530.0 343.0 307.0 432.0 517.0 515.0
ROBBERY 3384.0 3193.0 3428.0 3284.0 3216.0 3221.0 3273.0
RUNAWAY 344.0 280.0 268.0 205.0 305.0 275.0 269.0
SECONDARY CODES 1392.0 1483.0 1462.0 1543.0 1389.0 1343.0 1373.0
SEX OFFENSES FORCIBLE 620.0 607.0 662.0 690.0 585.0 597.0 626.0
SEX OFFENSES NON FORCIBLE 28.0 23.0 21.0 16.0 15.0 23.0 22.0
STOLEN PROPERTY 647.0 636.0 581.0 583.0 679.0 713.0 700.0
SUICIDE 72.0 75.0 73.0 67.0 89.0 66.0 66.0
SUSPICIOUS OCC 4923.0 4447.0 4155.0 4010.0 4510.0 4516.0 4851.0
TREA 1.0 1.0 2.0 0.0 1.0 1.0 0.0
TRESPASS 1064.0 1080.0 983.0 915.0 1047.0 1114.0 1122.0
VANDALISM 7092.0 5945.0 7326.0 6602.0 5980.0 5852.0 5927.0
VEHICLE THEFT 8612.0 7412.0 8117.0 7504.0 7454.0 7261.0 7412.0
WARRANTS 5926.0 5811.0 5364.0 5280.0 6372.0 6425.0 7028.0
WEAPON LAWS 1302.0 1183.0 1232.0 1128.0 1282.0 1176.0 1252.0

시각화

fg, ax = plt.subplots(figsize=(10, 10))
sns.heatmap(dw_category, linewidths=.5, ax=ax)
<matplotlib.axes._subplots.AxesSubplot at 0x124bf4240>

  • 가장 많은 수의 범죄인 LARCENY/THREFT가 Saturday, Friday순으로 높음
dw_category.loc['LARCENY/THEFT'].sort_values(ascending=False)
DayOfWeek
Saturday     27214.0
Friday       27102.0
Wednesday    24484.0
Thursday     24415.0
Sunday       24147.0
Tuesday      23955.0
Monday       23568.0
Name: LARCENY/THEFT, dtype: float64

PdDistrict Column

non_outliers["PdDistrict"].unique()
array(['NORTHERN', 'PARK', 'INGLESIDE', 'BAYVIEW', 'RICHMOND', 'CENTRAL',
       'TARAVAL', 'TENDERLOIN', 'MISSION', 'SOUTHERN'], dtype=object)
non_outliers["PdDistrict"].value_counts()
SOUTHERN      157174
MISSION       119907
NORTHERN      105284
BAYVIEW        89422
CENTRAL        85455
TENDERLOIN     81794
INGLESIDE      78841
TARAVAL        65592
PARK           49311
RICHMOND       45202
Name: PdDistrict, dtype: int64

시각화

  • 범죄의 관할서(PdDistrict)는 SOUTHERN, MISSION, MORTHERN순으로 높음
sns.countplot(data=non_outliers, y="PdDistrict")
<matplotlib.axes._subplots.AxesSubplot at 0x11ee6bd68>

  • PdDistrict와 X, Y 좌표의 위치를 시각화
    • SOUTHERN, MISSION, MORTHERN의 위치가 몰려있음을 알 수 있음
fg2, ax2 = plt.subplots(figsize=(10,10))
sns.scatterplot(x="X", y="Y", hue="PdDistrict", 
                data=non_outliers, ax=ax2)
<matplotlib.axes._subplots.AxesSubplot at 0x114f939b0>

sns.lmplot(x="X", y="Y", hue="PdDistrict", 
           data=non_outliers, fit_reg=False)
<seaborn.axisgrid.FacetGrid at 0x114c1a3c8>

Category와 PdDistrict 관계 확인

1. 첫번째 방법

# 다수개의 차트를 같이 표현하는 방법
figure, axes = plt.subplots(nrows=10, ncols=4)

# 각 차트의 사이즈 지정
figure.set_size_inches(40, 48)

category_list = non_outliers["Category"].value_counts().index

# 반복문 수행
for row in range(10):
    for col in range(4):
        index = row * 4 + col #category의 개수가 39개

        if index < len(category_list):
            ax = axes[row][col]
            category = category_list[index]
            #print(row, col, index, category)

            target = non_outliers[non_outliers["Category"] == category]
            sns.countplot(y="PdDistrict", data=target, ax=ax)
            ax.set(ylabel = category)

feadback

  1. 관할 경찰서가 곧 지역을 상징하므로, 범죄가 많이 발생하는 지역에 범죄 발생빈도가 몰릴 것 같다.
    • 범죄 발생 빈도가 SOUTHERN은 높고, PARK와 RICHMOND는 범죄 빈도가 낮은 편이다.
  2. 범죄의 종류에 따라 특정 지역에서 자주 발생하는 범죄가 있을 것이다.
  3. 관할 경찰서가 범죄를 예측하는 모델을 개선하는데 효과가 클 것 같다.
    • PdDistrict를 one-hot encoding해서 모델에 집어넣는다.
  4. 관할 경찰서라도 결국에는 지역 데이터를 상징한다. 그러므로 다른 지역 데이터인 주소, 좌표와 엮어줄 수 있는 방법이 있다면 모델의 성능을 개선하는데 큰 도움이 될 것 같다.

2. 두번째 방법

# 범죄 발생빈도가 높은 상위 10개 범죄 데이터
top10_category = non_outliers["Category"].value_counts().index[:10]
df_top10_category = non_outliers.loc[non_outliers["Category"].isin(top10_category)]

sns.catplot(data=df_top10_category, y="PdDistrict", 
            col="Category", col_wrap=5, kind="count",
           order=non_outliers["PdDistrict"].value_counts().index)
<seaborn.axisgrid.FacetGrid at 0x15e142be0>

 

3. 세번째 방법

groups2 = non_outliers.groupby(['Category', 'PdDistrict'])['PdDistrict'].count()
groups2
Category     PdDistrict
ARSON        BAYVIEW        393
             CENTRAL        111
             INGLESIDE      182
             MISSION        145
             NORTHERN       149
                           ... 
WEAPON LAWS  PARK           357
             RICHMOND       327
             SOUTHERN      1128
             TARAVAL        567
             TENDERLOIN     794
Name: PdDistrict, Length: 382, dtype: int64
ca_pdDistrict = groups2.unstack(fill_value=0.0)
ca_pdDistrict
PdDistrict BAYVIEW CENTRAL INGLESIDE MISSION NORTHERN PARK RICHMOND SOUTHERN TARAVAL TENDERLOIN
Category                    
ARSON 393.0 111.0 182.0 145.0 149.0 65.0 103.0 185.0 120.0 60.0
ASSAULT 9855.0 6977.0 8532.0 11149.0 8318.0 3515.0 3202.0 12183.0 5463.0 7678.0
BAD CHECKS 34.0 65.0 32.0 46.0 54.0 15.0 31.0 74.0 36.0 19.0
BRIBERY 56.0 12.0 53.0 66.0 18.0 7.0 8.0 37.0 17.0 15.0
BURGLARY 3929.0 4519.0 3331.0 3745.0 5854.0 2888.0 2696.0 4841.0 3463.0 1488.0
DISORDERLY CONDUCT 219.0 494.0 171.0 1099.0 450.0 271.0 108.0 511.0 162.0 833.0
DRIVING UNDER THE INFLUENCE 179.0 160.0 206.0 357.0 260.0 176.0 319.0 306.0 202.0 103.0
DRUG/NARCOTIC 4498.0 1805.0 2373.0 8757.0 4511.0 2573.0 999.0 9228.0 1531.0 17696.0
DRUNKENNESS 243.0 517.0 190.0 782.0 368.0 374.0 149.0 959.0 259.0 439.0
EMBEZZLEMENT 100.0 180.0 78.0 124.0 131.0 50.0 43.0 275.0 94.0 91.0
EXTORTION 13.0 51.0 29.0 26.0 24.0 8.0 23.0 38.0 36.0 8.0
FAMILY OFFENSES 73.0 28.0 71.0 142.0 12.0 26.0 23.0 42.0 35.0 39.0
FORGERY/COUNTERFEITING 774.0 1144.0 977.0 1281.0 1292.0 518.0 619.0 2345.0 1097.0 562.0
FRAUD 866.0 2344.0 1213.0 1970.0 2055.0 974.0 1137.0 3441.0 1549.0 1130.0
GAMBLING 29.0 31.0 16.0 18.0 10.0 1.0 4.0 17.0 8.0 12.0
KIDNAPPING 313.0 197.0 348.0 312.0 231.0 94.0 104.0 340.0 191.0 211.0
LARCENY/THEFT 10118.0 25058.0 10235.0 18223.0 28628.0 9146.0 9891.0 41841.0 11844.0 9901.0
LIQUOR LAWS 114.0 141.0 106.0 495.0 124.0 149.0 81.0 385.0 122.0 186.0
LOITERING 50.0 70.0 26.0 191.0 193.0 23.0 8.0 429.0 34.0 201.0
MISSING PERSON 5038.0 1348.0 3181.0 3283.0 1956.0 2713.0 1183.0 3064.0 3390.0 833.0
NON-CRIMINAL 6099.0 10940.0 6853.0 12372.0 10240.0 5925.0 5744.0 19745.0 6919.0 7463.0
OTHER OFFENSES 17051.0 8899.0 13201.0 19329.0 12230.0 6184.0 5631.0 21307.0 8612.0 13721.0
PORNOGRAPHY/OBSCENE MAT 2.0 2.0 0.0 3.0 5.0 0.0 1.0 4.0 4.0 1.0
PROSTITUTION 70.0 778.0 26.0 3629.0 1831.0 5.0 24.0 135.0 82.0 904.0
RECOVERED VEHICLE 735.0 178.0 662.0 325.0 273.0 117.0 125.0 326.0 262.0 135.0
ROBBERY 2714.0 1999.0 2788.0 3629.0 2640.0 957.0 787.0 3878.0 1405.0 2202.0
RUNAWAY 265.0 43.0 148.0 298.0 88.0 485.0 94.0 108.0 408.0 9.0
SECONDARY CODES 1662.0 704.0 1300.0 1439.0 999.0 444.0 525.0 1205.0 983.0 724.0
SEX OFFENSES FORCIBLE 387.0 394.0 504.0 718.0 435.0 212.0 216.0 818.0 383.0 320.0
SEX OFFENSES NON FORCIBLE 22.0 7.0 22.0 35.0 9.0 6.0 10.0 17.0 15.0 5.0
STOLEN PROPERTY 397.0 510.0 347.0 631.0 699.0 175.0 206.0 1007.0 231.0 336.0
SUICIDE 37.0 60.0 65.0 72.0 67.0 20.0 42.0 59.0 59.0 27.0
SUSPICIOUS OCC 3905.0 2841.0 3167.0 3808.0 3270.0 1656.0 2191.0 5065.0 2996.0 2513.0
TREA 3.0 1.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0
TRESPASS 636.0 904.0 416.0 1021.0 780.0 290.0 245.0 1456.0 386.0 1191.0
VANDALISM 5355.0 4469.0 5374.0 5294.0 5404.0 2613.0 3180.0 6550.0 4869.0 1616.0
VEHICLE THEFT 7219.0 4210.0 8960.0 7148.0 6291.0 3961.0 4113.0 4724.0 6142.0 1004.0
WARRANTS 4322.0 2777.0 2528.0 6615.0 4595.0 2318.0 1010.0 9101.0 1616.0 7324.0
WEAPON LAWS 1647.0 487.0 1130.0 1329.0 789.0 357.0 327.0 1128.0 567.0 794.0
fg3, ax3 = plt.subplots(figsize=(10, 10))
sns.heatmap(ca_pdDistrict, linewidths=.5, ax=ax3)
<matplotlib.axes._subplots.AxesSubplot at 0x1147b0fd0>

ca_pdDistrict.loc['LARCENY/THEFT'].sort_values(ascending=False)
PdDistrict
SOUTHERN      41841.0
NORTHERN      28628.0
CENTRAL       25058.0
MISSION       18223.0
TARAVAL       11844.0
INGLESIDE     10235.0
BAYVIEW       10118.0
TENDERLOIN     9901.0
RICHMOND       9891.0
PARK           9146.0
Name: LARCENY/THEFT, dtype: float64
soutern = non_outliers[non_outliers["PdDistrict"] == 'SOUTHERN']
soutern.head()
  Dates Category Descript DayOfWeek PdDistrict Resolution Address X Y Dates-year Dates-month Dates-day Dates-hour Dates-minute Dates-second
35 2015-05-13 20:30:00 LARCENY/THEFT GRAND THEFT FROM LOCKED AUTO Wednesday SOUTHERN NONE KING ST / 3RD ST -122.391846 37.778125 2015 5 13 20 30 0
40 2015-05-13 20:23:00 NON-CRIMINAL AIDED CASE, MENTAL DISTURBED Wednesday SOUTHERN NONE 700 Block of MARKET ST -122.405295 37.786307 2015 5 13 20 23 0
41 2015-05-13 20:15:00 LARCENY/THEFT PETTY THEFT FROM LOCKED AUTO Wednesday SOUTHERN NONE 1600 Block of MARKET ST -122.422128 37.773033 2015 5 13 20 15 0
52 2015-05-13 19:30:00 LARCENY/THEFT PETTY THEFT FROM A BUILDING Wednesday SOUTHERN NONE 0 Block of 3RD ST -122.403285 37.787306 2015 5 13 19 30 0
53 2015-05-13 19:30:00 LARCENY/THEFT GRAND THEFT FROM UNLOCKED AUTO Wednesday SOUTHERN NONE 500 Block of BRANNAN ST -122.397616 37.777392 2015 5 13 19 30 0
fg4, ax4 = plt.subplots(figsize=(10,10))
sns.scatterplot(x="X", y="Y", hue="Category", data=soutern, ax=ax4)
<matplotlib.axes._subplots.AxesSubplot at 0x115195dd8>

참고

seaborn 시각화: https://datascienceschool.net/view-notebook/4c2d5ff1caab4b21a708cc662137bc65/