[샌프란시스코 범죄 분류] - 2
지난 글에서 위경도에 따른 분포를 산점도 그래프를 통해 시각화해보았다.
마지막으로 Dates 칼럼을 확인해보고 피처 엔지니어링, 모델 구축까지 진행해볼 것이다.
EDA
날짜 데이터를 다룰 때는 데이터의 유형을 반드시 파악해야 한다.
# Dates 확인
train_df['Dates'].dtypes
데이터 타입에서 O는 Object를 의미하며, 문자열 데이터로 저장된 것을 확인할 수 있다.
이러한 데이터를 다룰 때는 문자열을 날짜 데이터로 변환해야 한다.
# 날짜 데이터로 변환
train_df['Dates'] = pd.to_datetime(train_df['Dates'])
train_df['Dates'].sample(1)
데이터를 변환한 뒤 샘플로 1개의 데이터를 출력해 보면 날짜 데이터로 변환된 것을 확인할 수 있다.
주어진 날짜 데이터를 다시 연월일 시분초로 쪼개어 추출할 수 있다. 하루 평균 몇 건의 범죄가 발생되는지, 연도별과 월별 범죄 발생 건수 추이를 확인할 수 있다.
그렇다면 데이터를 쪼개어 추출해보자.
# 날짜 데이터 쪼개기(연월일 시분초)
train_df['Date'] = train_df.Dates.dt.date
train_df['Hour'] = train_df.Dates.dt.hour
daily_df = train_df.groupby('Date').count().iloc[:, 0]
daily_df
2003-01-06일부터 2015-05-13까지의 데이터를 확인할 수 있다. 이번에는 데이터를 활용해 일자별 시계열 데이터와 일 평균 데이터 건수를 확인해보자.
# 일자별 시계열 데이터와 일 평균 데이터 건수 확인
col = sns.color_palette()
fig, ax = plt.subplots(ncols = 2, figsize = (16, 6))
sns.lineplot(daily_df.index, daily_df.values, ax = ax[0])
ax[0].set_title(
'Number of incidents per day', fontdict = {'fontsize' : 16})
ax[0].set_ylabel('Incidents')
sns.kdeplot(data = daily_df, shade = True, ax = ax[1])
ax[1].axvline(x = daily_df.median(), ymax = 0.95, linestyle = '-', color = col[1])
ax[1].annotate(
'Median : '+str(daily_df.median()),
xy = (daily_df.median(), 0.004),
xytext = (200, 0.005),
arrowprops = dict(arrowstyle = '->', color = col[1], shrinkB = 10))
ax[1].set_title(
'Disstribution of number of incidents per day', fontdict = {'fontsize' : 16})
ax[1].set_xlabel('Incidents')
ax[1].set_ylabel('Density')
일자별 시계열 데이터와 일평균 범죄 건수 분포를 시각화해봤다. 일자별 시계열 데이터에 2008년 부근에 이상치가 존재하는 것을 알 수 있다. 범죄 발생 건수가 가장 적은 날과 가장 많은 날을 조회해 확인해보자.
# 범죄 발생 건수가 가장 적은 날과 가장 많은 날을 조회
print('The minimum Incident day :\n', daily_df[daily_df.values == min(daily_df.values)])
print('The maximum Incident day :\n', daily_df[daily_df.values == max(daily_df.values)])
2007-12-16일에 범죄 건수가 2건이 나타났다. 이는 기록이 잘못된 건지, 실제로 그날 범죄가 2건만 발생했는지 확인할 수가 없다.
이번에는 하루 시간대별 범죄 발생을 확인해보자.
# 하루 시간대별 범죄 발생 확인
hour_df = train_df.groupby('Hour').count().iloc[:, 0]
print('The minimum Incident hour :\n', hour_df[hour_df.values == min(hour_df.values)])
print('The maximum Incident hour :\n', hour_df[hour_df.values == max(hour_df.values)])
fig, ax = plt.subplots(figsize = (16, 5))
ax = sns.lineplot(x = hour_df.index, y = hour_df.values)
plt.title('Total Incidents per Hour')
plt.show()
그래프로 확인해보면 5시가 범죄 발생 건수가 가장 적게 기록되고, 18시가 범죄 발생 건수가 가장 많이 기록됐다.
지금까지 EDA를 진행해봤고 본격적으로 모델링 작업에 들어가보자.
피처 엔지니어링
피처 엔지니어링을 진행할 때는 가급적 하나의 함수로 만드는 것이 좋다. 공통으로 처리해야 하는 내용과 각 데이터마다 전처리의 성질이 조금 다른 것을 구분하는 것이 좋다.
코드를 통해 확인해보자.
# 피처 엔지니어링
def feature_engineering(data):
data.drop_duplicates(inplace = True)
data['Dates'] = pd.to_datetime(data['Dates'])
data['Date'] = pd.to_datetime(data['Dates'].dt.date)
data['DayOfWeek'] = data['Dates'].dt.weekday
data['Month'] = data['Dates'].dt.month
data['Year'] = data['Dates'].dt.year
data['Hour'] = data['Dates'].dt.hour
data.drop(columns = ['Dates', 'Date', 'Address'], inplace = True)
if 'Id' in data.columns:
data.drop(['Id'], axis = 1, inplace = True)
else:
data.drop(['Descript', 'Resolution'], axis = 1, inplace = True)
return data
중복 값 제거와 날짜 데이터 변환은 Train 데이터와 Test 데이터 모두 공통적인 내용이다. 그런데 Train 데이터에만 Descript, Resolution 변수가 존재하는 것을 확인할 수 있고, Test 데이터에만 Id 값이 주어진 것을 확인할 수 있다. 따라서 if 조건문으로 관련 변수를 삭제한다. 그리고 데이터를 확인해보자.
train = feature_engineering(train)
test = feature_engineering(test)
train.shape, test.shape
제대로 삭제된 것을 확인할 수 있다.
Tree-Model 기반의 모형으로 진행해 별도의 Scaling은 진행하지 않을 것이다. 종속 변수에 해당하는 PdDistrict 지역에 Label Encoding을 적용해보자.
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
le1 = LabelEncoder()
train['PdDistrict'] = le1.fit_transform(train['PdDistrict'])
test['PdDistrict'] = le1.fit_transform(test['PdDistrict'])
le2 = LabelEncoder()
X = train.drop(columns = ['Category'])
y = le2.fit_transform(train['Category'])
모델링 - 기본 모델 구축
기본 모델을 구축해보자. 다중 분류 모형이기 때문에 다중 분류에 해당하는 파라미터를 설정한다.
# 모델링
import lightgbm as lgb
train_set =lgb.Dataset(
X, label = y, categorical_feature = ['PdDistrict'], free_raw_data = False)
params = {'objective' : 'multiclass',
'num_class' : 39}
lgbm_b0 = lgb.train(params, train_set, num_boost_round = 6)
preds = lgbm_b0.predict(test)
preds
예측값을 출력했다. 위 코드에 Params에 있는 값만 계속 변경되고, 교차 검증을 수행한다면 Num_Boost_Round의 값도 변경된다. 예측값을 Submission 형태로 바꿔 제출을 진행하면 된다.
이후 성능을 조금 더 올리기 위해 GridSearch나 RandomSearch 교차 검증을 진행하면 된다.
'Kaggle' 카테고리의 다른 글
[샌프란시스코 범죄 분류] - 1 (0) | 2021.10.05 |
---|---|
[진짜 재난 뉴스 판별기] - 2 (0) | 2021.10.04 |
[진짜 재난 뉴스 판별기] - 1 (0) | 2021.10.01 |
[자전거 수요 예측] - 1 (0) | 2021.09.10 |
[타이타닉 생존자 분류] - 2 (0) | 2021.09.09 |