Kwon's 데이터분석기
Daicon 경진대회 - 대구 교통사고 피해 예측 모델링 코드 본문
이전 글에서 대구 교통사고 피해 데이터에 대한 EDA 및 분석을 수행하였다.
이번에는 대구 교통사고 피해 데이터를 가지고 직접 모델링을 수행해보기로 한다.
먼저 필요할 것 같은 라이브러리를 불러온다.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
encoding_type = 'euc-kr'
import warnings
warnings.filterwarnings('ignore')
#데이터셋
train_data = pd.read_csv('./dataset/accident/train.csv')
test_data = pd.read_csv('./dataset/accident/test.csv')
result_data = pd.read_csv('./dataset/accident/sample_submission.csv')
daegu_cctv_data = pd.read_csv('./dataset/accident/daegu_cctv.csv', encoding = encoding_type)
daegu_child_protect_data = pd.read_csv('./dataset/accident/daegu_child_p.csv', encoding = encoding_type)
daegu_lamp_data = pd.read_csv('./dataset/accident/daegu_lamp.csv', encoding = encoding_type)
daegu_parking_area_data = pd.read_csv('./dataset/accident/daegu_parking_area.csv', encoding = encoding_type)
그 후 데이터셋을 불러왔다. 데이터 셋 중 대구 cctv데이터와 어린이보호구역 데이터, 보안등 데이터, 주차지역 데이터도 함께 사용하면 좋을 것 같아서 들고왔다. 이 cctv, 어린이 보호구역, 보안등, 주차지역 데이터는 한글을 사용하는 데이터 이기 때문에 한글을 표현하기 위해 인코딩 타입을 euc-kr을 사용해 들고왔다.
train데이터셋은 이전 EDA를 통해 어떤 변수가 있는지 확인했기 때문에 새로 들고온 데이터에 대해서 info()함수를 통해 무슨 데이터가 존재하는지 확인해본다.
print(daegu_cctv_data.info(), '\n\n')
print(daegu_child_protect_data.info())

print(daegu_lamp_data.info(), '\n\n')
print(daegu_parking_area_data.info())

이러한 데이터로 이루어져 있다.
여기서 우리가 사용할 데이터를 고를 것이다. 보안등 데이터의 경우에는 소재지지번주소와 설치개수를 사용할 것이고, 어린이보호구역 데이터는 어디에 어린이보호구역이 있는지를 사용하기 위해 소재지지번주소를 사용하고, 주차구역 데이터는 주차구역이 어디에 속하는지에 대한 소재지지번주소와 급지구분을 사용할 것이다. cctv데이터를 사용하지 않은 이유는 마지막에 알려줄 것이다.
먼저 보안등 데이터에 대해서 데이터 처리를 수행할 것이다.
cols_lamp = ['설치개수', '소재지지번주소']
light_df = daegu_lamp_data[cols_lamp]
# 수행결과 소재지지번주소는 ~~광역시 ~~구 ~~동 1722-4 라는 형식으로 나온다.
#이러한 형식을 처리하기 위해 소재지지번주소를 4개인 시 구 동 번지로 나눌 수 있도록 한다.
location_pattern = r'(\S+) (\S+) (\S+) (\S+)'
#location_pattern을 통해 소재지지번주소를 도시 구 동 번지로 나눠준다.
light_df[['도시', '구', '동',' 번지']] = light_df['소재지지번주소'].str.extract(location_pattern)
#그 후 필요없는 소재지지번주소를 삭제해준다.
light_df = light_df.drop(columns = ['소재지지번주소'])
# 그다음 train데이터의 시군구에는 번지가 존재하지 않기 때문에 번지 또한 삭제해준다.
light_df = light_df.drop(columns = [' 번지'])
이렇게 데이터를 처리를 해서 도시 구 동을 구해준 다음, 도시 구 동을 기준으로 보안등 설치개수를 합하여 동 별 보안등 설치개수를 구해준다.
light_df = light_df.groupby(['도시', '구', '동']).sum().reset_index()
light_df.reset_index(inplace = True, drop = True)
이렇게 동 별로 보안등의 설치개수를 구할 수 있게 되었다.

그다음은 어린이 보호구역의 위치에 대해서 데이터를 처리해보겠다.
col_protect = ['소재지지번주소']
#어린이 보호구역에 대한 소재지지번주소의 중복된 값을 삭제
child_protect_df = daegu_child_protect_data[col_protect].drop_duplicates()
# 새로운 열을 만들어 어린이보호구역 개수를 1을 넣어줌
child_protect_df['어린이보호구역 개수'] = 1
location_pattern = r'(\S+) (\S+) (\S+) (\S+)'
#어린이 보호구역의 소재지지번주소를 도시 구 동 번지로 분리해줌
child_protect_df[['도시', '구', '동', '번지']] = child_protect_df['소재지지번주소'].str.extract(location_pattern)
# 그중 소재지지번주소와 번지를 삭제해준다.
child_protect_df = child_protect_df.drop(columns = ['소재지지번주소', '번지'])
#그 후 도시, 구, 동을 기준으로 묶고 값을 값을 더해준다.
child_protect_df = child_protect_df.groupby(['도시', '구', '동']).sum().reset_index()
child_protect_df.reset_index(inplace = True, drop = True)
#결과확인
child_protect_df.head()

