본문 바로가기
국비 교육/머신러닝, 딥러닝

[머신러닝] [문제] 비행기 연착 추측 분류

by 육츠 2023. 11. 23.
Contents 접기

미국 교통부 교통 통계국에서 제공된 2011년 10월 한달 간의 비행기 출발/ 도착 정보를 이용하여 비행기 연착 추측 모델 구축하기

데이터 준비

기본적인 패키지 로딩

import numpy as np
import pandas as pd

from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression

 

데이터 1 로딩

df_airport = pd.read_csv('./dataset/Airport Codes Dataset.csv')
print(df_airport.shape)
display(df_airport.head())
(365, 4)

 

데이터 2 로딩

df_flight = pd.read_csv('./dataset/Flight on-time performance.csv')
print(df_flight.shape)
display(df_flight.head())
(504397, 18)

 

정답값 확인

df_flight['ArrDel15'].value_counts() # 종속변수
ArrDel15
0.0    431461
1.0     68219

0: 비행기가 제시간에 도착함 / 1: 비행기가 15분정도 지연됨

 

결측치 확인

많은 개수의 결측치가 존재 하지만 갖고 있는 양에 비해서는 적은 양이기 때문에 결측치는 삭제한다.
또한 결측치가 범주형인 것도 있기 때문에 다른 값으로 채우는 것이 애매하다.

print(df_airport.isna().sum())
print()
print(df_flight.isna().sum())
DepDelay           3697
DepDel15           3697
CRSArrTime            0
ArrTimeBlk            0
ArrDelay           4717
ArrDel15           4717
Cancelled             0

 

데이터 전처리

공항정보 데이터를 출발 공항 정도로 수정. 출발지와 도착지를 구분하기 위함이다.

df_ori_airport = df_airport.rename(columns= {'city':'Ori-city', 'state': 'Ori-state','name':'Ori-airport'})
df_ori_airport.head()

 

비행 정보 데이터와 출발 공항 정보 데이터 병합

how = 왼쪽 데이터 사이즈에 맞춘다.
join_on 을 사용하였기 때문에 중복되는 컬럼값이 존재한다. 때문에, 중복되는 값을 지워준다.

merged_df1 = pd.merge(df_flight,df_ori_airport, left_on='OriginAirportID', right_on= 'airport_id', how = 'left') # 왼쪽데이터기준 공통된 것
print(merged_df1.shape)
display(merged_df1.head())

# 공항 ID중복 컬럼 삭제
merged_df1.drop('airport_id', axis = 1, inplace= True)

 

 

공항정보 데이터를 도착 공항 정도로 수정

df_dest_airport = df_airport.rename(columns= {'city':'Dest-city', 'state': 'Dest-state','name':'Dest-airport'})
df_dest_airport.head()

 

비행정보 데이터 + 도착 정보 데이터

처음 join했던 정보에 다시 join 하게 되며 출발지와 도착지를 구분 할 수 있게 되었다.

merged_df2 = pd.merge(merged_df1,df_dest_airport, left_on='OriginAirportID', right_on= 'airport_id', how = 'left') # 왼쪽데이터기준 공통된 것
print(merged_df2.shape)
display(merged_df2.head())

# 공항 ID중복 컬럼 삭제
merged_df2.drop('airport_id', axis = 1, inplace= True)

 

 

24개의 컬럼 중 학습에 사용할 14개 컬럼 선택

독립변수로 사용할 컬럼 14가지를 선택한다.

selected_col = ['DayOfWeek', 'Carrier', 'DepTimeBlk','DepDelay', 'DepDel15','ArrTimeBlk', 'ArrDelay',
               'ArrDel15','Ori-city','Ori-state', 'Ori-airport', 'Dest-city', 'Dest-state', 'Dest-airport']
df_flight_info = merged_df2[selected_col]
df_flight_info.head()

 

결측치처리

df_flight_info = df_flight_info.dropna()
df_flight_info.isna().sum()

 

명목형 데이터의 유니크 값 개수 확인

dtype을 통해 만들어낸 df_flight_info() 함수의 컬럼별 타입이 'object' 인 것만을 구분한다.

obj_col = [col for col in df_flight_info.columns if df_flight_info[col].dtype == 'object']
# print(obj_col)
for col in obj_col:
    print(f'컬럼명:{col:15s}, unique 개수: {df_flight_info[col].unique().size} ')

for col in [col for col in df_flight_info.columns if df_flight_info[col].dtype == 'object']:
        print(f'컬럼명:{col:15s}, unique 개수: {df_flight_info[col].unique().size} ') 와 같은 의미 이다.

컬럼명:Carrier        , unique 개수: 16 
컬럼명:DepTimeBlk     , unique 개수: 19 
컬럼명:ArrTimeBlk     , unique 개수: 19 
컬럼명:Ori-city       , unique 개수: 268 
컬럼명:Ori-state      , unique 개수: 53 
컬럼명:Ori-airport    , unique 개수: 279 
컬럼명:Dest-city      , unique 개수: 268 
컬럼명:Dest-state     , unique 개수: 53 
컬럼명:Dest-airport   , unique 개수: 279 

 

