새소식

데이터 분석

[머신러닝] 타이타닉 데이터 분석

  • -

* [머신러닝] 타이타닉 분석

  • kaggle: https://www.kaggle.com/c/titanic
  • kaggle에서 제공하는 타이타닉 데이터를 통해 생존자와 사망자를 예측하는 모델 만들기.
  • 모델 생성 후 실제 사망자 데이터와 비교해보기

1. 문제정의

  • 타이타닉 탑승객의 데이터를 활용해서 생존자 / 사망자를 예측해보자.
  • kaggle에서 좋은(높은) 점수를 받는 것을 목표로 잡는다.

2. 데이터 수집

  • kaggle 사이트에서 train, test, gender_submission 불러오기
# data analysis and wrangling
import numpy as np
import pandas as pd

# visualization (데이터 시각화 라이브러리)
import matplotlib.pyplot as plt
import seaborn as sns
#data 
train = pd.read_csv('./train.csv')
test = pd.read_csv('./test.csv')
train.head()
test.head()
  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

3. 데이터 전처리

  • 결측값, 이상값이 있는지 확인한다.
  • 도출하고자 하는 결론과 관계가 없는 컬럼이 있는지 컬럼과 주제의 상관성을 고려한다.
  • 데이터 전처리는 행 열의 동일한 전처리가 중요하다. (행, 열 전처리를 한꺼번에 진행한다.)
# train 컬럼 확인 
train.columns.unique
<bound method Index.unique of Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
       'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'],
      dtype='object')>

결측치 확인

  • Age, Cabin, Embarked 컬럼에 결측치가 있음을 확인할 수 있다.
train.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

이상치 확인

  • Fare 에 비교적 높은 값이 있으나 이상치인지 실제로 높은 금액을 지불한 것인지는 확인되지 않는다.
  • 따라서 이상치가 없는 것으로 판단한다.
train.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
test.describe()
  PassengerId Pclass Age SibSp Parch Fare
count 418.000000 418.000000 332.000000 418.000000 418.000000 417.000000
mean 1100.500000 2.265550 30.272590 0.447368 0.392344 35.627188
std 120.810458 0.841838 14.181209 0.896760 0.981429 55.907576
min 892.000000 1.000000 0.170000 0.000000 0.000000 0.000000
25% 996.250000 1.000000 21.000000 0.000000 0.000000 7.895800
50% 1100.500000 3.000000 27.000000 0.000000 0.000000 14.454200
75% 1204.750000 3.000000 39.000000 1.000000 0.000000 31.500000
max 1309.000000 3.000000 76.000000 8.000000 9.000000 512.329200

Passengerid 컬럼 삭제

  • Passengerid는 순서를 나타내는 데이터이므로 생존 예측에는 영향을 미치지 않을 것이므로 컬럼을 삭제한다.
  • del DataFrame['삭제할 컬럼'] : 열 삭제만 가능 (키워드)
  • DataFrame.drop('삭제할 컬럼') : 행 / 열의 삭제가 가능
del train['PassengerId']
train.columns.unique
<bound method Index.unique of Index(['Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket',
       'Fare', 'Cabin', 'Embarked'],
      dtype='object')>
test.drop('PassengerId', axis=1, inplace=True)
test.columns.unique
<bound method Index.unique of Index(['Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket', 'Fare',
       'Cabin', 'Embarked'],
      dtype='object')>

정답 컬럼 분리

  • train, test 컬럼의 비교를 위해 Survived 컬럼은 삭제한다.
y_train = train['Survived']
train 에서 Survived 컬럼 삭제
train.drop('Survived', axis=1, inplace=True)
train.shape
(891, 11)
test.shape
(418, 10)

결측치 채우기

# 공통 : age, cabin
# train : Embarked
# test : Fare

train : Embarked 데이터 결측치 채우기

  • value_counts() : 컬럼 안에 값들의 개수
  • S가 가장 많다는 것을 알 수 있음.
