[샌프란시스코 범죄 분류] - 1

이번 시간에는 캐글 대회 샌프란시스코 범죄 분류에 대해서 진행해 볼 것이다.

이 대회는 총 39개의 범죄 유형을 예측해야 한다. 즉 다중 분류(Multi-Class Classification) 문제이다.

데이터는 아래의 링크에 있다.

https://www.kaggle.com/c/sf-crime

 

San Francisco Crime Classification | Kaggle

 

www.kaggle.com


데이터 불러오기

데이터를 불러오자.

import pandas as pd

train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
train.shape, test.shape

데이터는 약 88만 개 정도이고 Test 데이터의 개수가 Train 데이터의 개수보다 2개 적은 것을 확인할 수 있다.

그러면 Train 데이터와 Test 데이터의 변수 종류를 확인해보자.

train.info()

test.info()

데이터를 확인해보면 Train 데이터에 있는 Descript, Resolution, Category 데이터는 존재하지 않는다. Category는 종속 변수이지만, Descript, Resolution은 학습할 때 불필요하기 때문에 삭제할 것이다.

그렇다면 변수의 세부 사항을 알아보자.

변수를 확인해보면 크게 시간 데이터와 공간 데이터로 구분되는 것을 확인할 수 있다.

Dates, DayOfWeek 데이터는 시간 관련 데이터이고, PdDistrict, Address, X, Y 데이터는 공간과 관련된 데이터임을 확인할 수 있다. 본 대회는 시공간 데이터를 활용해 범죄의 유형을 예측하는 문제라고 정의할 수 있다.

 

이번에는 제출용 파일 정보를 확인해보자.

sample = pd.read_csv('sampleSubmission.csv')
sample.info()


탐색적 자료 분석(EDA)

데이터를 확인했으니 간단한 시각화나 데이터의 분포를 진행할 것이다.

# 탐색적 자료 분석

train_df = train.copy()
train_df.shape == train.shape

복제한 이유는 EDA를 진행할 때 다양한 형태로 데이터를 가공 및 전처리를 병행하면서 해야 하기 때문이다.

 

그렇다면 결측치와 중복 데이터가 있는지 확인해보자.

# 결측치 확인

def check_na(data):
    isnull_na = (data.isnull().sum() / len(data)) * 100
    data_na = isnull_na.drop(isnull_na[isnull_na == 0].index).sort_values(ascending = False)
    missing_data = pd.DataFrame({'Missing Ratio' : data_na,
                                'Data Type' : data.dtypes[data_na.index]})
    print('결측치 데이터 칼럼과 건수 : \n', missing_data)
    
check_na(train_df)

결측치는 없는 것을 확인할 수 있다.

이번에는 중복 데이터가 있는지 확인해보자.

# 중복 데이터 확인

train_df.duplicated().sum()

총 2,323개의 데이터가 있는 것을 확인한 후, 제거를 진행한다.

# 중복 제거

print('Before :', train_df.shape)
train_df.drop_duplicates(inplace = True)
print('After :', train_df.shape)

그 후에 범죄 내용과 관련된 두 개의 변수를 삭제하자.

# 범죄 내용과 관련된 변수 삭제

train_df.drop(['Descript', 'Resolution'], axis = 1, inplace = True)
train_df.shape

이제 종속 변수에 해당하는 Category 데이터의 상위 5개, 하위 5개를 출력해보자.

# Category 데이터 상위5개
train_df['Category'].value_counts()[:5]

데이터를 확인해보면 절도죄, 폭행, 마약 관련 범죄가 가장 많았음을 확인할 수 있다.

그렇다면 하위 5개도 알아보자.

train_df['Category'].value_counts()[-5:]

Extortion은 강요죄를 의미한다. 강요죄, 성범죄, 도박과 관련된 범죄가 하위에 있음을 확인할 수 있다.

 

Category에 대해 알아본 후 요일별 범죄 발생 건수에 대해서 알아보자.

# 요일별 범죄 발생 건수 확인

temp = train_df.groupby('DayOfWeek').count().iloc[:, 0]
temp = temp.reindex([
    'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
])

print(temp)

샌프란시스코에서는 금요일에 범죄가 가장 많이 일어나는 반면, 일요일에 상대적으로 범죄 발생 건수가 적은 것을 확인할 수 있다. 이것을 시각화로 알아보자.

import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import cm
import numpy as np

fig, ax = plt.subplots(figsize = (10, 6))

ax = sns.barplot(
    temp.index, (temp.values / temp.values.sum()) * 100,
    orient = 'v')
ax.set_title('Incident Rates Rates', fontdict = {'fontsize' : 16})
ax.set_xlabel('Weekday')
ax.set_ylabel('Incidents (%)')

plt.show()

비율적으로 확인해도 금요일에 가장 많은 범죄가 발생하고, 상대적으로 일요일에 적은 숫자가 관측된다. 그렇다면 각각 범죄 유형이 요일마다 어떻게 다른지 확인해보자.

# 범죄 유형이 요일마다 어떻게 다른지 확인

