Open In Colab

데이터 불러오기

from google.colab import drive
drive.mount('/content/drive')
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats

import warnings
warnings.filterwarnings("ignore")

path = '/content/drive/MyDrive/airport/'

train = pd.read_csv(path + 'train.csv')
test = pd.read_csv(path + 'test.csv')
sample_submission = pd.read_csv(path + 'sample_submission.csv')
train.head()
id Gender Customer Type Age Type of Travel Class Flight Distance Seat comfort Departure/Arrival time convenient Food and drink Gate location Inflight wifi service Inflight entertainment Online support Ease of Online booking On-board service Leg room service Baggage handling Checkin service Cleanliness Online boarding Departure Delay in Minutes Arrival Delay in Minutes target
0 1 Female disloyal Customer 22 Business travel Eco 1599 3 0 3 3 4 3 4 4 5 4 4 4 5 4 0 0.0 0
1 2 Female Loyal Customer 37 Business travel Business 2810 2 4 4 4 1 4 3 5 5 4 2 1 5 2 18 18.0 0
2 3 Male Loyal Customer 46 Business travel Business 2622 1 1 1 1 4 5 5 4 4 4 4 5 4 3 0 0.0 1
3 4 Female disloyal Customer 24 Business travel Eco 2348 3 3 3 3 3 3 3 3 2 4 5 3 4 3 10 2.0 0
4 5 Female Loyal Customer 58 Business travel Business 105 3 3 3 3 4 4 5 4 4 4 4 4 4 5 0 0.0 1

파일이 저장된 위치를 path로 지정하여 불러왔습니다. 이 코드를 사용하신다면 path 값을 파일 저장 위치로 지정하시면 잘 작동됩니다.

데이터를 간단히 살펴보면 만족여부를 판단하는 분류문제이며, 범주형 변수가 상당히 많은 것을 알 수 있습니다.

print(train.shape)
print(test.shape)
(3000, 24)
(2000, 23)

트레인 데이터는 3천개, 테스트 데이터는 2천개이며 특성 개수는 총 23개입니다.

train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3000 entries, 0 to 2999
Data columns (total 24 columns):
 #   Column                             Non-Null Count  Dtype  
---  ------                             --------------  -----  
 0   id                                 3000 non-null   int64  
 1   Gender                             3000 non-null   object 
 2   Customer Type                      3000 non-null   object 
 3   Age                                3000 non-null   int64  
 4   Type of Travel                     3000 non-null   object 
 5   Class                              3000 non-null   object 
 6   Flight Distance                    3000 non-null   int64  
 7   Seat comfort                       3000 non-null   int64  
 8   Departure/Arrival time convenient  3000 non-null   int64  
 9   Food and drink                     3000 non-null   int64  
 10  Gate location                      3000 non-null   int64  
 11  Inflight wifi service              3000 non-null   int64  
 12  Inflight entertainment             3000 non-null   int64  
 13  Online support                     3000 non-null   int64  
 14  Ease of Online booking             3000 non-null   int64  
 15  On-board service                   3000 non-null   int64  
 16  Leg room service                   3000 non-null   int64  
 17  Baggage handling                   3000 non-null   int64  
 18  Checkin service                    3000 non-null   int64  
 19  Cleanliness                        3000 non-null   int64  
 20  Online boarding                    3000 non-null   int64  
 21  Departure Delay in Minutes         3000 non-null   int64  
 22  Arrival Delay in Minutes           3000 non-null   float64
 23  target                             3000 non-null   int64  
dtypes: float64(1), int64(19), object(4)
memory usage: 562.6+ KB

결측치는 관찰되지 않습니다.

간단히 변수 관찰

