데이터 한 그릇

캐글 입문, 타이타닉 생존자 예측 모델 만들기! 본문

머신러닝

캐글 입문, 타이타닉 생존자 예측 모델 만들기!

장사이언스 2021. 3. 24. 02:56
Titanic 생존자예측 연습

Titanic 생존자 예측 모델 만들기 연습

1)EDA 작업

전체 데이터 구조 확인(train data부터)

titanic 데이터 불러오기

In [112]:
import pandas as pd

titanic_df = pd.read_csv('C:\ca_da\DataHandling/train.csv')
titanic_df
Out[112]:
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

In [113]:
titanic_df.head()
Out[113]:
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 [114]:
titanic_df.tail()
Out[114]:
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
886 887 0 2 Montvila, Rev. Juozas male 27.0 0 0 211536 13.00 NaN S
887 888 1 1 Graham, Miss. Margaret Edith female 19.0 0 0 112053 30.00 B42 S
888 889 0 3 Johnston, Miss. Catherine Helen "Carrie" female NaN 1 2 W./C. 6607 23.45 NaN S
889 890 1 1 Behr, Mr. Karl Howell male 26.0 0 0 111369 30.00 C148 C
890 891 0 3 Dooley, Mr. Patrick male 32.0 0 0 370376 7.75 NaN Q

Info로 데이터 정보 확인

데이터의 구조는 Data Frame

관측치와 칼럼의 개수는 891 rows * 12 column

각 칼럼의 자료형 확인

null 값이 Age, Cabin, Embarked에 있음을 확인

In [115]:
titanic_df.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

null 값을 가지고 있는 칼럼의 null 값 개수가 궁금하여 확인해보기

Age null 개수 : 177

Cabin null 개수 : 687

Embarked null 개수 : 2

In [116]:
titanic_df.isnull().sum()
Out[116]:
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

각 칼럼의 데이터 정보 확인

수치를 통한 각 칼럼(변수)의 정보 확인

통계 수치 값을 얻을 수 있는 칼럼(변수)의 유형은 구간데이터의 칼럼 뿐이다.

평균 값과 표준편차 값에 주의하여 보았다.

In [7]:
#데이터 안의 구간변수의 통계 수치 확인
#describe를 통해서 각 칼럼의 중심값과 변동성 값을 확인했다.
#특히 각 칼럼의 mean, std(평균과 표준편차) 에 집중하였다.

titanic_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

그래프를 통한 각 칼럼의 정보 확인

범주형 데이터 -> 구간데이터 순서로 그래프 확인

Survived

0과 1로 이루어진 Survived 데이터는 0은 죽음 1은 생존을 의미한다.

전체 데이터에서 0의 도수가 1의 도수보다 더 많았다.

In [119]:
titanic_df['Survived'].value_counts().plot(kind = "bar")
Out[119]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a2bcb308>
In [118]:
titanic_df['Survived'].value_counts().plot(kind = "pie")
Out[118]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a2bc6788>

Pclass

3등급 2등급 1등급 (3,2,1) 의 데이터를 가지고 있다.

도수를 따져보면

3> 1 >2

In [120]:
titanic_df['Pclass'].value_counts().plot(kind = "bar")
Out[120]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a25ed2c8>
In [121]:
titanic_df['Pclass'].value_counts().plot(kind = "pie")
Out[121]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a2ca94c8>

Sex

male 과 female 의 데이터를 가지고 있다.

male의 숫자가 더 많았고 female의 숫자가 더 적었다.

In [122]:
titanic_df['Sex'].value_counts().plot(kind = "bar")
Out[122]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a2cfba88>
In [123]:
titanic_df['Sex'].value_counts().plot(kind = "pie")
Out[123]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a2d66888>

Embarked

승선한 장소 S, C, Q의 데이터를 가지고 있다.

도수는

S > C > Q

In [124]:
titanic_df['Embarked'].value_counts().plot(kind = "bar")
Out[124]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a2dab588>
In [125]:
titanic_df['Embarked'].value_counts().plot(kind = "pie")
Out[125]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a2e1a4c8>

Age(구간데이터 시작)

히스토그램의 모양은 왜도가 양수를 보이고 있다.

20~30세의 나이가 많이 있었다.

70~80세의 나이가 가장 적었다.

In [18]:
%matplotlib inline
import matplotlib.pyplot as plt
plt.hist(titanic_df['Age'], bins = 10)
Out[18]:
714

Fare

0~100 사이의 데이터가 가장 많은 비중을 차지하고 있다.

In [126]:
plt.hist(titanic_df['Fare'], bins = 5)
Out[126]:
(array([838.,  33.,  17.,   0.,   3.]),
 array([  0.     , 102.46584, 204.93168, 307.39752, 409.86336, 512.3292 ]),
 <a list of 5 Patch objects>)

각 칼럼간 관계 데이터 정보 확인

다음으로는 각 변수간 관계를 살펴보도록 하겠다

분석의 목적 자체가 생존자 예측이기 때문에 생존과 상관관계가 높은 변수에 관심을 가져야만 한다.

따라서 생존을 디폴트 값으로 두고 각각의 상관관계를 살펴보도록 하겠다.

먼저 범주형데이터와 survived 관계를 표와 그래프를 통해서 살펴보고

구간데이터와 survived와의 관계를 상관관계분석을 통해서 살펴보도록 하겠다.

범주형 데이터 <-> Survived

groupby표와 교차그래프를 통해서 살펴보기

Pclass <-> Survived

1등급 2등급 3등급 별로 사망자 수와 생존자 수를 groupby를 통해서 살펴보기

1등급 : 생존자 수 > 사망자 수

2등급 : 생존자 수 < 사망자 수

3등급 : 생존자 수 < 사망자 수

