티스토리 뷰

  • 분석연습에 사용된 프로그램은 "Jupyter Notebook" 입니다. 

  • 캐글 커널 중, "Titanic Data Science Solutions - by Manav Sehgal" 의 상당 부분을 참고하였습니다.  

  • 그 외에도, 다수의 블로거분들의 분석을 참고하였습니다. 링크는 아래에 기재하였습니다.  

  • 데이터셋은 캐글 링크를 참고해주세요.

분석 과제

타이타닉 호 침몰 사건 당시의 사망자와 생존자를 구분하는 요인 분석을 통해,

"승객들의 생존 여부를 예측하는 모델 구축"

 

분석에 들어가기 앞서..

특히 모델과 알고리즘을 구축하는 경우, 분석의 과정은 크게 2가지로 나뉩니다.

 

1. 데이터 전처리: 주어진 데이터를 파악 후, 모델에 최적화된 형태로 변형합니다. 

2. 모델링: 최적화된 데이터에 모델을 적용 및 performance를 평가합니다. 

 

이렇게 계획한 분석 Work Flow는 아래와 같습니다. 

(2번과 3번 워크플로우는 다음 포스팅에서 진행됩니다.)

  1. 데이터 전처리

    • 데이터 준비

    • 데이터 변수(feature)확인

    • 탐색적 데이터 분석(EDA)

    • Feature Engineering 

  2. 예측 모델 구축 및 적용

  3. 모델 평가

 

1. 데이터 전처리 

  • 데이터 준비

  • 필요한 라이브러리를 설치합니다.

#데이터 불러오기
import pandas as pd
import numpy as np
import random as rnd

#시각화
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
  • 불러올 데이터셋은 다음과 같습니다. 

train.csv : 모델 학습에 사용되는 데이터

test.csv : 모델 적용 대상이 되는 데이터

모델 적용의 일관성을 위해, train과 test를 합한 combine데이터 셋도 지정합니다. 

train=pd.read_csv(".../train.csv")
test=pd.read_csv(".../test.csv")
combine=[train, test]

print(train.columns.values)
['PassengerId' 'Survived' 'Pclass' 'Name' 'Sex' 'Age' 'SibSp' 'Parch'
 'Ticket' 'Fare' 'Cabin' 'Embarked']
  • 데이터 변수(Feature)확인

print(train.head())
PassengerId  Survived  Pclass  \
0            1         0       3   
1            2         1       1   
2            3         1       3   
3            4         1       1   
4            5         0       3   

                                                Name     Sex   Age  SibSp  \
0                            Braund, Mr. Owen Harris    male  22.0      1   
1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1   
2                             Heikkinen, Miss. Laina  female  26.0      0   
3       Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  35.0      1   
4                           Allen, Mr. William Henry    male  35.0      0   

   Parch            Ticket     Fare Cabin Embarked  
0      0         A/5 21171   7.2500   NaN        S  
1      0          PC 17599  71.2833   C85        C  
2      0  STON/O2. 3101282   7.9250   NaN        S  
3      0            113803  53.1000  C123        S  
4      0            373450   8.0500   NaN        S  
  • 예측의 대상인 목적 변수(label)은 "Survived", 나머지는 설명 변수로 작용합니다.

train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB

・목적 변수

"Survived": 0= 사망, 1= 생존

・설명 변수

feature명

scale

Assumption

PassengerId 승객 식별 아이디

nominal

생존 여부와 큰 관련은 없을 것

제거*

Pclass

객실 등급

1st = Upper

2nd = Middle

3rd = Lower

discrete, ordinal

  • 생존 여부와 관련있을 것

  • 카테고리 기준

  • 높은 등급이 생존확률도 높음

Name 이름

nominal

  • 생존 여부와 큰 관련은 없을 것

  • Mr., Miss, Mrs. 등의 표시가 포함되어있음예측에 최적화된 변형 필요*

Sex

nominal

생존 여부와 관련있을 것

Age

continuous

생존 여부와 관련있을 것

SibSp 동반 형제자매, 배우자 수

discrete, nominal

  • 생존 여부와 관련있을 것

  • 변수 Parch와 비슷한 성격을 가질 것

  • 카테고리 기준

  • 가족이 있을 수록 생존확률 높음

예측 최적화된 변형 필요*

Parch 동반 부모 자녀 수

discrete, nominal

  • 생존 여부와 관련있을 것

  • 변수 SibSp와 비슷한 성격을 가질 것

  • 카테고리 기준

  • 가족이 있을 수록 생존확률 높음