print('DayOfWeek Incident for each category in percentage\n')
for idx, data in enumerate(train_df.groupby(['Category'])['DayOfWeek']):
    print('The Current index is :', data[0])
    print(round(data[1].value_counts() / data[1].count() * 100, 1))
    print()
    if idx == 3:
        break

전체를 확인하기엔 너무 많기 때문에 4개만 확인했다. 전체를 확인하고 싶을 땐 if문을 삭제하면 된다.

 

이번에는 PdDistrict에 대해 알아보자.

# PdDistrict 탐색

temp = train_df.groupby('PdDistrict').count().iloc[:, 0]
print(temp)

확인해보면 SOUTHERN, MISSION, NORTHERN 지역 순으로 범죄 발생 건수가 높은 것을 확인할 수 있다. 그리고 상대적으로 PARK, RICHMOND 지역에서 범죄 발생 건수가 낮음을 확인할 수 있다. 그렇다면 시각화를 통해 확인해보자.

# 실제 어느 정도 차이가 나는지 시각화를 통해 확인

fig, ax = plt.subplots(figsize = (10, 6))

ax = sns.barplot(
    temp.index, (temp.values / temp.values.sum()) * 100,
    orient = 'v')
ax.set_title('Incident Rates by PdDistrict', fontdict = {'fontsize' : 16})
ax.set_xlabel('PdDistrict')
ax.set_ylabel('Incidents (%)')
plt.show()

이렇게 시각화로 보기 편하게 작성할 수 있다.

이번에도 마찬가지로 각각 범죄 유형이 지역마다 어떻게 다른지 이번에도 4개만 뽑아서 확인해보자.

# 범죄 유형이 지역마다 어떻게 다른지 확인

print('DayOfWeek Incident for each category in percentage\n')
for idx, data in enumerate(train_df.groupby(['Category'])['PdDistrict']):
    
    print('The Current index is:', data[0])
    print(round(data[1].value_counts() / data[1].count() * 100, 1))
    print()
    if idx == 3:
        break

각 범죄의 유형마다 지역에서 차지하는 발생 비율도 다른 것을 확인할 수 있다.

이번에는 위도와 경도를 시각해보자.

# 위도 경도 시각화

fig, ax = plt.subplots(figsize = (10, 6))
sns.scatterplot(x = 'X', y = 'Y',
               data = train_df, alpha = 0.01, hue = 'PdDistrict', ax = ax)
plt.show()

시각화를 해보면 이상치가 있는 것을 확인할 수 있다. 그림을 확인해보면 극단적으로 어떤 값을 제거해야 하는지 알 수 있다.

위의 절차를 통해 이상치를 제거하자.

그 후의 산점도를 다시 그려보자.

# 산점도

fig, ax = plt.subplots(figsize = (10, 6))
sns.scatterplot(x = 'X', y = 'Y',
               data = train_df, alpha = 0.01, hue = 'PdDistrict', ax = ax)
plt.show()

산점도를 그려보면 각 지역별로 그룹화가 된 것을 확인할 수 있다. 한 단계 더 깊숙이 탐색하려면 범죄 유형을 세분화해 살펴보는 것도 방법이다. 예시로 LARCENY/THEFT의 값을 추출한 뒤 시각화해보자.

# LARCENY/THEFT 값의 데이터만 추출한 뒤 시각화(세분화)

theft_df = train_df[train_df['Category'] == 'LARCENY/THEFT']
theft_df.shape

fig, ax = plt.subplots(figsize = (10, 6))
sns.scatterplot(x = 'X', y = 'Y',
               data = theft_df, alpha = 0.01, hue = 'PdDistrict', ax = ax)
plt.show()

시각화를 해보면, 어느 지역에서 해당 범죄가 많이 일어나는지 확인할 수 있다. 전체적으로 보면 NORTHERN, SOUTHERN, CENTRAL 관할 지역에서 절도 범죄가 많이 일어나며, 상대적으로 RICHMOND, TARAVAL 지역에서는 덜 일어나는 것을 확인할 수 있다. 만약 다른 범죄 유형을 확인하고 싶다면 소스 코드에서 LARCENY/THEFT 부분을 수정해 확인할 수 있다.


지금까지 간단한 시각화, EDA를 진행해 보았다. 모든 데이터 분석에서는 데이터의 분포를 확인하고, 그것을 시각화하여 데이터를 이해하는 것이 굉장히 중요하다고 생각한다.

 

다음 시간에는 이어지는 EDA와 피처 엔지니어링, 모델 구축까지 진행해 볼 예정이다.

 

'Kaggle' 카테고리의 다른 글

[샌프란시스코 범죄 분류] - 2  (0) 2021.10.06
[진짜 재난 뉴스 판별기] - 2  (0) 2021.10.04
[진짜 재난 뉴스 판별기] - 1  (0) 2021.10.01
[자전거 수요 예측] - 1  (0) 2021.09.10
[타이타닉 생존자 분류] - 2  (0) 2021.09.09
TAGS.

Comments