프로그래밍/파이썬

EDA - 4단계 : 캐글 타이타닉 코드 분석

매 석 2023. 11. 2. 21:18
반응형

전체 코드

titanic.html
0.96MB

 

주피터 노트북으로 작성한 내용을 html 파일로 저장한 것이다.

아래에서 다룰 내용을 모두 포함한 코드이다.

1. 라이브러리 import

import pandas as pd
import numpy as np
from sklearn.preprocessing import LabelEncoder
train_df=pd.read_csv("train.csv")
test_df=pd.read_csv("test.csv")
submission = pd.read_csv("gender_submission.csv")

train_df.head()
#survivce : 생존여부, Pclass : 티켓 등급
#sibsp : 승선 중인 형제나 배우자의 수
#parch : 승선 중인 부모나 자녀의 수
#ticket : 티켓번호
#fare : 티켓요금
#cabin : 방 번호
#embarkder : 승선한 항구의 이름

우선 EDA를 하기 전에 사용할 라이브러리를 import 해준다.

또한 제공받은 csv 파일도 각각 저장해준다.

이중 train_df 데이터를 head를 통해 위 5줄을 확인해본다.

이를 해석할 때 필요한 내용들을 주석으로 미리 정리해놓는다.

이는 캐글에서 정보를 얻을 수 있다.

 

2. EDA

데이터의 행 열의 수, type, 통계값 등을 간단히 확인해보는 작업을 진행한다.

이는 데이터가 어떻게 구성되어있는지 확인하는 단계이다.

#카테고리 변수 확인하기
print(train_df["Sex"].value_counts())
print()
print(train_df["Embarked"].value_counts())
train_df["Cabin"].value_counts()

#결측치 확인
print(train_df.isnull().sum())
test_df.isnull().sum()

이후에는 각 카테고리의 변수들은 어떻게 구성되어있는지 확인해준다.

또한 결측치는 얼마나, 어떤 카테고리가 존재하는지 확인한다.

 

3. 시각화

시각화는 목적 변수인 Survived에 관하여 진행한다.

  • 목적 변수 : 기계학습의 교사 학습에 있어 예측하고 싶은 정보
  • 설명 변수 : 기계학습의 교사 학습에 있어 예측에 사용하는 정보
  •  
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use("ggplot")
#승선한 항구에 따른 생존
train_df[["Embarked","Survived","PassengerId"]]

위 내용은 승선항, 생존 여부를 확인할 수 있다.

#데이터 수평화
embarked_df=train_df[["Embarked","Survived","PassengerId"]].dropna().groupby(["Embarked","Survived"]).count().unstack()
embarked_df

 

이를 보기 쉽게 결측치를 제거하고 group으로 묶어 확인한다.

추가로 unstack()을 사용해서 수평화를 진행한다.

#누적 막대 
embarked_df.plot.bar(stacked=True)

누적 막대 형태로 어느 승선항에서 얼마나 생존했는지 눈으로 쉽게 확인할 수 있다.

 

#사망률 수치화
embarked_df["survived_rate"]=embarked_df.iloc[:,0] / (embarked_df.iloc[:,0]+embarked_df.iloc[:,1])
embarked_df

또한 위에서 수평화한 데이터를 iloc을 사용해서 생존율이 얼마있지 계산한다.

결과는 아래와 같다.

이러한 방식으로 성별 생존율을 시각화하면 아래와 같다.

이외에도 티켓 등급별, 연령별 생존율을 시각화하여 확인한다.

#원-핫 인코딩 : 1,0
train_df_corr = pd.get_dummies(train_df, columns=["Sex"], drop_first=True)
train_df_corr = pd.get_dummies(train_df_corr, columns=["Embarked"])
train_df_corr.head()

추가로 원-핫 인코딩이라는 개념은 컴퓨터는 문자보다 숫자를 더욱 잘 이해하기에

원하는 값에는 1을, 그렇지 않은 경우에는 0으로 인코딩하는 방식이다.

이는 각 변수와 Survived 변수 간의 상관관계를 계산하기 위해 수치 데이터로 변환하는 과정이다.

즉 카테고리 변수를 더미 변수화한다고 한다.

결과적으로 변수 간의 상관 관계를 상관 행렬로 작성하여 히트맵으로 시각화했다.

이를 통해 Survived와 가장 상관관계가 높은 것은 -0.54인 Sex_male이다.

이는 1이 남성, -1이 여성이기에 남성 생존율은 낮고, 여성 생존율은 높음을 의미한다.

2번째는 Pclass로 -0.34이다. 즉 티켓 등급이 높은 쪽의 생존율이 높았다.

이를 통해 어느 정도 유추는 할 수 있지만, 절대적인 지표는 아니다.

