5 분 소요

1. 타이타닉 데이터 살펴보기

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

titanic_df = pd.read_csv('./titanic/train.csv')
titanic_df

image

  • 컬럼
    • Passengerid: 탑승자 데이터 일련번호
    • survived: 생존여부, 0=사망, 1=생존
    • pclass: 티켓의 선실 등급 (1=일등석, 2=이등석, 3=삼등석)
    • sex: 탑승자 성별
    • name: 탑승자 이름
    • Age: 탑승자 나이
    • sibsp: 같이 탑승한 형제자매 또는 배우자 인원수
    • parch: 같이 탑승한 부모님 또는 어린이 인원수
    • ticket: 티켓번호
    • fare:요금
    • cabin: 선실번호
    • embarked:중간정착항구 C=Cherbourg, Q=Queenstown, S=Southampton

1) Null 처리

titanic_df.info()
---------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890               ## 전체 로우 수 
Data columns (total 12 columns):                ## 전체 컬럼 수
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
  • RangeIndex : DataFrame 인덱스의 범위 → 전체 로우 수
  • Age, Cabin, Embarked 칼럼은 각각 714개, 204개, 889개의 Not Null 값을 가지고 있다. ⇒ 사이킷런 머신러닝 알고리즘은 Null 값을 허용하지 않으므로 Null 값을 어떻게 처리할지 결정해야 한다.
# 사이킷런 머신러닝 알고리즘은 NULL 값을 허용하지 않으므로 NULL을 처리해줘야 한다. 
titanic_df['Age'].fillna(titanic_df['Age'].mean(), inplace=True)
titanic_df['Cabin'].fillna('N', inplace=True)
titanic_df['Embarked'].fillna('N', inplace=True)

# null 개수 확인
print('after null 개수 : ',titanic_df.isnull().sum().sum())

2) 값 분포 확인

  • Cabin(선실) 컬럼의 데이터 분포 확인
titanic_df['Cabin'].value_counts()    
-------------------------------------------------------------------------------
N              687                # N이 압도적으로 많다.
C23 C25 C27      4                # 여러 Cabin이 한거번에 표기됨
G6               4
B96 B98          4
C22 C26          3
              ... 
E34              1
C7               1
C54              1
E36              1
C148             1
Name: Cabin, Length: 148, dtype: int64

  • 데이터 분포를 균일하게 맞추기
titanic_df['Cabin'] = titanic_df['Cabin'].str[:1]
titanic_df['Cabin'].value_counts()
---------------------------------------------------------------------------------
N    687
C     59
B     47
D     33
E     32
A     15
F     13
G      4
T      1
Name: Cabin, dtype: int64

3) 성별과 선실 등급에 따른 생존자 수 확인

  • 성별에 따른 생존자 수 비교
# 성별에 따른 생존자 수 비교
titanic_df.groupby(['Sex', 'Survived'])['Survived'].count()
------------------------------------------------------------------------------
Sex     Survived
female  0            81
        1           233
male    0           468
        1           109
Name: Survived, dtype: int64
sns.barplot(x='Sex', y = 'Survived', data=titanic_df)

image

  • 선실 등급에 따른 생존자 분포
sns.barplot(x='Pclass', y = 'Survived', hue='Sex' ,data=titanic_df)

image

  • 연령대에 따른 생존자 분포
# Age에 따른 생존 확률 -> 값 종류가 많으므로 범위별로 분류해 카테고리 값을 할당

# 입력 age에 따라 구분 값을 반환하는 함수 설정. DataFrame의 apply lambda 식에 활용
def get_category(age):
    cat = ''
    if age <=-1: cat='Unkown'
    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 Adult'
    elif age <=60: cat='Adult'
    else: cat='Elderly'
    
    return cat

# 막대 그래프의 크기 figure를 더 크게 설정
plt.figure(figsize=(10, 6))

# X축의 값을 순차적으로 표시하기 위한 설정
group_names=['Unkown', 'Baby', 'Child', 'Teenager', 'Student', 'Young Adult', 'Adult', 'Elderly']

titanic_df['Age_cat'] = titanic_df['Age'].apply(lambda x : get_category(x))

sns.barplot(x='Age_cat', y = 'Survived', hue='Sex', data=titanic_df, order=group_names)

titanic_df.drop('Age_cat', axis=1, inplace=True)

image

4) 문자열 타입 → 숫자형 피처로 변환

  • LabelEncoder : 카테고리 값의 유형 수에 따라 0 ~(카테고리 유형 수 -1)까지의 숫자 값으로 변환
from sklearn import preprocessing
# 남아있는 문자열 카테고리 피처를 숫자형 카테고리 피처로 변환 => LabelEncoder

