데이터/데이터 사이언스 및 분석

[캐클 입문] 타이타닉 생존자 예측하기 (전처리부터 예측까지)

개발하는 감자입니다 2023. 12. 17. 03:45
728x90

안녕하세요, 개발감자 박그냥입니다.

캐글 데이터 분석의 입문 데이터로 알려진 타이타닉 데이터셋을 이용하여

생존자를 예측하는 과정을 한 번 진행해보려고 합니다. 

분석에 대한 기본적인 개념이 있으신 분들이 실습을 해볼 때에 참고해보시면 좋을 것 같습니다.

 

 

Titanic - Machine Learning from Disaster | Kaggle

 

www.kaggle.com

 

목차

1) 데이터 가져오기

2) 데이터 전처리 및 시각화

3) 모델 학습 및 평가

4) 데이터 예측

1) 데이터 가져오기

데이터를 가져오기 전에 필요한 패키지를 임포트하도록 하겠습니다. 

 

# 패키지 임포트하기
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline

 

패키지를 임포트했으면 데이터를 불러와야겠죠?

저는 구글 코랩 환경에서 분석을 진행하고 있습니다. 그래서 구글 드라이브에 캐글에서 다운로드 받은 파일을 업로드 하였고,

이를 구글 드라이브와 연동하여 사용해보려고 합니다. 자세한 설명은 더 잘 설명해주신 분의 글이 있어 첨부해놓겠습니다.

# 데이터 불러오기
df_gender_submission = pd.read_csv("/content/drive/MyDrive/포트폴리오/input/titanic/gender_submission.csv")
df_test = pd.read_csv("/content/drive/MyDrive/포트폴리오/input/titanic/test.csv")
df_train = pd.read_csv("/content/drive/MyDrive/포트폴리오/input/titanic/train.csv")
df_train

 

캐글에 나와있는 변수 정의를 한 번 살펴볼까요? 이 변수 정의를 보고 데이터를 전처리해봅시다.

 

**변수 정의**

- survival 생존 0 = 아니오, 1 = 예

- pclass 티켓 등급 (1 = 1등석, 2 = 2등석, 3 = 3등석)

- sex 성별

- Age 나이 (세)

- sibsp 타이타닉호에 탑승한 형제자매/배우자의 수

- parch 타이타닉호에 탑승한 부모/자녀의 수

- ticket 티켓 번호

- fare 승객 운임(요금)

- cabin 선실 번호

- embarked 탑승 항구 (C = 쉐부르, Q = 퀸스타운, S = 사우스햄톤)

 

변수 참고 사항

- pclass: 사회경제적 지위(Socio-Economic Status, SES)의 대리자

1st = 상류층

2nd = 중산층

3rd = 하류층

- age: 나이가 1보다 작으면 소수점 형태로 표시됩니다. 만약 나이가 추정되었다면 xx.5 형식으로 표시됩니다.

- sibsp: 이 데이터셋은 가족 관계를 다음과 같이 정의합니다...

Sibling = 형제, 자매, 이복형제, 이복자매

Spouse = 남편, 아내 (연인 및 약혼자는 무시됨)

parch: 이 데이터셋은 가족 관계를 다음과 같이 정의합니다...

Parent = 어머니, 아버지

Child = 딸, 아들, 이복딸, 이복아들

일부 어린이는 유모와 함께만 여행했으므로 그들의 경우 parch=0으로 표시됩니다.

 

2) 데이터 전처리 및 시각화

데이터를 불러왔으면 데이터 모델에 적용하기 위해서 전처리를 해주어야 합니다.

칼럼을 속성하기 위하여 아래의 코드를 실행해보세요.

# 칼럼 속성 확인하기
print("칼럼 속성 확인하기")
df_train.info()

 

Non-Null은 Null 이 아닌 값을 보여주는 것이고, Dtype은 열의 속성을 알려주는 것입니다.

Non-Null의 경우 891 이외에 다른 값들이 보이는 데 이러한 값을 가진 열은 결측치를 가진다는 뜻입니다.