그렇기에 실제로 모델링을 하면서 결과를 통해 어느 변수가 중요한 지 알 수 있다.

 

4. 데이터 전처리

all_df = pd.concat([train_df,test_df], sort=False).reset_index(drop=True)

concat을 사용해 학습, 테스트 데이터를 통합해준다.

 

Fare_mean=all_df[["Pclass", "Fare"]].groupby("Pclass").mean().reset_index()
Fare_mean.columns = ["Pclass","Fare_mean"]
#결측치 채우기
all_df = pd.merge(all_df, Fare_mean, on="Pclass", how="left")
all_df.loc[(all_df["Fare"].isnull()), "Fare"] = all_df["Fare_mean"]
all_df = all_df.drop("Fare_mean", axis=1)

Fare의 결측치를 채우기 위해 Pclass별로 Fare의 평균값을 구한 후

빈 Fare값의 Pclass를 확인하여 해당 평균값으로 채워넣는다.

 

name_df = all_df["Name"].str.split("[,.]",2,expand=True)
name_df.columns=["family_name","honorific","name"]
name_df["family_name"]=name_df["family_name"].str.strip()
name_df["honorific"]=name_df["honorific"].str.strip()
name_df["name"]=name_df["name"].str.strip()
name_df

이번엔 호칭별로 분석을 하기위해 Name 카테고리를 작업해준다.

,나 .를 기준으로 Name 값을 구분하여 name_df에 저장한다.

이후 공백을 모두 제거해준다.

추가로 호칭마다 인원수도 세주고, 연령 분포도 확인해준다.

이는 호칭별 연령 분포에 대해 시각화한 것이다.

이를 통해 호칭에 따라 평균 연령에 차이가 있음을 확인할 수 있다.

이후 호칭에 따라 생존율에도 차이가 있는지 확인해본다.

 

#데이터 결합
train_df = pd.concat([train_df, name_df[0:len(train_df)].reset_index(drop=True)], axis=1)
test_df = pd.concat([test_df, name_df[len(train_df):].reset_index(drop=True)], axis=1)
#호칭별 생존 여부 집계
honorific_df = train_df[["honorific", "Survived", "PassengerId"]].dropna().groupby(["honorific", "Survived"]).count().unstack()
honorific_df.plot.bar(stacked=True)

결측치를 제외한 호칭, 생존율 데이터로 시각화한 것이 아래와 같다.

 

honorific_age_mean = all_df[["honorific","Age"]].groupby("honorific").mean().reset_index()
honorific_age_mean.columns=["honorific","honorific_Age"]
all_df = pd.merge(all_df, honorific_age_mean, on="honorific", how="left")
all_df.loc[(all_df["Age"].isnull()), "Age"] = all_df["honorific_Age"]
all_df = all_df.drop(["honorific_Age"], axis=1)
honorific_age_mean

결측치를 호칭별 평균 연령으로 보완해준다.

추가로 가족 인원수, 홀로 승선했는지에 대한 여부, 불필요한 변수 삭제와 같은

작업을 진행할 것이다.

#가족 인원수 추가하기
all_df["family_num"] = all_df["Parch"] + all_df["SibSp"]
all_df["family_num"].value_counts()

#홀로 승선했는지 여부
all_df.loc[all_df["family_num"]==0, "alone"] = 1
all_df["alone"].fillna(0, inplace=True)

#불필요한 변수 제거
all_df = all_df.drop(["PassengerId", "Name", "family_name", "name", "Ticket", "Cabin"], axis=1)
all_df.head()

이제는 카테고리 변수를 수치로 변환해야 한다.

이를 위해 object인 경우 categories 변수로 관리한다.

categories= all_df.columns[all_df.dtypes=="object"]
all_df.loc[~((all_df["honorific"]=="Mr") | (all_df["honorific"]=="Miss") | (all_df["honorific"]=="Mrs") | (all_df["honorific"]=="Master")),"honorific"]="other"
all_df.honorific.value_counts()

 

또한 Mr, Miss, Mrs, Master 이외의 호칭은 other로 통합해준다.

le = LabelEncoder()
le = le.fit(all_df["Sex"])
all_df["Sex"] = le.transform(all_df["Sex"])

for cat in categories:
    le = LabelEncoder()
    print(cat)
    if all_df[cat].dtypes == "object":
        le= le.fit(all_df[cat])
        all_df[cat] = le.transform(all_df[cat])
all_df.head()

문자열을 수치로 변환할 때 라벨 인코딩을 사용한다.

이전에는 더미 변수화 방법을 사용했었다.

 

4. 모델링(LightGBM) - 결정 트리 알고리즘