train.describe()
id Age Flight Distance Seat comfort Departure/Arrival time convenient Food and drink Gate location Inflight wifi service Inflight entertainment Online support Ease of Online booking On-board service Leg room service Baggage handling Checkin service Cleanliness Online boarding Departure Delay in Minutes Arrival Delay in Minutes target
count 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000 3000.00000 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000 3000.000000
mean 1500.500000 39.203000 1983.079333 2.863333 3.009667 2.874333 3.016667 3.259667 3.352333 3.50500 3.488000 3.497000 3.485000 3.728667 3.370000 3.728667 3.356333 15.634333 15.922000 0.556000
std 866.169729 15.108802 1028.109117 1.394981 1.519543 1.431511 1.294713 1.322683 1.352826 1.31068 1.302211 1.283436 1.294218 1.154190 1.258158 1.161678 1.294057 45.083228 45.203411 0.496937
min 1.000000 7.000000 52.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 1.00000 0.000000 1.000000 0.000000 1.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000
25% 750.750000 27.000000 1348.250000 2.000000 2.000000 2.000000 2.000000 2.000000 2.000000 3.00000 2.000000 3.000000 2.000000 3.000000 3.000000 3.000000 2.000000 0.000000 0.000000 0.000000
50% 1500.500000 39.000000 1937.000000 3.000000 3.000000 3.000000 3.000000 3.000000 4.000000 4.00000 4.000000 4.000000 4.000000 4.000000 3.000000 4.000000 4.000000 0.000000 0.000000 1.000000
75% 2250.250000 51.000000 2547.250000 4.000000 4.000000 4.000000 4.000000 4.000000 4.000000 5.00000 5.000000 5.000000 5.000000 5.000000 4.000000 5.000000 4.000000 12.000000 13.000000 1.000000
max 3000.000000 80.000000 6882.000000 5.000000 5.000000 5.000000 5.000000 5.000000 5.000000 5.00000 5.000000 5.000000 5.000000 5.000000 5.000000 5.000000 5.000000 1128.000000 1115.000000 1.000000
Categorical = ['Gender', 'Customer Type', 'Type of Travel', 'Class']
Order = ['Seat comfort', 'Departure/Arrival time convenient', 'Food and drink','Gate location', 
         'Inflight wifi service', 'Inflight entertainment', 'Online support', 'Ease of Online booking',
         'On-board service', 'Leg room service', 'Baggage handling', 'Checkin service',
         'Cleanliness', 'Online boarding']
Continuous = ['Age', 'Flight Distance', 'Departure Delay in Minutes', 'Arrival Delay in Minutes']

변수를 질적 변수, 이산형 변수, 연속형 변수로 구분해서 관찰할 수 있을 거 같아요.

질적 변수

Gender : 성별 (M, F), Customer Type : Loyal 여부 (Disloyal 또는 Loyal), Type of Travel : 여행 목적 (Business 또는 Personal Travel), Class : 좌석 종류 ( Eco < Eco Plus < Business)

이산형 변수 (모두 만족도 변수이기 때문에 값이 0~5 사이 입니다.)

Seat comfort : 좌석 만족도, Departure/Arrival time convenient : 출발/도착 시간 편의성 만족도, Food and drink : 식음료 만족도, Gate location : 게이트 위치 만족도, Inflight wifi service : 기내 와이파이 서비스 만족도, Inflight entertainment : 기내 엔터테인먼트 만족도, Online support : 온라인 지원 만족도, Ease of Online booking : 온라인 예매 편리성 만족도, On-board service : 탑승 서비스 만족도, Leg room service : Leg room 서비스 만족도, Baggage handling : 수하물 처리 만족도, Checkin service : 체크인 서비스 만족도,Cleanliness : 청결도 만족도, Online boarding : 온라인보딩 만족도

연속형 변수

Age : 나이, Flight Distance : 비행거리, Departure Delay in Minutes : 출발 지연 시간, Arrival Delay in Minutes : 도착 지연 시간

데이터 시각적으로 관찰하기

타겟 변수 관찰

plt.figure(figsize=[12,8])
plt.text(s="Target variables",x=0,y=1.3, va='bottom',ha='center',color='#189AB4',fontsize=25)
plt.pie(train['target'].value_counts(),autopct='%1.1f%%', pctdistance=1.1)
plt.legend(['Good', 'Bad'], loc = "upper right",title="Programming Languages", prop={'size': 15})
plt.show()

제 개인적으로 가장 먼저 관찰해야한다고 생각하는 변수인 반응변수 입니다. 만족하는 비율이 조금 높긴 하지만 빈도차이가 크지 않습니다.

따로 오버샘플링 등 조치를 취할 필요는 없을 것 같습니다.