2등급의 전체도수 비교 생존자 수의 상대도수를 봤을 때 3등급보다는 생존자 수의 상대도수가 높음을 확인할 수 있다.

즉, 2등급과 3등급이 생존자 수보다 사망자 수 가 더 많다는 공통점이 있지만 비율로 따졌을 때 2등급이 생존 확률이 높을 수 있음을 추측할 수 있다.

따라서 등급이 낮아질수록 생존의 확률이 낮아지고 있음을 추론 가능하다.

In [127]:
titanic_df.groupby(['Pclass','Survived'])['Survived'].count()
Out[127]:
Pclass  Survived
1       0            80
        1           136
2       0            97
        1            87
3       0           372
        1           119
Name: Survived, dtype: int64
In [128]:
import matplotlib.pyplot as plt
import seaborn as sns

sns.countplot('Pclass',hue = 'Survived', data = titanic_df )
Out[128]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a2eb4fc8>

Sex <-> Survived

female => 생존자 > 사망자 male => 생존자 < 사망자

남성이 타이타닉에서 생존확률이 더 낮았음을 확인할 수 있다.

In [129]:
titanic_df.groupby(['Sex','Survived'])['Survived'].count()
Out[129]:
Sex     Survived
female  0            81
        1           233
male    0           468
        1           109
Name: Survived, dtype: int64
In [130]:
sns.countplot('Sex', hue = 'Survived', data = titanic_df)
Out[130]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a2f4cb48>

Embarked <-> Survived

S => 사망자 > 생존자

Q => 사망자 > 생존자

C => 생존자 < 사망자

S선착장 (사우스햄튼) 에서 승선한 승객들이 많이 죽었음을 알 수 있다.

왜 그렇지? 사실 S선착장에서 탑승한 것과 죽음과 어떤 연관관계가 있을까 계속 고민해 봤다.

최종적으로 추측한 것은, S선착장에서 탄 사람들은 Pclass를 낮은 등급을 사야만 하는 사람들이 많아서 그랬을까? 생각해 봤다.

(Pclass가 높을 수록 사는 확률이 높았으니까)

여기서 고민하여 부가적으로 여러 가지를 시도해봤다.

In [131]:
titanic_df.groupby(['Embarked','Survived'])['Survived'].count()
Out[131]:
Embarked  Survived
C         0            75
          1            93
Q         0            47
          1            30
S         0           427
          1           217
Name: Survived, dtype: int64
In [132]:
#S선착장에서 사망비율이 가장 높은 것으로 나타났다.

titanic_df['Embarked'].value_counts()

print('S선착장 데이터 내에서의 사람들의 사망비율 = %.2f' %(titanic_df.loc[(titanic_df['Survived'] == 0) & (titanic_df['Embarked'] == 'S'),'Embarked'].count()/titanic_df.loc[titanic_df['Embarked'] =='S','Embarked'].count()))
print('Q선착장 데이터 내에서의 사람들의 사망비율 = %.2f' %(titanic_df.loc[(titanic_df['Survived'] == 0) & (titanic_df['Embarked'] == 'Q'),'Embarked'].count()/titanic_df.loc[titanic_df['Embarked'] =='Q','Embarked'].count()))
print('C선착장 데이터 내에서의 사람들의 사망비율 = %.2f' %(titanic_df.loc[(titanic_df['Survived'] == 0) & (titanic_df['Embarked'] == 'C'),'Embarked'].count()/titanic_df.loc[titanic_df['Embarked'] =='C','Embarked'].count()))

#전체 선착장 탑승명 대비 각 선착장 사망비율


print(titanic_df.loc[(titanic_df['Survived'] == 0) & (titanic_df['Embarked'] == 'S'),'Embarked'].count()/titanic_df['Embarked'].count() )
print(titanic_df.loc[(titanic_df['Survived'] == 0) & (titanic_df['Embarked'] == 'Q'),'Embarked'].count()/titanic_df['Embarked'].count() )
print(titanic_df.loc[(titanic_df['Survived'] == 0) & (titanic_df['Embarked'] == 'C'),'Embarked'].count()/titanic_df['Embarked'].count() )
S선착장 데이터 내에서의 사람들의 사망비율 = 0.66
Q선착장 데이터 내에서의 사람들의 사망비율 = 0.61
C선착장 데이터 내에서의 사람들의 사망비율 = 0.45
0.48031496062992124
0.052868391451068614
0.0843644544431946
In [133]:
sns.countplot('Embarked', hue = 'Survived', data = titanic_df)
Out[133]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a2fcf888>

구간데이터 <-> Survived

상관관계표를 통해서 (피어슨 방식을 사용)

(Survived와의 비교 한정) Survived 변수와 Pclass 변수가 이중에서 가장 강한 상관관계를 가지고 있음을 알 수 있다.

그 다음은 Fare

In [135]:
titanic_df.corr(method = 'pearson')
Out[135]:
PassengerId Survived Pclass Age SibSp Parch Fare
PassengerId 1.000000 -0.005007 -0.035144 0.036847 -0.057527 -0.001652 0.012658
Survived -0.005007 1.000000 -0.338481 -0.077221 -0.035322 0.081629 0.257307
Pclass -0.035144 -0.338481 1.000000 -0.369226 0.083081 0.018443 -0.549500
Age 0.036847 -0.077221 -0.369226 1.000000 -0.308247 -0.189119 0.096067
SibSp -0.057527 -0.035322 0.083081 -0.308247 1.000000 0.414838 0.159651
Parch -0.001652 0.081629 0.018443 -0.189119 0.414838 1.000000 0.216225
Fare 0.012658 0.257307 -0.549500 0.096067 0.159651 0.216225 1.000000
In [136]:
sns.heatmap(titanic_df.corr(), annot=True)
Out[136]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a303b2c8>

titanic Test 데이터 탐색

train 데이터를 탐색했던 것과 같은 방식으로