예측 최적화된 변형 필요*

Ticket

nominal

생존 여부와 큰 관련은 없을 것

제거*

Fare 요금

continuous

  • 생존 여부와 관련있을 것

  • 높을 수록 생존확률 높음

Cabin 객실 번호

discrete, nominal 

생존 여부와 큰 관련은 없을 것

제거*

Embarked 승선 항

nominal

생존 여부와 큰 관련은 없을 것

제거*

*변수의 변형은, feature engineering단계에서 진행하도록 하겠습니다.  

 

  • 결측치(null data)를 확인합니다.

train data

train.isnull().sum()
Out[20]:
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64

결측치 존재 변수

결측치 수

Assumption

Age

177

생존 여부와 관련이 있을 것으로 예상

결측치를 채우거나 변형이 필요*

Cabin

687

생존 여부와 큰 관련 없을 것으로 예상

변수 제거*

Embarked

2

생존 여부와 큰 관련 없을 것으로 예상

변수 제거*

*변수의 변형은, feature engineering단계에서 진행하도록 하겠습니다.  

 

test data

test.isnull().sum()
Out[21]:
PassengerId      0
Pclass           0
Name             0
Sex              0
Age             86
SibSp            0
Parch            0
Ticket           0
Fare             1
Cabin          327
Embarked         0
dtype: int64

결측치 존재 변수

결측치 수

Assumption

Age

86

생존 여부와 관련이 있을 것으로 예상

결측치를 채우거나 변형이 필요*

Fare

1

결측치 수가 크지 않음

결측치를 채우거나 변형이 필요*

Cabin

327

생존 여부와 큰 관련 없을 것으로 예상

변수 제거*

*변수의 변형은, feature engineering단계에서 진행하도록 하겠습니다.  

  • object data(category)와 non object data(non category) 묘사

    • 생존 여부에 대한 설명력이 있는 변수를 예상, 살펴봅니다.

・object data

train.describe(include=['O'])
#or, train.describe(include=['O',"category"])
                                 Name   Sex  Ticket    Cabin Embarked
count                             891   891     891      204      889
unique                            891     2     681      147        3
top     Ilmakangas, Miss. Pieta Sofia  male  347082  B96 B98        S
freq                                1   577       7        4      644
  1. TicketCabin은 고유값(unique)의 비중이 큼 → categorical 성격 약함 →제거 확실*

  2. Name은 고유값 비중이 커 categorical성격이 약하지만, miss, mr.등의 결혼 여부 표시가 포함되어 있어 변형 필요*

  3. Sex: male이 대다수를 차지(891건 중, 577건)

  4. Embarked: 승객 대부분이 S항에서 탑승(889건 중, 644건)

*변수의 변형은, feature engineering단계에서 진행하도록 하겠습니다.  

 

・non object data(float, integer..)

print(train.describe())
#or,print(train.describe(exclude=['O']))
PassengerId    Survived      Pclass         Age       SibSp  \
count   891.000000  891.000000  891.000000  714.000000  891.000000   
mean    446.000000    0.383838    2.308642   29.699118    0.523008   
std     257.353842    0.486592    0.836071   14.526497    1.102743   
min       1.000000    0.000000    1.000000    0.420000    0.000000   
25%     223.500000    0.000000    2.000000   20.125000    0.000000   
50%     446.000000    0.000000    3.000000   28.000000    0.000000   
75%     668.500000    1.000000    3.000000   38.000000    1.000000   
max     891.000000    1.000000    3.000000   80.000000    8.000000   

            Parch        Fare  
count  891.000000  891.000000  
mean     0.381594   32.204208  
std      0.806057   49.693429  
min      0.000000    0.000000  
25%      0.000000    7.910400  
50%      0.000000   14.454200  
75%      0.000000   31.000000  
max      6.000000  512.329200  
  1. PassengerId: 큰 관련 없어 보임 →제거 확실*

  2. Survived: 대부분의 승객(75%정도)가 사망

  3. Age: 대부분의 승객이 2-30대

  4. Pclass: 객실 등급 중 1st는 25%미만 → 즉 대부분 2nd, 3rd 클래스 

  5. SibSp, Parch: 대부분의 승객이 형제, 배우자(75%정도), 혹은 부모, 자녀와 탑승하지 않음

  6. Fare: 요금의 표준 편차가 가장 큼 → 즉 승객들의 요금 수준에 차이가 큼 

    • 요금의 최대치는 512, 하지만 대부분의 요금(75%이상)은 30, 혹은 40 언저리