train_0 = train[train['target']==0]
train_1 = train[train['target']==1]

향후 코드 분석을 위해 타겟 값에 따라 트레인 데이터를 두 그룹으로 분리합니다.

범주형 변수 관찰

def cat_plot(column):
    f, ax = plt.subplots(1, 3, figsize=(16, 6))
    sns.countplot(x = column,
                data = train,
                ax = ax[0],
                order = train[column].value_counts().index)
    ax[0].tick_params(labelsize=12)
    ax[0].set_title('Full train data')
    ax[0].set_ylabel('count')
    ax[0].tick_params(rotation=50)


    sns.countplot(x = column,
                data = train_1,
                ax = ax[1],
                order = train_1[column].value_counts().index)
    ax[1].tick_params(labelsize=12)
    ax[1].set_title('target = 1')
    ax[1].set_ylabel('count')
    ax[1].tick_params(rotation=50)

    sns.countplot(x = column,
                data = train_0,
                ax = ax[2],
                order = train_0[column].value_counts().index)
    ax[2].tick_params(labelsize=12)
    ax[2].set_title('target = 0')
    ax[2].set_ylabel('count')
    ax[2].tick_params(rotation=50)

    plt.subplots_adjust(wspace=0.3, hspace=0.3)
    plt.show()

cat_plot("Gender")

범주형 변수들을 먼저 시각적으로 관찰하겠습니다. 주요 목표는 이 변수가 과연 타겟값에 영향을 주는지 입니다.

이를 확인하는 방법으로 원본 데이터, 타겟 값이 1인 데이터, 타겟 값이 0인 데이터 각각에서 특정 변수가 다른 모양을 가지고 있는지를 관찰합니다.

먼저 성별 변수를 관찰해보면 타겟이 1인 데이터에서는 여성이 많고, 타겟이 0인 데이터에서는 남성이 많습니다.

그래프 차이가 눈에 띄기 때문에 성별 변수는 타겟 변수에 유의미한 영향이 있다, 여성이 긍정적 응답을 유의미하게 많이 했다라고 판단할 수 있겠습니다.

cat_plot("Customer Type")

Customer Type 로얄 여부 변수 입니다. 우선 전체 데이터에서 Loyal 항목이 disloyal 항목보다 훨씬 많습니다.

다만 타겟이 1인 데이터와 타겟이 0인 데이터를 비교하면 타겟이 0인 데이터에서 disloyal 항목의 빈도가 높게 나옵니다.

통계적으로 검증까진 하진 않았지만, 시각적으로 봐도 두 집단 간 유의미한 차이가 있어보입니다. 즉 이 변수는 유의미한 변수입니다.

cat_plot("Type of Travel")

Type of Travel 여행 목적 변수 입니다. 전체 데이터를 보면 비지니스 목적의 비행이 더 많습니다.

다만 타겟이 0인 데이터, 즉 불만족하다는 응답을 준 데이터를 살펴보면 개인적인 여행을 한 사람에 비중이 조금 높아지는데요.

여행을 목적으로 비행을 한 손님은 만족의 기준이 상대적으로 높다고 볼 수 있겠네요.

이 변수 역시 통계적 검증은 하지 않았지만 시각적으로 봤을때 유의미하게 타겟 값에 영향을 주는 변수인 것 같습니다.

cat_plot("Class")

Class 항공 좌석 변수입니다. 우선 변수에 대해 설명을 하면 이코노미 < 이코노미 플러스 < 비지니스 순으로 높은 등급의 좌석입니다.

데이터를 살펴보면 비지니스 좌석을 사용한 사람은 대부분 만족하고, 이코노미 좌석을 사용한 사람은 대부분 불만족하는 것 같습니다.

이코노미 플러스 좌석 같은 경우 중간 등급 좌석이기 때문에 두 그룹간 차이가 눈에 띄진 않으나 불만족한 비율이 조금 높군요.

타겟 변수에 따른 두 그룹간 그래프에 모양이 아에 달라지기 때문에 이 변수는 상당히 유의미한 변수 입니다.

train = pd.get_dummies(train)
test = pd.get_dummies(test)

