1. 데이터 불러오기

import os
import urllib.request

DOWNLOAD_ROOT = "https://raw.githubusercontent.com/Jin-Sang/titanic1/main/"
TITANIC_PATH = os.path.join("datasets", "titanic")
TITANIC_TRAIN_URL = DOWNLOAD_ROOT + "train.csv"
TITANIC_TEST_URL = DOWNLOAD_ROOT + "test.csv"
def download_data():

  if not os.path.isdir(TITANIC_PATH):
          os.makedirs(TITANIC_PATH)

  train_path = os.path.join(TITANIC_PATH, "train.csv")
  urllib.request.urlretrieve(TITANIC_TRAIN_URL, train_path)

  test_path = os.path.join(TITANIC_PATH, "test.csv")
  urllib.request.urlretrieve(TITANIC_TEST_URL, test_path)   
download_data()
import pandas as pd

def load_titanic_data(filename, titanic_path=TITANIC_PATH):


    csv_path = os.path.join(titanic_path, filename)
    return pd.read_csv(csv_path)
train_data = load_titanic_data("train.csv")
test_data = load_titanic_data("test.csv")
train_data
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
... ... ... ... ... ... ... ... ... ... ... ... ...
886 887 0 2 Montvila, Rev. Juozas male 27.0 0 0 211536 13.0000 NaN S
887 888 1 1 Graham, Miss. Margaret Edith female 19.0 0 0 112053 30.0000 B42 S
888 889 0 3 Johnston, Miss. Catherine Helen "Carrie" female NaN 1 2 W./C. 6607 23.4500 NaN S
889 890 1 1 Behr, Mr. Karl Howell male 26.0 0 0 111369 30.0000 C148 C
890 891 0 3 Dooley, Mr. Patrick male 32.0 0 0 370376 7.7500 NaN Q

891 rows × 12 columns

test_data
PassengerId Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
0 892 3 Kelly, Mr. James male 34.5 0 0 330911 7.8292 NaN Q
1 893 3 Wilkes, Mrs. James (Ellen Needs) female 47.0 1 0 363272 7.0000 NaN S
2 894 2 Myles, Mr. Thomas Francis male 62.0 0 0 240276 9.6875 NaN Q
3 895 3 Wirz, Mr. Albert male 27.0 0 0 315154 8.6625 NaN S
4 896 3 Hirvonen, Mrs. Alexander (Helga E Lindqvist) female 22.0 1 1 3101298 12.2875 NaN S
... ... ... ... ... ... ... ... ... ... ... ...
413 1305 3 Spector, Mr. Woolf male NaN 0 0 A.5. 3236 8.0500 NaN S
414 1306 1 Oliva y Ocana, Dona. Fermina female 39.0 0 0 PC 17758 108.9000 C105 C
415 1307 3 Saether, Mr. Simon Sivertsen male 38.5 0 0 SOTON/O.Q. 3101262 7.2500 NaN S
416 1308 3 Ware, Mr. Frederick male NaN 0 0 359309 8.0500 NaN S
417 1309 3 Peter, Master. Michael J male NaN 1 1 2668 22.3583 NaN C

418 rows × 11 columns

속성은 다음과 같은 의미를 가집니다:

train_data.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

Age, Cabin, Embarked 속성의 일부가 null입니다(891개의 non-null 보다 작습니다). 특히 Cabin은 77%가 null입니다. 일단 Cabin은 무시하고 나머지를 활용하겠습니다. Age는 19%가 null이므로 이를 어떻게 처리할지 결정해야 합니다. null을 중간 나이로 바꾸는 것이 괜찮아 보입니다.

NameTicket 속성도 값을 가지고 있지만 머신러닝 모델이 사용할 수 있는 숫자로 변환하는 것이 조금 까다롭습니다. 그래서 지금은 이 두 속성을 무시하겠습니다.