In [137]:
import pandas as pd

titanic_test_df = pd.read_csv('C:\ca_da\DataHandling/test.csv')
titanic_test_df
Out[137]:
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

In [138]:
titanic_test_df.info()
<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.0+ KB
In [139]:
titanic_test_df.isnull().sum()
Out[139]:
PassengerId      0
Pclass           0
Name             0
Sex              0
Age             86
SibSp            0
Parch            0
Ticket           0
Fare             1
Cabin          327
Embarked         0
dtype: int64
In [140]:
titanic_test_df.describe()
Out[140]:
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
In [141]:
titanic_test_df['Pclass'].value_counts().plot(kind = 'bar')
Out[141]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a317a3c8>
In [142]:
titanic_test_df['Pclass'].value_counts().plot(kind = 'pie')
Out[142]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a31cfe08>
In [143]:
titanic_test_df['Sex'].value_counts().plot(kind = 'bar')
Out[143]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a3220088>
In [144]:
titanic_test_df['Sex'].value_counts().plot(kind = 'pie')
Out[144]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a327b108>
In [145]:
titanic_test_df['Embarked'].value_counts().plot(kind = 'bar')
Out[145]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a32cab48>
In [146]:
titanic_test_df['Embarked'].value_counts().plot(kind = 'pie')
Out[146]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a42fc748>
In [147]:
import matplotlib.pyplot as plt

plt.hist(titanic_test_df['Age'], bins = 10)
Out[147]:
(array([16., 16., 71., 97., 43., 37., 25., 17.,  9.,  1.]),
 array([ 0.17 ,  7.753, 15.336, 22.919, 30.502, 38.085, 45.668, 53.251,
        60.834, 68.417, 76.   ]),
 <a list of 10 Patch objects>)
In [148]:
plt.hist(titanic_test_df['Fare'],bins = 10)
Out[148]:
(array([338.,  48.,  11.,   2.,  10.,   7.,   0.,   0.,   0.,   1.]),
 array([  0.     ,  51.23292, 102.46584, 153.69876, 204.93168, 256.1646 ,
        307.39752, 358.63044, 409.86336, 461.09628, 512.3292 ]),
 <a list of 10 Patch objects>)

2)전처리 작업

엑셀로 데이터 합치고 통합 정보 얻기

train + test

In [157]:
import pandas as pd

titanic_total_df = pd.read_csv('C:\ca_da\DataHandling\\total.csv')
titanic_total_df

#엑셀파일로 수작업으로 합침
Out[157]:
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
0 1 0.0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
1 2 1.0 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
2 3 1.0 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
3 4 1.0 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
4 5 0.0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S
... ... ... ... ... ... ... ... ... ... ... ... ...
1304 1305 NaN 3 Spector, Mr. Woolf male NaN 0 0 A.5. 3236 8.0500 NaN S
1305 1306 NaN 1 Oliva y Ocana, Dona. Fermina female 39.0 0 0 PC 17758 108.9000 C105 C
1306 1307 NaN 3 Saether, Mr. Simon Sivertsen male 38.5 0 0 SOTON/O.Q. 3101262 7.2500 NaN S
1307 1308 NaN 3 Ware, Mr. Frederick male NaN 0 0 359309 8.0500 NaN S
1308 1309 NaN 3 Peter, Master. Michael J male NaN 1 1 2668 22.3583 NaN C

1309 rows × 12 columns

In [158]:
titanic_total_df.head()
Out[158]:
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
0 1 0.0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
1 2 1.0 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
2 3 1.0 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
3 4 1.0 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
4 5 0.0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S
In [159]:
titanic_total_df.tail()
Out[159]:
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
1304 1305 NaN 3 Spector, Mr. Woolf male NaN 0 0 A.5. 3236 8.0500 NaN S
1305 1306 NaN 1 Oliva y Ocana, Dona. Fermina female 39.0 0 0 PC 17758 108.9000 C105 C
1306 1307 NaN 3 Saether, Mr. Simon Sivertsen male 38.5 0 0 SOTON/O.Q. 3101262 7.2500 NaN S
1307 1308 NaN 3 Ware, Mr. Frederick male NaN 0 0 359309 8.0500 NaN S
1308 1309 NaN 3 Peter, Master. Michael J male NaN 1 1 2668 22.3583 NaN C
In [160]:
titanic_total_df.info()

#데이터 프레임
#1309개의 관측치와 12개의 칼람
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1309 entries, 0 to 1308
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  1309 non-null   int64  
 1   Survived     891 non-null    float64
 2   Pclass       1309 non-null   int64  
 3   Name         1309 non-null   object 
 4   Sex          1309 non-null   object 
 5   Age          1046 non-null   float64
 6   SibSp        1309 non-null   int64  
 7   Parch        1309 non-null   int64  
 8   Ticket       1309 non-null   object 
 9   Fare         1308 non-null   float64
 10  Cabin        295 non-null    object 
 11  Embarked     1307 non-null   object 
dtypes: float64(3), int64(4), object(5)
memory usage: 122.8+ KB

titanic total 데이터 null 값 확인

Survived 데이터의 null 값은 test 데이터의 특성상 없는 잠시 없는 데이터라고 할 수 있다.

Age : 263개

Cabin : 1014개

Embarked : 2개

In [161]:
titanic_total_df.isnull().sum()
Out[161]:
PassengerId       0
Survived        418
Pclass            0
Name              0
Sex               0
Age             263
SibSp             0
Parch             0
Ticket            0
Fare              1
Cabin          1014
Embarked          2
dtype: int64

변수 엔지니어링과 null 값 처리

Name 변수 엔지니어링

먼저 가장 거슬리는 칼럼이 Name 이였다. 왜냐하면 NULL 값은 전혀 존재하지 않아서 데이터 퀄리티는 좋지만,