train.head()
id Age Flight Distance Seat comfort Departure/Arrival time convenient Food and drink Gate location Inflight wifi service Inflight entertainment Online support Ease of Online booking On-board service Leg room service Baggage handling Checkin service Cleanliness Online boarding Departure Delay in Minutes Arrival Delay in Minutes target Gender_Female Gender_Male Customer Type_Loyal Customer Customer Type_disloyal Customer Type of Travel_Business travel Type of Travel_Personal Travel Class_Business Class_Eco Class_Eco Plus
0 1 22 1599 3 0 3 3 4 3 4 4 5 4 4 4 5 4 0 0.0 0 1 0 0 1 1 0 0 1 0
1 2 37 2810 2 4 4 4 1 4 3 5 5 4 2 1 5 2 18 18.0 0 1 0 1 0 1 0 1 0 0
2 3 46 2622 1 1 1 1 4 5 5 4 4 4 4 5 4 3 0 0.0 1 0 1 1 0 1 0 1 0 0
3 4 24 2348 3 3 3 3 3 3 3 3 2 4 5 3 4 3 10 2.0 0 1 0 0 1 1 0 0 1 0
4 5 58 105 3 3 3 3 4 4 5 4 4 4 4 4 4 5 0 0.0 1 1 0 1 0 1 0 1 0 0

모든 범주형 변수가 유의미 하기 때문에 제외하는 변수 없이 사용하고자 합니다.

밑에 다루는 이산형 변수들은 순서라는게 있지만, 범주형 변수들은 순서가 없는 쌩 범주이기 때문에 라벨인코딩 보다는 원핫인코딩이 적절합니다.

판다스에 get_dummies 함수를 사용하면 데이터 내 범주형 변수만 알아서 뽑아서 자동으로 원핫인코딩을 해줍니다.

이산형 변수 관찰

cat_plot("Seat comfort")

이산형 변수에 경우 0~5까지 크기 순서가 정해저 있다는 점에서 범주형 변수와 차이가 있으나 항목 별로 그래프를 관찰할 수 있다는 점에서 범주형 변수와 동일하게 생각할 수 있습니다. 그렇기 때문에 범주형 변수와 동일하게 시각화하여 관찰하겠습니다.

Seat comfort 좌석 만족도 변수인데요. 우리가 보통 만족도 설문조사를 할 때 양 극단으로 답변(5, 0)하는 개인의 소신을 보여주는 기입은 잘 하지 않고 평범한 대답(2,3,4)을 하려는 특성이 있습니다.

여기서도 앞서 말한 특성이 잘 들어나 있는 것 같습니다. 다만 분석에서 중요한 건 이 부분이 아니라 타겟 변수가 다를 때 응답의 형태가 다른가 인데요.

좌석 만족도가 4/5로 높은 경우 대부분 만족한다는 응답을 많이 보였고, 좌석 만족도가 1/2/3인 경우 고객 만족도가 불만족인 경우가 많습니다.

특이한 점은 0인데요. 0을 응답한 거의 대부분의 사람이 고객 만족도에서 만족한다는 응답을 보였습니다.

0이 가장 안좋은 응답이라고 생각 했었는데, 안좋은 응답이라기 보단 결측치를 표기한 것으로 생각 됩니다. 변수를 더 확인해야겠습니다.

cat_plot('Departure/Arrival time convenient')

다음 변수는 Departure/Arrival time convenient 출발/도착시간 만족도 입니다. 대부분 4,5점을 주어 만족한다는 응답입니다.

전체 트레인 데이터 분포로 보아 출발/도착시간이 연착되지 않았다면 대부분 만족한다고 답변한 것 같습니다.

다만 두 그룹 간 그래프 차이가 크게 나지 않는데요. 2, 3번 항목이 순서가 뒤바뀐 것 이외에는 눈에 띄는 차이가 안보입니다.

특히 1번 항목은 출발/도착시간 만족도가 형편 없었다는 건데, 실제 만족도는 더 높은 것을 보면 이 변수가 의미가 없다고 생각되네요.

앞서 말한 0번 항목은 여기선 반반 분포가 되있습니다. 조금 이상한데, 다음 변수를 또 봐야할 것 같습니다.

cat_plot('Food and drink')

Food and drink 식/음료 만족도 변수 입니다.