train_data.describe()
PassengerId Survived Pclass Age SibSp Parch Fare
count 891.000000 891.000000 891.000000 714.000000 891.000000 891.000000 891.000000
mean 446.000000 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208
std 257.353842 0.486592 0.836071 14.526497 1.102743 0.806057 49.693429
min 1.000000 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000
25% 223.500000 0.000000 2.000000 20.125000 0.000000 0.000000 7.910400
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
75% 668.500000 1.000000 3.000000 38.000000 1.000000 0.000000 31.000000
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
train_data["Survived"].value_counts()
0    549
1    342
Name: Survived, dtype: int64
train_data["Pclass"].value_counts()
3    491
1    216
2    184
Name: Pclass, dtype: int64
train_data["Sex"].value_counts()
male      577
female    314
Name: Sex, dtype: int64
train_data["Embarked"].value_counts()
S    644
C    168
Q     77
Name: Embarked, dtype: int64

Embarked 특성은 승객이 탑승한 곳을 알려 줍니다: C=Cherbourg, Q=Queenstown, S=Southampton.

2. 특성 전처리를 위한 파이프라인

2.1 특정열을 선택 클래스 DataFrameSelector

from sklearn.base import BaseEstimator, TransformerMixin

class DataFrameSelector(BaseEstimator, TransformerMixin):
    def __init__(self, attribute_names):
        self.attribute_names = attribute_names
    def fit(self, X, y=None):
        return self
    def transform(self, X):
        return X[self.attribute_names]

2.2 숫자 특성 처리를 위한 파이프 라인

from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer

num_pipeline = Pipeline([
        ("select_numeric", DataFrameSelector(["Age", "SibSp", "Parch", "Fare"])),
        ("imputer", SimpleImputer(strategy="median")),
    ])
num_pipeline.fit_transform(train_data)
array([[22.    ,  1.    ,  0.    ,  7.25  ],
       [38.    ,  1.    ,  0.    , 71.2833],
       [26.    ,  0.    ,  0.    ,  7.925 ],
       ...,
       [28.    ,  1.    ,  2.    , 23.45  ],
       [26.    ,  0.    ,  0.    , 30.    ],
       [32.    ,  0.    ,  0.    ,  7.75  ]])

숫자 특성에 대해 파이프라인 처리를 한 것입니다.

2.3 범주형 특성 처리 파이프 라인