Survived를 예측하는 상황에서 Name이 어떠한 관계를 지닐수나 있을지 생각했다.

데이터 분석하는데 칼럼을 함부로 지우는 게 아니라는 것을 알고 있었기 때문에 곰곰히 생각하다 결국 구글링을 했다.. ㅎ

많은 사람들이 Name 가지고 유의미한 정보를 얻었다.. 지우면 큰일날뻔 했다.

Name의 Mr,Miss,Mrs.. 등등의 값만을 추출하여 이 값을 통해 Age를 추측하는 작업을 진행했다.

(결혼을 했는지의 유무에 따라서 나이가 더 정확하게 추측이 될 것이라는 판단 하에서)

In [162]:
titanic_total_df['Name']
titanic_total_df['Title'] = ""

~어려웠던 부분

Name에서 Strip 함수로 추출하는 과정에서 추출된 결과 값이 공백을 가지고 있음을 알지 못했다.

따라서 30분넘게 원하는 데이터 분석 과정을 밟지 못했는데, 혹시나 해서 Strip 함수를 주니 해결이 되었다.

다음 분석에서 문자열 데이터에서 문자를 추출할 때 공백의 여부를 꼭 체크해야겠다.

In [163]:
#문자형 내장함수 Strip을 하지 않을 경우에는 공백이 있기 때문에 후에 변수명을 제대로 읽지 못한다. 주의!

for i in range(len(titanic_total_df['Name'])):
    titanic_total_df['Title'][i] = titanic_total_df['Name'][i].split(',')[1].split('.')[0].strip()
C:\ca_da\anaconda\envs\ca-da\lib\site-packages\ipykernel_launcher.py:4: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  after removing the cwd from sys.path.

Title 데이터 정리 - 1

Strip으로 데이터를 추출하면 Mr,Miss,Mrs,Master,등등 다양한 데이터가 포함이 된다. 하지만 그 도수를 확인해보면 굉장히 적은 수의

데이터를 가지고 있음을 확인할 수 있다.

결국 잘못 기입하거나, 쓸모없는 데이터일 가능성이 크다고 판단하고 이것들을 Others라는 데이터로 변환하여 관리하였다.

이 때 Strip을 사용하지 않고 데이터를 가져온다면 넘파이 내장함수 where를 사용하지 못한다.

where을 통해서 Others로 데이터를 변환하자.

In [164]:
import numpy as np
titanic_total_df['Title'] = np.where(titanic_total_df['Title'].isin(['Mr','Miss','Mrs','Master']), titanic_total_df['Title'], 'Others')
titanic_total_df['Title'].value_counts()
Out[164]:
Mr        757
Miss      260
Mrs       197
Master     61
Others     34
Name: Title, dtype: int64
In [165]:
titanic_total_df.head()
Out[165]:
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked Title
0 1 0.0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S Mr
1 2 1.0 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C Mrs
2 3 1.0 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S Miss
3 4 1.0 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S Mrs
4 5 0.0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S Mr

Title 데이터 정리 - 2

교차 그래프를 통해서 각 데이터당 생존자와 사망자 수를 파악했다

이 역시도 여성이 남성보다 생존률이 높았음을 확인시켜준다.

Mr : 사망자 > 생존자

Mrs : 사망자 < 생존자

Miss : 사망자 < 생존자

Master : 사망자 < 생존자

Others : 사망자 > 생존자

In [166]:
#X축으로 확인하는 것보다 Y축으로 확인하는 게 더 한 눈에 들어왔다.

import seaborn as sns

sns.countplot(y = 'Title',hue = 'Survived', data = titanic_total_df)
Out[166]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a44ab508>
In [167]:
sns.countplot('Survived',hue = 'Title', data = titanic_total_df)
Out[167]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a4503e88>

Age 칼럼 엔지니어링과 null 값 처리 - 1

Age를 통해서 게급구간을 지어서 파생변수를 만드려고 하는데, 먼저 null 값을 채운 이후에 파생변수를 만드는 게 수월하다고 생각하여 null 값부터 채웠다.

단순대치로 Age의 평균을 넣는 것보다는 다른 방식을 사용하는 게 더 정확도가 높을 것이라 판단했다.

앞서 이를 위해서 Title을 구했기 때문에, Title 데이터 별 나이 평균(Mr,Miss... 등등) 을 구한 후

age null 값이 있는 관측치의 Title 을 확인하여 mr면, mr age의 평균 값을 그리고 miss면 miss의 평균 값을 넣기로 했다.

In [168]:
titanic_total_df['Title']
Out[168]:
0           Mr
1          Mrs
2         Miss
3          Mrs
4           Mr
         ...  
1304        Mr
1305    Others
1306        Mr
1307        Mr
1308    Master
Name: Title, Length: 1309, dtype: object

밑에 이걸 혹시 for문을 사용하여 출력할 수 없을까 고민해봐야겠다.. 너무 귀찮았어

In [54]:
#각 title 별 평균나이를 계산한 후에 변수로 저장한다.

