오늘은 지금까지 작성했던 데이터 분석의 5단계
문제 정의 -> 데이터 수집 -> 데이터 전처리/시각화 -> 예측 및 모델링 -> 평가 순서를 기반으로
타이타닉 생존자 예측 프로젝트를 진행하려고 한다.
먼저, 구글에 접속하여 kaggle titanic analysis를 검색하여 두 번째 위치한 Titanic Data Science Solutions를 클릭한다.

이후 해당 kaggle 사이트에 접속하여 타이타닉 생존자 예측 프로젝트 분석 글을 확인한다.

하단으로 글을 스크롤하여 보면, Kaggle competiton description page here이라는 문구를 확인할 수 있다.

해당 링크로 접속하게 되면 Titanic - Machine Learning from Diaster라는 글을 확인할 수 있다.

우리는 해당 글의 데이터셋을 다운로드하여 데이터 수집 단계를 먼저 진행해 볼 것이다.
Data 창을 클릭하여 글의 하단으로 내려와 Download all 버튼을 클릭한다.

다운로드한 파일의 압축을 풀어 확인해 보면 총 3가지 csv파일이 존재하는 것을 확인할 수 있다.

이 세 가지 csv파일에 대해 간단히 설명하자면:
- train.csv: 모델을 학습시키기 위한 훈련 데이터 파일이다. 파일 내부를 간단히 살펴보면 승객의 나이, 성별, 좌석 등급 등 다양한 정보와 실제 생존 여부가 포함되어 있다. 이 데이터를 통해 모델이 생존 여부를 예측할 수 있다.
- test.csv: 훈련된 모델의 성능을 평가하기 위한 테스트 데이터 파일이다. train.csv와 동일한 형태의 승객 정보가 있지만, 생존 여부는 포함되어 있지 않다. (모델이 전혀 알지 못하는 데이터에 대해 얼마나 정확히 예측하는지를 평가하기 위함). 모델이 예측한 결과와 실제 결과를 비교해 성능을 확인하는 역할을 한다.
- gender_submission.csv: 제출 예시 파일로, 단순히 성별에 따른 생존 예측이 포함되어 있다. 모델이 성능을 검증하는 단계에서 이 파일을 참고하여 제출 형식을 확인할 수 있다.
다시 해당 글로 들어와 본격적인 코드 이전의 글을 번역하여 요약해보면:
- 1912년 4월 15일 타이타닉 호는 침몰하여 승객과 승무원 2224명 중 1502명이 사망하였다.( 생존율 약 32%로 환산)
- 여성, 어린이, 상류층 등 일부 집단은 생존 가능성이 다른 집단보다 높았다.
- 이번 data science를 통한 핵심 키워드는 다음과 같다.
- 분류(샘플을 분류하거나 카테고리로 묶을 수 있음)
- 상관관계(각 피처 간 관계 및 어떤 피처가 목표(생존 여부)에 가장 많은 영향을 미치는가
- 변환(범주형 데이터 등을 숫자형 값으로 변환)
- 완성(결측치 추정)
- 수정(부적절한 값을 탐지하여 수정하거나 제외)
- 생성(새 기능이 상관관계, 변환, 완전성 목표를 따르도록 할 수 있는가)
- 시각화(차트 그리기)
데이터 분석 및 wrangling(보다 쉽게 접근하고 분석할 수 있도록 데이터를 정리하고 통합하는 과정)¶
In [1]:
import pandas as pd #빠르고 강력하며, 유연하여 사용하기 쉬운 오픈 소스 데이터 분석 및 조작 도구
import numpy as np #파이썬의 과학 컴퓨팅을 위한 기본 패키지
import random as rd #랜덤 함수
시각화 모듈 import¶
In [2]:
import seaborn as sns #맷플롯립 기반의 파이썬 데이터 시각화 라이브러리
import matplotlib.pyplot as plt #정적, 애니메이션 및 대화형 시각화를 생성하기 위한 포괄적인 라이브러리
%matplotlib inline
#맷플롯립을 이용해 생성한 그래프가 별도의 창이 아닌 주피터 노트북 내에 바로 표시되도록 함
In [3]:
train_df = pd.read_csv('train.csv') #pandas를 사용해 train.csv 파일을 읽어와 DataFrame 형식으로 train_df에 저장
test_df = pd.read_csv('test.csv') #pandas를 사용해 test.csv 파일을 읽어와 DataFrame 형식으로 test_df에 저장
combine = [train_df, test_df] #읽어온 train.csv와 test.csv를 통합
In [4]:
print(train_df.columns.values) #train_df의 칼럼값들을 출력
['PassengerId' 'Survived' 'Pclass' 'Name' 'Sex' 'Age' 'SibSp' 'Parch'
'Ticket' 'Fare' 'Cabin' 'Embarked']
In [5]:
train_df.head() #데이터를 상위부터 5개 확인
Out[5]:
| 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 |
각 칼럼 설명¶
어떤 피처가 범주형일까?¶
어떤 피처가 수치형일까?¶
In [6]:
train_df.info() #train_df의 정보를 출력
print('\n' + '-'*50 + '\n') #파이썬의 구분자 '+'를 이용
test_df.info() #test_df의 정보를 출력
<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
--------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 418 non-null int64
1 Pclass 418 non-null int64
2 Name 418 non-null object
3 Sex 418 non-null object
4 Age 332 non-null float64
5 SibSp 418 non-null int64
6 Parch 418 non-null int64
7 Ticket 418 non-null object
8 Fare 417 non-null float64
9 Cabin 91 non-null object
10 Embarked 418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.1+ KB
어떤 피처가 null값이나 공백, 빈 값을 포함하고 있을까?¶
In [7]:
train_df.describe() #데이터 분포 확인
Out[7]:
| 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 |
샘플 전체에 걸친 수치 값 분포는?¶
*std(표준 편차는 분산의 제곱근을 나타냄)¶
총 샘플 수는 타이타닉호에 탑승한 실제 승객 수(2,224명)의 40%인 891명¶
Survived는 0 또는 1 값을 가진 범주형 데이터¶
Parch의 75% 분위수가 0이라는 것은 75%의 승객의 Parch값이 0이라는 것을 의미¶
즉, 부모나 자녀가 동행하지 않은 승객이 75%이상이라는 것을 의미¶
SibSp 평균은 약 0.52이므로, 형재자매나 배우자가 있는 승객이 1명도 채 되지 않음¶
즉, 형제자매나 배우자가 없는 승객이 대부분임¶
Fare의 경우 최대 512달러까지 지불하는 승객(<1%)이 거의 없을 정도로 큰 차이를 보였다.¶
Age의 경우 상위 75%값이 38인 것으로 보아, 고령 승객은 많지 않음¶
필자와 같이 수학이 생소한 사람들을 위해 표준편차 개념을 간단하게 짚고 넘어가려 한다.¶
표준편차는, 데이터가 평균에서 얼마나 분포하는지를 측정하는 값이다.¶
해당 describe를 살펴보면, Fare 열의 표준편차가 약 49.69인데, 이는 평균(32.20)보다 높은 값으로, 평균에서 크게 벗어난 값이 많다는 사실을 알 수 있다.¶
간단한 법칙 하나를 살펴보면, 표준편차를 사용해 데이터의 분포를 이해할 때는 68-95-99.7 법칙이 유용하다.¶
이는 정규분포에서 평균을 기준으로 각 표준편차 범위 내에 들어가는 데이터 비율을 알려준다.¶
평균에서 ±1 표준편차 범위 (32.20 ± 49.69)인 -17.49에서 81.89 사이에는 약 68%의 값이 포함된다.¶
±2 표준편차 (32.20 ± 2*49.69 = -66.18에서 130.58)에는 약 95%의 값이 포함된다.¶
±3 표준편차 (32.20 ± 3*49.69 = -115.87에서 179.27)에는 거의 99.7%의 값이 포함된다.¶
이 데이터를 통해 요금 분포를 보면, 대부분의 승객이 약 $0부터 $81.89 사이의 요금을 지불했을 가능성이 높다.¶
Fare의 최대값이 $512.33로, 이는 평균에서 약 9배 이상의 표준편차에 해당하는 값으로 매우 드문 예외, 즉 이상치라고 할 수 있다.¶
표준편차가 평균에 비해 작을수록 데이터가 평균에 집중되어 있고, 클수록 평균에서 많이 퍼져 있음을 의미한다.¶
예를 들어, 평균이 50인데 표준편차가 10이면 데이터가 평균 근처에 밀집해 있는 반면, 표준편차가 50 이상이라면 데이터가 훨씬 넓게 분포되어 있다는 뜻이다.¶
표준점수(Z-score):특정 값이 평균에서 얼마나 떨어져 있는지를 표준편차 단위로 나타내는 값이다.¶
예를 들어, 평균이 50, 표준편차가 10일 때 특정 값이 70이면 Z-score는 +2가 되어, 이 값이 평균보다 2 표준편차 만큼 떨어져 있음을 뜻한다.¶
Z-score가 ±2를 넘으면 다소 드문 값, ±3을 넘으면 이상치로 판단할 수 있는 기준이 된다.¶
In [8]:
train_df.describe(include=['O']) #O(object) 객체형 데이터 타입을 가져온다.
Out[8]:
| Name | Sex | Ticket | Cabin | Embarked | |
|---|---|---|---|---|---|
| count | 891 | 891 | 891 | 204 | 889 |
| unique | 891 | 2 | 681 | 147 | 3 |
| top | Braund, Mr. Owen Harris | male | 347082 | B96 B98 | S |
| freq | 1 | 577 | 7 | 4 | 644 |
Name칼럼부터 살펴보면, Name은 unique값이 891 즉, 중복되는 값이 없을 의미한다.(동명인이 하나도 없음)¶
Sex칼럼의 경우 unique한 값이 2개, (남/여)로 이루어져있고, freq(빈도수)를 이용해 891을 577로 나누어서 비율을 확인해보면 Male이 65%를 차지하는 것을 알 수 있다.¶
Cabin 칼럼의 경우 전체 수가 204인 데 비해 고유한 값은 147 뿐이기에, 같은 호실을 공유한 사람이 있음을 알 수 있다. (유추)¶
Embarked 칼럼의 경우 S가 가장 많이 쓰인 것을 확인할 수 있고, 그 빈도수는 644번이다.¶
Ticket 칼럼의 경우에 중복값 비율은 약 22%이다.¶
데이터 분석에 기반한 가정¶
지금까지 수행된 데이터 분석을 기반으로 다음과 같은 가정에 도달했다. (*지난 글 키워드 참고)¶
*상관관계¶
우리는 각 기능이 서바이벌과 얼마나 높은 상관관계가 있는지 알고 싶다.¶
프로젝트 초기에 이 작업을 우선 수행하고, 프로젝트 후반부에 모델링 될 상관관계와 이러한 빠른 상관관계를 매치시키려 한다.¶
*완성¶
연령 칼럼은 생존과 확실히 상관관계가 있을 것이므로 완성하는 것이 좋다.¶
Embarked 칼럼은 생존 또는 다른 중요한 기능과도 관련이 있을 수 있으므로 완성하는 것이 좋다.¶
*수정¶
Ticket 칼럼은 중복 비율이 높고(22%) 티켓과 생존율 사이에 상관관계가 없을 수 있으므로 분석에서 삭제할 수 있다.¶
Cabin 칼럼은 매우 불완전하거나 훈련 및 테스트 데이터 세트 모두에서 많은 null 값을 포함하므로 삭제할 수 있다.¶
PassengerId 칼럼은 생존에 기여하지 않으므로 교육 데이터 세트에서 삭제될 수 있다.¶
Name 칼럼은 상대적으로 표준이 아니기 때문에 생존에 직접적으로 기여하지 않을 수 있으므로 삭제할 수 있다.¶
*생성:¶
Parch 및 SibSp를 기반으로 Family라는 새로운 기능을 생성하여 총 가족 수를 가입시키는 것이 좋다.¶
이름 기능을 설계하여 제목을 새 기능으로 추출할 수 있다.¶
연령 밴드에 대한 새로운 기능을 생성할 수 있다. 이렇게 하면 연속적인 숫자 기능이 순서형 범주형 기능으로 전환된다.¶
분석에 도움이 되는 경우 요금 범위 기능을 생성할 수도 있다.¶
*분류:¶
앞서 언급한 문제 설명을 바탕으로 가정(*)을 추가할 수도 있다.¶
여성(성별=여성)은 생존했을 가능성이 더 높았다.¶
어린이(연령<?)는 생존했을 가능성이 더 높았다.¶
상류층 승객(Pclass=1)은 생존했을 가능성이 더 높았다.¶
기능을 피벗하여 분석¶
관찰과 가정 중 일부를 확인하기 위해 피처를 서로 피벗하여 피처 상관관계를 빠르게 분석할 수 있었다.¶
이 단계에서는 빈 값이 없는 피처에 대해서만 그것이 가능했다.¶
또한 범주형(Sex), 순서형(Pclass) 또는 이산형(SibSp, Parch) 유형의 피처에만 그렇게 하는 것이 합리적이다.¶
Pclass : 우리는 Pclass=1과 Survive(3번 분류) 간에 유의미한 상관관계(>0.5)를 관찰했다. 이 피처를 모델에 포함하기로 결정했다.¶
Sex : 문제 정의 과정에서 Sex=여성의 생존율이 74%로 매우 높다는 관찰 결과를 확인했다.¶
SibSp 및 Parch : 이 피처는 특정 값에 대해 상관관계가 없다. 이러한 개별 피처에서 피처 또는 피처 집합을 도출하는 것이 가장 좋다.¶
In [9]:
#train_df에서 Pclass와 Survived 칼럼을 선택한 후, Pclass를 기준으로 그룹화하여 각 Pclass별 생존률의 평균을 구한 뒤, 생존률 평균을 기준으로 내림차순 정렬한다.
train_df[['Pclass', 'Survived']].groupby(['Pclass'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Out[9]:
| Pclass | Survived | |
|---|---|---|
| 0 | 1 | 0.629630 |
| 1 | 2 | 0.472826 |
| 2 | 3 | 0.242363 |
In [10]:
train_df[["Sex", "Survived"]].groupby(['Sex'], as_index=False).mean().sort_values(by='Survived', ascending=False) #as_index:그룹화한 열을 인덱스가 아닌 일반 열로 출력
Out[10]:
| Sex | Survived | |
|---|---|---|
| 0 | female | 0.742038 |
| 1 | male | 0.188908 |
In [11]:
train_df[["SibSp", "Survived"]].groupby(['SibSp'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Out[11]:
| SibSp | Survived | |
|---|---|---|
| 1 | 1 | 0.535885 |
| 2 | 2 | 0.464286 |
| 0 | 0 | 0.345395 |
| 3 | 3 | 0.250000 |
| 4 | 4 | 0.166667 |
| 5 | 5 | 0.000000 |
| 6 | 8 | 0.000000 |
In [12]:
train_df[["Parch", "Survived"]].groupby(['Parch'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Out[12]:
| Parch | Survived | |
|---|---|---|
| 3 | 3 | 0.600000 |
| 1 | 1 | 0.550847 |
| 2 | 2 | 0.500000 |
| 0 | 0 | 0.343658 |
| 5 | 5 | 0.200000 |
| 4 | 4 | 0.000000 |
| 6 | 6 | 0.000000 |
데이터를 시각화하여 분석¶
*상관관계가 있는 수치형 피처¶
수치적 특징과 솔루션 목표(Survived) 사이의 상관관계를 이해하는 것부터 시작한다.¶
히스토그램 차트는 밴딩이나 범위가 유용한 패턴을 식별하는 데 도움이 되는 연령과 같은 연속형 수치 변수를 분석하는 데 유용하다.¶
히스토그램은 자동으로 정의된 빈 또는 동일한 범위의 밴드를 사용하여 샘플의 분포를 나타낼 수 있다.¶
이는 특정 밴드와 관련된 질문에 답하는 데 도움이 된다(유아의 생존율이 더 높았는가)¶
히스토리그램 시각화에서 x축은 샘플 또는 승객의 수를 나타낸다.¶
*관찰¶
유아(=4세 미만)의 생존율이 높았다.¶
최고령 승객(연령 = 80세)은 생존했다.¶
15~25세 청소년 중 상당수가 생존하지 못했다.¶
대부분의 승객은 15~35세이다.¶
*결정¶
이 간단한 분석을 통해 후속 워크플로 단계의 결정에 대한 당사의 가정을 확인할 수 있다.¶
모델 학습에서 연령(2번을 분류하는 가정)을 고려해야 한다.¶
null 값에 대한 연령 기능을 완료한다.¶
연령대를 묶어야 한다.¶
In [13]:
g = sns.FacetGrid(train_df, col='Survived') #칼럼을 Survived로 하여 FacetGrid(하나의 데이터를 여러 개의 plot으로 나눠서 보기)
g.map(plt.hist, 'Age', bins=20) #Age를 열로 하여 히스토그램을 그림
plt.show() #시각화
In [14]:
grid = sns.FacetGrid(train_df, col = 'Survived', row = 'Pclass', height = 2.2, aspect = 1.6)
grid.map(plt.hist, 'Age', alpha =.5, bins = 20)
grid.add_legend();
plt.show()
수치 및 순서 특징 상관 관계¶
In [15]:
import warnings
warnings.filterwarnings('ignore')
In [16]:
# grid = sns.FacetGrid(train_df, col='Embarked')
grid = sns.FacetGrid(train_df, row='Embarked', height=2.2, aspect=1.6)
grid.map(sns.pointplot, 'Pclass', 'Survived', 'Sex', palette='deep')
grid.add_legend()
plt.show()
범주형 기능의 상관 관계¶
이제 범주형 기능과 솔루션 목표를 연관시킬 수 있다.¶
*관찰.¶
여성 승객은 남성보다 생존율이 훨씬 높았다.¶
남성의 생존율이 더 높았던 Embarked = C는 예외이다. 이는 Pclass 와 Embarked, 그리고 Pclass와 Survived 간의 상관관계일 수 있으며,¶
반드시 Embarked와 Survived 간의 직접적인 상관관계는 아니다.¶
남성은 C 및 Q 포트의 경우 Pclass=2에 비해 Pclass=3에서 생존율이 더 좋았다.¶
탑승 항구는 Pclass=3과 남성 승객의 생존율이 다양히다.¶
*결정.¶
모델 학습에 Sex 피처를 추가한다.¶
모델 학습에 Embarked 피처 기능을 완성하여 추가한다.¶
In [17]:
# grid = sns.FacetGrid(train_df, col='Embarked', hue='Survived', palette={0: 'k', 1: 'w'})
grid = sns.FacetGrid(train_df, row='Embarked', col='Survived', height=2.2, aspect=1.6)
grid.map(sns.barplot, 'Sex', 'Fare', alpha=.5, ci=None)
grid.add_legend()
plt.show()
범주형 및 숫자형 기능의 상관관계¶
In [18]:
print("Before", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)
train_df = train_df.drop(['Ticket', 'Cabin'], axis=1)
test_df = test_df.drop(['Ticket', 'Cabin'], axis=1)
combine = [train_df, test_df]
"After", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape
Before (891, 12) (418, 11) (891, 12) (418, 11)
Out[18]:
('After', (891, 10), (418, 9), (891, 10), (418, 9))
데이터 정리¶
우리는 데이터 세트 및 솔루션 요구 사항에 관한 몇 가지 가정과 결정을 수집했다.¶
지금까지는 이러한 목표를 달성하기 위해 단일 피처나 값을 변경할 필요가 없었다.¶
이제 목표 수정, 생성 및 완료를 위한 결정과 가정을 실행해본다.¶
*피처 삭제를 통한 수정¶
이것은 실행하기 좋은 시작 목표이다. 기능을 삭제함으로써 더 적은 데이터 포인트를 처리할 수 있다. 노트북 속도를 높이고 분석을 용이하게 한다.¶
가정과 결정에 따라 Cabin 및 Ticket 피처를 삭제하고자 한다.¶
해당되는 경우 일관성을 유지하기 위해 학습 데이터 세트와 테스트 데이터 세트에 대한 연산을 함께 수행한다는 점에 유의하자.¶
In [19]:
for dataset in combine:
dataset['Title'] = dataset.Name.str.extract(' ([A-Za-z]+)\.', expand=False)
pd.crosstab(train_df['Title'], train_df['Sex'])
Out[19]:
| Sex | female | male |
|---|---|---|
| Title | ||
| Capt | 0 | 1 |
| Col | 0 | 2 |
| Countess | 1 | 0 |
| Don | 0 | 1 |
| Dr | 1 | 6 |
| Jonkheer | 0 | 1 |
| Lady | 1 | 0 |
| Major | 0 | 2 |
| Master | 0 | 40 |
| Miss | 182 | 0 |
| Mlle | 2 | 0 |
| Mme | 1 | 0 |
| Mr | 0 | 517 |
| Mrs | 125 | 0 |
| Ms | 1 | 0 |
| Rev | 0 | 6 |
| Sir | 0 | 1 |
기존 피처에서 새로운 피처를 추출하기¶
Name 기능을 조작하여 제목을 추출하고 제목과 생존 간의 상관관계를 테스트한 후 Name 및 PassengerId 기능을 삭제할 수 있는지 분석하고자 한다.¶
다음 코드에서는 정규 표현식을 사용하여 제목 기능을 추출한다.¶
RegEx 패턴(\w+.)은 이름 기능 내에서 점 문자로 끝나는 첫 번째 단어와 일치한다. expand=false 플래그는 DataFrame을 반환한다.¶
*관찰.¶
제목, 연령 및 생존자를 플롯할 때 다음과 같은 관찰 결과를 확인할 수 있다.¶
대부분의 타이틀은 연령대 그룹을 정확하게 표시한다. 예를 들어: 마스터 타이틀의 연령 평균은 5년이다.¶
타이틀 연령대 밴드의 생존율은 약간 다르다.¶
특정 타이틀은 대부분 살아남았거나 살아남지 못했다.¶
*결정.¶
모델 학습을 위해 새로운 타이틀 기능을 유지하기로 결정했다.¶
In [20]:
for dataset in combine:
dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess','Capt', 'Col',\
'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')
dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss')
dataset['Title'] = dataset['Title'].replace('Ms', 'Miss')
dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs')
train_df[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
Out[20]:
| Title | Survived | |
|---|---|---|
| 0 | Master | 0.575000 |
| 1 | Miss | 0.702703 |
| 2 | Mr | 0.156673 |
| 3 | Mrs | 0.793651 |
| 4 | Rare | 0.347826 |
많은 타이틀을 더 일반적인 이름으로 대체하거나 희귀한 것으로 분류할 수 있다.¶
In [21]:
title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Rare": 5}
for dataset in combine:
dataset['Title'] = dataset['Title'].map(title_mapping)
dataset['Title'] = dataset['Title'].fillna(0)
train_df.head()
Out[21]:
| PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Fare | Embarked | Title | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22.0 | 1 | 0 | 7.2500 | S | 1 |
| 1 | 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Th... | female | 38.0 | 1 | 0 | 71.2833 | C | 3 |
| 2 | 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26.0 | 0 | 0 | 7.9250 | S | 2 |
| 3 | 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35.0 | 1 | 0 | 53.1000 | S | 3 |
| 4 | 5 | 0 | 3 | Allen, Mr. William Henry | male | 35.0 | 0 | 0 | 8.0500 | S | 1 |
범주형 제목을 순서형으로 변환할 수 있다.¶
In [22]:
train_df = train_df.drop(['Name', 'PassengerId'], axis=1)
test_df = test_df.drop(['Name'], axis=1)
combine = [train_df, test_df]
train_df.shape, test_df.shape
Out[22]:
((891, 9), (418, 9))
이제 훈련 및 테스트 데이터 세트에서 이름 기능을 안전하게 삭제할 수 있다. 또한 훈련 데이터 세트에는 PassengerId 기능이 필요하지 않다.¶
In [23]:
for dataset in combine:
dataset['Sex'] = dataset['Sex'].map( {'female': 1, 'male': 0} ).astype(int)
train_df.head()
Out[23]:
| Survived | Pclass | Sex | Age | SibSp | Parch | Fare | Embarked | Title | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3 | 0 | 22.0 | 1 | 0 | 7.2500 | S | 1 |
| 1 | 1 | 1 | 1 | 38.0 | 1 | 0 | 71.2833 | C | 3 |
| 2 | 1 | 3 | 1 | 26.0 | 0 | 0 | 7.9250 | S | 2 |
| 3 | 1 | 1 | 1 | 35.0 | 1 | 0 | 53.1000 | S | 3 |
| 4 | 0 | 3 | 0 | 35.0 | 0 | 0 | 8.0500 | S | 1 |
In [27]:
grid = sns.FacetGrid(train_df, row='Pclass', col='Sex', height=2.2, aspect=1.6)
grid.map(plt.hist, 'Age', alpha=.5, bins=20)
grid.add_legend()
plt.show()
연속 수치형 피처 채우기¶
이제 누락되거나 null 값이 있는 기능 추정 및 완성을 시작해야 한다. 먼저 연령 기능에 대해 이 작업을 수행한다.¶
우리는 연속 수치형 피처를 채우기 위해 세 가지 방법을 고려할 수 있다.¶
1.간단한 방법은 평균과 표준 편차 사이의 난수를 생성하는 것이다.¶
2.결측값을 추측하는 더 정확한 방법은 다른 상관관계가 있는 특징을 사용하는 것이다. 이 경우 연령, 성별 및 Pclass 간의 상관관계를 확인한다.¶
Pclass 및 성별 특징 조합 집합에서 연령의 중앙값을 사용하여 연령 값을 추측한다. 따라서 Pclass=1 및 Gender=0, Pclass=1 및 Gender=1의 중앙 나이 ...¶
3.방법 1과 방법 2를 결합한다. 따라서 중앙값을 기준으로 연령 값을 추측하는 대신 Pclass와 Gender 조합 집합을 기준으로 평균과 표준 편차 사이의 난수를 사용한다.¶
방법 1과 3은 모델에 random noise를 도입한다. 여러 번 실행한 결과는 다를 수 있어, 방법 2를 선호한다.¶
In [28]:
guess_ages = np.zeros((2,3))
guess_ages
Out[28]:
array([[0., 0., 0.],
[0., 0., 0.]])
먼저 Pclass x Gender 조합을 기반으로 추측된 연령 값이 포함되도록 빈 배열을 준비한다.¶
In [29]:
for dataset in combine:
for i in range(0, 2):
for j in range(0, 3):
guess_df = dataset[(dataset['Sex'] == i) & \
(dataset['Pclass'] == j+1)]['Age'].dropna()
# age_mean = guess_df.mean()
# age_std = guess_df.std()
# age_guess = rnd.uniform(age_mean - age_std, age_mean + age_std)
age_guess = guess_df.median()
# Convert random age float to nearest .5 age
guess_ages[i,j] = int( age_guess/0.5 + 0.5 ) * 0.5
for i in range(0, 2):
for j in range(0, 3):
dataset.loc[ (dataset.Age.isnull()) & (dataset.Sex == i) & (dataset.Pclass == j+1),\
'Age'] = guess_ages[i,j]
dataset['Age'] = dataset['Age'].astype(int)
train_df.head()
Out[29]:
| Survived | Pclass | Sex | Age | SibSp | Parch | Fare | Embarked | Title | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3 | 0 | 22 | 1 | 0 | 7.2500 | S | 1 |
| 1 | 1 | 1 | 1 | 38 | 1 | 0 | 71.2833 | C | 3 |
| 2 | 1 | 3 | 1 | 26 | 0 | 0 | 7.9250 | S | 2 |
| 3 | 1 | 1 | 1 | 35 | 1 | 0 | 53.1000 | S | 3 |
| 4 | 0 | 3 | 0 | 35 | 0 | 0 | 8.0500 | S | 1 |
이제 Sex(0 또는 1)와 Pclass(1, 2, 3)를 반복하여 여섯 가지 조합에 대한 연령의 추측값을 계산한다.¶
In [30]:
train_df['AgeBand'] = pd.cut(train_df['Age'], 5)
train_df[['AgeBand', 'Survived']].groupby(['AgeBand'], as_index=False).mean().sort_values(by='AgeBand', ascending=True)
Out[30]:
| AgeBand | Survived | |
|---|---|---|
| 0 | (-0.08, 16.0] | 0.550000 |
| 1 | (16.0, 32.0] | 0.337374 |
| 2 | (32.0, 48.0] | 0.412037 |
| 3 | (48.0, 64.0] | 0.434783 |
| 4 | (64.0, 80.0] | 0.090909 |
Age 밴드를 생성하고 Survived의 상관관계를 확인한다.¶
In [31]:
for dataset in combine:
dataset.loc[ dataset['Age'] <= 16, 'Age'] = 0
dataset.loc[(dataset['Age'] > 16) & (dataset['Age'] <= 32), 'Age'] = 1
dataset.loc[(dataset['Age'] > 32) & (dataset['Age'] <= 48), 'Age'] = 2
dataset.loc[(dataset['Age'] > 48) & (dataset['Age'] <= 64), 'Age'] = 3
dataset.loc[ dataset['Age'] > 64, 'Age']
train_df.head()
Out[31]:
| Survived | Pclass | Sex | Age | SibSp | Parch | Fare | Embarked | Title | AgeBand | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3 | 0 | 1 | 1 | 0 | 7.2500 | S | 1 | (16.0, 32.0] |
| 1 | 1 | 1 | 1 | 2 | 1 | 0 | 71.2833 | C | 3 | (32.0, 48.0] |
| 2 | 1 | 3 | 1 | 1 | 0 | 0 | 7.9250 | S | 2 | (16.0, 32.0] |
| 3 | 1 | 1 | 1 | 2 | 1 | 0 | 53.1000 | S | 3 | (32.0, 48.0] |
| 4 | 0 | 3 | 0 | 2 | 0 | 0 | 8.0500 | S | 1 | (32.0, 48.0] |
Age를 이 밴드를 기반으로 한 서수로 대체한다.¶
In [32]:
train_df = train_df.drop(['AgeBand'], axis=1)
combine = [train_df, test_df]
train_df.head()
Out[32]:
| Survived | Pclass | Sex | Age | SibSp | Parch | Fare | Embarked | Title | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3 | 0 | 1 | 1 | 0 | 7.2500 | S | 1 |
| 1 | 1 | 1 | 1 | 2 | 1 | 0 | 71.2833 | C | 3 |
| 2 | 1 | 3 | 1 | 1 | 0 | 0 | 7.9250 | S | 2 |
| 3 | 1 | 1 | 1 | 2 | 1 | 0 | 53.1000 | S | 3 |
| 4 | 0 | 3 | 0 | 2 | 0 | 0 | 8.0500 | S | 1 |
AgeBand 피처를 제거할 수 없다.¶
In [36]:
for dataset in combine:
dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1
train_df[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Out[36]:
| FamilySize | Survived | |
|---|---|---|
| 3 | 4 | 0.724138 |
| 2 | 3 | 0.578431 |
| 1 | 2 | 0.552795 |
| 6 | 7 | 0.333333 |
| 0 | 1 | 0.303538 |
| 4 | 5 | 0.200000 |
| 5 | 6 | 0.136364 |
| 7 | 8 | 0.000000 |
| 8 | 11 | 0.000000 |
In [37]:
for dataset in combine:
dataset['IsAlone'] = 0
dataset.loc[dataset['FamilySize'] == 1, 'IsAlone'] = 1
train_df[['IsAlone', 'Survived']].groupby(['IsAlone'], as_index=False).mean()
Out[37]:
| IsAlone | Survived | |
|---|---|---|
| 0 | 0 | 0.505650 |
| 1 | 1 | 0.303538 |
isAlone이라 불리는 피처도 새로 만들 수 있다.¶
In [38]:
train_df = train_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
test_df = test_df.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
combine = [train_df, test_df]
train_df.head()
Out[38]:
| Survived | Pclass | Sex | Age | Fare | Embarked | Title | IsAlone | |
|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3 | 0 | 1 | 7.2500 | S | 1 | 0 |
| 1 | 1 | 1 | 1 | 2 | 71.2833 | C | 3 | 0 |
| 2 | 1 | 3 | 1 | 1 | 7.9250 | S | 2 | 1 |
| 3 | 1 | 1 | 1 | 2 | 53.1000 | S | 3 | 0 |
| 4 | 0 | 3 | 0 | 2 | 8.0500 | S | 1 | 1 |
Parch, SibSp 및 FamilySize 피처를 삭제하고 IsAlone으로 통합한다.¶
In [39]:
for dataset in combine:
dataset['Age*Class'] = dataset.Age * dataset.Pclass
train_df.loc[:, ['Age*Class', 'Age', 'Pclass']].head(10)
Out[39]:
| Age*Class | Age | Pclass | |
|---|---|---|---|
| 0 | 3 | 1 | 3 |
| 1 | 2 | 2 | 1 |
| 2 | 3 | 1 | 3 |
| 3 | 2 | 2 | 1 |
| 4 | 6 | 2 | 3 |
| 5 | 3 | 1 | 3 |
| 6 | 3 | 3 | 1 |
| 7 | 0 | 0 | 3 |
| 8 | 3 | 1 | 3 |
| 9 | 0 | 0 | 2 |
또한 Pclass 와 Age를 결합한 인공 피처를 만들 수도 있다.¶
In [40]:
freq_port = train_df.Embarked.dropna().mode()[0]
freq_port
Out[40]:
'S'
In [41]:
for dataset in combine:
dataset['Embarked'] = dataset['Embarked'].fillna(freq_port)
train_df[['Embarked', 'Survived']].groupby(['Embarked'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Out[41]:
| Embarked | Survived | |
|---|---|---|
| 0 | C | 0.553571 |
| 1 | Q | 0.389610 |
| 2 | S | 0.339009 |
범주형 피처 채우기¶
In [42]:
for dataset in combine:
dataset['Embarked'] = dataset['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2} ).astype(int)
train_df.head()
Out[42]:
| Survived | Pclass | Sex | Age | Fare | Embarked | Title | IsAlone | Age*Class | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3 | 0 | 1 | 7.2500 | 0 | 1 | 0 | 3 |
| 1 | 1 | 1 | 1 | 2 | 71.2833 | 1 | 3 | 0 | 2 |
| 2 | 1 | 3 | 1 | 1 | 7.9250 | 0 | 2 | 1 | 3 |
| 3 | 1 | 1 | 1 | 2 | 53.1000 | 0 | 3 | 0 | 2 |
| 4 | 0 | 3 | 0 | 2 | 8.0500 | 0 | 1 | 1 | 6 |
범주형 피처를 수치형 데이터로 변환하기¶
In [43]:
test_df['Fare'].fillna(test_df['Fare'].dropna().median(), inplace=True)
test_df.head()
Out[43]:
| PassengerId | Pclass | Sex | Age | Fare | Embarked | Title | IsAlone | Age*Class | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 892 | 3 | 0 | 2 | 7.8292 | 2 | 1 | 1 | 6 |
| 1 | 893 | 3 | 1 | 2 | 7.0000 | 0 | 3 | 0 | 6 |
| 2 | 894 | 2 | 0 | 3 | 9.6875 | 2 | 1 | 1 | 6 |
| 3 | 895 | 3 | 0 | 1 | 8.6625 | 0 | 1 | 1 | 3 |
| 4 | 896 | 3 | 1 | 1 | 12.2875 | 0 | 3 | 0 | 3 |
수치형 피처를 변환하고 빠르게 채우기¶
In [44]:
train_df['FareBand'] = pd.qcut(train_df['Fare'], 4)
train_df[['FareBand', 'Survived']].groupby(['FareBand'], as_index=False).mean().sort_values(by='FareBand', ascending=True)
Out[44]:
| FareBand | Survived | |
|---|---|---|
| 0 | (-0.001, 7.91] | 0.197309 |
| 1 | (7.91, 14.454] | 0.303571 |
| 2 | (14.454, 31.0] | 0.454955 |
| 3 | (31.0, 512.329] | 0.581081 |
FareBand -¶
In [45]:
for dataset in combine:
dataset.loc[ dataset['Fare'] <= 7.91, 'Fare'] = 0
dataset.loc[(dataset['Fare'] > 7.91) & (dataset['Fare'] <= 14.454), 'Fare'] = 1
dataset.loc[(dataset['Fare'] > 14.454) & (dataset['Fare'] <= 31), 'Fare'] = 2
dataset.loc[ dataset['Fare'] > 31, 'Fare'] = 3
dataset['Fare'] = dataset['Fare'].astype(int)
train_df = train_df.drop(['FareBand'], axis=1)
combine = [train_df, test_df]
train_df.head(10)
Out[45]:
| Survived | Pclass | Sex | Age | Fare | Embarked | Title | IsAlone | Age*Class | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3 | 0 | 1 | 0 | 0 | 1 | 0 | 3 |
| 1 | 1 | 1 | 1 | 2 | 3 | 1 | 3 | 0 | 2 |
| 2 | 1 | 3 | 1 | 1 | 1 | 0 | 2 | 1 | 3 |
| 3 | 1 | 1 | 1 | 2 | 3 | 0 | 3 | 0 | 2 |
| 4 | 0 | 3 | 0 | 2 | 1 | 0 | 1 | 1 | 6 |
| 5 | 0 | 3 | 0 | 1 | 1 | 2 | 1 | 1 | 3 |
| 6 | 0 | 1 | 0 | 3 | 3 | 0 | 1 | 1 | 3 |
| 7 | 0 | 3 | 0 | 0 | 2 | 0 | 4 | 0 | 0 |
| 8 | 1 | 3 | 1 | 1 | 1 | 0 | 3 | 0 | 3 |
| 9 | 1 | 2 | 1 | 0 | 2 | 1 | 3 | 0 | 0 |
Fare 피처를 FareBand에 따라 순서대로 변환한다.¶
In [46]:
test_df.head(10)
Out[46]:
| PassengerId | Pclass | Sex | Age | Fare | Embarked | Title | IsAlone | Age*Class | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 892 | 3 | 0 | 2 | 0 | 2 | 1 | 1 | 6 |
| 1 | 893 | 3 | 1 | 2 | 0 | 0 | 3 | 0 | 6 |
| 2 | 894 | 2 | 0 | 3 | 1 | 2 | 1 | 1 | 6 |
| 3 | 895 | 3 | 0 | 1 | 1 | 0 | 1 | 1 | 3 |
| 4 | 896 | 3 | 1 | 1 | 1 | 0 | 3 | 0 | 3 |
| 5 | 897 | 3 | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
| 6 | 898 | 3 | 1 | 1 | 0 | 2 | 2 | 1 | 3 |
| 7 | 899 | 2 | 0 | 1 | 2 | 0 | 1 | 0 | 2 |
| 8 | 900 | 3 | 1 | 1 | 0 | 1 | 3 | 1 | 3 |
| 9 | 901 | 3 | 0 | 1 | 2 | 0 | 1 | 0 | 3 |
그리고 테스트 데이터셋이다.¶
In [47]:
X_train = train_df.drop("Survived", axis=1)
Y_train = train_df["Survived"]
X_test = test_df.drop("PassengerId", axis=1).copy()
X_train.shape, Y_train.shape, X_test.shape
Out[47]:
((891, 8), (891,), (418, 8))
모델링 및 예측¶
머신러닝을 진행한다.¶
In [53]:
# machine learning
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC, LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import Perceptron
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier
In [54]:
# Logistic Regression
logreg = LogisticRegression()
logreg.fit(X_train, Y_train)
Y_pred = logreg.predict(X_test)
acc_log = round(logreg.score(X_train, Y_train) * 100, 2)
acc_log
Out[54]:
80.36
In [55]:
coeff_df = pd.DataFrame(train_df.columns.delete(0))
coeff_df.columns = ['Feature']
coeff_df["Correlation"] = pd.Series(logreg.coef_[0])
coeff_df.sort_values(by='Correlation', ascending=False)
Out[55]:
| Feature | Correlation | |
|---|---|---|
| 1 | Sex | 2.201445 |
| 5 | Title | 0.397484 |
| 2 | Age | 0.286911 |
| 4 | Embarked | 0.261583 |
| 6 | IsAlone | 0.126942 |
| 3 | Fare | -0.086368 |
| 7 | Age*Class | -0.310963 |
| 0 | Pclass | -0.750392 |
로지스틱 회귀를 사용하여 기능 생성 및 완료 목표에 대한 가정과 결정을 검증할 수 있다. 이는 결정 함수에서 기능의 계수를 계산하여 수행할 수 있다.¶
양의 계수는 반응의 로그 확률을 증가시켜 확률을 증가시키고, 음의 계수는 반응의 로그 확률을 감소시켜 확률을 감소시킨다.¶
Sex는 가장 높은 양수 계수로, Sex 값이 증가함에 따라(남성: 0에서 여성: 1) 생존 확률이 가장 많이 증가한다는 것을 의미한다.¶
P클래스가 증가함에 따라 반대로 Survived=1의 이 가장 크게 감소한다.¶
이렇게 Age*Class는 Survived와 두 번째로 높은 음의 상관관계를 가지고 있기 때문에 모델링하기에 좋은 인공 피처이다.¶
두 번째로 높은 양의 상관관계를 보인 타이틀도 마찬가지다.¶
In [56]:
# Support Vector Machines
svc = SVC()
svc.fit(X_train, Y_train)
Y_pred = svc.predict(X_test)
acc_svc = round(svc.score(X_train, Y_train) * 100, 2)
acc_svc
Out[56]:
78.23
In [57]:
knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(X_train, Y_train)
Y_pred = knn.predict(X_test)
acc_knn = round(knn.score(X_train, Y_train) * 100, 2)
acc_knn
Out[57]:
83.84
In [58]:
# Gaussian Naive Bayes
gaussian = GaussianNB()
gaussian.fit(X_train, Y_train)
Y_pred = gaussian.predict(X_test)
acc_gaussian = round(gaussian.score(X_train, Y_train) * 100, 2)
acc_gaussian
Out[58]:
72.28
In [59]:
# Perceptron
perceptron = Perceptron()
perceptron.fit(X_train, Y_train)
Y_pred = perceptron.predict(X_test)
acc_perceptron = round(perceptron.score(X_train, Y_train) * 100, 2)
acc_perceptron
Out[59]:
78.34
In [60]:
# Decision Tree
decision_tree = DecisionTreeClassifier()
decision_tree.fit(X_train, Y_train)
Y_pred = decision_tree.predict(X_test)
acc_decision_tree = round(decision_tree.score(X_train, Y_train) * 100, 2)
acc_decision_tree
Out[60]:
86.76
In [61]:
# Random Forest
random_forest = RandomForestClassifier(n_estimators=100)
random_forest.fit(X_train, Y_train)
Y_pred = random_forest.predict(X_test)
random_forest.score(X_train, Y_train)
acc_random_forest = round(random_forest.score(X_train, Y_train) * 100, 2)
acc_random_forest
Out[61]:
86.76
모델 평가¶
In [66]:
models = pd.DataFrame({
'Model': ['Support Vector Machines', 'KNN', 'Logistic Regression',
'Random Forest', 'Naive Bayes', 'Perceptron', 'Decision Tree'],
'Score': [acc_svc, acc_knn, acc_log, acc_random_forest,
acc_gaussian, acc_perceptron, acc_decision_tree]})
models.sort_values(by='Score', ascending=False)
Out[66]:
| Model | Score | |
|---|---|---|
| 3 | Random Forest | 86.76 |
| 6 | Decision Tree | 86.76 |
| 1 | KNN | 83.84 |
| 2 | Logistic Regression | 80.36 |
| 5 | Perceptron | 78.34 |
| 0 | Support Vector Machines | 78.23 |
| 4 | Naive Bayes | 72.28 |
submission에 저장¶
In [67]:
submission = pd.DataFrame({
"PassengerId": test_df["PassengerId"],
"Survived": Y_pred
})
# submission.to_csv('../output/submission.csv', index=False)
'AI' 카테고리의 다른 글
| Malware 예측 프로젝트 (0) | 2024.12.01 |
|---|---|
| AI 데이터 경진대회 태양광 발전 예측 프로젝트 (3위 수상) (0) | 2024.11.24 |
| AI #9 (TensorFlow vs PyTorch) (0) | 2024.10.30 |
| AI #8 (머신러닝 모델) (0) | 2024.10.29 |
| AI #7 (데이터 시각화) (0) | 2024.10.28 |