[주제]
- 교통사고 정보로부터 사고위험도(ECLO) 예측 AI 모델 개발
[배경]
- 교통사고의 원인을 규명하고 대책을 세우기 위해 조건에 따른 ECLO를 예측하고자 함
※ ECLO(Equivalent Casualty Loss Only) : 인명피해 심각도
ECLO = 사망자수 10 + 중상자수 5 + 경상자수 3 + 부상자수 1
본 대회에서는 사고의 위험도를 인명피해 심각도로 측정
비산동
에서 자전거 사고 발생 횟수가 많음 통행량이 많은 곳에서 사고 발생 횟수가 많아짐
도로 폭, 레인 수, 혼잡 지역 여부(관광지, 시장, 시내, 교회 등)가 주요 외부 요소가 될 수 있음
# dataset의 각 column에 존재하는 null 값 비율 확인
nullvalues = ((train_df.isna().sum() / train_df.shape[0])*100).reset_index().rename({0:'count'}, axis = 1)
nullvalues.style.highlight_max('count')
# 991건
train_df[train_df['피해운전자 성별'].isna()].groupby('사고유형').count()
차량 단독 사고인 경우, 피해운전자의 정보가 제공되지 않음
# 필터링된 데이터 추출
filtered_data = train_df[(train_df['사상자합계'] > 1) & (train_df['피해운전자 성별'].isna())]
# ECLO 컬럼 시각화 - 바이올린 플롯
plt.figure(figsize=(5, 4))
sns.violinplot(y=filtered_data['ECLO'], color='skyblue')
plt.ylabel('ECLO')
plt.title('사상자가 2명이상인 경우 ECLO')
plt.show()
# 노면상태 별 사고 횟수 시각화 (막대 그래프)
plt.figure(figsize=(10, 6))
ax1 = sns.countplot(x='노면상태', data=filtered_data, color='skyblue')
ax1.set_ylabel('사고 발생 횟수')
# 노면 상태 별 ECLO 평균 시각화 (선 그래프)
ax2 = ax1.twinx()
sns.lineplot(x='노면상태', y='ECLO', data=filtered_data, color='r', ax=ax2)
ax2.set_ylabel('ECLO')
# ECLO 평균 값의 수평선 추가
avg = filtered_data['ECLO'].mean()
ax2.axhline(avg, ls='--', label=f'ECLO 평균 ({round(avg, 2)})', color='g')
ax2.legend()
plt.title('노면상태에 따른 사고 횟수와 ECLO 평균')
plt.show()
# 노면상태 별 사고 횟수 시각화 (막대 그래프)
plt.figure(figsize=(10, 6))
ax1 = sns.countplot(x='노면상태', data=train_df, color='skyblue')
ax1.set_ylabel('사고 발생 횟수')
# 노면 상태 별 ECLO 평균 시각화 (선 그래프)
ax2 = ax1.twinx()
sns.lineplot(x='노면상태', y='ECLO', data=train_df, color='r', ax=ax2)
ax2.set_ylabel('ECLO')
# ECLO 평균 값의 수평선 추가
avg = train_df['ECLO'].mean()
ax2.axhline(avg, ls='--', label=f'ECLO 평균 ({round(avg, 2)})', color='g')
ax2.legend()
plt.title('노면상태에 따른 사고 횟수와 ECLO 평균')
plt.show()
# '도로형태'와 'ECLO'의 관계 시각화
plt.figure(figsize=(10, 6))
ax1 = sns.barplot(x='도로형태', y='ECLO', data=train_df, color='skyblue')
plt.xticks(rotation=45)
plt.title('도로형태별 ECLO')
avg = train_df['ECLO'].mean()
ax1.axhline(avg, ls='--', label=f'ECLO 평균 ({round(avg, 2)})', color='g')
ax1.legend()
plt.show()
import seaborn as sns
import matplotlib.pyplot as plt
victim_car_kind_df = train_df.groupby('피해운전자 차종')[['ECLO']].mean().reset_index()
attacker_car_kind_df = train_df.groupby('가해운전자 차종')[['ECLO']].mean().reset_index()
plt.figure(figsize=(10,6))
sns.lineplot(data = victim_car_kind_df, x = '피해운전자 차종', y = 'ECLO', label='피해운전자 차종')
sns.lineplot(data = attacker_car_kind_df, x = '가해운전자 차종', y = 'ECLO', label='가해운전자 차종')
plt.legend(title='운전자 차종')
plt.title('운전자 차종별 ECLO 평균')
plt.xlabel('운전자 차종')
plt.xticks(rotation=25)
plt.ylabel('ECLO 평균')
plt.show()
plt.figure(figsize=(10, 6))
ax1 = sns.barplot(x='기상상태', y='ECLO', data=train_df, color='skyblue')
plt.xticks(rotation=45)
plt.title('기상상태별 ECLO 평균')
avg = train_df['ECLO'].mean()
ax1.axhline(avg, ls='--', label=f'ECLO 평균 ({round(avg, 2)})', color='g')
ax1.legend()
plt.show()
# ECLO upper bound 구하기
Q1 = train_df['ECLO'].quantile(0.25)
Q3 = train_df['ECLO'].quantile(0.75)
IQR = Q3-Q1
upper_bound = Q3 + 1.5 * IQR
plt.figure(figsize=(6,4))
sns.boxplot(x='ECLO', data=train_df,color='skyblue')
plt.axvline(x=upper_bound, color='r', linestyle='--', label='Upper Bound')
plt.text(upper_bound+12, 0.5, f'Upper Bound: {upper_bound:.2f}', color='r', ha='center', va='bottom')
plt.legend()
plt.title("box plot of ECLO")
plt.show()
# ECLO 10.5를 기준으로 데이터 분리
high_ECLO_df = train_df[train_df['ECLO'] > 10.5]
no_high_ECLO_df = train_df[train_df['ECLO'] <= 10.5]
x_order = ['월요일', '화요일', '수요일', '목요일', '금요일', '토요일', '일요일']
fig, axes = plt.subplots(1, 2, figsize=(6,4))
sns.countplot(x='요일', data=high_ECLO_df,color='skyblue', order=x_order, ax=axes[0])
sns.countplot(x='요일', data=no_high_ECLO_df,color='skyblue', order=x_order, ax=axes[1])
axes[0].tick_params(axis='x', rotation=30)
axes[1].tick_params(axis='x', rotation=30)
axes[0].set_title("ECLO > 10.5")
axes[1].set_title("ECLO <= 10.5 ")
plt.tight_layout()
plt.show()
train_df['주말_주중_구분'] = train_df['요일'].apply(lambda x: '주말' if x in ['토요일','일요일'] else '주중')
attacked_car_df = train_df.groupby(['가해운전자 차종','주말_주중_구분','시군구']).size().reset_index(name='count')
accidnet_cnt_per_attacked_car = attacked_car_df.loc[attacked_car_df.groupby(['가해운전자 차종','주말_주중_구분'])['count'].idxmax()]
accidnet_cnt_per_attacked_car.sort_values(['가해운전자 차종','count'], ascending = False).style.background_gradient()
자전거 사고의 경우 비산동에서 많이 발생
- 비산동은 달서천과 인접해있고 자전거 대여소가 있어 주말에 많이 찾는 공간
plt.figure(figsize=(10, 6))
ax1 = sns.countplot(x='사고시간', hue ='주말_주중_구분', data = train_df, color='skyblue')
ax1.set_ylabel('사고 발생 횟수')
# 노면 상태 별 ECLO 평균 시각화 (선 그래프)
ax2 = ax1.twinx()
ax1 = sns.lineplot(x='사고시간', y='ECLO', data=train_df, color='r', ax=ax2)
ax2.set_ylabel('ECLO')
plt.title('사고 시간대별 사고 횟수와 ECLO 평균')
plt.show()
나이대 가공 코드 생략
import matplotlib.pyplot as plt
# '피해운전자 상해정도'와 '가해운전자 상해정도'가 '사망'인 데이터를 세대별로 그룹화
fatal_by_generation = train_df[train_df['피해운전자 상해정도'] == '사망'].groupby('피해운전자 세대').count()
fatal_by_generation['가해운전자 상해정도'] = train_df[train_df['가해운전자 상해정도'] == '사망'].groupby('가해운전자 세대').count()['가해운전자 상해정도']
# pie plot 그리기
labels = fatal_by_generation.index
sizes1 = fatal_by_generation['피해운전자 상해정도']
sizes2 = fatal_by_generation['가해운전자 상해정도']
colors = sns.color_palette('hls',len(labels))
fig, axs = plt.subplots(1, 2, figsize=(10, 5)) # 1행 2열의 subplot 생성
# 피해운전자 상해정도 pie plot
axs[0].pie(sizes1, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
axs[0].set_title('피해운전자 세대별 사망 비율')
# 가해운전자 상해정도 pie plot
axs[1].pie(sizes2, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
axs[1].set_title('가해운전자 세대별 사망 비율')
plt.axis('equal')
plt.show()
주의
로드링크 정보는 갱신되는 정보로, 되도록이면 조회일시를 맞추어서 보는게 좋음