연속형 변수는 카테고리화와 레이블 인코딩을 진행해주고, 필요없는 열을 삭제할 것입니다.

# 결측치 제거
# Age - 평균값으로 대체
df_train['Age'].fillna(df_train['Age'].mean(), inplace = True)
# Cabin - N으로 대체
df_train['Cabin'].fillna('N', inplace = True)
# Embarked - N으로 대체
df_train['Embarked'].fillna('N', inplace = True)
list_col = ['Age', 'Cabin', 'Embarked']
for col in list_col:
  print( col+' null data count :', df_train[col].isnull().sum())

 

# 1) name - 데이터 전처리
# mr, mrs, miss, Dr, Rev, Col 이 포함된 경우 -> 이름 제외한 후 카테고리화
# name 열은 삭제합니다.
def transform_status(x):
    if "Mrs" in x or "Ms" in x:
        return "Mrs"
    elif "Mr" in x:
        return "Mr"
    elif "Miss" in x:
        return "Miss"
    elif "Master" in x:
        return "Master"
    elif "Dr" in x:
        return "Dr"
    elif "Rev" in x:
        return "Rev"
    elif "Col" in x:
        return "Col"
    else:
        return "0"

df_train["social_status"] = df_train["Name"].map(lambda x : transform_status(x))

# name 열은 삭제합니다.
del df_train['Name']

 

# 2) Sex - 전처리 (male : 0 / female :1)
df_train['Sex'] = df_train['Sex'].replace({"male" : 0, "female" : 1 })
df_train['Sex']

# 전처리가 잘 된 것을 확인할 수 있음

# 3) Ticket - 티켓번호는 랜덤이므로 상관없을 것이라 예상하여 삭제합니다.
del df_train['Ticket']

# 4) Carbin (선실 번호) - 맨 앞 글자만 추출하여 대체합니다.
df_train['Cabin'] = df_train['Cabin'].str[:1] # str : 글자 추출 메소드
df_train['Cabin']

 

# 5) Fare - 그래프 그려서 카테고리화 고민해보기
import seaborn as sns
import matplotlib.pyplot as plt

# df_train은 데이터프레임의 이름이라고 가정합니다.
sns.histplot(data=df_train, x="Fare", bins=30)  # 히스토그램을 30개의 구간으로 나누어 그립니다.

# 그래프 제목과 축 이름 설정
plt.title('Distribution of Fare')
plt.xlabel('Fare')
plt.ylabel('Frequency')

# 그래프 보여주기
plt.show()

# 그래프를 봤을 때 카테고리화 시키기는 어려워보입니다.
del df_train['Fare']

# 6) Age - 연속형 변수를 카테고리화 합니다.
sns.histplot(data=df_train, x="Age", bins=10)  # 히스토그램을 30개의 구간으로 나누어 그립니다.

# 그래프 제목과 축 이름 설정
plt.title('Distribution of Age')
plt.xlabel('Age')
plt.ylabel('Frequency')

# 그래프 보여주기
plt.show()

# df_train.describe()을 봤을 때 age의 Min과 max가 0에서 80입니다.
# 아기부터 노인까지 직접 나이를 설정하여 카테고리화합니다.
# 카테고리값 할당을 위한 함수
def get_category(age) :
    cat = ''
    if age <= -1 : cat = 'Unknown'
    elif age <= 5 : cat = 'Baby'
    elif age <= 12 : cat = 'Child'
    elif age <= 18 : cat = 'Teenager'
    elif age <= 25 : cat = 'Student'
    elif age <= 35 : cat = 'Young Child'
    elif age <= 60 : cat = 'Adult'
    else : cat = 'Elderly'
    
    return cat

df_train['Age_cat'] = df_train['Age'].apply(lambda x : get_category(x))
# 원래 있던 'Age'열은 삭제합니다.
del df_train['Age']
df_train # 카테고리화를 완료한 데이터를 확인해봅시다.

지금까지 한 작업은 필요없는 열은 제거하고, 카테고리화 시킬 수 있는 열은 카테고리화시키는 것입니다.