값이 클 수록 타겟 1에 속할 확률이 늘어나는 모습을 보입니다. 하지만 1 항목의 경우 타겟 1일 확률과 0일 확률이 반반입니다.

또 0 항목의 경우 전체 개수가 100개 이상으로 적지 않은 표본임에도 타겟 1일 확률이 매우 높은 것이 5 항목과 유사한 정도입니다.

직관적으로 이해되진 않으나 조정이 필요해보입니다.

cat_plot('Gate location')

Gate location 게이트 위치 만족도 변수 입니다. 앞서 말한 대로 이 변수 또한 가운데 응답이 몰려있습니다.

조금 특이한 것은 3번 항목은 대부분 타겟 0 데이터이고, 1/2 항목은 타겟 1 데이터 입니다.

타겟 별 데이터의 그래프가 눈에 띄게 다르지만 해석하는데는 어려움이 있습니다. 답변자가 응답을 성실히 했는지도 의심해봐야겠습니다.

또 특이한건 0 응답이 없습니다. 이 부분도 조금 이상하네요.

cat_plot('Inflight wifi service')

Inflight wifi service 기내 와이파이 만족도 변수 입니다.

그래프를 관찰하면 대채로 와이파이 만족도가 높을 수록 타겟 값이 1일 확률이 유의미하게 높은 것 같습니다.

특이한 점은 0 항목이 적은 수로 존재하는데 저 값은 이상치로 추정되므로 다른 값으로 대체해야겠습니다.

cat_plot('Inflight entertainment')

Inflight entertainment 기내 엔터테이먼트 만족도 변수 입니다.

와이파이 만족도 변수와 비슷하게 4/5 항목일수록 타겟 값이 1일 확률이 높아집니다.

이 변수에서도 0이 일부 관찰되는데, 0의 활용을 고민해야겠습니다. 가장 많이 나온 4로 대체하는 것도 하나에 방법입니다.

cat_plot('Online support')

Online support 온라인 지원 만족도 변수 입니다.

앞선 두 변수와 비슷하게 전반적으로 4/5 항목이 많으며 4/5 항목일수록 타겟 1일 확률이 많이 높아집니다.

cat_plot('Ease of Online booking')

Ease of Online booking 온라인 예매 편의성 만족도 변수 입니다.

앞선 세 변수와 마찬가지로 4/5 항목일수록 타겟 1일 확률이 높아집니다.

여기서도 0 항목이 극소수로 존재하는데 빈도가 높은 항목인 4 항목으로 대체하는게 좋을 것 같아요.

cat_plot('On-board service')

On-board service 탑승 서비스 만족도 변수 입니다.

높은 점수를 받을 수록 타겟 1을 받을 확률이 점점 높아지는 형태가 뚜렷한 것을 그래프를 보면 알 수 있습니다.

계속 같은 말을 반복하는데, 이 말을 하는 것은 타겟을 판단하는데 굉장히 좋은 변수라는 것 입니다.

cat_plot('Leg room service')

Leg room service 발이 편안했는지 묻는 변수 입니다.

일반적으로는 숫자가 커질수록 타겟 1이 될 가능성이 높습니다만, 여기서는 조금 의외인 점이 3 항목 보다 2 항목이 타겟 1이 될 확률이 높습니다.

표본이 조금 튄 것으로 생각할 수 있겠습니다만, 다르게 말하면 2나 3이나 타겟을 가리는데 별 차이가 없다고도 생각할 수 있겠죠.

2/3 항목을 병합하는 것도 좋은 아이디어인 것 같아요. 여기서도 0이 극소수 관찰되는데 가장 큰 빈도인 4로 바꿔주겠습니다.

cat_plot('Baggage handling')

Baggage handling 수하물 처리 만족도 변수 입니다.

이 변수 또한 윗 변수와 비슷하게 숫자가 커질수록 타겟 1일 확률이 늘어나나 2/3 항목은 다소 뒤바뀐 결과입니다.

이 변수 또한 2/3 항목을 병합하겠습니다.

cat_plot('Checkin service')

Checkin service 체크인 서비스 만족도 변수 입니다.

전반적으로 높은 만족도 점수를 기록하며, 점수가 클 수록 타겟 1 값을 가질 확률이 뚜렷히 높아지는 것을 확인할 수 있습니다.