print('Mr의 평균 나이 = {:.2f}' .format(titanic_total_df.loc[titanic_total_df['Title'] == 'Mr','Age'].mean()))
Mr_Mean = titanic_total_df.loc[titanic_total_df['Title'] == 'Mr','Age'].mean()
print('Mrs의 평균 나이 = {:.2f}'.format(titanic_total_df.loc[titanic_total_df['Title'] == 'Mrs','Age'].mean()))
Mrs_Mean = titanic_total_df.loc[titanic_total_df['Title'] == 'Mrs','Age'].mean()
print('Miss의 평균 나이 = {:.2f}'.format(titanic_total_df.loc[titanic_total_df['Title'] == 'Miss','Age'].mean()))
Miss_Mean = titanic_total_df.loc[titanic_total_df['Title'] == 'Miss','Age'].mean()
print('Master의 평균 나이 = {:.2f}'.format(titanic_total_df.loc[titanic_total_df['Title'] == 'Master','Age'].mean()))
Master_Mean = titanic_total_df.loc[titanic_total_df['Title'] == 'Master','Age'].mean()
print('Others의 평균 나이 = {:.2f}'.format(titanic_total_df.loc[titanic_total_df['Title'] == 'Others','Age'].mean()))
Others_Mean = titanic_total_df.loc[titanic_total_df['Title'] == 'Others','Age'].mean()
Mr의 평균 나이 = 32.25
Mrs의 평균 나이 = 36.99
Miss의 평균 나이 = 21.77
Master의 평균 나이 = 5.48
Others의 평균 나이 = 42.66
In [169]:
#null 값 있는 관측치를 확인하고 title 데이터를 확인 후에 위에서 구한 맞는 평균 값 삽입

titanic_total_df.loc[(titanic_total_df['Title'] == 'Mr') & (titanic_total_df['Age'].isnull()),'Age'] = Mr_Mean
titanic_total_df.loc[(titanic_total_df['Title'] == 'Mrs') & (titanic_total_df['Age'].isnull()),'Age'] = Mrs_Mean
titanic_total_df.loc[(titanic_total_df['Title'] == 'Miss') & (titanic_total_df['Age'].isnull()),'Age'] = Miss_Mean
titanic_total_df.loc[(titanic_total_df['Title'] == 'Master') & (titanic_total_df['Age'].isnull()),'Age'] = Master_Mean
titanic_total_df.loc[(titanic_total_df['Title'] == 'Others') & (titanic_total_df['Age'].isnull()),'Age'] = Others_Mean
In [170]:
#Age의 널값을 전부 해결하였다.
titanic_total_df.isnull().sum()
Out[170]:
PassengerId       0
Survived        418
Pclass            0
Name              0
Sex               0
Age               0
SibSp             0
Parch             0
Ticket            0
Fare              1
Cabin          1014
Embarked          2
Title             0
dtype: int64

Age 칼럼 엔지니어링과 null 값 처리 - 2

Age의 null 값을 처리했으니 Age를 더 범주데이터로 바꾸어서 관리해야겠다고 생각했다.

왜냐면 나중에 머신러닝 할 때 encoder 작업에서 수월할 것으로 봤기 때문이다.

따라서 확률도 더 높여줄 수 있을거라 기대했다.

칼럼 데이터의 4분위수를 통해서 데이터를 범주화 시키기로 마음 먹었다.

In [171]:
titanic_total_df['Age'].describe()
Out[171]:
count    1309.000000
mean       29.907391
std        13.197710
min         0.170000
25%        21.774238
50%        30.000000
75%        36.000000
max        80.000000
Name: Age, dtype: float64

2-2

사분위수대로 하려고 했는데 3분위수와 MAX값의 차이가 너무 크게 났기 때문에 구간을 조금 조정을 하였다.

In [172]:
bins = [0,21,30,40,60,81]
bins_Name = ['child','young','adult','middle','senior']

titanic_total_df['Age_bin'] = ""
titanic_total_df['Age_bin'] = pd.cut(titanic_total_df['Age'],bins = bins, labels = bins_Name, include_lowest = True)
titanic_total_df
Out[172]:
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked Title Age_bin
0 1 0.0 3 Braund, Mr. Owen Harris male 22.000000 1 0 A/5 21171 7.2500 NaN S Mr young
1 2 1.0 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.000000 1 0 PC 17599 71.2833 C85 C Mrs adult
2 3 1.0 3 Heikkinen, Miss. Laina female 26.000000 0 0 STON/O2. 3101282 7.9250 NaN S Miss young
3 4 1.0 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.000000 1 0 113803 53.1000 C123 S Mrs adult
4 5 0.0 3 Allen, Mr. William Henry male 35.000000 0 0 373450 8.0500 NaN S Mr adult
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1304 1305 NaN 3 Spector, Mr. Woolf male 32.252151 0 0 A.5. 3236 8.0500 NaN S Mr adult
1305 1306 NaN 1 Oliva y Ocana, Dona. Fermina female 39.000000 0 0 PC 17758 108.9000 C105 C Others adult
1306 1307 NaN 3 Saether, Mr. Simon Sivertsen male 38.500000 0 0 SOTON/O.Q. 3101262 7.2500 NaN S Mr adult
1307 1308 NaN 3 Ware, Mr. Frederick male 32.252151 0 0 359309 8.0500 NaN S Mr adult
1308 1309 NaN 3 Peter, Master. Michael J male 5.482642 1 1 2668 22.3583 NaN C Master child

1309 rows × 14 columns

In [173]:
import seaborn as sns

sns.countplot('Age_bin',hue = 'Survived', data = titanic_total_df)
Out[173]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a45a78c8>
In [174]:
sns.countplot('Survived',hue = 'Age_bin', data = titanic_total_df)
Out[174]:
<matplotlib.axes._subplots.AxesSubplot at 0x2d7a4624188>

SibSp, Parch 변수 엔지니어링과 null값 처리

SibSp와 Parch 데이터의 본질적인 의미는 가족 동반자 수라고 생각했기 때문에 두 변수의 데이터끼리 합한 값을 새 변수로 만들어야 겠다고 판단했다.

In [175]:
titanic_total_df['Family_Num'] = ""
titanic_total_df['Family_Num'] = titanic_total_df['SibSp'] + titanic_total_df['Parch']
In [176]:
print('동반 가족수의 최댓값 = {}'.format(titanic_total_df['Family_Num'].max()))
print('동반 가족수의 최솟값 = {}'.format(titanic_total_df['Family_Num'].min()))