이렇게 도시, 구, 동 별 어린이 보호구역 개수를 구할 수 있다.
그 다음으로 주차지역에 대한 데이터를 처리해보았다.
# 사용할 소재지지번주소와 급지구분을 들고온다.
col_parking = ['소재지지번주소', '급지구분']
parking_df = daegu_parking_area_data[col_parking]
location_pattern = r'(\S+) (\S+) (\S+) (\S+)'
parking_df[['도시', '구', '동', '번지']] = parking_df['소재지지번주소'].str.extract(location_pattern)
#소재지지번주소와 번지 삭제
parking_df = parking_df.drop(columns = ['소재지지번주소', '번지'])
#도시 구 동 기준으로 데이터의 합을 구함
parking_df = parking_df.groupby(['도시', '구', '동']).sum().reset_index()
parking_df.reset_index(inplace = True, drop = True)
#데이터 확인
parking_df.head()

주차지역에 따른 합을 잘 구한 것 같다.
이제 train, test데이터에 대한 전처리를 수행한다.
import datetime as dt
#사고일시를 datetime형식으로 변환
train_org['사고일시'] = pd.to_datetime(train_org['사고일시'])
test_org['사고일시'] = pd.to_datetime(test_org['사고일시'])
#변환한 데이터에서 사고일시를 년 월 일 시로 분리해서 새로운 열에 넣음
train_org['년'] = train_org['사고일시'].dt.year
train_org['월'] = train_org['사고일시'].dt.month
train_org['일'] = train_org['사고일시'].dt.day
train_org['시'] = train_org['사고일시'].dt.hour
test_org['년'] = test_org['사고일시'].dt.year
test_org['월'] = test_org['사고일시'].dt.month
test_org['일'] = test_org['사고일시'].dt.day
test_org['시'] = test_org['사고일시'].dt.hour
# 그 후 사고일시라는 데이터를 삭제함
train_org = train_org.drop(columns = ['사고일시'])
test_org = test_org.drop(columns = ['사고일시'])
# train, test데이터의 시군구 데이터도 분리한다.
location_pattern_3 = r'(\S+) (\S+) (\S+)'
train_org[['도시', '구', '동']] = train_org['시군구'].str.extract(location_pattern_3)
test_org[['도시', '구', '동']] = test_org['시군구'].str.extract(location_pattern_3)
# 그 후 시군구라는 열의 값을 삭제한다.
train_org = train_org.drop(columns = ['시군구'])
test_org = test_org.drop(columns= ['시군구'])
이렇게 train, test데이터에서의 사고일시와 시군구를 분리하여 새로운 열의 데이터로 만들어준다.
그 다음 데이터 중 도로형태라는 데이터의 모습을 살펴보니 '단일로 - 기타' 와 같은 형식으로 이루어진 것을 확인하였다.
train_org['도로형태'].head()

이 또한 데이터를 처리해준다.
#도로형태를 분리해준다.
road_pattern = r'(.+) - (.+)'
train_org[['도로형태1', '도로형태2']] = train_org['도로형태'].str.extract(road_pattern)
test_org[['도로형태1', '도로형태2']] = test_org['도로형태'].str.extract(road_pattern)
#기존의 도로형태를 삭제해준다.
train_org = train_org.drop(columns = ['도로형태'])
test_org = test_org.drop(columns = ['도로형태'])
그 다음 만들어 두었던 보안등 데이터와 어린이보호구역 데이터, 주차구역에 대한 데이터를 합쳐준다.
# 도시, 구, 동에 맞게 데이터를 합쳐준다.
train_org = pd.merge(train_org, light_df, how='left', on=['도시', '구', '동'])
train_org = pd.merge(train_org, child_protect_df, how='left', on=['도시', '구', '동'])
train_org = pd.merge(train_org, parking_df, how='left', on=['도시', '구', '동'])
test_org = pd.merge(test_org, light_df, how='left', on = ['도시', '구', '동'])
test_org = pd.merge(test_org, child_protect_df, how='left', on = ['도시', '구', '동'])
test_org = pd.merge(test_org, parking_df, how='left', on = ['도시', '구', '동'])
# 데이터 info() 결과
print(train_org.info(), '\n\n')
print(test_org.info())