cat_plot('Cleanliness')

Cleanliness 청결도 만족도 변수 입니다.

윗 변수와 마찬가지로 대체로 높은 만족도 점수를 기록하며, 점수가 클수록 타겟 1 값을 가질 확률이 높아지는 것으로 보입니다.

cat_plot('Online boarding')

Online boarding 온라인 보딩 만족도 변수 입니다.

이 변수 또한 값이 커질수록 타겟 1 값을 가질 확률이 높아집니다.

0 값이 관찰되고 있는데, 마찬가지로 최고 빈도 항목으로 대체하겠습니다.

train['Seat comfort'][train['Seat comfort'] == 0] = 5
test['Seat comfort'][test['Seat comfort'] == 0] = 5

train['Inflight wifi service'][train['Inflight wifi service'] == 0] = 4
test['Inflight wifi service'][test['Inflight wifi service'] == 0] = 4

train['Ease of Online booking'][train['Ease of Online booking'] == 0] = 4
test['Ease of Online booking'][test['Ease of Online booking'] == 0] = 4

train['On-board service'][train['On-board service'] == 0] = 4
test['On-board service'][test['On-board service'] == 0] = 4


# 1,2 항목 병합 필요한 변수들
train['Inflight entertainment'][train['Inflight entertainment'] == 1] = 2
train['Inflight entertainment'][train['Inflight entertainment'] == 0] = 4

test['Inflight entertainment'][test['Inflight entertainment'] == 1] = 2
test['Inflight entertainment'][test['Inflight entertainment'] == 0] = 4

train['Online support'][train['Online support'] == 1] = 2
train['Online support'][train['Online support'] == 0] = 4

test['Online support'][test['Online support'] == 1] = 2
test['Online support'][test['Online support'] == 0] = 4

train['Checkin service'][train['Checkin service'] == 1] = 2
train['Checkin service'][train['Checkin service'] == 0] = 4

test['Checkin service'][test['Checkin service'] == 1] = 2
test['Checkin service'][test['Checkin service'] == 0] = 4

train['Cleanliness'][train['Cleanliness'] == 1] = 2
train['Cleanliness'][train['Cleanliness'] == 0] = 4

test['Cleanliness'][test['Cleanliness'] == 1] = 2
test['Cleanliness'][test['Cleanliness'] == 0] = 4

train['Online boarding'][train['Online boarding'] == 1] = 2
train['Online boarding'][train['Online boarding'] == 0] = 4

test['Online boarding'][test['Online boarding'] == 1] = 2
test['Online boarding'][test['Online boarding'] == 0] = 4


# 2,3 항목 변환 필요한 변수들
train['Leg room service'][train['Leg room service'] == 2] = 3
train['Leg room service'][train['Leg room service'] == 0] = 4

test['Leg room service'][test['Leg room service'] == 2] = 3
test['Leg room service'][test['Leg room service'] == 0] = 4

train['Baggage handling'][train['Baggage handling'] == 2] = 3
train['Baggage handling'][train['Baggage handling'] == 0] = 4

test['Baggage handling'][test['Baggage handling'] == 2] = 3
test['Baggage handling'][test['Baggage handling'] == 0] = 4


# 조금 특별한 변환 필요한 변수들
train['Food and drink'][train['Food and drink'] == 1] = -1
train['Food and drink'][train['Food and drink'] == 2] = 1
train['Food and drink'][train['Food and drink'] == 3] = 2
train['Food and drink'][train['Food and drink'] == -1] = 3
train['Food and drink'][train['Food and drink'] == 0] = 5

test['Food and drink'][test['Food and drink'] == 1] = -1
test['Food and drink'][test['Food and drink'] == 2] = 1
test['Food and drink'][test['Food and drink'] == 3] = 2
test['Food and drink'][test['Food and drink'] == -1] = 3
test['Food and drink'][test['Food and drink'] == 0] = 5

train['Gate location'][train['Gate location'] == 1] = 5
train['Gate location'][train['Gate location'] == 2] = 5
train['Gate location'][train['Gate location'] == 0] = 3