plt.hist(titanic_total_df['Family_Num'])
동반 가족수의 최댓값 = 10
동반 가족수의 최솟값 = 0
Out[176]:
(array([790., 235., 159.,  43.,  22.,  25.,  16.,   8.,   0.,  11.]),
 array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.]),
 <a list of 10 Patch objects>)

Embarked null값 처리

null 값이 2개가 보이는데, 데이터의 대부분이 S를 가지고 있기 때문에 null 값 2개도 S일 확률이 높다고 판단하여 S로 채워넣었다.

In [177]:
print(titanic_total_df['Embarked'].value_counts())
titanic_total_df['Embarked'] == 'S'
titanic_total_df.loc[titanic_total_df['Embarked'].isnull(),'Embarked'] = 'S'
S    914
C    270
Q    123
Name: Embarked, dtype: int64

Fare null값 처리

Fare의 null 값은 한 개이다. Fare가 Pclass와 연관이 있다고 판단하여,

Fare의 null 값을 가지고 있는 관측치의 Pclass를 확인하고,

그 Pclass에 해당하는 Fare의 평균 값을 구하여 null 값을 채웠다.

In [178]:
#3등급의 Fare 평균값으로 null 값을 대체해준다.

round(titanic_total_df.loc[titanic_total_df['Pclass']==3,'Fare'].mean(),2)
titanic_total_df.loc[titanic_total_df['Fare'].isnull(),'Fare'] = round(titanic_total_df.loc[titanic_total_df['Pclass']==3,'Fare'].mean(),2)
titanic_total_df.isnull().sum()
Out[178]:
PassengerId       0
Survived        418
Pclass            0
Name              0
Sex               0
Age               0
SibSp             0
Parch             0
Ticket            0
Fare              0
Cabin          1014
Embarked          0
Title             0
Age_bin           0
Family_Num        0
dtype: int64

최종 분석용 데이터 셋 선정

Cabin 과 같은 경우에는 null 값이 너무나 많기 때문에 필요없는 데이터라고 판단하여 삭제하였다.

물론 타이타닉이 침몰할 때 Cabin의 위치가 생존에 중요했을 것이라고 판단했는데, null 값이 너무 많아서 어떻게 채울 방법을 생각하지 못했다.

만일 Cabin 값을 채울 수 있는 방법이 있다면 이후 머신러닝 하여 만든 모델의 정확도가 굉장히 많이 올라갈 것 같다.

In [179]:
titanic_total_df
titanic_data = titanic_total_df[['Survived','Pclass','Sex','Age_bin','Family_Num','Fare','Embarked','Title']]
titanic_data
Out[179]:
Survived Pclass Sex Age_bin Family_Num Fare Embarked Title
0 0.0 3 male young 1 7.2500 S Mr
1 1.0 1 female adult 1 71.2833 C Mrs
2 1.0 3 female young 0 7.9250 S Miss
3 1.0 1 female adult 1 53.1000 S Mrs
4 0.0 3 male adult 0 8.0500 S Mr
... ... ... ... ... ... ... ... ...
1304 NaN 3 male adult 0 8.0500 S Mr
1305 NaN 1 female adult 0 108.9000 C Others
1306 NaN 3 male adult 0 7.2500 S Mr
1307 NaN 3 male adult 0 8.0500 S Mr
1308 NaN 3 male child 2 22.3583 C Master

1309 rows × 8 columns

3)Encoder

test 데이터와 train 데이터 나누기

모델링 전에 test와 train을 다시 나눌 필요가 있다.

엑셀을 통해서 나누었는데, Survived 데이터가 있는 것은 train 데이터이기 때문에 Survived를 기준으로 나누었다.

사실 여기서 실수를 하는데 범주형 데이터를 encoder 하는 작업을 먼저 하고 나누었으면 분석의 시간이 짧아졌을텐데

미리 나누고 두 개를 따로 따로 encoder 하였다.

빠가였다..

encoder 방식도 너무 수작업으로 했던 것 같다. 다른 좋은 방식 없을까...

In [200]:
train_X = pd.read_csv('C:\ca_da\DataHandling/train_X.csv')
test_X = pd.read_csv('C:\ca_da\DataHandling/test_X.csv')
train_X.drop(['Column1'],inplace = True, axis = 1)
train_X
Out[200]:
Survived Pclass Sex Age_bin Family_Num Fare Embarked Title
0 0 3 male young 1 7.2500 S Mr
1 1 1 female adult 1 71.2833 C Mrs
2 1 3 female young 0 7.9250 S Miss
3 1 1 female adult 1 53.1000 S Mrs
4 0 3 male adult 0 8.0500 S Mr
... ... ... ... ... ... ... ... ...
886 0 2 male young 0 13.0000 S Others
887 1 1 female young 0 30.0000 S Miss
888 0 3 female young 3 23.4500 S Miss
889 1 1 male young 0 30.0000 C Mr
890 0 3 male young 0 7.7500 Q Mr

891 rows × 8 columns

In [201]:
test_X.drop(['Survived'], axis = 1, inplace = True)
In [202]:
test_X.drop(['Column1'],axis = 1, inplace = True)
test_X
Out[202]:
Pclass Sex Age_bin Family_Num Fare Embarked Title
0 3 male adult 0 7.8292 Q Mr
1 3 female adult 1 7.0000 S Mrs
2 2 male middle 0 9.6875 Q Mr
3 3 male young 0 8.6625 S Mr
4 3 female young 2 12.2875 S Mrs
... ... ... ... ... ... ... ...
413 3 male adult 0 8.0500 S Mr
414 1 female adult 0 108.9000 C Others
415 3 male adult 0 7.2500 S Mr
416 3 male adult 0 8.0500 S Mr
417 3 male child 2 22.3583 C Master

418 rows × 7 columns

모델링을 위해 데이터 encoder

딕셔너리 자료구조와 map함수를 이용하여 작업을 진행했다.