train['Embarked'].value_counts(normalize=True)
S    0.724409
C    0.188976
Q    0.086614
Name: Embarked, dtype: float64
  • 최빈값 S로 결측치 채우기
  • 결측치가 적은 경우에만 추천함.
  • fillna() 함수 활용
  • fillna('S')
train['Embarked'].fillna('S', inplace = True)

test: Fare 데이터 결측치 채우기

test['Fare'].value_counts()
7.7500     21
26.0000    19
8.0500     17
13.0000    17
7.8958     11
           ..
9.3250      1
14.4583     1
15.0333     1
25.4667     1
21.0750     1
Name: Fare, Length: 169, dtype: int64
train['Fare'].describe()
count    891.000000
mean      32.204208
std       49.693429
min        0.000000
25%        7.910400
50%       14.454200
75%       31.000000
max      512.329200
Name: Fare, dtype: float64
  • 중앙값으로 채우기
test['Fare'].fillna('14', inplace = True)

공통 : Age 데이터 결측치 채우기

  • 다른 컬럼과의 상관관계를 통해서 결측치를 채워보자
  • corr() 함수로 상관관계 파악
  • 정확도는 -1 ~ 1 사이의 값으로 나타낸다.
  • 양의 정수는 비례하는 정도를, 음의 정수는 반비례하는 정도를 의미한다.
# - : 음의  상관관계, 반비례 (ex. -0.369 : 반비례 관계로, 36% 정도로 정확하다.)
# 숫자 컬럼만 나옴.
train.corr()
  Survived Pclass Age SibSp Parch Fare
Survived 1.000000 -0.338481 -0.077221 -0.035322 0.081629 0.257307
Pclass -0.338481 1.000000 -0.369226 0.083081 0.018443 -0.549500
Age -0.077221 -0.369226 1.000000 -0.308247 -0.189119 0.096067
SibSp -0.035322 0.083081 -0.308247 1.000000 0.414838 0.159651
Parch 0.081629 0.018443 -0.189119 0.414838 1.000000 0.216225
Fare 0.257307 -0.549500 0.096067 0.159651 0.216225 1.000000
  • Age 컬럼과 Pclass 컬럼의 상관관계가 가장 높다는 것을 확인할 수 있다.
train['Pclass'].value_counts()
3    491
1    216
2    184
Name: Pclass, dtype: int64

DataFrame.groupby() 함수

  • 같은 값을 하나로 묶어 통계 또는 집계 결과를 얻기 위해 사용하는 것
  • 그룹화 시킬 DataFrame 은 숫자로만 되있어야 한다.
  • 전체 groupby 할 경우, 숫자로 된 DataFrame 내 데이터만 분석되어 나온다.
  • Aggregation: 뒤에 함수를 지정하여 원하는 값을 도출해 낼 수 있다.
  • Parameter
  • by: default=None이다.
  • axis: default=0이다.
  • level: default=None이다.
  • as_index: default=True이다.
  • sort: default=True이다.
  • group_keys: default=True이다.
    # 'A'별로 DataFrame 내 숫자 데이터를 보고 싶을 경우
    DataFrame.groupby('A').mean() 
    DataFrame.groupby('A').sum() 
    DataFrame.groupby('A').mean() 

    # 내가 만든 함수를 적용하고 싶을 때 agg() 
    DataFrame.groupby('A').agg('__')
  • train의 Pclass 컬럼 별로 train 전체 컬럼(중 숫자 데이터) 값을 보여줌.
train.groupby(train["Pclass"]).mean()
  Survived Age SibSp Parch Fare
Pclass          
1 0.629630 38.233441 0.416667 0.356481 84.154687
2 0.472826 29.877630 0.402174 0.380435 20.662183
3 0.242363 25.140620 0.615071 0.393075 13.675550
  • train의 Embarked 컬럼 별로 train의 Pclass 컬럼의 평균 값을 보여줌.