#랜덤 포레스트 - 결정 트리를 여러 개 만들어 합하는 방식 (앙상블)
#그레이디언트 부스팅 결정 트리 - 결정 트리를 순서대로 갱신
#특징 : 빠르고 결측치나 카테고리 변수가 포함된 상태에서도 모델 학습 가능

import lightgbm as lgb

#혿드 아웃 :학습, 검증 데이터 분할
#교차 검증 : 검증 데이터 사용해서 반복 
#잭나이프법 : 전체 테스트 중 하나를 검증 데이터로, 나머지를 학습데이터로 데이터 수만큼 반복
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
X_train, X_valid, y_train, y_valid = train_test_split(train_X, train_Y, test_size=0.2)

#LightGBM용 데이터셋
categories = ["Embarked", "Pclass", "Sex", "honorific", "alone"]
lgb_train = lgb.Dataset(X_train, y_train, categorical_feature=categories)
lgb_eval = lgb.Dataset(X_valid, y_valid, categorical_feature=categories, reference=lgb_train)

#하이퍼파라미터 - 2차분류
lgbm_params={
    "objective":"binary",
    "random_seed":1234
}
#모델 학습
model_lgb = lgb.train(lgbm_params, lgb_train, valid_sets=lgb_eval, num_boost_round=100, early_stopping_rounds=20, verbose_eval=10)

#변수의 중요도
importance = pd.DataFrame(model_lgb.feature_importance(), index=X_train.columns, columns=["importance"]).sort_values(by="importance", ascending=True)
importance.plot.barh()

train_test_split을 통해 홀드 아웃을 진행해준다.

이를 lgb에 사용할 train, test 데이터로 구분하여 저장한다.

train 데이터셋을 이용해 학습하였을 때 가장 중요도가 높은 변수가 무엇인지 시각화한다.

 

#검증 데이터로 예측 정확도 확인
from sklearn.metrics import accuracy_score
y_pred = model_lgb.predict(X_valid, num_iteration=model_lgb.best_iteration)
accuracy_score(y_valid, np.round(y_pred)) #정확도

검증 데이터를 사용해 예측 정확도를 확인해본다.

정확도는 accuracy_socre를 사용했다.

더 좋은 정확도를 얻기 위해 변화를 준다.

 

#하이퍼파라미터 변경
lgbm_params={
    "objective" : "binary",
    "max_bin" : 331,
    "num_leaves" : 20,
    "min_data_in_leaf" : 57,
    "andom_seed" : 1234
}
lgb_train = lgb.Dataset(X_train, y_train, categorical_feature=categories)
lgb_eval = lgb.Dataset(X_valid, y_valid, categorical_feature=categories, reference=lgb_train)
model_lgb = lgb.train(lgbm_params, lgb_train, valid_sets=lgb_eval, num_boost_round=100, early_stopping_rounds=20, verbose_eval=10)

위와 같이 하이퍼파라미터를 변경하여 학습하여 정확도를 확인해보면

이전보다 소폭 상승된 것을 확인할 수 있다.

#교차 검증
folds=3
kf = KFold(n_splits=folds)
models=[]
for train_index, val_index in kf.split(train_X):
    X_train = train_X.iloc[train_index]
    X_valid = train_X.iloc[val_index]
    y_train = train_Y.iloc[train_index]
    y_valid = train_Y.iloc[val_index]
    
    lgb_train = lgb.Dataset(X_train, y_train, categorical_feature=categories)
    lgb_eval = lgb.Dataset(X_valid, y_valid, categorical_feature=categories, reference=lgb_train)
    model_lgb = lgb.train(lgbm_params, lgb_train, valid_sets=lgb_eval, num_boost_round=100, early_stopping_rounds=20, verbose_eval=10)
    y_pred = model_lgb.predict(X_valid, num_iteration=model_lgb.best_iteration)
    print(accuracy_score(y_valid, np.round(y_pred)))
    models.append(model_lgb)

마지막으로 교차 검증을 이용하여 학습한다.

3폴드 교차 검증을 진행했다.

마찬가지로 lgb에 맞는 데이터셋을 구현하고 이 데이터를 이용하여 train한다.

그리고 그 정확도를 확인한다.

 

#테스트 데이터 예측 결과 산출
preds=[]
for model in models:
    pred=model.predict(test_X)
    preds.append(pred)

#예측 결과 평균
preds_array = np.array(preds)
preds_mean  = np.mean(preds_array, axis=0)
preds_int = (preds_mean>0.5).astype(int)

#파일 생성
submission["Survived"] = preds_int
submission

submission.to_csv("titanic_submit01.csv", index=False)

마지막으로 test 데이터를 사용해 예측한 결과에 대한 파일을 만들어 캐글에 제출한다.