지금와서 정보를 확인했는데, 이 방식 이외에 사이킷런 라이브러리로 처리하는 방법이 있다고 한다.

다음 데이터 분석은 그 방식을 써봐야겠다. 지금은 실수를 기록하는 쪽으로 기록하도록 하자.

In [203]:
title = {"Mr":1,"Miss":2,"Mrs":3,"Master":4,"Others":5}
train_X['Title'] = train_X['Title'].map(title)
test_X['Title'] = test_X['Title'].map(title)
In [204]:
train_X['Age_bin'].value_counts()
age = {"young":1,"adult":2,"child":3,"middle":4,"senior":5}
train_X['Age_bin'] = train_X['Age_bin'].map(age)
test_X['Age_bin'] = test_X['Age_bin'].map(age)
In [205]:
sex = {"male":1,"female":2}
train_X['Sex'] = train_X['Sex'].map(sex)
test_X['Sex'] = test_X['Sex'].map(sex)
In [206]:
train_X['Embarked'].value_counts()
embarked = {"S":1,"C":2,"Q":3}
train_X['Embarked'] = train_X['Embarked'].map(embarked)
test_X['Embarked'] = test_X['Embarked'].map(embarked)
In [207]:
train_X['Fare']
titanic_total_df['Fare'].describe()
Out[207]:
count    1309.000000
mean       33.280204
std        51.741831
min         0.000000
25%         7.895800
50%        14.454200
75%        31.275000
max       512.329200
Name: Fare, dtype: float64

~실수

뒤늦게 Fare도 계급구간을 정하여 범주형 데이터로 전환하고 encoder를 해야만 모델링이 가능한 것을 깨달았다.

Fare의 존재를 까먹고 있었다.

Fare는 완벽하게 상대위치의 척도인 사분위수를 이용하여 계급구간을 지었다.

이번에는 pd.cut을 사용하지 않고, 인덱싱을 통하여 계급구간을 짓는 것과 encoder를 동시에 진행하였다.

(판다스 cut함수를 사용하면 다시 한번 딕셔너리로 encoder를 해야했기 때문에) .

In [208]:
train_X.loc[train_X['Fare']<7.8958,'Fare'] = 1
train_X.loc[(train_X['Fare']>=7.8958) & (train_X['Fare']<14.4542),'Fare'] = 2
train_X.loc[(train_X['Fare']>=14.4542) & (train_X['Fare']<31.2750),'Fare'] = 3
train_X.loc[(train_X['Fare']>31.2750),'Fare'] = 4
In [209]:
test_X.loc[test_X['Fare']<7.8958,'Fare'] = 1
test_X.loc[(test_X['Fare']>=7.8958) & (test_X['Fare']<14.4542),'Fare'] = 2
test_X.loc[(test_X['Fare']>=14.4542) & (test_X['Fare']<31.2750),'Fare'] = 3
test_X.loc[(test_X['Fare']>31.2750),'Fare'] = 4
In [210]:
test_X['Fare'].value_counts()
Out[210]:
4.0    108
2.0    105
1.0    103
3.0    102
Name: Fare, dtype: int64
In [211]:
train_X['Fare'].value_counts()
Out[211]:
2.000     255
3.000     229
4.000     215
1.000     185
31.275      7
Name: Fare, dtype: int64
In [212]:
train_X.info()
test_X.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 8 columns):
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   Survived    891 non-null    int64  
 1   Pclass      891 non-null    int64  
 2   Sex         891 non-null    int64  
 3   Age_bin     891 non-null    int64  
 4   Family_Num  891 non-null    int64  
 5   Fare        891 non-null    float64
 6   Embarked    891 non-null    int64  
 7   Title       891 non-null    int64  
dtypes: float64(1), int64(7)
memory usage: 55.8 KB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 7 columns):
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   Pclass      418 non-null    int64  
 1   Sex         418 non-null    int64  
 2   Age_bin     418 non-null    int64  
 3   Family_Num  418 non-null    int64  
 4   Fare        418 non-null    float64
 5   Embarked    418 non-null    int64  
 6   Title       418 non-null    int64  
dtypes: float64(1), int64(6)
memory usage: 23.0 KB
In [213]:
#이로써 모델링 할 준비가 완벽하게 준비되었다.

train_X
Out[213]:
Survived Pclass Sex Age_bin Family_Num Fare Embarked Title
0 0 3 1 1 1 1.0 1 1
1 1 1 2 2 1 4.0 2 3
2 1 3 2 1 0 2.0 1 2
3 1 1 2 2 1 4.0 1 3
4 0 3 1 2 0 2.0 1 1
... ... ... ... ... ... ... ... ...
886 0 2 1 1 0 2.0 1 5
887 1 1 2 1 0 3.0 1 2
888 0 3 2 1 3 3.0 1 2
889 1 1 1 1 0 3.0 2 1
890 0 3 1 1 0 1.0 3 1

891 rows × 8 columns

In [214]:
test_X
Out[214]:
Pclass Sex Age_bin Family_Num Fare Embarked Title
0 3 1 2 0 1.0 3 1
1 3 2 2 1 1.0 1 3
2 2 1 4 0 2.0 3 1
3 3 1 1 0 2.0 1 1
4 3 2 1 2 2.0 1 3
... ... ... ... ... ... ... ...
413 3 1 2 0 2.0 1 1
414 1 2 2 0 4.0 2 5
415 3 1 2 0 1.0 1 1
416 3 1 2 0 2.0 1 1
417 3 1 3 2 3.0 2 4

418 rows × 7 columns

4)모델링

타이타닉 생존자 예측이 분석의 목적이기 때문에 이게 맞는 데이터 마이닝 기법을 선택해야만 한다.

먼저 종속변수가 범주형 데이터 Survived(0,1) 이기 때문에 회귀 분석은 로지스틱 회귀분석이 적합하다고 생각했다.