・종합

Most of the Passengers,,,

Survived

Sex

Embarked

Age

Pclass

Fare

SibSp, Parch

사망

male 남성

S항

2-30대

낮은 클래스 

낮은 가격대

동반하지 않음

즉, 승객의 사망 여부가 위의 7개의 변수와 관련이 있을 것이라 예상 가능합니다. 

- 나머지가 이산형(discrete)혹은 범주형(categorical)변수인 것과 달리, Age와 Fare은 연속형(Continuous)변수

  →Age와 Fare변수의 변형이 필요*

- 위에서 Embarked변수를 제거해야한다고 예상했던 것과는 다른 결과가 나타남 

※참고

상관관계 분석

print(train.corr())
             PassengerId  Survived    Pclass       Age     SibSp     Parch  \
PassengerId     1.000000 -0.005007 -0.035144  0.036847 -0.057527 -0.001652   
Survived       -0.005007  1.000000 -0.338481 -0.077221 -0.035322  0.081629   
Pclass         -0.035144 -0.338481  1.000000 -0.369226  0.083081  0.018443   
Age             0.036847 -0.077221 -0.369226  1.000000 -0.308247 -0.189119   
SibSp          -0.057527 -0.035322  0.083081 -0.308247  1.000000  0.414838   
Parch          -0.001652  0.081629  0.018443 -0.189119  0.414838  1.000000   
Fare            0.012658  0.257307 -0.549500  0.096067  0.159651  0.216225   

                 Fare  
PassengerId  0.012658  
Survived     0.257307  
Pclass      -0.549500  
Age          0.096067  
SibSp        0.159651  
Parch        0.216225  
Fare         1.000000  

※한계점 존재

  1. 숫자형 변수만 계산

  2. 라벨(Survived)과의 상관계수로만의 판단→낮은 상관계수를 가진 변수 간과 위험

  3. 변수 엔지니어링의 시나리오 발견 난해

*변수의 변형은, feature engineering단계에서 진행하도록 하겠습니다.  

  • 탐색적 데이터 분석(EDA; Explore Data Analysis)

이렇게 각 변수들의 특징 파악과 예상을 그린 후,  

탐색적 데이터 분석(EDA)에서 각 변수들의 관계를 분석하고 시각화하는 과정을 통해

모델에 최적화된 변수 엔지니어링을 위한 준비를 진행합니다.

 

  • 가장 간단한 방법은, category별 생존 여부를 집계하는 것입니다. 

・범주형 변수 별 생존 평균 집계

1. 성별

ss=train[["Sex", "Survived"]].groupby(['Sex'], as_index=False).mean().sort_values(by='Survived', ascending=False)
print(ss)
ss.plot("Sex",kind="bar").set_xlabel("Sex")
 Sex  Survived
0  female  0.742038
1    male  0.188908

여성이 남성보다 더 많이 생존했습니다. 

 

2. 객실 등급

ps=train[['Pclass', 'Survived']].groupby(['Pclass'], as_index=False).mean().sort_values(by='Survived', ascending=False)
print(ps)
ps.plot("Pclass",kind="bar").set_xlabel("Pclass")
   Pclass  Survived
0       1  0.629630
1       2  0.472826
2       3  0.242363

객실 등급이 높을 수록 더 많이 생존했습니다. 

 

3. 동반한 형제 자매

sbs=train[["SibSp", "Survived"]].groupby(['SibSp'], as_index=False).mean().sort_values(by='Survived', ascending=False)
print(sbs)
sbs.plot("SibSp",kind="bar").set_xlabel("SibSp")
   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

뚜렷한 관계성을 알 순 없으나,

1-2명의 형제 자매를 동반한 승객이 가장 많이 생존했습니다. 

 

4. 동반한 부모, 자녀 

pcs=train[["Parch", "Survived"]].groupby(['Parch'], as_index=False).mean().sort_values(by='Survived', ascending=False)
print(pcs)
pcs.plot("Parch",kind="bar").set_xlabel("Parch")
   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

뚜렷한 관계성을 알 순 없으나,

1-3명의 부모 자녀를 동반한 승객이 가장 많이 생존했습니다. 

 

가족과 동반했는지 여부를 알 수 있는 새로운 변수를 생성한다면*

다른 결과가 나올 수도 있을 것 같습니다. 

*변수의 변형은, feature engineering단계에서 진행하도록 하겠습니다.  

 

5. 승선 항