train['Pclass'].groupby(train["Embarked"]).agg('mean')
Embarked
C    1.886905
Q    2.909091
S    2.346749
Name: Pclass, dtype: float64
train['Pclass'].groupby(train["Embarked"]).mean()
Embarked
C    1.886905
Q    2.909091
S    2.346749
Name: Pclass, dtype: float64
  • Pclass, Sex 컬럼 별로 train 내 전체 데이터의 평균값을 보여줌.
train.groupby(["Pclass", "Sex"]).mean()
    Survived Age SibSp Parch Fare
Pclass Sex          
1 female 0.968085 34.611765 0.553191 0.457447 106.125798
male 0.368852 41.281386 0.311475 0.278689 67.226127
2 female 0.921053 28.722973 0.486842 0.605263 21.970121
male 0.157407 30.740707 0.342593 0.222222 19.741782
3 female 0.500000 21.750000 0.895833 0.798611 16.118810
male 0.135447 26.507589 0.498559 0.224784 12.661633
  • Pclass, Sex 컬럼 별로 trian의 Pclass, Sex, Age 컬럼의 평균을 보여줌.
train[['Pclass', 'Sex' , 'Age']].groupby(["Pclass", "Sex"]).mean()
    Age
Pclass Sex  
1 female 34.611765
male 41.281386
2 female 28.722973
male 30.740707
3 female 21.750000
male 26.507589
  • 객실과 성별 별로 나이의 중앙값
  • 중앙값을 결측치에 채우기 위해 age_table 에 담는다.
age_table = train[['Pclass', 'Sex' , 'Age']].groupby(by=["Pclass", "Sex"]).median()
age_table
    Age
Pclass Sex  
1 female 35.0
male 40.0
2 female 28.0
male 30.0
3 female 21.5
male 25.0
# Series 형태
age_table.loc[3,'female'][0]
21.5
# 튜플 방식으로 출력 (행, 열)
age_table.loc[(3,'female'), 'Age']
21.5
for i in range(1, 100):
    train.iloc[i]

np.isnan() 함수

  • 데이터 값이 결측치인지 아닌지 확인해주는 함수
  • 결측치를 채우는 함수를 만든다.
  • 결측치가 있다면 age_table 데이터 값으로 채워준다.
def fill_age(person):
    if np.isnan(person['Age']):
        return age_table.loc[person['Pclass'],person['Sex']][0]
    else:
        return person['Age']
# fill_age 의 parameter

fill_age(train.iloc[1])
38.0
# 5번째 행의 age 는 null 이다.
np.isnan(train.iloc[5]['Age'])
True
### 결측치 확인
train.iloc[5]
Survived                   0
Pclass                     3
Name        Moran, Mr. James
Sex                     male
Age                      NaN
SibSp                      0
Parch                      0
Ticket                330877
Fare                  8.4583
Cabin                    NaN
Embarked                   Q
Name: 5, dtype: object
# apply : 한 줄씩 입출력하는 것
train['Age'] = train.apply(fill_age, axis=1)
test['Age'] = test.apply(fill_age, axis=1)

공통: cabin 데이터 결측치 채우기

  • 결측치 양이 많을 때는 상관관계를 보기가 어렵다.
  • 모든 결측치를 하나의 값으로 채우는 방법을 택한다.
  • 결측치 의미 : Cabin 값 확인을 못했다. (= 사망을 했거나 cabin 이 없는 3등급의 사람일 수 있다.)
  • 원래 순서대로라면 결측치가 의미하는 게 무엇인지 상관관계를 먼저 파악해야 한다.