test['Gate location'][test['Gate location'] == 1] = 5
test['Gate location'][test['Gate location'] == 2] = 5
test['Gate location'][test['Gate location'] == 0] = 3


# 삭제할 변수
train.drop(['Departure/Arrival time convenient'], axis = 1, inplace = True)
test.drop(['Departure/Arrival time convenient'], axis = 1, inplace = True)

우선 0이 관찰되는 변수도 있고 아닌 변수도 있는데, 관찰되지 않더라도 테스트 데이터 있을 수 있으므로 공통적으로 적용하겠습니다.

대부분 변수의 가장 큰 빈도인 항목이 4입니다. 특별한 언급이 없는 변수는 0 항목을 4로 대체하였습니다.

이산형 변수를 시각적으로 다루면서 느낀점은 역시 사람이 하는 설문조사다 보니깐 데이터의 질이 높진 못한 것 같아요.

이산형 변수를 앞서 관찰한 결과를 통해 다섯 가지 종류로 나눠서 전처리 하였습니다. 다섯 가지 종류는 다음과 같습니다.

깔끔한 변수들

Seat comfort (0은 5로), Inflight wifi service, Ease of Online booking, On-board service

1,2 항목 병합 필요한 변수들

Inflight entertainment, Online support, Checkin service, Cleanliness, Online boarding

2,3 항목 병합 필요한 변수들

Leg room service, Baggage handling

형태가 다소 이상하지만 유의미한 변수들

Food and drink(1은 3으로, 3은 2로, 2는 1로, 0은 5로), Gate location(2,1을 5로, 0은 3으로)

유의미 하지 않은 변수(사용하지 않을 변수)

Departure/Arrival time convenient

연속형 변수 관찰

def num_plot(column):
  
    fig, axes = plt.subplots(1, 3, figsize=(16, 6))

    sns.distplot(train[column],
                ax = axes[0])
    axes[0].tick_params(labelsize=12)
    axes[0].set_title('Full train data')
    axes[0].set_ylabel('count')

    sns.distplot(train_1[column],
                ax = axes[1])
    axes[1].tick_params(labelsize=12)
    axes[1].set_title('target = 1')
    axes[1].set_ylabel('count')

    sns.distplot(train_0[column],
                ax = axes[2])
    axes[2].tick_params(labelsize=12)
    axes[2].set_title('target = 0')
    axes[2].set_ylabel('count')

    plt.subplots_adjust(wspace=0.3, hspace=0.3)

    print('타겟 1 데이터의 평균 :', (train_1[column]).mean())
    print('타겟 0 데이터의 평균 :', (train_0[column]).mean())
    print('데이터의 표준오차 :', train[column].std() / np.sqrt(3000))

num_plot("Age")
타겟 1 데이터의 평균 : 40.65047961630695
타겟 0 데이터의 평균 : 37.390390390390394
데이터의 표준오차 : 0.2758477133981643

연속형 변수는 데이터를 타겟이 1과 0인 두 그룹으로 나눠서 그룹 별 히스토그램이 차이가 있는지를 시각적으로 관찰하겠습니다.

우선 전체 트레인 데이터의 나이 변수는 정규분포와 유사합니다. 변수로써 좋은 성질이죠.

타겟 값이 1인 데이터의 나이 평균은 40살, 타겟 값이 0인 데이터의 나이 평균은 37살로 크게 차이나진 않습니다.

다만 표본이 3천개면 통계적으로 상당히 많은 편인데(요즘 많이 수행하는 대통령 여론조사도 천명 뽑습니다.) 3살 차이는 두 그룹간 나이 평균이 유의미하게 난다고 볼 수 있습니다.

또 그래프도 봉우리가 있는 위치가 조금 다른 것이 보이기도 합니다. 그렇기 때문에 이 변수는 사용하겠습니다.

num_plot("Flight Distance")
타겟 1 데이터의 평균 : 1935.2583932853718
타겟 0 데이터의 평균 : 2042.9632132132133
데이터의 표준오차 : 18.770618492960658

Flight Distance 비행거리 변수 입니다. 단순하게 생각했을때 비행 거리가 길면 만족도가 떨어질 확률이 높겠죠.

전체 트레인 데이터의 분포가 정규분포와 유사하나 우측 꼬리가 조금 길어보입니다. 로그변환 해주는 것도 좋겠군요.