def encode_features(dataDF):
    features = ['Cabin', 'Sex', 'Embarked']
    for feature in features:
        le = preprocessing.LabelEncoder()
        le = le.fit(dataDF[feature])
        dataDF[feature] = le.transform(dataDF[feature])
    
    return dataDF

titanic_df = encode_features(titanic_df)
titanic_df.head()

2. 타이타닉 머신러닝 분석 수행

1) 데이터 전처리

  • 데이터 전처리 함수 생성
# 데이터 전처리 함수 
def fillna(df):
    df['Age'].fillna(df['Age'].mean(), inplace=True)
    df['Cabin'].fillna('N', inplace=True)
    df['Embarked'].fillna('N', inplace=True)
    df['Fare'].fillna(0, inplace=True)
    return df

# 머신러닝 알고리즘에 불필요한 속성 제거
def drop_features(df):
    df.drop(['PassengerId', 'Name','Ticket'], axis=1, inplace=True)
    return df

# 레이블 인코딩 수행
def format_features(df):
    df['Cabin'] = df['Cabin'].str[:1]
    features = ['Cabin', 'Sex', 'Embarked']
    for feature in features:
        le = preprocessing.LabelEncoder()
        le = le.fit(df[feature])
        df[feature] = le.transform(df[feature])
    return df

def transform_features(df):
    df = fillna(df)
    df = drop_features(df)
    df = format_features(df)
    return df
  • 데이터 전처리 수행
from sklearn import preprocessing

titanic_df = pd.read_csv('./titanic/train.csv')
y_titanic_df = titanic_df['Survived']
X_titanic_df = titanic_df.drop('Survived', axis=1)

X_titanic_df = transform_features(X_titanic_df)
X_titanic_df.head(3)

2) 머신러닝 분석 수행

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

# 테스트 데이터 세트 추출
X_train, X_test, y_train, y_test = train_test_split(X_titanic_df, y_titanic_df, test_size=0.2, random_state=11)

# 머신러닝 알고리즘 3개 비교
# 결정트리, Random Forest, 로지스틱 회귀를 위한 사이킷런 Classifier 클래스 생성
dt_clf = DecisionTreeClassifier(random_state=11)
rf_clf = RandomForestClassifier(random_state=11)
lr_clf = LogisticRegression()

# DecisionTreeClassifier 학습/예측/평가
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('RandomForest 정확도 : {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('logistic Regression 정확도 : {0:.4f}'.format(accuracy_score(y_test, lr_pred)))
  • DecisionTreeClassifier 정확도: 0.7877
  • RandomForestClassifier 정확도:0.8547
  • LogisticRegression 정확도: 0.8492

3) 교차 검증

  • KFold 교차 검증
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]
        y_train, y_test = y_titanic_df.values[train_index], y_titanic_df.values[test_index]
        
        # Classifier 학습, 예측, 정확도 계산 
        clf.fit(X_train, y_train)
        predictions = clf.predict(X_test)
        accuracy = accuracy_score(y_test, predictions)
        scores.append(accuracy)
        print("교차 검증 {0} 정확도: {1:.4f}".format(iter_count, accuracy))     
    
    # 5개 fold에서의 평균 정확도 계산. 
    mean_score = np.mean(scores)
    print("평균 정확도: {0:.4f}".format(mean_score)) 

# exec_kfold 호출
exec_kfold(dt_clf , folds=5)
교차 검증 0 정확도: 0.7542
교차 검증 1 정확도: 0.7809
교차 검증 2 정확도: 0.7865
교차 검증 3 정확도: 0.7697
교차 검증 4 정확도: 0.8202
평균 정확도: 0.7823
  • cross_val_score() 검증
    • K 폴드의 평균 정확도와 다름
    • StratifiedKFold를 이용해 폴드 세트를 분할하기 때문
from sklearn.model_selection import cross_val_score

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

print("평균 정확도 : {0: .4f}.format(np.mean(scores))")
교차 검증 0 정확도 : 0.7430
교차 검증 1 정확도 : 0.7753
교차 검증 2 정확도 : 0.7921
교차 검증 3 정확도 : 0.7865
교차 검증 4 정확도 : 0.8427
평균 정확도 : {0: .4f}.format(np.mean(scores))
  • GridSearchCV
    • cv : 5개의 폴드 세트 지정
    • 하이퍼 파라미터 : max_depth, min_samples_split, min_samples_leaf를 변경하면서 성능을 측정
    • 최적 하이퍼 파라미터로 학습된 Estimator를 이용해 train_test_split()으로 분리된 테스트 데이터 세트에 예측을 수행해 예측 정확도를 출력
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))
GridSearchCV 최적 하이퍼 파라미터 : {'max_depth': 3, 'min_samples_leaf': 5, 'min_samples_split': 2}
GridSearchCV 최고 정확도 :  0.7992
테스트 세트에서의 DecisionTreeClassifier 정확도 : 0.8715