es=train[["Embarked", "Survived"]].groupby(['Embarked'], as_index=False).mean().sort_values(by='Survived', ascending=False)
print(es)
es.plot("Embarked",kind="bar").set_xlabel("Embarked")
  Embarked  Survived
0        C  0.553571
1        Q  0.389610
2        S  0.336957

S에서 승선한 사람은 낮은, C에서 승선한 사람은 높은 생존 평균을 보이고 있지만,

승선한 항구를 요인으로 보기에는 어려워보입니다. 

다른 변수와의 결합을 통한 생존 여부를 살펴볼 필요가 있습니다. 

 

5.1 승선 항과 객실 등급

sns.countplot('Embarked', hue='Pclass', data=train)

 C의 객실 등급 중 1st가 가장 많이 차지하는 것이 C의 높은 생존 평균을,

 S의 객실 등급 중 3rd가 가장 많이 차지하는 것이 S의 낮은 생존 평균을 설명할 수도 있을 것 같습니다. 

 

 

・연속적 변수 별 생존 집계

연속적인 성격을 지니고 있기에 group by가 아닌 히스토그램을 적용합니다. 

1. 나이

g = sns.FacetGrid(train, col='Survived')
g.map(plt.hist, 'Age', bins=20)

생존자 중 나이가 어린 층이 많다는 걸 알 수 있지만, ( Survived=1 그래프에서 오른쪽으로 약간 비대칭)

사망자의 그래프(Survived=0)도 그다지 다르지 않습니다.

결측치가 존재하고(82건), 연속적 변수이므로 범주형 번수로의 변형* 및 다중 변수 비교 등이 필요합니다. 

*변수의 변형은, feature engineering단계에서 진행하도록 하겠습니다. 

 

1.1 다중 변수 비교(categorical변수와의 결합) ; 나이, 객실 등급

생존 구분이 비교적 명확한 categorical변수와의 결합을 통해 Continuous변수의 설명력 보유 여부를 살펴봅니다. 

grid = sns.FacetGrid(train, col='Survived', row='Pclass', size=2.2, aspect=1.6)
grid.map(plt.hist, 'Age', alpha=.5, bins=20)
grid.add_legend();

- 객실 등급 1st :  생존이 많으며, 비교적 대칭적입니다.

- 객실 등급 2nd : 생존과 사망이 거의 균등하며, 생존 중 0-10세 연령층이 증가하였습니다. 

- 객실 등급 3nd : 대부분 사망하였지만, 어린 연령층의 생존이 나타납니다.

 

즉, 낮은 객실 등급은 대부분 사망함에도 불구, "어린 나이"의 승객들은 생존하였고

나이와 생존의 관계성이 명확해짐을 알 수 있습니다. 

 

2. 요금 

f = sns.FacetGrid(train, col='Survived')
f.map(plt.hist, 'Fare', bins=10)

  요금이 높을 수록 생존한다는 예상은 틀려보이지만, 

  객실 등급(Pclass)과 반대 경향이 나타나는 것이 이상합니다. 심한 비대칭 또한 보입니다. 

  결측치가 존재하고(1건), 연속적 변수이므로 범주형 변수로의 변형*및 다중 변수 비교 등이 필요합니다. 

 *변수의 변형은, feature engineering단계에서 진행하도록 하겠습니다.  

 

2.1 다중 변수 비교(categorical변수와의 결합) ; 요금, 성별, 승선 항

#요금의 Central Tendency(평균, 최빈치, 중앙값 등)을 나타냅니다.
grid = sns.FacetGrid(train, row='Embarked', col='Survived', size=2.2, aspect=1.6)
grid.map(sns.barplot, 'Sex', 'Fare', alpha=.5, ci=None)
grid.add_legend()

Survived=1에서의 요금이 Survived=0보다 높습니다.

승선 항 C의 요금이 다른 항구보다 높고, 

1st 및 생존여부가 많았던 것과 관련이 있을 것 같습니다. 

 

  • Feature Engineering

탐색적 데이터 분석(EDA)에서 살펴본 것과 같이 변수의 변형을 진행해보도록 하겠습니다. 

변수의 변형은 train과 test데이터의 일관성 보장을 위해, combine데이터셋으로 진행합니다.  

변형이 필요한 변수

변형 내용

전반적 변수

모델 적용에 최적화된 숫자형 변수로 변형

=정수 인코딩

PassengerId

변수 제거

Name

숫자 범주형 변수로의 변형(정수 인코딩)

Sex