라벨 인코딩

유니크 값이 많아 라벨 인코딩이 적합하다. 라벨 인코딩 학습과 적용을 바로 시킴

from sklearn.preprocessing import LabelEncoder

encoder = LabelEncoder()
for col in [col for col in df_flight_info.columns if df_flight_info[col].dtype == 'object']:
    df_flight_info[col] = encoder.fit_transform(df_flight_info[col])
<class 'pandas.core.frame.DataFrame'>
Index: 499680 entries, 0 to 504396
Data columns (total 14 columns):
 #   Column        Non-Null Count   Dtype  
---  ------        --------------   -----  
 0   DayOfWeek     499680 non-null  int64  
 1   Carrier       499680 non-null  int32  
 2   DepTimeBlk    499680 non-null  int32  
 3   DepDelay      499680 non-null  float64
 4   DepDel15      499680 non-null  float64
 5   ArrTimeBlk    499680 non-null  int32  
 6   ArrDelay      499680 non-null  float64
 7   ArrDel15      499680 non-null  float64
 8   Ori-city      499680 non-null  int32  
 9   Ori-state     499680 non-null  int32  
 10  Ori-airport   499680 non-null  int32  
 11  Dest-city     499680 non-null  int32  
 12  Dest-state    499680 non-null  int32  
 13  Dest-airport  499680 non-null  int32  
dtypes: float64(4), int32(9), int64(1)
memory usage: 40.0 MB

전부 명목형으로 변경된 것을 알 수 있다.

<문제를 풀때 데이터 전처리 오류 해결에서 가장 많이 걸리는 것을 알 수 있었다.. 앞에 배운 것들을 계속 기억해서 잘 꺼내 써야할텐데 기억이 잘 안나서 고민이다..>

 

데이터 분할 및 모델 생성

from sklearn.model_selection import train_test_split

x = df_flight_info.drop('ArrDel15',axis= 1)
y = df_flight_info['ArrDel15']

x_train,x_test,y_train,y_test = train_test_split(x,y, train_size= 0.8, stratify= y)

 

1. RandomForestClassifier(앙상블모형) 분류

model = RandomForestClassifier()
model.fit(x_train,y_train)

 

2. LogisticRegression(분류분석) 분류

model = LogisticRegression(solver='sag', max_iter= 600) # 확률적 경사 하강법
model.fit(x_train,y_train)

 

모델 평가

앙상블모형

from sklearn.metrics import accuracy_score, roc_auc_score

y_hat = model.predict(x_test)
print(f'Accuracy : {accuracy_score(y_test,y_hat):.3f}')
print(f'AUC :{roc_auc_score(y_test, model.predict_proba(x_test)[:,1]):.3f}')
Accuracy : 1.000
AUC :1.000

앙상블모형은 정확도와 auc 값이 매우 높은 값으로 도출 되었다.

 

분류 분석

# LogisticRegression
y_hat = model.predict(x_test)
print(f'Accuracy : {accuracy_score(y_test,y_hat):.3f}')
print(f'AUC :{roc_auc_score(y_test, model.predict_proba(x_test)[:,1]):.3f}')
Accuracy : 0.982
AUC :0.998

분류분석은 앙상블 모형에 비해 적은 값이지만, 그래도 높은 값을 도출한다.

 

Feature Importance # RandomForestClassifier()

Importance = { k:v for k,v in zip(x.columns, model.feature_importances_)}
df_importance = pd.DataFrame(pd.Series(importance),columns= ['Importance'])
df_importance.sort_values('Importance', ascending= False)

 

회귀계수(가중치) # LogisticRegression()

model.coef_를 통해 설명변수 X를 구성하는 각 특성별 가중치를 확인하기 앞서 차원을 1차원으로 줄여준다. # ** 

coef = model.coef_.squeeze(axis = 0)
odds_rate = np.exp(coef)

coef_df = pd.DataFrame({'가중치': coef, 'odd비': odds_rate}, index=x.columns)
coef_df.sort_values('가중치', ascending= False)

 

교차검증 # RandomForestClassifier() 

from sklearn.model_selection import cross_validate

scores = cross_validate(model, x,y, cv = 5, scoring = ['accuracy', 'roc_auc_ovr'])
# 검증에 사용된 평가지표를 딕셔너리로
for k,v in scores.items():
    print('Key',k)
    print(f'mean value: {np.mean(v):.3f}')
    print('='*30)
Key fit_time
mean value: 18.138
==============================
Key score_time
mean value: 1.317
==============================
Key test_accuracy
mean value: 1.000
==============================
Key test_roc_auc_ovr
mean value: 1.000
==============================

교차검증을 통해 과적합 발생도 일어나지 않았다는 것을 알 수 있다.