# unique
train['Cabin'].unique()
array([nan, 'C85', 'C123', 'E46', 'G6', 'C103', 'D56', 'A6',
       'C23 C25 C27', 'B78', 'D33', 'B30', 'C52', 'B28', 'C83', 'F33',
       'F G73', 'E31', 'A5', 'D10 D12', 'D26', 'C110', 'B58 B60', 'E101',
       'F E69', 'D47', 'B86', 'F2', 'C2', 'E33', 'B19', 'A7', 'C49', 'F4',
       'A32', 'B4', 'B80', 'A31', 'D36', 'D15', 'C93', 'C78', 'D35',
       'C87', 'B77', 'E67', 'B94', 'C125', 'C99', 'C118', 'D7', 'A19',
       'B49', 'D', 'C22 C26', 'C106', 'C65', 'E36', 'C54',
       'B57 B59 B63 B66', 'C7', 'E34', 'C32', 'B18', 'C124', 'C91', 'E40',
       'T', 'C128', 'D37', 'B35', 'E50', 'C82', 'B96 B98', 'E10', 'E44',
       'A34', 'C104', 'C111', 'C92', 'E38', 'D21', 'E12', 'E63', 'A14',
       'B37', 'C30', 'D20', 'B79', 'E25', 'D46', 'B73', 'C95', 'B38',
       'B39', 'B22', 'C86', 'C70', 'A16', 'C101', 'C68', 'A10', 'E68',
       'B41', 'A20', 'D19', 'D50', 'D9', 'A23', 'B50', 'A26', 'D48',
       'E58', 'C126', 'B71', 'B51 B53 B55', 'D49', 'B5', 'B20', 'F G63',
       'C62 C64', 'E24', 'C90', 'C45', 'E8', 'B101', 'D45', 'C46', 'D30',
       'E121', 'D11', 'E77', 'F38', 'B3', 'D6', 'B82 B84', 'D17', 'A36',
       'B102', 'B69', 'E49', 'C47', 'D28', 'E17', 'A24', 'C50', 'B42',
       'C148'], dtype=object)
# unique
test['Cabin'].unique()
array([nan, 'B45', 'E31', 'B57 B59 B63 B66', 'B36', 'A21', 'C78', 'D34',
       'D19', 'A9', 'D15', 'C31', 'C23 C25 C27', 'F G63', 'B61', 'C53',
       'D43', 'C130', 'C132', 'C101', 'C55 C57', 'B71', 'C46', 'C116',
       'F', 'A29', 'G6', 'C6', 'C28', 'C51', 'E46', 'C54', 'C97', 'D22',
       'B10', 'F4', 'E45', 'E52', 'D30', 'B58 B60', 'E34', 'C62 C64',
       'A11', 'B11', 'C80', 'F33', 'C85', 'D37', 'C86', 'D21', 'C89',
       'F E46', 'A34', 'D', 'B26', 'C22 C26', 'B69', 'C32', 'B78',
       'F E57', 'F2', 'A18', 'C106', 'B51 B53 B55', 'D10 D12', 'E60',
       'E50', 'E39 E41', 'B52 B54 B56', 'C39', 'B24', 'D28', 'B41', 'C7',
       'D40', 'D38', 'C105'], dtype=object)
train['Cabin']
0       NaN
1       C85
2       NaN
3      C123
4       NaN
       ... 
886     NaN
887     B42
888     NaN
889    C148
890     NaN
Name: Cabin, Length: 891, dtype: object
# Object 형식은 indexing 할 수 없으므로 str 형식으로 바꿔준다.
# str 형식으로 나온 A,B,C (첫 번째 글자) 를 저장한다.
train['Cabin'] = train['Cabin'].str[0]
test['Cabin'] = test['Cabin'].str[0]
  • 결측치를 'M' 값으로 채운다.
    train['Cabin'].fillna('M', inplace=True)
    test['Cabin'].fillna('M', inplace=True)
train['Cabin']
0      M
1      C
2      M
3      C
4      M
      ..
886    M
887    B
888    M
889    C
890    M
Name: Cabin, Length: 891, dtype: object
  • 결측치가 모두 채워진 것을 확인한다.
train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 11 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Survived  891 non-null    int64  
 1   Pclass    891 non-null    int64  
 2   Name      891 non-null    object 
 3   Sex       891 non-null    object 
 4   Age       891 non-null    float64
 5   SibSp     891 non-null    int64  
 6   Parch     891 non-null    int64  
 7   Ticket    891 non-null    object 
 8   Fare      891 non-null    float64
 9   Cabin     891 non-null    object 
 10  Embarked  891 non-null    object 