숫자 범주형 변수로의 변형(정수 인코딩; 원 핫 인코딩)

Age

결측치 채우기 

숫자 범주형 변수로의 변형(정수 인코딩)

SibSp,Parch

새로운 변수로의 통합(정수 인코딩; 원 핫 인코딩)

Ticket

변수 제거

Fare

결측치 채우기

숫자 범주형 변수로의 변형(정수 인코딩)

Cabin

변수 제거 

Embarked

결측치 채우기

숫자 범주형 변수로의 변형(정수 인코딩)

 

・변수 Ticket, Cabin

변수를 제거합니다. 

print("Before", train.shape, test.shape, combine[0].shape, combine[1].shape)

train= train.drop(['Ticket', 'Cabin'], axis=1)
test= test.drop(['Ticket', 'Cabin'], axis=1)
combine = [train, test]

"After", train.shape, test.shape, combine[0].shape, combine[1].shape
Before (891, 12) (418, 11) (891, 12) (418, 11)
Out[4]:
('After', (891, 10), (418, 9), (891, 10), (418, 9))

・변수 Name

먼저 범주형 변수로의 변형을 위해 Title이라는 새로운 변수를 만들어냅니다.

그 후 정규식과 성별과의 통합을 통해 범주 구분을 시도합니다.

for dataset in combine:
    dataset['Title'] = dataset.Name.str.extract(' ([A-Za-z]+)\.', expand=False)

print(pd.crosstab(train['Title'], train['Sex']))
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

- female에서는 Miss와 Mrs가, male에서는 Master와 Mr가 두드러지게 나타납니다. 

  (Mlle와 Ms 는 Miss의, Ms는 Mrs의 불어식 표현)  

- 나머지는 Rare로 분류합니다. 

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')
    
print(train[['Title', 'Survived']].groupby(['Title'], as_index=False).mean())
    Title  Survived
0  Master  0.575000
1    Miss  0.702703
2      Mr  0.156673
3     Mrs  0.793651
4    Rare  0.347826

Title변수를 숫자형 변수로 바꿔줍니다. 

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)

print(train.head())
   PassengerId  Survived  Pclass  \
0            1         0       3   
1            2         1       1   
2            3         1       3   
3            4         1       1   
4            5         0       3   

                                                Name     Sex   Age  SibSp  \
0                            Braund, Mr. Owen Harris    male  22.0      1   
1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1   
2                             Heikkinen, Miss. Laina  female  26.0      0   
3       Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  35.0      1   
4                           Allen, Mr. William Henry    male  35.0      0   

   Parch     Fare Embarked  Title  
0      0   7.2500        S      1  
1      0  71.2833        C      3  
2      0   7.9250        S      2  
3      0  53.1000        S      3  
4      0   8.0500        S      1  

변수 PassengerId, name

이제 Name변수를 제거할 수 있습니다. 

PassengerId변수도 같이 제거합니다. 

train = train.drop(['Name', 'PassengerId'], axis=1)
test = test.drop(['Name'], axis=1)
combine = [train, test]
#test데이터셋에는 PassegnerId변수가 없습니다. 

print(train.shape, test.shape)
(891, 9) (418, 9)

변수 Sex

숫자 범주형 변수로 바꿔줍니다. 

+0과 1로 변형시키는 것을 원 핫 인코딩(one-hot encoding)이라고도 부릅니다. 

for dataset in combine:
    dataset['Sex'] = dataset['Sex'].map( {'female': 1, 'male': 0} ).astype(int)

print(train.head())
   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

・변수 Age

결측치를 채워줍니다. 

다른 변수와 결합 시의 추측되는 Age중앙값으로 대체하겠습니다. 

EDA단계에서의 객실 등급(Pclass)과의 관계성을 근거로, 이번에는 Sex변수를 열로 잡겠습니다.

 

먼저, 시각화해봅니다. 

grid = sns.FacetGrid(train, row='Pclass', col='Sex', size=2.2, aspect=1.6)
grid.map(plt.hist, 'Age', alpha=.5, bins=20)
grid.add_legend()

Pclass, Sex와 Age와의 결합이 2열 3행으로 구성됨을 알 수 있습니다.  

guess_ages = np.zeros((2,3))
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()
            # 위에서 guess_ages사이즈를 [2,3]으로 잡아뒀으므로 j의 범위도 이를 따릅니다.                        
            
            age_guess = guess_df.median()

            # age의 random값의 소수점을 .5에 가깝도록 변형시킵니다.
            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.isnull().sum()