아직 Cabin, Embarked, soscial_status, Age_cat의 Dtype이 object로 나타납니다.

레이블 인코딩 패키지를 이용하여 숫자로 바꿔볼까요?

from sklearn import preprocessing

list_col = ['Cabin','Embarked','social_status','Age_cat']
for col in list_col :
        le = preprocessing.LabelEncoder()
        le.fit(df_train[col])
        df_train[col] = le.transform(df_train[col])
        label_mapping = dict(zip(le.classes_, le.transform(le.classes_)))  # 원래 값과 인코딩된 값을 매핑한 딕셔너리 생성
        print(f"Label Mapping for '{col}': {label_mapping}")

 

Label Mapping for 'Cabin': {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'N': 7, 'T': 8}

Label Mapping for 'Embarked': {'C': 0, 'N': 1, 'Q': 2, 'S': 3}

Label Mapping for 'social_status': {'0': 0, 'Col': 1, 'Dr': 2, 'Master': 3, 'Miss': 4, 'Mr': 5, 'Mrs': 6, 'Rev': 7}

Label Mapping for 'Age_cat': {'Adult': 0, 'Baby': 1, 'Child': 2, 'Elderly': 3, 'Student': 4, 'Teenager': 5, 'Young Child': 6}

 

아래는 레이블 인코딩을 어떻게 시켰는지 출력한 것입니다. 시각화할 때에 참고하면 좋겠죠?

 

# 시각화를 하는데에 PassengerId는 필요없어보입니다. 그저 승객번호이기 때문입니다.
del df_train['PassengerId']
df_train

 

이제, 데이터 시각화는 바 그래프를 이용하여 시각화해보겠습니다.

 

seaborn 패키지를 활용하여 시각화를 해보았습니다. x축에는 성별, y축에는 생존여부를 넣고, 데이터는 df_train을 활용한다는 뜻입니다.

성별 뿐만 아니라 다른 것들도 시각화해보세요!

sns.barplot(x = 'Sex', y = 'Survived', data = df_train)
# 여성이 더 생존을 많이 한 것을 알 수 있습니다.

 

 

상관계수를 한 번 시각화해볼까요?

# 상관계수를 시각화해봅시다.
sns.clustermap(df_train.corr(), 
               annot = True,      # 실제 값 화면에 나타내기
               cmap = 'RdYlBu_r',  # Red, Yellow, Blue 색상으로 표시
               vmin = -1, vmax = 1, #컬러차트 -1 ~ 1 범위로 표시
              )
# 빨갛수록 더 상관이 많은 것입니다. SibSp<Parch<Sex순으로 빨간 편에 속합니다.

 

생존여부와 가장 관련있는 것은 SibSp<Parch<Sex순이라는 것이 나타나고 있습니다.

3) 모델 학습 및 평가

시각화를 통해서 데이터를 파악해보는 시간을 가졌습니다. 이제 모델을 생성하고 학습하고 평가해볼까요?

# 데이터를 3:7로 나누어봅시다.
from sklearn.model_selection import train_test_split

y_titanic_df = df_train['Survived']
X_titanic_df = df_train.drop('Survived', axis = 1, inplace = False)
X_train, X_test, y_train, y_test = train_test_split(X_titanic_df, y_titanic_df, test_size = 0.3, random_state = 11)


from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

# 각 모델에 대한 Classifier 클래스 생성
dt_clf = DecisionTreeClassifier(random_state = 11)
rf_clf = RandomForestClassifier(random_state = 11)
lr_clf = LogisticRegression(random_state = 11)

# DecisionTreeClassfier 학습/예측평가
dt_clf.fit(X_train, y_train)
dt_pred = dt_clf.predict(X_test)
print('DecisionTreeClassifier 정확도 : {0:.4f}'.format(accuracy_score(y_test, dt_pred)))