실제 데이터도 예측한대로 만족도가 1인 그룹의 평균 비행 시간이 짧게 나옵니다. 그래프를 봐도 타겟 1인 그래프가 앞쪽에 값이 많이있어보이죠.

저 차이가 유의미한 것인지도 생각해야하는데, 표준오차 대비 두 그룹간 차이가 꽤 있으므로 이 변수도 유의미한 변수로 취급하겠습니다.

num_plot("Departure Delay in Minutes")
타겟 1 데이터의 평균 : 11.405875299760192
타겟 0 데이터의 평균 : 20.92942942942943
데이터의 표준오차 : 0.8231033660699084
num_plot("Arrival Delay in Minutes")
타겟 1 데이터의 평균 : 11.384892086330936
타겟 0 데이터의 평균 : 21.603603603603602
데이터의 표준오차 : 0.8252975959121616
print('두 변수간 상관계수:', train['Arrival Delay in Minutes'].corr(train["Departure Delay in Minutes"]))
print('출발 지연시간이 0인 값:', sum(train['Departure Delay in Minutes'] == 0))
print('도착 지연시간이 0인 값:', sum(train['Arrival Delay in Minutes'] == 0))
두 변수간 상관계수: 0.9768732919464286
출발 지연시간이 0인 값: 1705
도착 지연시간이 0인 값: 1661

Departure Delay in Minutes, Arrival Delay in Minutes 출발 지연 시간, 도착 지연 시간 변수 입니다.

근데 비행기가 출발이 지연되면 도착도 자연스럽게 지연이 되겠죠? 즉 두 변수간 상관계수가 매우 높을 것으로 추축되고 실제로도 그러합니다.

이 말은 굳이 두 변수를 사용할 필요가 없다, 오히려 다중공선성 문제를 가져오게 됩니다. 0.97이면 거의 한 변수나 다름 없죠.

또 지연이 됬는지 안됬는지를 구분할 수도 있습니다. 실제로 절반 이상의 값이 0을 기록했는데 지연이 안됬음을 의미합니다.

다만 출발은 정상적으로 했는데, 도착은 연착될 수도 있으므로 도착 지연 시간 변수를 사용하도록 하겠습니다.

그리고 큰 값은 엄청 큰 우측 꼬리가 긴 분포형태이기 때문에 로그변환을 해야합니다.

이때 0은 로그변환이 안되므로 전체 값에 1을 더한 뒤 로그변환 하는 log1p 함수를 사용해야 한다는 것도 유의해야합니다.

train['Flight Distance'] = np.log1p(train['Flight Distance'])
train['Arrival Delay in Minutes'] = np.log1p(train['Arrival Delay in Minutes'])

test['Flight Distance'] = np.log1p(test['Flight Distance'])
test['Arrival Delay in Minutes'] = np.log1p(test['Arrival Delay in Minutes'])


train.drop(['Departure Delay in Minutes'], axis = 1, inplace = True)
test.drop(['Departure Delay in Minutes'], axis = 1, inplace = True)

연속형 변수를 시각적으로 관찰하면서 해결하려 했던 부분을 적은 코드입니다.

간단한 랜덤포레스트 모델 적합

train_label = train['target']

train.drop(['id', 'target'], axis = 1, inplace= True)
test.drop(['id'], axis = 1, inplace= True)

타겟 값을 라벨이라는 변수에 따로 뺀 뒤 분석에 의미 없는 변수인 id와 함께 지웁니다.

from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(random_state = 0, n_estimators = 100)
rf.fit(train,train_label)
sample_submission['target'] = rf.predict(test)

sample_submission.to_csv('airport_1.csv',index=False)

노말한 랜덤포레스트 분류 모델에 적용하여 결과 값을 csv 파일로 저장하였습니다.

결과는 public 기준 0.921 인데요. 조금 더 좋은 모델을 사용하거나 앙상블을 시키면 더 점수가 좋아지지 않을가 생각되네요.

EDA 중심에 코드이니 우선 이정도 결과로 만족하겠습니다. 변수를 하나하나 살펴봤는데 많은 인사이트 얻으셨으면 좋겠습니다!