안녕하세요, 개발감자 박그냥입니다.
캐글 데이터 분석의 입문 데이터로 알려진 타이타닉 데이터셋을 이용하여
생존자를 예측하는 과정을 한 번 진행해보려고 합니다.
분석에 대한 기본적인 개념이 있으신 분들이 실습을 해볼 때에 참고해보시면 좋을 것 같습니다.
Titanic - Machine Learning from Disaster | Kaggle
www.kaggle.com
목차
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
그럼 더 알찬 포스팅으로 돌아올게요! 개발감자였습니다.
참고한 블로그 :
'데이터 > 데이터 사이언스 및 분석' 카테고리의 다른 글
[ 데이터 분석 마스터 ] #2. 머신러닝의 학습 프로세스와 종류 (2) | 2023.10.25 |
---|---|
[ 데이터 분석 마스터 ] #1. 머신러닝 톺아보기 (feat. 추천 알고리즘) (2) | 2023.10.24 |