# RandomForestClassifier 학습/예측/평가
rf_clf.fit(X_train, y_train)
rf_pred = rf_clf.predict(X_test)
print('RandomForestClassifier 정확도 : {0:.4f}'.format(accuracy_score(y_test, rf_pred)))

# LogisticRegression 학습/예측/평가
lr_clf.fit(X_train, y_train)
lr_pred = lr_clf.predict(X_test)
print('LogisticRegression 정확도 : {0:.4f}'.format(accuracy_score(y_test, lr_pred)))

#0.8이상의 정확도가 나온 것을 알 수 있습니다. 그 중에서도 랜덤포레스트가 가장 높게 나타납니다.

 

랜덤포레스트가 가장 높게 나타난 것을 알 수 있습니다.

# 교차검증을 통해 결정트리 모델을 평가해볼까요?
from sklearn.model_selection import KFold

def exec_kfold(clf, folds = 5) :
    # 폴드 세트가 5개인 KFold 객체 생성. 폴드 수만큼 예측결과 저장 위한 리스트 생성
    kfold = KFold(n_splits = folds)
    scores = []
    
    # KFold 교차 검증 수행
    for iter_count, (train_index, test_index) in enumerate(kfold.split(X_titanic_df)) :
        # X_titanic_df 데이터에서 교차 검증별로 학습과 검증 데이터를 가리키는 index 생성
        X_train, X_test = X_titanic_df.values[train_index], X_titanic_df.values[test_index] # values를 통해 df를 ndarray로 변환
        y_train, y_test = y_titanic_df.values[train_index], y_titanic_df.values[test_index]
        
        # Classifier 학습/예측/평가
        clf.fit(X_train, y_train)
        clf_pred = clf.predict(X_test)
        accuracy = accuracy_score(y_test, clf_pred)
        scores.append(accuracy)
        print("교차 검증 {0} 정확도 : {1:.4f}".format(iter_count+1, accuracy))
        
    # 5개의 fold에서 평균 계산
    mean_score = np.mean(scores)
    print("평균 정확도: {0:.4f}".format(mean_score))

#exec_fold 호출
exec_kfold(dt_clf, folds = 5)

from sklearn.model_selection import cross_val_score

scores = cross_val_score(dt_clf, X_titanic_df, y_titanic_df, cv = 5)
print("scores : ", scores)
for iter_count, accuracy in enumerate(scores) :
    print("교차 검증 {0} 정확도: {1:.4f}".format(iter_count+1, accuracy))
    
print("평균 정확도: {0:.4f}".format(np.mean(scores)))

#GridSearhCV을 이용하여 최적의 하라미터값을 구해볼까요?
# 결정트리
from sklearn.model_selection import GridSearchCV

parameters = {'max_depth':[2, 3, 5, 10],
             'min_samples_split':[2, 3, 5],
             'min_samples_leaf':[1, 5, 8]}
grid_dclf = GridSearchCV(dt_clf, param_grid = parameters, scoring = 'accuracy', cv = 5)
grid_dclf.fit(X_train, y_train)

print('GridSearchCV 최적 하이퍼 파라미터 : ', grid_dclf.best_params_)
print('GridSearchCV 최고 정확도: {0:.4f}'.format(grid_dclf.best_score_))
best_dclf = grid_dclf.best_estimator_

#GridSearchCV의 최적 하이퍼 파라미터로 학습된 Estimator로 예측 및 평가 수행
dpredictions = best_dclf.predict(X_test)
accuracy = accuracy_score(y_test, dpredictions)
print('테스트 세트에서의 DecisionTreeClassifier 정확도 : {0:.4f}'.format(accuracy))

# 최적의 하이퍼 파라미터로 학습시키니 정확도가 0.8172까지 올라온 것을 확인할 수 있습니다.

parameters = {
    'n_estimators': [100, 200],
    'max_depth': [5, 10],
    'min_samples_split': [2, 4]
}
grid_rclf = GridSearchCV(rf_clf, param_grid = parameters, scoring = 'accuracy', cv = 5)
grid_rclf.fit(X_train, y_train)