dtypes: float64(2), int64(4), object(5)
memory usage: 76.7+ KB
test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 10 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Pclass    418 non-null    int64  
 1   Name      418 non-null    object 
 2   Sex       418 non-null    object 
 3   Age       418 non-null    float64
 4   SibSp     418 non-null    int64  
 5   Parch     418 non-null    int64  
 6   Ticket    418 non-null    object 
 7   Fare      418 non-null    object 
 8   Cabin     418 non-null    object 
 9   Embarked  418 non-null    object 
dtypes: float64(1), int64(3), object(6)
memory usage: 32.8+ KB

4. EDA 탐색적 데이터 분석

  • 특성공학 : 컬럼에 연산을 통해서 의미있는 새로운 정보를 추출하는 행위

Family_size column 분석

  • 4-1) 특성 확장
  • SibSp, Parch 사이의 연관성을 파악한다.
  • Family_size : 형제자매수(SibSp) + 부모자식수 (Parch) + 1
test['Family_size']=test['SibSp'] + test['Parch'] + 1
train['Family_size']=train['SibSp'] + train['Parch'] + 1

sns.countplot

  • 그래프를 그려 연관성을 확인한다.
# x 축 하나에 비교값이 여러 개 가능 (hue == 구분자) 
sns.countplot(data = train, x='Family_size', hue='Survived')
<AxesSubplot:xlabel='Family_size', ylabel='count'>

output_63_1


결과: 가족이 없는 경우 사망률이 더 높고, 2-4 명의 가족이 있는 경우 생존률이 비교적 높다.

Binning

  • 수치형 데이터를 범주형 데이터로 바꾼다.

DataFrame.cut()

  • 수치형 데이터를 범주형 데이터로 바꿔주는 것
  • Parameter
  • bins: 구간(속성 값), int형으로 입력한다.
  • labels: 구간에 대한 범주 이름(속성에 대한 이름), int형으로 입력한다.
bins=[0,1,4,20]
# 0< x <=1 / 1< x <=4 / 4< x <=20
# 0 < x <=1 == Alone 

labels = ['Alone','Small','Large']
train['Family_Group'] = pd.cut(train['Family_size'],bins=bins, labels=labels)
train.columns
Index(['Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket',
       'Fare', 'Cabin', 'Embarked', 'Family_size', 'Family_Group'],
      dtype='object')
test['Family_Group'] = pd.cut(test['Family_size'],bins=bins, labels=labels)
sns.countplot(data = train, x='Family_Group', hue='Survived')
<AxesSubplot:xlabel='Family_Group', ylabel='count'>

output_72_1


Cabin column 분석

sns.countplot(data = train, x='Cabin', hue='Survived')
<AxesSubplot:xlabel='Cabin', ylabel='count'>

output_66_1


결과: M 에서 상대적으로 많은 사람이 죽었다. (M == null)
  • 생존 여부 판단에 사용할 수 있다.
  • Pclass column 분석
sns.countplot(data = train, x='Pclass', hue='Survived')
<AxesSubplot:xlabel='Pclass', ylabel='count'>

output_74_1


결과: 등급이 높아질수록 생존률이 높다.

Cabin과 Pclass 컬럼 확인

sns.countplot(data = train, x='Cabin', hue='Pclass')
<AxesSubplot:xlabel='Cabin', ylabel='count'>

output_77_1


결과: M은 3등급 사람들이 많다. 사망자의 비율이 높음.
  • A, B, C 구역에는 1등급만 존재한다. 생존 확률이 더 높다.

Embarked column 분석

sns.countplot(data = train, x='Embarked', hue='Survived')
<AxesSubplot:xlabel='Embarked', ylabel='count'>

output_80_1


결과: S, Q에서 탑승한 경우 사망률이 생존률보다 높다.

Age column 확인

# violinplot > 두 데이터 비교에 용이, 차지하는 크기는 데이터 수와 비례하지 않음. (비율과 비례함.)
sns.violinplot(data=train, x='Sex', y='Age', hue='Survived', split=True)
<AxesSubplot:xlabel='Sex', ylabel='Age'>

