- 1 Introduction
- 2 Load and check data
- 2.1 load data
- 2.2 Outlier detection
- 2.3 joining train and test set
- 2.4 check for null and missing values
- 3 Feature analysis
- 3.1 Numerical values
- 3.2 Categorical values
- 4 Filling missing Values
- 4.1 Age
- 5 Feature engineering
- 5.1 Name/Title
- 5.2 Family Size
- 5.3 Cabin
- 5.4 Ticket
- 6 Modeling
- 6.1 Simple modeling
- 6.1.1 Cross validate models
- 6.1.2 Hyperparamater tunning for best models
- 6.1.3 Plot learning curves
- 6.1.4 Feature importance of the tree based classifiers
- 6.2 Ensemble modeling
- 6.2.1 Combining models
- 6.3 Prediction
- 6.3.1 Predict and Submit results
- 6.1 Simple modeling
1. Introduction
- 해당 작성자의 첫번째 커널로 타이타닉을 선택한 이유는 데이터 엔지니어링과 모델링에 있어 좋은 feature를 가지고 있다고 한다.
- 첫번째로 몇몇의 feature를 분석하고 그것에 집중하여 엔지니어링을 할것이다
- 마지막 부분은 voting procedure를 사용하여 타이타닉의 생존을 모델링하고 예측할것 이다.
해당 노트북은 아래의 스크립트를 따라간다. :
- Feature analysis
- Feature engineering
- Modeling
- 아래 출처의 notebook을 따라서 필사한것 임
- 출처 : https://www.kaggle.com/yassineghouzam/titanic-top-4-with-ensemble-modeling
필수 패키지 import
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
from collections import Counter
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier, GradientBoostingClassifier, ExtraTreesClassifier, VotingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.svm import SVC
from sklearn.model_selection import GridSearchCV, cross_val_score, StratifiedKFold, learning_curve
sns.set(style = 'white', context = 'notebook', palette = 'deep')
2. Load and check data
2.1 Load data
1
2
3
train = pd.read_csv('data/train.csv')
test = pd.read_csv('data/test.csv')
IDtest = test['PassengerId']
2.2 Outlier detection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def detect_outliers(df, n, features):
outlier_indices = []
for col in features:
Q1 = np.percentile(df[col], 25)
Q3 = np.percentile(df[col], 75)
IQR = Q3 - Q1
outlier_step = 1.5 * IQR
outlier_list_col = df[(df[col] < Q1 - outlier_step) | (df[col] > Q3 + outlier_step)].index
outlier_indices.extend(outlier_list_col)
outlier_indices = Counter(outlier_indices)
multiple_outliers = list(k for k, v in outlier_indices.items() if v > n)
return multiple_outliers
1
Outliers_to_drop = detect_outliers(train, 2, ['Age', 'SibSp', 'Parch', 'Fare'])
outlier는 예측에 극적인 영향을 미칠 수 있기 때문에 (퇴행 문제에 대해) 관리하기로 선택했다.
나는 Tukey 방법(Tukey JW, 1977)을 사용하여 분포 값(IQR)의 1 사분위수와 3 사분위수 사이의 범위를 정의하는 outlier를 검출했다. outlier란 피쳐 값이 (IQR +- 특이치 단계) 외부에 있는 행이다.
나는 숫자 값 특징(Age, SibSp, Sarch 및 Fair)에서 outlier를 검출하기로 결정했다. 그리고 outlier를 최소 두 개의 outlier가 있는 행으로 생각했다.
1
2
# outlier를 가지고 있는 행들
train.loc[Outliers_to_drop]
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
27 | 28 | 0 | 1 | Fortune, Mr. Charles Alexander | male | 19.0 | 3 | 2 | 19950 | 263.00 | C23 C25 C27 | S |
88 | 89 | 1 | 1 | Fortune, Miss. Mabel Helen | female | 23.0 | 3 | 2 | 19950 | 263.00 | C23 C25 C27 | S |
159 | 160 | 0 | 3 | Sage, Master. Thomas Henry | male | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
180 | 181 | 0 | 3 | Sage, Miss. Constance Gladys | female | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
201 | 202 | 0 | 3 | Sage, Mr. Frederick | male | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
324 | 325 | 0 | 3 | Sage, Mr. George John Jr | male | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
341 | 342 | 1 | 1 | Fortune, Miss. Alice Elizabeth | female | 24.0 | 3 | 2 | 19950 | 263.00 | C23 C25 C27 | S |
792 | 793 | 0 | 3 | Sage, Miss. Stella Anna | female | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
846 | 847 | 0 | 3 | Sage, Mr. Douglas Bullen | male | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
863 | 864 | 0 | 3 | Sage, Miss. Dorothy Edith "Dolly" | female | NaN | 8 | 2 | CA. 2343 | 69.55 | NaN | S |
- 10개의 outlier를 가지고 있는 행들 중 28, 89, 342는 fare가 너무 높고, 나머지 7명은 SibSp가 너무 높다
1
2
# outlier 삭제
train = train.drop(Outliers_to_drop, axis = 0).reset_index(drop = True)
2.3 joining train and test set
1
2
train_len = len(train)
dataset = pd.concat(objs= [train, test], axis = 0).reset_index(drop = True)
- categorical 변환 중에 동일한 수의 형상을 얻기 위해 train 및 test dataset을 concat함.
2.4 check for null and missing values
1
2
3
dataset = dataset.fillna(np.nan)
dataset.isnull().sum()
1
2
3
4
5
6
7
8
9
10
11
12
13
PassengerId 0
Survived 418
Pclass 0
Name 0
Sex 0
Age 256
SibSp 0
Parch 0
Ticket 0
Fare 1
Cabin 1007
Embarked 2
dtype: int64
- Age와 Cabin에 nan이 많음
1
train.info()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 881 entries, 0 to 880
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 881 non-null int64
1 Survived 881 non-null int64
2 Pclass 881 non-null int64
3 Name 881 non-null object
4 Sex 881 non-null object
5 Age 711 non-null float64
6 SibSp 881 non-null int64
7 Parch 881 non-null int64
8 Ticket 881 non-null object
9 Fare 881 non-null float64
10 Cabin 201 non-null object
11 Embarked 879 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 82.7+ KB
1
train.isnull().sum()
1
2
3
4
5
6
7
8
9
10
11
12
13
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 170
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 680
Embarked 2
dtype: int64
1
train.head()
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22.0 | 1 | 0 | A/5 21171 | 7.2500 | NaN | S |
1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.0 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.0 | 0 | 0 | STON/O2. 3101282 | 7.9250 | NaN | S |
3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 113803 | 53.1000 | C123 | S |
4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35.0 | 0 | 0 | 373450 | 8.0500 | NaN | S |
1
train.dtypes
1
2
3
4
5
6
7
8
9
10
11
12
13
PassengerId int64
Survived int64
Pclass int64
Name object
Sex object
Age float64
SibSp int64
Parch int64
Ticket object
Fare float64
Cabin object
Embarked object
dtype: object
1
train.describe()
PassengerId | Survived | Pclass | Age | SibSp | Parch | Fare | |
---|---|---|---|---|---|---|---|
count | 881.000000 | 881.000000 | 881.000000 | 711.000000 | 881.000000 | 881.000000 | 881.000000 |
mean | 446.713961 | 0.385925 | 2.307605 | 29.731603 | 0.455165 | 0.363224 | 31.121566 |
std | 256.617021 | 0.487090 | 0.835055 | 14.547835 | 0.871571 | 0.791839 | 47.996249 |
min | 1.000000 | 0.000000 | 1.000000 | 0.420000 | 0.000000 | 0.000000 | 0.000000 |
25% | 226.000000 | 0.000000 | 2.000000 | 20.250000 | 0.000000 | 0.000000 | 7.895800 |
50% | 448.000000 | 0.000000 | 3.000000 | 28.000000 | 0.000000 | 0.000000 | 14.454200 |
75% | 668.000000 | 1.000000 | 3.000000 | 38.000000 | 1.000000 | 0.000000 | 30.500000 |
max | 891.000000 | 1.000000 | 3.000000 | 80.000000 | 5.000000 | 6.000000 | 512.329200 |
3. Feature analysis
3.1 Numerical values
1
2
# 상관관계 확인
g = sns.heatmap(train[['Survived', 'SibSp', 'Parch', 'Age', 'Fare']].corr(), annot = True, fmt = '.2f', cmap = 'coolwarm')
- Fare만 survived에 상관관계가 있어보임 그렇다고, 다른 feature들이 유의미하지 않다는것은 아니기에 하나씩 살펴보자
SibSp
1
2
3
g = sns.factorplot(x = 'SibSp', y = 'Survived', data = train, kind = 'bar', size = 6, palette = 'muted')
g.despine(left = True)
g = g.set_ylabels('survival probability')
1
2
3
4
C:\ProgramData\Anaconda3\lib\site-packages\seaborn\categorical.py:3669: UserWarning: The `factorplot` function has been renamed to `catplot`. The original name will be removed in a future release. Please update your code. Note that the default `kind` in `factorplot` (`'point'`) has changed `'strip'` in `catplot`.
warnings.warn(msg)
C:\ProgramData\Anaconda3\lib\site-packages\seaborn\categorical.py:3675: UserWarning: The `size` parameter has been renamed to `height`; please update your code.
warnings.warn(msg, UserWarning)
형제/자매들이 많은 승객들은 생존할 기회가 적은 것 같다.
싱글 승객(0 SibSP) 또는 다른 두 사람(SibSP 1 또는 2)과 함께 생존할 수 있는 기회가 더 많음
이 관찰은 상당히 흥미로우며, 이러한 범주를 설명하는 새로운 특징을 고려할 수 있다.
Parch
1
2
3
g = sns.factorplot(x = 'Parch', y = 'Survived', data = train, kind = 'bar', size = 6, palette = 'muted')
g.despine(left = True)
g = g.set_ylabels('survival probability')
1
2
3
4
C:\ProgramData\Anaconda3\lib\site-packages\seaborn\categorical.py:3669: UserWarning: The `factorplot` function has been renamed to `catplot`. The original name will be removed in a future release. Please update your code. Note that the default `kind` in `factorplot` (`'point'`) has changed `'strip'` in `catplot`.
warnings.warn(msg)
C:\ProgramData\Anaconda3\lib\site-packages\seaborn\categorical.py:3675: UserWarning: The `size` parameter has been renamed to `height`; please update your code.
warnings.warn(msg, UserWarning)
- 핵가족은 독신(Parch 0), 중간(Parch 3,4), 대가족(Parch 5,6)보다 생존할 기회가 더 많다.
- 부모/자녀가 3명인 승객의 생존에 중요한 표준 편차가 있을 수 있음
Age
1
2
g = sns.FacetGrid(train, col = 'Survived')
g = g.map(sns.distplot, 'Age')
- 나이 분포는 꼬리가 있는 분포, 어쩌면 가우스 분포인 것 같다.
- 우리는 생존자와 생존하지 않은 하위 인구에서 연령 분포가 같지 않다는 것을 알아챘다. 실제로 살아남은 젊은 승객에 해당하는 봉우리가 있다. 우리는 또한 60-80세 사이의 승객들이 덜 살아남았다는 것을 안다.
- 그래서 “나이”가 “생존”과 상관관계가 없다고 해도, 우리는 생존할 기회가 더 많거나 적은 승객들의 연령 범주가 있음을 알 수 있다.
- 아주 어린 승객들은 생존할 기회가 더 많은 것 같다.
1
2
3
4
5
g = sns.kdeplot(train['Age'][(train['Survived'] == 0) & (train['Age'].notnull())], color = 'Red', shade = True)
g = sns.kdeplot(train['Age'][(train['Survived'] == 1) & (train['Age'].notnull())], ax = g, color = 'Blue', shade = True)
g.set_xlabel('Age')
g.set_ylabel('Frequency')
g = g.legend(['Not Survived', 'Survived'])
- 두 그래프를 포개놓으니, 0~5세 아이들의 생존율이 높음을 알수 있었다
Fare
1
dataset['Fare'].isnull().sum()
1
1
1
dataset["Fare"] = dataset["Fare"].fillna(dataset["Fare"].median())
- 요금의 Null값은 중앙값으로 대체 하였다
1
2
g = sns.distplot(dataset['Fare'], color = 'm', label = 'Skewness : %.2f'%(dataset['Fare'].skew()))
g = g.legend(loc = 'best')
- 요금 분배는 매우 왜곡되어 있다. 이렇게 되면 크기가 조정되더라도 모형의 매우 높은 값이 초과될 수 있다.
- 이 경우 로그 기능으로 변환해 이 skew를 줄이는 것이 좋다.
1
dataset['Fare'] = dataset['Fare'].map(lambda i : np.log(i) if i > 0 else 0 )
1
2
g = sns.distplot(dataset['Fare'], color = 'b', label = 'Skewness : %.2f'%(dataset['Fare'].skew()))
g = g.legend(loc = 'best')
- 로그 변환 후 왜도가 명확하게 감소함
3.2 Categorical values
Sex
1
2
g = sns.barplot(x = 'Sex', y = 'Survived', data = train)
g = g.set_ylabel('Survival Probability')
1
train[['Sex','Survived']].groupby('Sex').mean()
Survived | |
---|---|
Sex | |
female | 0.747573 |
male | 0.190559 |
- 남성이 여성보다 생존할 기회가 적다는 것은 명백하다.
- 그래서 Sex는 생존을 예측하는 데 중요한 역할을 할 수도 있다.
- 타이타닉 영화(1997년)를 본 사람들에게, 나는 우리 모두가 대피 중에 “여성과 아이들이 먼저”라는 문장을 기억할 것이라고 확신한다.
Pclass
1
2
3
g = sns.factorplot(x = 'Pclass', y = 'Survived', data = train, kind = 'bar', size = 6, palette = 'muted')
g.despine(left = True)
g = g.set_ylabels('survival probability')
1
2
3
4
C:\ProgramData\Anaconda3\lib\site-packages\seaborn\categorical.py:3669: UserWarning: The `factorplot` function has been renamed to `catplot`. The original name will be removed in a future release. Please update your code. Note that the default `kind` in `factorplot` (`'point'`) has changed `'strip'` in `catplot`.
warnings.warn(msg)
C:\ProgramData\Anaconda3\lib\site-packages\seaborn\categorical.py:3675: UserWarning: The `size` parameter has been renamed to `height`; please update your code.
warnings.warn(msg, UserWarning)
1
2
3
g = sns.factorplot(x = 'Pclass', y = 'Survived', hue = 'Sex', data = train, size = 6, kind = 'bar', palette = 'muted')
g.despine(left = True)
g = g.set_ylabels('survival probability')
- 승객 생존은 3개 등급에서 같지 않다. 일등석 승객은 이등석, 삼등석 승객보다 생존 기회가 더 많다.
- 이러한 경향은 남녀 승객 모두를 볼 때 보존된다.
Embarked
1
dataset['Embarked'].isnull().sum()
1
2
1
dataset['Embarked'] = dataset['Embarked'].fillna('S')
- 2개의 null값은 가장 많은 항구인 S로 넣음
1
2
3
4
g = sns.factorplot(x="Embarked", y="Survived", data=train,
size=6, kind="bar", palette="muted")
g.despine(left=True)
g = g.set_ylabels("survival probability")
- 체르부르(C)에서 오는 승객의 생존 기회가 더 많아진 것으로 보인다.
- 내 가설은 1등석 승객의 비율이 퀸스타운(Q), 사우샘프턴(S)보다 더 높다는 것이다.
1
2
3
4
g = sns.factorplot("Pclass", col="Embarked", data=train,
size=6, kind="count", palette="muted")
g.despine(left=True)
g = g.set_ylabels("Count")
- 실제로 사우샘프턴(S)과 퀸스타운(Q)에서 오는 승객이 3등급을 가장 많이 타는 반면, 체르부르크 승객은 대부분 생존율이 가장 높은 1등석을 타고 있다.
- 현시점에서 일등석의 생존율이 높은 이유를 설명할 수 없다. 나의 가설은 1등석 승객들이 그들의 영향 때문에 대피하는 동안 우선순위가 결정되었다는 것이다.
4. Filling missing Values
4.1 Age
- 보시다시피 Age 열에는 전체 데이터 집합에 256개의 결측값이 포함되어 있다.
- (예를 들어) 생존 기회가 더 많은 하위인구(0세 ~ 5세)가 있기 때문에 연령 특징을 유지하고 누락된 가치를 귀속시키는 것이 바람직하다.
- 이 문제를 해결하기 위해 나는 에이지(Sex, Parch, Pclass, SibSP)와 가장 상관관계가 있는 특징을 살펴보았다.
1
2
3
4
g = sns.factorplot(y='Age', x='Sex', data=dataset, kind='box')
g = sns.factorplot(y='Age', x='Sex', hue='Pclass', data=dataset, kind='box')
g = sns.factorplot(y='Age', x='Parch', data=dataset, kind='box')
g = sns.factorplot(y='Age', x='SibSp', data=dataset, kind='box')
- 남녀 하위집단에서 연령 분포가 동일한 것 같아 성별은 나이를 예측하는 데 도움이 되지 않는다.
- 그러나 1등석 승객은 2등석 승객보다 나이가 많고 3등석 승객보다 나이가 많다.
- 더욱이, 승객은 나이가 많을수록 부모/자식을 더 많이 가지고 있고, 동생/여동생을 더 많이 가지고 있다.
1
2
# 성별을 categorical로 분류
dataset['Sex'] = dataset['Sex'].map({'male': 0, 'female': 1})
1
2
g = sns.heatmap(
dataset[['Age', 'Sex', 'SibSp', 'Parch', 'Pclass']].corr(), cmap='summer_r', annot=True)
- correlation map은 Parch를 제외한 인자 그림 관측치를 확인한다. Age는 Sex와는 상관관계가 없지만, Pclass, Parch, SibSp와 부정적으로 상관관계가 있다.
- Age는 부모/자녀 수와 함께 성장하고 있다. 그러나 일반적인 상관관계는 부정적이다.
- 그래서 나는 SibSP, Parch, Pclass를 사용하여 Null Age을 귀속시키기로 결정했다.
- Pclass, Parch, SibSp에 따라 비슷한 행의 중위연령으로 Age를 채우는 전략이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Filling missing value of Age
# Fill Age with the median age of similar rows according to Pclass, Parch and SibSp
# Index of NaN age rows
index_NaN_age = list(dataset['Age'][dataset['Age'].isnull()].index)
for i in index_NaN_age:
age_med = dataset['Age'].median()
age_pred = dataset['Age'][((dataset['SibSp'] == dataset.iloc[i]['SibSp']) &
(dataset['Parch'] == dataset.iloc[i]['Parch']) &
(dataset['Pclass'] == dataset.iloc[i]['Pclass']))].median()
if not np.isnan(age_pred) :
dataset['Age'].iloc[i] = age_pred
else :
dataset['Age'].iloc[i] = age_med
1
2
g = sns.factorplot(x="Survived", y = "Age",data = train, kind="box")
g = sns.factorplot(x="Survived", y = "Age",data = train, kind="violin")
- 연령의 중위수 값과 하위 모집단에서의 중위수 사이의 차이는 없었다.
- 그러나 생존한 승객들의 violin plot에서 우리는 여전히 매우 어린 승객들이 더 높은 생존율을 가지고 있다는 것을 알아차린다.
5. Feature engineering
5.1 Name / Title
1
dataset['Name'].head()
1
2
3
4
5
6
0 Braund, Mr. Owen Harris
1 Cumings, Mrs. John Bradley (Florence Briggs Th...
2 Heikkinen, Miss. Laina
3 Futrelle, Mrs. Jacques Heath (Lily May Peel)
4 Allen, Mr. William Henry
Name: Name, dtype: object
- 이름 기능에는 승객의 제목에 대한 정보가 포함되어 있다.
- 이름이 다른 승객은 대피하는데 선호될수 있다
1
2
3
4
# get title from name
dataset_title = [i.split(',')[1].split('.')[0].strip() for i in dataset['Name']]
dataset['Title'] = pd.Series(dataset_title)
dataset['Title'].head()
1
2
3
4
5
6
0 Mr
1 Mrs
2 Miss
3 Mrs
4 Mr
Name: Title, dtype: object
1
2
g = sns.countplot(x = 'Title', data = dataset)
g = plt.setp(g.get_xticklabels(), rotation = 45)
- 데이터 집합에는 17개의 제목이 있는데, 대부분은 몇개 없다 우리는 4개의 카테고리로 분류할 수 있다.
1
2
3
4
5
6
# Convert to categorical values Title
dataset["Title"] = dataset["Title"].replace(
['Lady', 'the Countess', 'Countess', 'Capt', 'Col', 'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')
dataset["Title"] = dataset["Title"].map(
{"Master": 0, "Miss": 1, "Ms": 1, "Mme": 1, "Mlle": 1, "Mrs": 1, "Mr": 2, "Rare": 3})
dataset["Title"] = dataset["Title"].astype(int)
1
2
g = sns.countplot(dataset["Title"])
g = g.set_xticklabels(["Master","Miss/Ms/Mme/Mlle/Mrs","Mr","Rare"])
1
2
3
g = sns.factorplot(x="Title",y="Survived",data=dataset,kind="bar")
g = g.set_xticklabels(["Master","Miss-Mrs","Mr","Rare"])
g = g.set_ylabels("survival probability")
- “여자와 아이들이 먼저”
- 희귀한 title 을 가진 승객들이 생존할 수 있는 기회가 더 많다는 점은 흥미롭다.
1
2
# Drop Name variable
dataset.drop(labels = ["Name"], axis = 1, inplace = True)
5.2 Family size
- 우리는 대가족들이 대피하는 동안 그들의 자매/형제/부모를 찾으면서 대피하는 데 더 많은 어려움을 겪을 것이라고 상상할 수 있다. 그래서, 나는 SibSp , Parch 그리고 1 (승객 포함)의 합인 “Fize” (가족 크기) 특징을 창조하기로 선택했다.
1
2
# create a family size descriptor from SibSp, and Parch
dataset['Fsize'] = dataset['SibSp'] + dataset['Parch'] + 1
1
2
g = sns.factorplot(x = 'Fsize', y = 'Survived', data = dataset)
g = g.set_ylabels('Survival Probability')
- 가족의 크기가 중요한 역할을 하는 것 같은데, 대가족에게는 생존 확률이 가장 낮다.
- 4개의 가족사이즈를 만들기로 결심했다.
1
2
3
4
5
# create new feature of family size
dataset['Single'] = dataset['Fsize'].map(lambda s : 1 if s == 1 else 0 )
dataset['SmallF'] = dataset['Fsize'].map(lambda s : 1 if s == 2 else 0 )
dataset['MedF'] = dataset['Fsize'].map(lambda s : 1 if 3 <= s <= 4 else 0 )
dataset['LargeF'] = dataset['Fsize'].map(lambda s : 1 if s >= 5 else 0 )
1
2
3
4
5
6
7
8
g = sns.factorplot(x="Single",y="Survived",data=dataset,kind="bar")
g = g.set_ylabels("Survival Probability")
g = sns.factorplot(x="SmallF",y="Survived",data=dataset,kind="bar")
g = g.set_ylabels("Survival Probability")
g = sns.factorplot(x="MedF",y="Survived",data=dataset,kind="bar")
g = g.set_ylabels("Survival Probability")
g = sns.factorplot(x="LargeF",y="Survived",data=dataset,kind="bar")
g = g.set_ylabels("Survival Probability")
- Family size plot은 중소 가족이 단일 승객이나 대가족보다 생존할 기회가 더 많다는 것을 보여준다.
1
2
3
# convert to indicator values Title and Embarked
dataset = pd.get_dummies(dataset, columns= ['Title'])
dataset = pd.get_dummies(dataset, columns= ['Embarked'], prefix = 'Em')
1
dataset.head()
PassengerId | Survived | Pclass | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | ... | SmallF | MedF | LargeF | Title_0 | Title_1 | Title_2 | Title_3 | Em_C | Em_Q | Em_S | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0.0 | 3 | 0 | 22.0 | 1 | 0 | A/5 21171 | 1.981001 | NaN | ... | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 |
1 | 2 | 1.0 | 1 | 1 | 38.0 | 1 | 0 | PC 17599 | 4.266662 | C85 | ... | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
2 | 3 | 1.0 | 3 | 1 | 26.0 | 0 | 0 | STON/O2. 3101282 | 2.070022 | NaN | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
3 | 4 | 1.0 | 1 | 1 | 35.0 | 1 | 0 | 113803 | 3.972177 | C123 | ... | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
4 | 5 | 0.0 | 3 | 0 | 35.0 | 0 | 0 | 373450 | 2.085672 | NaN | ... | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 |
5 rows × 22 columns
- 지금까지 23개의 features가 있음
5.3 Cabin
1
dataset['Cabin'].head()
1
2
3
4
5
6
0 NaN
1 C85
2 NaN
3 C123
4 NaN
Name: Cabin, dtype: object
1
dataset["Cabin"].describe()
1
2
3
4
5
count 292
unique 186
top G6
freq 5
Name: Cabin, dtype: object
1
dataset["Cabin"].isnull().sum()
1
1007
- Cabin 열에는 292개의 값과 1007개의 결측값이 포함되어 있다.
- 객실 없는 승객은 객실 번호 대신 누락된 값이 표시되어 있는 줄 알았는데.
1
dataset["Cabin"][dataset["Cabin"].notnull()].head()
1
2
3
4
5
6
1 C85
3 C123
6 E46
10 G6
11 C103
Name: Cabin, dtype: object
1
2
# Replace the Cabin number by the type of cabin 'X' if not
dataset["Cabin"] = pd.Series([i[0] if not pd.isnull(i) else 'X' for i in dataset['Cabin'] ])
- 선실의 첫 번째 글자는 데스크를 가리키며, 타이타닉호에서 승객의 위치를 나타내기 때문에 나는 이 정보만을 보관하기로 선택했다.
1
g = sns.countplot(dataset["Cabin"],order=['A','B','C','D','E','F','G','T','X'])
1
2
g = sns.factorplot(y="Survived",x="Cabin",data=dataset,kind="bar",order=['A','B','C','D','E','F','G','T','X'])
g = g.set_ylabels("Survival Probability")
- 객실이 있는 승객의 수가 적기 때문에 생존 확률은 중요한 표준 편차를 가지고 있고 우리는 다른 desk에 있는 승객의 생존 확률을 구별할 수 없다.
- 그러나 우리는 객실을 가진 승객들이 (X)가 없는 승객들보다 일반적으로 생존할 기회가 더 많다는 것을 알 수 있다.
- 특히 객실 B, C, D, E, F에 해당된다.
1
dataset = pd.get_dummies(dataset, columns = ['Cabin'], prefix = 'Cabin')
5.4 Ticket
1
dataset['Ticket'].head()
1
2
3
4
5
6
0 A/5 21171
1 PC 17599
2 STON/O2. 3101282
3 113803
4 373450
Name: Ticket, dtype: object
- 그것은 같은 접두사를 공유하는 티켓이 함께 배치된 선실에 예약될 수 있다는 것을 의미할 수 있다. 따라서 그것은 배 안에 선실을 실제로 배치하는 것으로 이어질 수 있다.
- 동일한 접두사를 가진 티켓은 등급과 생존이 유사할 수 있다.
- 그래서 나는 티켓 기능 열을 티켓 접두사로 바꾸기로 결정했다.
1
2
3
4
5
6
7
8
Ticket = []
for i in list(dataset.Ticket):
if not i.isdigit():
Ticket.append(i.replace('.','').replace('/','').strip().split(' ')[0])
else:
Ticket.append('X')
dataset['Ticket'] = Ticket
dataset['Ticket'].head()
1
2
3
4
5
6
0 A5
1 PC
2 STONO2
3 X
4 X
Name: Ticket, dtype: object
1
dataset = pd.get_dummies(dataset, columns = ["Ticket"], prefix="T")
1
2
3
# Create categorical values for Pclass
dataset["Pclass"] = dataset["Pclass"].astype("category")
dataset = pd.get_dummies(dataset, columns = ["Pclass"],prefix="Pc")
1
2
# Drop useless variables
dataset.drop(labels = ["PassengerId"], axis = 1, inplace = True)
1
dataset.head()
Survived | Sex | Age | SibSp | Parch | Fare | Fsize | Single | SmallF | MedF | ... | T_STONO | T_STONO2 | T_STONOQ | T_SWPP | T_WC | T_WEP | T_X | Pc_1 | Pc_2 | Pc_3 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.0 | 0 | 22.0 | 1 | 0 | 1.981001 | 2 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
1 | 1.0 | 1 | 38.0 | 1 | 0 | 4.266662 | 2 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
2 | 1.0 | 1 | 26.0 | 0 | 0 | 2.070022 | 1 | 1 | 0 | 0 | ... | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
3 | 1.0 | 1 | 35.0 | 1 | 0 | 3.972177 | 2 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 |
4 | 0.0 | 0 | 35.0 | 0 | 0 | 2.085672 | 1 | 1 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
5 rows × 67 columns
6.Modeling
1
2
3
4
5
## Separate train dataset and test dataset
train = dataset[:train_len]
test = dataset[train_len:]
test.drop(labels=["Survived"],axis = 1,inplace=True)
1
2
3
4
5
6
7
## Separate train features and label
train["Survived"] = train["Survived"].astype(int)
Y_train = train["Survived"]
X_train = train.drop(labels = ["Survived"],axis = 1)
6.1 Simple modeling
6.1.1 Cross validate models
- popular classifier 10개 을 비교하고 층화된 kfold 교차 검증 절차를 통해 각각의 평균 정확도를 평가할것이다.
- SVC
- Decision Tree
- AdaBoost
- Random Forest
- Extra Trees
- Gradient Boosting
- Multiple layer perceprton (neural network)
- KNN
- Logistic regression
- Linear Discriminant Analysis
1
2
# Cross validate model with Kfold stratified cross val
kfold = StratifiedKFold(n_splits=10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
random_state = 2
classifiers = []
classifiers.append(SVC(random_state=random_state))
classifiers.append(DecisionTreeClassifier(random_state=random_state))
classifiers.append(AdaBoostClassifier(DecisionTreeClassifier(random_state=random_state),random_state=random_state, learning_rate=0.1))
classifiers.append(RandomForestClassifier(random_state=random_state))
classifiers.append(ExtraTreesClassifier(random_state=random_state))
classifiers.append(GradientBoostingClassifier(random_state=random_state))
classifiers.append(MLPClassifier(random_state=random_state))
classifiers.append(KNeighborsClassifier())
classifiers.append(LogisticRegression(random_state=random_state))
classifiers.append(LinearDiscriminantAnalysis())
cv_results = []
for classifier in classifiers:
cv_results.append(cross_val_score(classifier, X_train,
y=Y_train, scoring='accuracy', cv=kfold, n_jobs=4))
cv_means = []
cv_std = []
for cv_result in cv_results:
cv_means.append(cv_result.mean())
cv_std.append(cv_result.std())
cv_res = pd.DataFrame({"CrossValMeans": cv_means, "CrossValerrors": cv_std, "Algorithm": ["SVC", "DecisionTree", "AdaBoost",
"RandomForest", "ExtraTrees", "GradientBoosting", "MultipleLayerPerceptron", "KNeighboors", "LogisticRegression", "LinearDiscriminantAnalysis"]})
g = sns.barplot("CrossValMeans", "Algorithm", data=cv_res,
palette="Set3", orient="h", **{'xerr': cv_std})
g.set_xlabel("Mean Accuracy")
g = g.set_title("Cross validation scores")
- 앙상블 모델링을 위해 SVC, AdaBoost, Random Forest , ExtraTree, 그리고 GradientBoosting 분류기를 선택하기로 결정했다.
6.1.2 Hyperparameter tunning for best models
- AdaBoost, ExtraTree, RandomForest, GradientBoosting 및 SVC 분류기에 대한 그리드 검색 최적화를 수행했다.
- 나는 4개의 cpu가 있기 때문에 “n_jobs” 파라미터를 4로 설정했다. 계산 시간이 분명히 단축되었다.
- notebook에는 n_jobs를 4로 설정하였지만, 여기서는 -1로 하겠음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Adaboost
DTC = DecisionTreeClassifier()
adaDTC = AdaBoostClassifier(DTC, random_state=7)
ada_param_grid = {"base_estimator__criterion": ["gini", "entropy"],
"base_estimator__splitter": ["best", "random"],
"algorithm": ["SAMME", "SAMME.R"],
"n_estimators": [1, 2],
"learning_rate": [0.0001, 0.001, 0.01, 0.1, 0.2, 0.3, 1.5]}
gsadaDTC = GridSearchCV(adaDTC, param_grid=ada_param_grid,
cv=kfold, scoring="accuracy", n_jobs=-1, verbose=1)
gsadaDTC.fit(X_train, Y_train)
ada_best = gsadaDTC.best_estimator_
1
2
3
4
5
6
7
Fitting 10 folds for each of 112 candidates, totalling 1120 fits
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 12 concurrent workers.
[Parallel(n_jobs=-1)]: Done 26 tasks | elapsed: 2.4s
[Parallel(n_jobs=-1)]: Done 664 tasks | elapsed: 3.7s
[Parallel(n_jobs=-1)]: Done 1120 out of 1120 | elapsed: 4.4s finished
1
gsadaDTC.best_score_
1
0.82073544433095
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ExtraTrees
ExtC = ExtraTreesClassifier()
## Search grid for optimal parameters
ex_param_grid = {"max_depth": [None],
"max_features": [1, 3, 10],
"min_samples_split": [2, 3, 10],
"min_samples_leaf": [1, 3, 10],
"bootstrap": [False],
"n_estimators" :[100,300],
"criterion": ["gini"]}
gsExtC = GridSearchCV(ExtC,param_grid = ex_param_grid, cv=kfold, scoring="accuracy", n_jobs= -1, verbose = 1)
gsExtC.fit(X_train,Y_train)
ExtC_best = gsExtC.best_estimator_
# Best score
gsExtC.best_score_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 12 concurrent workers.
Fitting 10 folds for each of 54 candidates, totalling 540 fits
[Parallel(n_jobs=-1)]: Done 26 tasks | elapsed: 1.2s
[Parallel(n_jobs=-1)]: Done 176 tasks | elapsed: 6.5s
[Parallel(n_jobs=-1)]: Done 426 tasks | elapsed: 16.4s
[Parallel(n_jobs=-1)]: Done 540 out of 540 | elapsed: 21.0s finished
0.8308733401430031
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# RFC
RFC = RandomForestClassifier()
rf_param_grid = {'max_depth' : [None],
'max_features' : [1,3,10],
'min_samples_split' : [2,3,10],
'min_samples_leaf' : [1,3,10],
'bootstrap' : [False],
'n_estimators' : [100,300],
'criterion' :['gini']}
gsRFC = GridSearchCV(RFC, param_grid = rf_param_grid, cv = kfold, scoring='accuracy', n_jobs = -1, verbose = 1)
gsRFC.fit(X_train, Y_train)
RFC_best = gsRFC.best_estimator_
#bset score
gsRFC.best_score_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Fitting 10 folds for each of 54 candidates, totalling 540 fits
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 12 concurrent workers.
[Parallel(n_jobs=-1)]: Done 26 tasks | elapsed: 3.5s
[Parallel(n_jobs=-1)]: Done 176 tasks | elapsed: 8.8s
[Parallel(n_jobs=-1)]: Done 426 tasks | elapsed: 19.1s
[Parallel(n_jobs=-1)]: Done 540 out of 540 | elapsed: 24.4s finished
0.8365806945863126
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Gradient boosting
GBC = GradientBoostingClassifier()
gb_param_grid = {'loss' : ['deviance'],
'n_estimators' : [100, 200, 300],
'learning_rate' : [0.1, 0.05, 0.01],
'max_depth' : [4, 8],
'min_samples_leaf' : [100, 150],
'max_features' : [0.3, 0.1]}
gsGBC = GridSearchCV(GBC, param_grid= gb_param_grid, cv = kfold, scoring= 'accuracy', n_jobs = -1, verbose = 1)
gsGBC.fit(X_train, Y_train)
GBC_best = gsGBC.best_estimator_
gsGBC.best_score_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Fitting 10 folds for each of 72 candidates, totalling 720 fits
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 12 concurrent workers.
[Parallel(n_jobs=-1)]: Done 26 tasks | elapsed: 2.8s
[Parallel(n_jobs=-1)]: Done 176 tasks | elapsed: 5.5s
[Parallel(n_jobs=-1)]: Done 426 tasks | elapsed: 9.7s
[Parallel(n_jobs=-1)]: Done 720 out of 720 | elapsed: 14.6s finished
0.8331460674157304
1
2
3
4
5
6
7
8
9
# SVC
SVMC = SVC(probability=True)
svc_param_grid = {'kernel' : ['rbf'],
'gamma': [0.001, 0.01, 0.1, 1],
'C':[1, 10, 50, 100, 200, 300, 1000]}
gsSVMC = GridSearchCV(SVMC, param_grid = svc_param_grid, cv = kfold, scoring='accuracy', n_jobs = -1, verbose = 1)
gsSVMC.fit(X_train, Y_train)
SVMC_best = gsSVMC.best_estimator_
gsSVMC.best_score_
1
2
3
4
5
6
7
8
9
Fitting 10 folds for each of 28 candidates, totalling 280 fits
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 12 concurrent workers.
[Parallel(n_jobs=-1)]: Done 26 tasks | elapsed: 0.8s
[Parallel(n_jobs=-1)]: Done 176 tasks | elapsed: 5.4s
[Parallel(n_jobs=-1)]: Done 280 out of 280 | elapsed: 10.1s finished
0.8331332992849847
6.1.3 Plot learning curves
- 학습 곡선은 훈련 세트에 대한 오버핏 효과와 훈련 크기가 정확도에 미치는 영향을 확인할 수 있는 좋은 방법이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None, n_jobs=-1, train_sizes=np.linspace(.1, 1.0, 5)):
plt.figure()
plt.title(title)
if ylim is not None:
plt.ylim(*ylim)
plt.xlabel('Training examples')
plt.ylabel('Score')
train_sizes, train_scores, test_scores = learning_curve(
estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes)
train_score_mean = np.mean(train_scores, axis=1)
train_score_std = np.std(train_scores, axis=1)
test_score_mean = np.mean(test_scores, axis=1)
test_score_std = np.std(test_scores, axis=1)
plt.grid()
plt.fill_between(train_sizes, train_score_mean - train_score_std, train_score_mean + train_score_std, alpha=0.1, color='r')
plt.fill_between(train_sizes, test_score_mean - test_score_std, test_score_mean + test_score_std, alpha = 0.1, color = 'g')
plt.plot(train_sizes, train_score_mean, 'o-', color = 'r', label = 'Training score')
plt.plot(train_sizes, test_score_mean, 'o-', color = 'g', label = 'Cross-validation score')
plt.legend(loc = 'best')
return plt
1
2
3
4
5
g = plot_learning_curve(gsRFC.best_estimator_,"RF mearning curves",X_train,Y_train,cv=kfold)
g = plot_learning_curve(gsExtC.best_estimator_,"ExtraTrees learning curves",X_train,Y_train,cv=kfold)
g = plot_learning_curve(gsSVMC.best_estimator_,"SVC learning curves",X_train,Y_train,cv=kfold)
g = plot_learning_curve(gsadaDTC.best_estimator_,"AdaBoost learning curves",X_train,Y_train,cv=kfold)
g = plot_learning_curve(gsGBC.best_estimator_,"GradientBoosting learning curves",X_train,Y_train,cv=kfold)
- GradientBoosting과 Adaboost 분류자는 훈련 세트에 오버핏하는 경향이 있다. 증가하는 교차 검증 곡선에 따르면 GradientBoosting과 Adaboost는 더 많은 훈련 사례를 통해 더 나은 성과를 낼 수 있다.
- SVC와 ExtraTree 분류자는 훈련 곡선과 교차 검증 곡선이 서로 가깝기 때문에 예측을 더 잘 일반화하는 것으로 보인다.
6.1.4 Feature importance of tree based classifiers
- 승객 생존 예측에 가장 유용한 기능을 보기 위해 4개의 트리 기반 분류기에 대한 기능 중요도를 표시했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
nrows = ncols = 2
fig, axes = plt.subplots(nrows = nrows, ncols = ncols, sharex="all", figsize=(15,15))
names_classifiers = [("AdaBoosting", ada_best),("ExtraTrees",ExtC_best),("RandomForest",RFC_best),("GradientBoosting",GBC_best)]
nclassifier = 0
for row in range(nrows):
for col in range(ncols):
name = names_classifiers[nclassifier][0]
classifier = names_classifiers[nclassifier][1]
indices = np.argsort(classifier.feature_importances_)[::-1][:40]
g = sns.barplot(y=X_train.columns[indices][:40],x = classifier.feature_importances_[indices][:40] , orient='h',ax=axes[row][col])
g.set_xlabel("Relative importance",fontsize=12)
g.set_ylabel("Features",fontsize=12)
g.tick_params(labelsize=9)
g.set_title(name + " feature importance")
nclassifier += 1
- 4개의 트리 기반 분류자(Adaboost, ExtraTree, RandomForest 및 GradientBoosting)에 대한 피쳐 중요도를 표시한다.
- 우리는 4개의 분류자가 상대적 중요도에 따라 상위 특징을 가지고 있다는 것을 주목한다. 이들의 예측이 같은 특징을 바탕으로 한 것이 아니라는 뜻이다. 그럼에도 불구하고, 그들은 분류에 대한 몇 가지 공통적인 중요한 특징들을 공유한다. 예를 들어, ‘Par’, ‘Title_2’, ‘Age’, ‘Sex’ 등이 그것이다.
- Title_2는 Mrs/Mme/Miss/Ms 범주가 Sex와 높은 상관관계를 가지고 있음을 나타낸다.
- 우리는 다음과 같이 말할 수 있다.
- pc_1, pc_2, pc_3 및 운임은 승객의 일반적인 사회적 지위를 말한다.
- Sex and Title_2(Mrs/Mlle/Mme/Miss/Ms)와 Title_3(Mr)은 성별을 가리킨다.
- Age 및 Title_1(Master)은 승객의 나이를 말한다.
- Fsize, LargeF, MedF, Single은 승객 가족의 크기를 가리킨다.
- 이 4가지 분류기의 특징적인 중요성에 따라 생존 예측은 배 안의 위치보다 ‘나이’ ‘성’ ‘가족 규모’ ‘승객의 사회적 지위’와 더 관련이 있는 것으로 보인다.
1
2
3
4
5
6
7
8
9
10
11
test_Survived_RFC = pd.Series(RFC_best.predict(test), name="RFC")
test_Survived_ExtC = pd.Series(ExtC_best.predict(test), name="ExtC")
test_Survived_SVMC = pd.Series(SVMC_best.predict(test), name="SVC")
test_Survived_AdaC = pd.Series(ada_best.predict(test), name="Ada")
test_Survived_GBC = pd.Series(GBC_best.predict(test), name="GBC")
# Concatenate all classifier results
ensemble_results = pd.concat([test_Survived_RFC,test_Survived_ExtC,test_Survived_AdaC,test_Survived_GBC, test_Survived_SVMC],axis=1)
g= sns.heatmap(ensemble_results.corr(),annot=True)
- 이 예측은 Adaboost 다른 분류자와 비교했을 때를 제외하고는 5개 분류자의 경우 상당히 유사한 것으로 보인다.
- 5개의 분류자는 거의 같은 예측을 하지만 약간의 차이가 있다. 5가지 분류자 예측 간의 차이점은 통합 투표를 고려하기에 충분하다.
6.2 Ensemble modeling
6.2.1 Combining models
- 나는 5개의 분류자에서 나온 예측을 결합하기 위해 voting classifier를 선택했다.
- 나는 각 투표의 확률을 고려하기 위해 투표 매개변수에 “soft” 주장을 전달하는 것을 선호했다.
1
2
3
4
5
# 각 모델별 베스트 파라미터를 뽑고, voting을 시킴
votingC = VotingClassifier(estimators=[('rfc', RFC_best), ('extc', ExtC_best),
('svc', SVMC_best), ('adac',ada_best),('gbc',GBC_best)], voting='soft', n_jobs=4)
votingC = votingC.fit(X_train, Y_train)
6.3 Prediction
6.3.1 Predict and Submit results
1
2
3
4
5
test_Survived = pd.Series(votingC.predict(test), name="Survived")
results = pd.concat([IDtest,test_Survived],axis=1)
results.to_csv("result/03.ensemble_python_voting.csv",index=False)