보안등과 어린이보호구역, 주차구역의 데이터가 잘 합쳐진 것을 확인할 수 있었다.
이제 모델을 학습하기전에 train데이터와 test데이터와 곂치지 않는 데이터는 삭제를 해준다.
test_x = test_org.drop(columns=['ID']).copy()
train_x = train_org.drop(columns=['ID', '사고유형 - 세부분류', '가해운전자 차종','가해운전자 성별','가해운전자 연령','가해운전자 상해정도', '피해운전자 차종', '피해운전자 성별', '피해운전자 연령', '피해운전자 상해정도', '법규위반', 'ECLO']).copy()
train_y = train_org['ECLO'].copy()
이제 데이터를 모델링 하기전 수치형변수와 범주형 변수에 대해서 전처리를 수행할 것이다.
먼저 범주형 변수에 대해서 전처리를 수행할 것이며, 사용할 방법은 타겟인코더이다.
# 만약 타겟 인코더가 안불러와진다면 -> pip install category_encoders
from category_encoders.target_encoder import TargetEncoder
# 범주형 변수를 리스트형태로 들고온다
categorical_features = train_x.select_dtypes(include='object').columns.tolist()
#리스트형태로 범주형 변수가 잘 불러와졌는지 확인
display(categorical_features)
# target_encoder를 사용해 범주형 변수 인코딩 할 때 해당 변수의 값이 특정 타겟변수에 대한
# 평균값으로 인코딩을 수행하기 때문에 타겟변수를 같이 넣어준다.
for i in categorical_features:
te = TargetEncoder(cols=[i])
train_x[i] = te.fit_transform(train_x[i], train_y)
test_x[i] = te.transform(test_x[i])
# 범주형 변수를 타겟 인코더로 변환한 값 확인
display(train_x.head())
display(test_x.head())
train_x값과 test_x 값을 다시 확인해보니 사망자수, 중상자수, 경상자수, 부상자수를 삭제하지 않은 것이 보였다. 이를 삭제해준다

그리고 데이터 내에 결측값이 존재하는 곳이 있기 때문에 결측값에 0을 넣어서 채워준다.
#train, test와 데이터가 맞게 삭제해준다.
train_x = train_x.drop(columns = ['사망자수', '중상자수', '경상자수', '부상자수', 'ECLO'])
# 결측값이 존재하는 곳에 0의값을 채워넣음
train_x.fillna(0, inplace = True)
test_x.fillna(0, inplace = True)
수치형 데이터에 대해서는 따로 손봐줄 것이 없어보이기 때문에 처리를 하지않았다.
대부분의 데이터가 범주형 object 타입이며, 수치형이더라도 전부 년 월 일 시와 같은 데이터였기 때문이다.
그 외에 설치개수나 어린이보호구역 개수, 급지구분은 따로 스케일링을 하지 않았다.
모델 학습을 위한 train, test 분리를 수행한다.
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(train_x, train_y, test_size = 0.2, random_state=42)
데이터를 분리 한 후 RandomForestRegressor를 수행해보도록 한다.
from sklearn.ensemble import RandomForestRegressor
rfr = RandomForestRegressor(n_estimators = 100, random_state = 42)
#랜덤포레스트회귀 모델 생성
rfr.fit(x_train, y_train)
# 평가 기준인 rmsle 점수를 구하기 위한 분리한 x_test의 예측값을 구해준다.
y_pred_rfr = rfr.predict(x_test)
from sklearn.metrics import mean_squared_log_error
# rmsle 값
rmsle_result = np.sqrt(mean_squared_log_error(y_test, y_pred_rfr))
print(rmsle_result)
이렇게 값을 구하니 rmsle값이 0.48541 이 나오게 되었다.
이제 모델을 통해 직접 test_data에 맞는 예측값을 구해본다.
# test_x에 모델을 적용시켜 예측값을 구함
y_predictions = rfr.predict(test_x)
y_predictions
#모델의 예측 결과값을 넣어야하는 파일을 복사후 값을 넣어준다.
result_data1 = result_data.copy()
result_data1['ECLO'] = y_prediction
#결과값
result_data1

이렇게 결과값을 얻을 수 있었다.
이제 이러한 결과값을 저장한다.
result_data1.to_csv('./dataset/resultdata/rfr_model.csv', index = False)
이렇게 데이터 값을 저장한 후 제출을 수행하였다.
*cctv 데이터를 사용하지 않은 이유는 cctv 데이터의 소재지지번주소에는 test데이터셋에 존재하지 않는 지번주소가 존재했기 때문에 모델이 학습 및 예측이 되지 않았기 때문에 사용하지 않았다.
이 방법외에도 많은 방법이 존재하므로 많은 방법을 사용하여 좋은 성능을 내는 모델을 만들어내면 된다.
많은 모델을 사용하고 데이터를 처리를 하면서 한 결과
Private Score기준: 942명 중 202등 (상위 21%) 를 얻게 되었다.
'데이터 모델링' 카테고리의 다른 글
| Daicon 경진대회 - 학습플랫폼 이용자 구독 갱신 예측 모델링(2) (0) | 2023.12.26 |
|---|