output_83_1


결과
  • 남자는 산 사람과 죽은 사람의 나이대가 차이가 있다.
  • 여자는 산 사람과 죽은 사람의 나이대가 비슷하다.
  • 어린 아이들의 경우 상대적으로 남자 아이의 생존률이 더 높다.

Fare column 확인

sns.violinplot(data=train, x='Sex', y='Fare', hue='Survived', split=True)
<AxesSubplot:xlabel='Sex', ylabel='Fare'>

output_86_1


결과: 저렴한 요금을 낸 경우 사망률이 더 높다.
sns.countplot(data = train, x='Sex', hue='Survived')
<AxesSubplot:xlabel='Sex', ylabel='count'>

output_88_1


sns.countplot(data = train, y='Sex', hue='Survived')
<AxesSubplot:xlabel='count', ylabel='Sex'>

output_89_1


Text 데이터 (Name, Ticket column)

  • 4-2) 비정형 데이터(문자 데이터) > 정형 데이터(수치형 데이터)
  • 텍스트 데이터(비정형 데이터)는 머신러닝이 학습을 할 수 없다.
  • 비정형 데이터를 특성공학을 통해 정형데이터로 만들어보자.

Name Column

  • 이름 내 호칭을 사용할 수 있음.
  • 이름에 대한 특성 파악
  • 첫 번째는 '성'과 ','이 들어감.
  • 두 번째는 호칭 뒤에 '.(온점)'이 들어감.
train['Name'][0]
'Braund, Mr. Owen Harris'
  • 정형화
  • split 함수로 (',') 기준으로 나눔
  • 나눈 데이터 중 두 번째 데이터 씀.
  • 그 데이터를 다시 ('.') 기준으로 나눔.
  • 나눈 데이터 중 첫 번째 데이터 씀.
  • strip(): 문자 앞 뒤의 띄어쓰기(공백) 제거
train['Name'][0].split(',')[1].split('.')[0].strip()
'Mr'
# 함수 작성
# 테이블의 한 줄이 parameter 로 들어옴.
def split_title(name):
    return name.split(',')[1].split('.')[0].strip()
train['Name'].apply(split_title)
0        Mr
1       Mrs
2      Miss
3       Mrs
4        Mr
       ... 
886     Rev
887    Miss
888    Miss
889      Mr
890      Mr
Name: Name, Length: 891, dtype: object
# Name 컬럼을 Title 로 바꾸기 
train['Title'] = train['Name'].apply(split_title)
test['Title'] = test['Name'].apply(split_title)
train.drop('Name', axis=1, inplace=True)
test.drop('Name', axis=1, inplace=True)
train['Title'].value_counts()
Mr              517
Miss            182
Mrs             125
Master           40
Dr                7
Rev               6
Col               2
Mlle              2
Major             2
Mme               1
Ms                1
Don               1
Sir               1
the Countess      1
Lady              1
Jonkheer          1
Capt              1
Name: Title, dtype: int64
# def map_title(name):
#     if(name == 'Mr' or 'Miss' or 'Mrs' or 'Master' or 'Dr'):
#         return name
#     else:
#         return name.map('Other')
# train['Title2'] = train['Title'].apply(map_title)
# train['Title2'][30]
# train['Title2'].unique()

호칭의 범주를 줄여보자.

  • Mr Mrs Miss Master Dr Other
  • Map 함수를 활용해서 mapping
convert_title_dic = {
    'Mr' : 'Mr' , 
    'Mrs' : 'Mrs', 
    'Miss' : 'Miss', 
    'Master' : 'Master', 
    'Don' : 'Other', 
    'Rev' : 'Rev', 
    'Dr': 'Other', 
    'Mme' : 'Other', 
    'Ms' : 'Other',
    'Major' : 'Other',
    'Lady' : 'Other',
    'Sir' : 'Other',
    'Mlle' : 'Other',
    'Col' : 'Other', 
    'Capt' : 'Other', 
    'the Countess' : 'Other',
    'Jonkheer' : 'Other',
    'Dona' : 'Other'
}
train['Title'] = train['Title'].map(convert_title_dic)
test['Title'] = test['Title'].map(convert_title_dic)
train['Title'].unique()
array(['Mr', 'Mrs', 'Miss', 'Master', 'Other', 'Rev'], dtype=object)
test['Title'].unique()
array(['Mr', 'Mrs', 'Miss', 'Master', 'Other', 'Rev'], dtype=object)