print('GridSearchCV 최적 하이퍼 파라미터 : ', grid_rclf.best_params_)
print('GridSearchCV 최고 정확도: {0:.4f}'.format(grid_rclf.best_score_))
best_rclf = grid_rclf.best_estimator_

#GridSearchCV의 최적 하이퍼 파라미터로 학습된 Estimator로 예측 및 평가 수행
dpredictions = best_rclf.predict(X_test)
accuracy = accuracy_score(y_test, dpredictions)
print('테스트 세트에서의 RandomForestClassifier 정확도 : {0:.4f}'.format(accuracy))

parameters = {'C': [0.1, 1, 10]}

grid_lclf = GridSearchCV(lr_clf, param_grid = parameters, scoring = 'accuracy', cv = 5)
grid_lclf.fit(X_train, y_train)

print('GridSearchCV 최적 하이퍼 파라미터 : ', grid_lclf.best_params_)
print('GridSearchCV 최고 정확도: {0:.4f}'.format(grid_lclf.best_score_))
best_lclf = grid_lclf.best_estimator_

#GridSearchCV의 최적 하이퍼 파라미터로 학습된 Estimator로 예측 및 평가 수행
dpredictions = best_lclf.predict(X_test)
accuracy = accuracy_score(y_test, dpredictions)
print('테스트 세트에서의 LogisticRegression 정확도 : {0:.4f}'.format(accuracy))

가장 성능이 높게 나온 모델을 저장하고 데이터 예측할 때 사용해볼게요!

# 랜덤포레스트분류가 가장 정확도가 높게 나오는 것을 알 수 있습니다. 
# 최적의 분류 모델을 저장합니다.
import joblib

# 위에서 최적의 랜덤 포레스트 분류기가 best_rclf 가정합니다.
best_rclf.fit(X_train, y_train)  # 최적의 모델을 다시 전체 훈련 데이터로 학습시킵니다.

# 모델 저장
joblib.dump(best_rclf, 'best_random_forest_model.pkl')

4) 데이터 예측

데이터를 예측하기 위해서 test.csv 를 활용할겁니다. 아까 저장한 모델을 가져와서 바로 예측해볼게요!

( 예측하기 전에 전처리를 똑같이 진행해주셔야 합니다! 다운 받은 데이터 그대로를 예측하면 예측 안 될 겁니다.)

 

loaded_model = joblib.load('best_random_forest_model.pkl')
predictions = loaded_model.predict(df_test)
print(predictions)

와우! 예측이 완료되었어요.

위에서 우리 변수 정의한 거 기억나나요? Survived는 0은 사망 1은 생존을 의미합니다.

이 예측 결과를 한 번 시각화해볼게요.

# 예측 결과의 분포를 시각화하는 막대 그래프
plt.figure(figsize=(6, 4))
survived_count = np.sum(predictions)  # 생존자 수
not_survived_count = len(predictions) - survived_count  # 사망자 수

plt.bar(['Not Survived', 'Survived'], [not_survived_count, survived_count], color=['skyblue', 'salmon'])
plt.xlabel('Outcome')
plt.ylabel('Count')
plt.title('Survival Prediction Distribution')
print(f'Not Survived:{not_survived_count} , Survived :{survived_count}')
plt.show()

 

전체(418) 중 261명은 사망 157명은 생존했을 것이라 예측하고 있습니다.

이렇게 타이타닉 데이터를 이용해서 예측까지 완료해보았습니다.

기본 데이터들 뿐만 아니라 다른 새로운 데이터를 이용해서 예측해보세요! 

 

 

더 자세한 코드가 궁금하신 분들을 위해 제 깃허브에 올린 자료를 공유하도록 하겠습니다.

 

GitHub - Park-kxng/titanic_survivor_prediction

Contribute to Park-kxng/titanic_survivor_prediction development by creating an account on GitHub.

 

 

github.co

 

 

그럼 더 알찬 포스팅으로 돌아올게요! 개발감자였습니다.

 


참고한 블로그 :

https://computer-science-student.tistory.com/113

728x90
반응형