그리고 생존과 죽음의 분류를 해야하기 때문에 예측으로도 많이 쓰이지만 분류로도 많이 쓰이는 의사결정나무가 적합하다고 생각했다.

따라서 사이킷런의 로지스틱 회귀분석 모델과 의사결정 나무를 활용하려고 한다.

Logistic linear model

먼저 학습을 시키기 위해서 train_X 데이터를 두 개의 변수에 나누어서 저장하려고 했다. (X_train, Y_train)

Y_train에는 우리의 타겟변수인 Survived의 정보만을 담았고 X_train은 그 이외의 데이터를 담았다.

1) 학습

In [217]:
import pandas as pd
from sklearn.linear_model import LogisticRegression
In [218]:
Y_train = train_X['Survived']
X_train = train_X.drop(['Survived'],axis = 1)
In [219]:
#fit 함수로 모델을 학습시켰다. (X,Y)

logistic_model = LogisticRegression()
logistic_model.fit(X_train, Y_train)
Out[219]:
LogisticRegression()

2) 예측 및 평가

Y_pred 변수에 test데이터를 집어넣어서 예측한 결과를 저장했다.

리스트로 배열로 반환이 되었는데 개수를 세어보니 418개 모두 예측됐음을 확인할 수 있다.

In [85]:
Y_pred = logistic_model.predict(test_X)
In [221]:
print(len(Y_pred))
Y_pred
418
Out[221]:
array([0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0,
       1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 1, 1,
       1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0,
       0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1,
       1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
       0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,
       1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
       0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0,
       1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
       0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
       0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0,
       0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
       0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0,
       1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0,
       0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0,
       1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1,
       0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0],
      dtype=int64)

평가의 어려웠던 점

score를 통해서 정확도를 확인했다.

그런데 모델을 다른 방식으로 평가할 방법은 없나 고민했다.

R스튜디오 같은 경우에 각 변수의 중요도를 확인하고 P-값과 유의수준을 비교하는 콘솔 값이 있었던 것 같은데

파이썬은 잘 알지 못해 찾지 못했다. 다음 데이터 분석 모델링 평가 때는 더 많은 평가기법을 써봐야겠다.

In [223]:
logistic_model.score(X_train,Y_train)
Out[223]:
0.7934904601571269
In [227]:
#로지스틱 회귀분석 모델의 종속변수와의 상관관계

print(logistic_model.coef_)
[[-0.93742235  2.19730642 -0.07826753 -0.15311013 -0.05366003  0.24364828
   0.50359921]]

친구의 생존률, 나의 생존률을 구해보았다.

친구가 사는지 죽는지를 예측하고

생존확률과 사망확률을 계산하였다.

모델의 predict() 함수로는 0,1을 추측했고

proba 함수로는 확률을 계산했다.

상범씨의 생존 예측과 생존 확률이다.

In [224]:
sangbeom = np.array([[3,1,2,1,3,1,1]])
In [225]:
a = logistic_model.predict_proba(sangbeom)
b = logistic_model.predict(sangbeom)
orSurvive = ""
probability = ""


if b[0] == 0 :
    orSurvive = "might be dead"
    probability = a[0,0] * 100
else:
    orSurvive = "might be survived"
    probability = a[0,1] * 100
    
print('Sangbeom {} at {:.0f} %'.format(orSurvive,probability))
Sangbeom might be dead at 93 %
In [228]:
Me = np.array([[1,1,2,0,3,1,1]])
a = logistic_model.predict_proba(Me)
b = logistic_model.predict(Me)
orSurvive = ""
probability = ""

if b[0] == 0 :
    orSurvive = "might be dead"
    probability = a[0,0] * 100
else:
    orSurvive = "might be survived"
    probability = a[0,1] * 100
    
print('Eunjun {} at {:.0f} %'.format(orSurvive,probability))

#잘 가라 정상범ㅎ
Eunjun might be dead at 63 %

Decision Tree model

overfitting 을 방지하기 위해서 최대 깊이를 3으로 설정했다.

random_state는 13으로 설정했다.

In [231]:
from sklearn.tree import DecisionTreeClassifier
tree_model = DecisionTreeClassifier(max_depth = 3, random_state = 13)
tree_model.fit(X_train,Y_train)
'예측 정확도는 {} 입니다'.format(tree_model.score(X_train,Y_train))
Out[231]:
'예측 정확도는 0.8237934904601572 입니다'
In [233]:
a = tree_model.predict(Me)
b = tree_model.predict_proba(Me)
a
Out[233]:
array([0], dtype=int64)
In [234]:
b
Out[234]:
array([[0.59259259, 0.40740741]])
보완점

첫 번째로, 가장 크게 느낀 부분은 파이썬에서 각 모델을 평가하는 방법을 알아야 겠다는 점이다.

"회귀 분석, 분류 모델, 군집 분석" 등등의 데이터 마이닝 모델링 기법들을 "만드는 법과 평과 법" 을 따로 공부를 해야겠다.

두 번째로, encoder 방식의 문제다. 찾아본 바로는 라이브러리를 활용하여 작업을 진행하는 방법이 있다고 한다. 또한 사이킷런에 test 데이터와 train 데이터를 분리하게 해주는 모듈이 있다고 하는데 이 또한 보완해야 할 것 같다.

세 번째로는, 다양한 시각화. 데이터 정보를 한눈에 들어오게 하는 시각화 기법들을 여러 개 추가로 더 알아둬야 될 것 같다.

캐글 분석을 처음 해보게 되었는데, 이번 기회가 데이터 분석의 좋은 밑거름이 된 것 같다.

'머신러닝' 카테고리의 다른 글

피마 인디언 당뇨병 예측(모델 평가)  (1) 2021.03.31
Comments