Ticket data

  • 중복되지 않는 값이 681개, 사용하기 어렵다.
  • 삭제
train['Ticket'].value_counts()
1601        7
CA. 2343    7
347082      7
3101295     6
347088      6
           ..
349223      1
349246      1
2697        1
370372      1
36967       1
Name: Ticket, Length: 681, dtype: int64
train['Ticket'].str[:1].value_counts()
3    301
2    183
1    146
P     65
S     65
C     47
A     29
W     13
4     10
7      9
F      7
6      6
L      4
5      3
8      2
9      1
Name: Ticket, dtype: int64
train.drop('Ticket', axis = 1, inplace = True)
test.drop('Ticket', axis = 1, inplace = True)
train.columns.unique()
Index(['Survived', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Cabin',
       'Embarked', 'Family_size', 'Family_Group', 'Title'],
      dtype='object')
train.shape, test.shape
((891, 12), (418, 11))

글자 데이터를 숫자 데이터로 변경해보자.

  • 문자 데이터 > Sex, Cabin, Embarked, Family_group, Title
  • 4-3) 원핫 인코딩(OneHotEncoding): 5개 문자 컬럼을 수치화 한다.
  • 머신러닝 모델은 수치 데이터만 학습할 수 있기 때문에 문자 데이터를 수치 데이터로 변경해야 함.
# One-hot Encoding 함수
categorical_feature = ['Sex', 'Cabin', 'Embarked', 'Family_Group', 'Title']
# train, test 함수를 합친 상태에서 진행해야 함. 
# 합친 후 다시 7:3 비율로 나눠야함.
# 합칠 수 있도록 column 개수를 맞춰줘야 함.
train.shape, test.shape
((891, 12), (418, 11))
# 정답 컬럼 분리
y_train = train['Survived']
# train 에서 Survived 컬럼 삭제
train.drop('Survived', axis=1, inplace=True)
# concat : 행, 열을 기준으로 데이터프레임을 합침.
# merge : 데이터를 기준으로 데이터프레임을 합침.
pd.concat([train, test])
  Pclass Sex Age SibSp Parch Fare Cabin Embarked Family_size Family_Group Title
0 3 male 22.0 1 0 7.25 M S 2 Small Mr
1 1 female 38.0 1 0 71.2833 C C 2 Small Mrs
2 3 female 26.0 0 0 7.925 M S 1 Alone Miss
3 1 female 35.0 1 0 53.1 C S 2 Small Mrs
4 3 male 35.0 0 0 8.05 M S 1 Alone Mr
... ... ... ... ... ... ... ... ... ... ... ...
413 3 male 25.0 0 0 8.05 M S 1 Alone Mr
414 1 female 39.0 0 0 108.9 C C 1 Alone Other
415 3 male 38.5 0 0 7.25 M S 1 Alone Mr
416 3 male 25.0 0 0 8.05 M S 1 Alone Mr
417 3 male 25.0 1 1 22.3583 M C 3 Small Master

1309 rows × 11 columns

combine = pd.concat([train, test], ignore_index=True)
combine.head()
  Pclass Sex Age SibSp Parch Fare Cabin Embarked Family_size Family_Group Title