Age의 결측치가 채워진 것을 확인할 수 있습니다. 

Survived    0
Pclass      0
Sex         0
Age         0
SibSp       0
Parch       0
Fare        0
Embarked    2
Title       0
dtype: int64

이제 범주형 변수로 바꿔줍니다.

생존 평균으로 그룹화된 변수 "AgeBand"를 생성합니다.

train['AgeBand'] = pd.cut(train['Age'], 5)
#임의로 5개의 그룹을 지정합니다.
print(train[['AgeBand', 'Survived']].groupby(['AgeBand'], as_index=False).mean().sort_values(by='AgeBand', ascending=True))
        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

AgeBand를 바탕으로 Age를 범주형 변수로 바꿔준 후, AgeBand변수는 제거합니다. 

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 = train.drop(['AgeBand'], axis=1)
combine = [train, test]
print(train.head())
   Survived  Pclass  Sex  Age  SibSp  Parch     Fare Embarked  Title
0         0       3    0    0      1      0   7.2500        S      1
1         1       1    1    0      1      0  71.2833        C      3
2         1       3    1    0      0      0   7.9250        S      2
3         1       1    1    0      1      0  53.1000        S      3
4         0       3    0    0      0      0   8.0500        S      1

+ 추가로 참고한 커널은 Age변수와 Pclass를 곱한 Age*Class변수도 생성하였습니다.

EDA단계에서 Pclass와 결합 시, Age의 생존 여부에 대한 설명력이 증가한 것에 근거하였다고 추측할 수 있습니다.

for dataset in combine:
    dataset['Age*Class'] = dataset.Age * dataset.Pclass

print(train.loc[:, ['Age*Class', 'Age', 'Pclass']].head(3))
   Age*Class  Age  Pclass
0          3    1       3
1          2    2       1
2          3    1       3

・변수 SibSp,Parch

가족과의 동반여부를 알 수 있는 새로운 변수로 통합시킵니다.

for dataset in combine:
    dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1
                                                               #자기 자신을 포함시킵니다.
print(train[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=False).mean().sort_values(by='Survived', ascending=False))
   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

FamilySize=1는 가족과 동반하지 않았음을 의미하여,

1은 동반X, 0은 동반했다는 새로운 변수 IsAlone을 생성합니다.

for dataset in combine:
    dataset['IsAlone'] = 0
    dataset.loc[dataset['FamilySize'] == 1, 'IsAlone'] = 1

print(train[['IsAlone', 'Survived']].groupby(['IsAlone'], as_index=False).mean())
   IsAlone  Survived
0        0  0.505650
1        1  0.303538

이제 SibSp, Parch, FamilySize변수를 제거합니다.

train = train.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
test = test.drop(['Parch', 'SibSp', 'FamilySize'], axis=1)
combine = [train, test]

・변수 Embarked

결측치가 2개밖에 되지 않으므로, 간단히 최빈값으로 대체합니다. 

그 후 숫자 범주형 변수로 바꿔줍니다.

(EDA에서 승선 항에 따라 생존 평균값이 달라지고 이는 Pclass에 따른 것임을 확인하였으므로. 

Ordinal성격을 띄는 변수, 즉 생존 평균값이 높은 순서대로 C=2, Q=1, S=0을 부여할 것 같았지만 그렇지 않았습니다. )

freq_port = train.Embarked.dropna().mode()[0] # "S"
for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].fillna(freq_port)
    dataset['Embarked'] = dataset['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2} ).astype(int)

・변수 Fare

test데이터셋에서 결측치는 1개밖에 존재하지 않으므로, 이번에는 간단히 중앙값으로 대체합니다.

test['Fare'].fillna(test['Fare'].dropna().median(), inplace=True)

그 후 숫자 범주형 변수로 바꿔줍니다.

방법은 Age와 동일합니다.

train['FareBand'] = pd.qcut(train['Fare'], 4)
print(train[['FareBand', 'Survived']].groupby(['FareBand'], as_index=False).mean().sort_values(by='FareBand', ascending=True))
         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
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 = train.drop(['FareBand'], axis=1)
combine = [train, test]

 

이렇게 해서 준비된 train데이터셋은 다음과 같습니다.

print(train.head(3))
   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     0         1      3        0          2
2         1       3    1    1     0         0      2        1          3

이렇게 데이터 준비가 완료되었습니다.

번거러워보이지만, 필수적인 단계입니다. 

 

모델 적용 및 평가는 다음 포스팅에서 진행됩니다. 

 

 

참고

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함