# stackoverflow.com/questions/25239958 에서 착안했습니다
class MostFrequentImputer(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        self.most_frequent_ = pd.Series([X[c].value_counts().index[0] for c in X],
                                        index=X.columns)
        return self
    def transform(self, X, y=None):
        return X.fillna(self.most_frequent_)

비어있는 값을 가장 많이 나오는 값으로 채워준다고 할 수 있다.

from sklearn.preprocessing import OneHotEncoder
cat_pipeline = Pipeline([
        ("select_cat", DataFrameSelector(["Pclass", "Sex", "Embarked"])),
        ("imputer", MostFrequentImputer()),
        ("cat_encoder", OneHotEncoder(sparse=False)),
    ])
cat_pipeline.fit_transform(train_data)
array([[0., 0., 1., ..., 0., 0., 1.],
       [1., 0., 0., ..., 1., 0., 0.],
       [0., 0., 1., ..., 0., 0., 1.],
       ...,
       [0., 0., 1., ..., 0., 0., 1.],
       [1., 0., 0., ..., 1., 0., 0.],
       [0., 0., 1., ..., 0., 1., 0.]])

문자열의 범주형 특성에 대해 파이프라인 처리를 한 것입니다.

2.4 전처리 파이프 라인 완성

from sklearn.pipeline import FeatureUnion
preprocess_pipeline = FeatureUnion(transformer_list=[
        ("num_pipeline", num_pipeline),
        ("cat_pipeline", cat_pipeline),
    ])

머신러닝 모델을 훈련시키기위한 데이터 전처리 파이프를 완성하였습니다.

X_train = preprocess_pipeline.fit_transform(train_data)
X_train
array([[22.,  1.,  0., ...,  0.,  0.,  1.],
       [38.,  1.,  0., ...,  1.,  0.,  0.],
       [26.,  0.,  0., ...,  0.,  0.,  1.],
       ...,
       [28.,  1.,  2., ...,  0.,  0.,  1.],
       [26.,  0.,  0., ...,  1.,  0.,  0.],
       [32.,  0.,  0., ...,  0.,  1.,  0.]])

레이블도 가지고 옵니다.

y_train = train_data["Survived"]

3.SVC 모델 훈련

3.1 SVC 분류기

SVC 모델에 훈련을 시킵니다.

from sklearn.svm import SVC

svm_clf = SVC(gamma="auto")
svm_clf.fit(X_train, y_train)
SVC(C=1.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
    max_iter=-1, probability=False, random_state=None, shrinking=True,
    tol=0.001, verbose=False)

이제 테스트 셋트에 대한 예측을 만들어서 홈페이지에서 검사를 맡을 수 있습니다.

X_test = preprocess_pipeline.transform(test_data)
y_pred = svm_clf.predict(X_test)

하지만 좋은 점수를 위해 자체적으로 평가해보겠습니다.

3.2 모델 평가

교차검증

from sklearn.model_selection import cross_val_score

svm_scores = cross_val_score(svm_clf, X_train, y_train, cv=10)
svm_scores.mean()
0.7329588014981274

73% 정도입니다. 좀 더 높은 모델을 훈련시켜 보도록 하겠습니다.

RandomForestClassifier을 훈련시키고 교차검증 해보았습니다.

4. RandomForestClassifier 모델

from sklearn.ensemble import RandomForestClassifier

forest_clf = RandomForestClassifier(n_estimators=100, random_state=42)
forest_scores = cross_val_score(forest_clf, X_train, y_train, cv=10)
forest_scores.mean()
0.8126466916354558

81% 로 성능이 상승 되었음을 알 수 있습니다.

%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rc('axes', labelsize=14)
mpl.rc('xtick', labelsize=12)
mpl.rc('ytick', labelsize=12)

plt.figure(figsize=(8, 4))
plt.plot([1]*10, svm_scores, ".")
plt.plot([2]*10, forest_scores, ".")
plt.boxplot([svm_scores, forest_scores], labels=("SVM","Random Forest"))
plt.ylabel("Accuracy", fontsize=14)
plt.show()

png

10 폴드 교차 검증에 대한 평균 정확도 대신 대신 모델에서 얻은 10개의 점수를 1사분위, 3사분위를 표현해주는 상자 수염 그림 그래프로 보면 SVM에서 보다 Random Forest 모델이 훨씬 더 1사분위에서 3사분위에 박스 안에 모여 있고, 이상치(수염 밖의 값들은 표시 되지 않는데, 이것이 이상치이다.)도 훨씬 적음을 알 수 있다. 즉, 더욱 성능이 높은 모델은 Random Forest 라고 할 수 있다.

5. 성능 향상

1. 동행자의 수

부모, 자녀, 형제의 수로 하지말고 동행자의 수로 보는 것은 어떨지 특성을 변화시켜보자.

동행자 특성 추가

train_data["RelativesOnboard"] = train_data["SibSp"] + train_data["Parch"]
train_data[["RelativesOnboard", "Survived"]].groupby(['RelativesOnboard']).mean()
Survived
RelativesOnboard
0 0.303538
1 0.552795
2 0.578431
3 0.724138
4 0.200000
5 0.136364
6 0.333333
7 0.000000
10 0.000000

부모,자녀와 형제를 묶어서 동행자로 나누어보았다. 그리고 생존률도 확인해보았다.

train_data
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked RelativesOnboard
0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S 1
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C 1
2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S 0
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S 1
4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S 0
... ... ... ... ... ... ... ... ... ... ... ... ... ...
886 887 0 2 Montvila, Rev. Juozas male 27.0 0 0 211536 13.0000 NaN S 0
887 888 1 1 Graham, Miss. Margaret Edith female 19.0 0 0 112053 30.0000 B42 S 0
888 889 0 3 Johnston, Miss. Catherine Helen "Carrie" female NaN 1 2 W./C. 6607 23.4500 NaN S 3
889 890 1 1 Behr, Mr. Karl Howell male 26.0 0 0 111369 30.0000 C148 C 0
890 891 0 3 Dooley, Mr. Patrick male 32.0 0 0 370376 7.7500 NaN Q 0

891 rows × 13 columns

train_data[train_data["Survived"] == 1]["RelativesOnboard"].value_counts()

0    163
1     89
2     59
3     21
6      4
5      3
4      3
Name: RelativesOnboard, dtype: int64

혼자만 탑승한 탑승객의 생존수가 가장 많다. 따라서 위의 전처리는 의미가 있을 것 같다.

파이프 라인 수정

num_pipeline1 = Pipeline([
        ("select_numeric", DataFrameSelector(["Age", "Fare", "RelativesOnboard"])),
        ("imputer", SimpleImputer(strategy="median")),
    ])

preprocess_pipeline1 = FeatureUnion(transformer_list=[
        ("num_pipeline1", num_pipeline1),
        ("cat_pipeline", cat_pipeline),
    ])
X_train1 = preprocess_pipeline1.fit_transform(train_data)
X_train1
array([[22.    ,  7.25  ,  1.    , ...,  0.    ,  0.    ,  1.    ],
       [38.    , 71.2833,  1.    , ...,  1.    ,  0.    ,  0.    ],
       [26.    ,  7.925 ,  0.    , ...,  0.    ,  0.    ,  1.    ],
       ...,
       [28.    , 23.45  ,  3.    , ...,  0.    ,  0.    ,  1.    ],
       [26.    , 30.    ,  0.    , ...,  1.    ,  0.    ,  0.    ],
       [32.    ,  7.75  ,  0.    , ...,  0.    ,  1.    ,  0.    ]])

훈련 및 평가

from sklearn.ensemble import RandomForestClassifier

forest_clf = RandomForestClassifier(n_estimators=100, random_state=42)
forest_scores = cross_val_score(forest_clf, X_train1, y_train, cv=10)
forest_scores.mean()
0.8025717852684146

80%로 오히려 성능이 더 안좋아졌다.

아마도 전체 생존자 수는 혼자 다닌 사람이 많았지만, 혼자 다닌 사람의 생존률은 30%로 그다지 높지 않았다. 그래서 오히려 생존률과 동행자의 수가 밀접함이 확연하게 크지 않았던 것 같다.

2. 나이 범주화

그렇다면 여기다가 구체적인 나이보다 나이 범위 특성으로 설정하여 하는 것을 추가하여 보자.

나이 범주화 특성 추가

train_data["AgeBucket"] = train_data["Age"] // 15 * 15
train_data[["AgeBucket", "Survived"]].groupby(['AgeBucket']).mean()
Survived
AgeBucket
0.0 0.576923
15.0 0.362745
30.0 0.423256
45.0 0.404494
60.0 0.240000
75.0 1.000000

나이를 15로 나누어서 몪을 구하고 15를 곱해 15단위로 범주화 시켰다.

그리고 생존률도 확인해 보았다.

train_data
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked RelativesOnboard AgeBucket
0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S 1 15.0
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C 1 30.0
2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S 0 15.0
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S 1 30.0
4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S 0 30.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
886 887 0 2 Montvila, Rev. Juozas male 27.0 0 0 211536 13.0000 NaN S 0 15.0
887 888 1 1 Graham, Miss. Margaret Edith female 19.0 0 0 112053 30.0000 B42 S 0 15.0
888 889 0 3 Johnston, Miss. Catherine Helen "Carrie" female NaN 1 2 W./C. 6607 23.4500 NaN S 3 NaN
889 890 1 1 Behr, Mr. Karl Howell male 26.0 0 0 111369 30.0000 C148 C 0 15.0
890 891 0 3 Dooley, Mr. Patrick male 32.0 0 0 370376 7.7500 NaN Q 0 30.0

891 rows × 14 columns

train_data[train_data["Survived"] == 1]["AgeBucket"].value_counts()
15.0    111
30.0     91
0.0      45
45.0     36
60.0      6
75.0      1
Name: AgeBucket, dtype: int64

나이에 따른 생존자 수를 보니 15세로 묶인 범주화가 가장 높고 다음 순으로 되어있다. 비교해보니 위의 생존률의 크기와 큰 연관이 없어 보이긴 하지만 일단 진행하겠다.

파이프 라인 수정

수치형 파이프라인에서 Age 대신 방금 추가한 AgeBucket 특성을 선택하게 해주어야한다.

num_pipeline2 = Pipeline([
        ("select_numeric", DataFrameSelector(["AgeBucket", "Fare", "RelativesOnboard"])),
        ("imputer", SimpleImputer(strategy="median")),
    ])

따라서 전체 전처리 파이프 라인도 바뀐다.

preprocess_pipeline2 = FeatureUnion(transformer_list=[
        ("num_pipeline2", num_pipeline2),
        ("cat_pipeline", cat_pipeline),
    ])
X_train2 = preprocess_pipeline2.fit_transform(train_data)
X_train2
array([[15.    ,  7.25  ,  1.    , ...,  0.    ,  0.    ,  1.    ],
       [30.    , 71.2833,  1.    , ...,  1.    ,  0.    ,  0.    ],
       [15.    ,  7.925 ,  0.    , ...,  0.    ,  0.    ,  1.    ],
       ...,
       [15.    , 23.45  ,  3.    , ...,  0.    ,  0.    ,  1.    ],
       [15.    , 30.    ,  0.    , ...,  1.    ,  0.    ,  0.    ],
       [30.    ,  7.75  ,  0.    , ...,  0.    ,  1.    ,  0.    ]])

훈련 및 평가

from sklearn.ensemble import RandomForestClassifier

forest_clf = RandomForestClassifier(n_estimators=100, random_state=42)
forest_scores = cross_val_score(forest_clf, X_train2, y_train, cv=10)
forest_scores.mean()


0.812621722846442

81%로 성능이 향상 되었음을 볼 수 있다!!!

테스트 세트로 예측하기

테스트 세트에 앞에서 추가한 RelativesOnboard 특성과 AgeBucket 특성을 추가한다.

test_data["RelativesOnboard"] = test_data["SibSp"] + test_data["Parch"]
test_data["AgeBucket"] = test_data["Age"] // 15 * 15

테스트 세트를 새로 바꾼 전처리 파이프로 넣어서 전처리를 해준다.

X_test = preprocess_pipeline2.fit_transform(test_data)

forest_clf.fit(X_train2,  y_train)
submit = forest_clf.predict(X_test)
submit
array([0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1,
       1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1,
       1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1,
       1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0,
       1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0,
       0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1,
       0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,
       1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0,
       0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0,
       1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1,
       0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1,
       0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
       0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,
       1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0,
       0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
       1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
       0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0])

제출을 csv 파일로 제출해야 하므로 파일을 바꿔준다

submit_DataFrame = pd.DataFrame(submit)
submit_DataFrame
0
0 0
1 0
2 0
3 0
4 1
... ...
413 0
414 1
415 0
416 0
417 0

418 rows × 1 columns

submit_DataFrame1 = pd.DataFrame(test_data["PassengerId"])
submit_DataFrame1
PassengerId
0 892
1 893
2 894
3 895
4 896
... ...
413 1305
414 1306
415 1307
416 1308
417 1309

418 rows × 1 columns

submit_DataFrame1["Survived"] = submit_DataFrame[0]
submit_DataFrame1 
PassengerId Survived
0 892 0
1 893 0
2 894 0
3 895 0
4 896 1
... ... ...
413 1305 0
414 1306 1
415 1307 0
416 1308 0
417 1309 0

418 rows × 2 columns

submit_DataFrame1.set_index('PassengerId', inplace=True)
submit_DataFrame1
Survived
PassengerId
892 0
893 0
894 0
895 0
896 1
... ...
1305 0
1306 1
1307 0
1308 0
1309 0

418 rows × 1 columns

submit_DataFrame1.to_csv('submit1.csv',sep=',', na_rep='NaN')