0 3 male 22.0 1 0 7.25 M S 2 Small Mr
1 1 female 38.0 1 0 71.2833 C C 2 Small Mrs
2 3 female 26.0 0 0 7.925 M S 1 Alone Miss
3 1 female 35.0 1 0 53.1 C S 2 Small Mrs
4 3 male 35.0 0 0 8.05 M S 1 Alone Mr
# 원핫 인코딩
# get_dummies 함수
one_hot = pd.get_dummies(combine[categorical_feature])
one_hot.head()
  Sex_female Sex_male Cabin_A Cabin_B Cabin_C Cabin_D Cabin_E Cabin_F Cabin_G Cabin_M ... Embarked_S Family_Group_Alone Family_Group_Small Family_Group_Large Title_Master Title_Miss Title_Mr Title_Mrs Title_Other Title_Rev
0 0 1 0 0 0 0 0 0 0 1 ... 1 0 1 0 0 0 1 0 0 0
1 1 0 0 0 1 0 0 0 0 0 ... 0 0 1 0 0 0 0 1 0 0
2 1 0 0 0 0 0 0 0 0 1 ... 1 1 0 0 0 1 0 0 0 0
3 1 0 0 0 1 0 0 0 0 0 ... 1 0 1 0 0 0 0 1 0 0
4 0 1 0 0 0 0 0 0 0 1 ... 1 1 0 0 0 0 1 0 0 0

5 rows × 23 columns

# 기존 문자데이터 삭제
combine.head()
  Pclass Sex Age SibSp Parch Fare Cabin Embarked Family_size Family_Group Title
0 3 male 22.0 1 0 7.25 M S 2 Small Mr
1 1 female 38.0 1 0 71.2833 C C 2 Small Mrs
2 3 female 26.0 0 0 7.925 M S 1 Alone Miss
3 1 female 35.0 1 0 53.1 C S 2 Small Mrs
4 3 male 35.0 0 0 8.05 M S 1 Alone Mr
# combine 함수 내 categoricatl_feature(문자 데이터 전부)를 삭제한다.
combine.drop(categorical_feature, axis=1, inplace=True)
combine.head()
  Pclass Age SibSp Parch Fare Family_size
0 3 22.0 1 0 7.25 2
1 1 38.0 1 0 71.2833 2
2 3 26.0 0 0 7.925 1
3 1 35.0 1 0 53.1 2
4 3 35.0 0 0 8.05 1
# 지운 후, one_hot encoding data 를 합쳐준다. total_combine 함수에 저장 
total_combine = pd.concat([combine, one_hot], axis=1)
total_combine.shape
(1309, 29)
total_combine.head()
  Pclass Age SibSp Parch Fare Family_size Sex_female Sex_male Cabin_A Cabin_B ... Embarked_S Family_Group_Alone Family_Group_Small Family_Group_Large Title_Master Title_Miss Title_Mr Title_Mrs Title_Other Title_Rev
0 3 22.0 1 0 7.25 2 0 1 0 0 ... 1 0 1 0 0 0 1 0 0 0
1 1 38.0 1 0 71.2833 2 1 0 0 0 ... 0 0 1 0 0 0 0 1 0 0
2 3 26.0 0 0 7.925 1 1 0 0 0 ... 1 1 0 0 0 1 0 0 0 0
3 1 35.0 1 0 53.1 2 1 0 0 0 ... 1 0 1 0 0 0 0 1 0 0
4 3 35.0 0 0 8.05 1 0 1 0 0 ... 1 1 0 0 0 0 1 0 0 0

5 rows × 29 columns

5. 모델 선택 및 하이퍼파라미터 조정

from sklearn.tree import DecisionTreeClassifier # 분류
tree_model = DecisionTreeClassifier() 

6. 학습

X_train = total_combine.iloc[:891]
X_test = total_combine.iloc[891:]
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
(891, 29)
(418, 29)
(891,)
tree_model.fit(X_train, y_train)
DecisionTreeClassifier()
pre = tree_model.predict(X_test)
gender = pd.read_csv('./gender_submission.csv')
# gender DataFrame 에 Survived 라는 컬럼으로 pre 값 저장 
gender['Survived']= pre
gender.to_csv('mySubmission01.csv', index=False)

'데이터 분석' 카테고리의 다른 글

[머신러닝 데이터 분석] Iris 품종 분류  (0) 2021.07.18
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.