이전 포스트

 

 

 

 

2023.01.12 - [분류 전체보기] - 타이타닉 생존자 예측 분석 (3-2) 데이터 정리 - v.1.0.1

 

타이타닉 생존자 예측 분석 (3-2) 데이터 정리 - v.1.0.1

이전 포스트 2023.01.09 - [Kaggle/타이타닉] - 타이타닉 생존자 예측 분석 (3-1) 전처리 (나이예측) - v.1.0.1 타이타닉 생존자 예측 분석 (3-1) 전처리 (나이예측) - v.1.0.1 이전 포스트 2023.01.09 - [분류 전체

drewvvv.tistory.com


 

 

1. 데이터 인코딩

  • 모델에 학습을 하기 위해선 사실 몇가지 준비가 필요하다.
  • 모델에 학습시킬 데이터에 대한 내용인데, 
    • 1. 데이터프레임 안의 데이터는 숫자여야 하며(could not convert string to float)
      • 이때 범주형 변수를 숫자로 나타내게될 때, 사실 서로 아무 의미가 없지만 모델이 의미를 갖는 경우가 생기는 것에 주의해야 한다.  (참고링크)
      • 위의 차이를 확인해보고자, 레이블 인코딩과 원핫 인코딩으로 비교 제출해 볼 것이다.
    • 2. 원핫인코딩의 경우 값을 기준으로 분류하기 때문에, 값을 컬럼명으로 사용하는 경우가 발생한다. 
      • 이때 원핫인코딩은 컬럼명을 도출하지만, 각기 다른 컬럼에 값이 같은 경우 문제가 될 수 있으며,
      • 가장 중요한 모델의 학습의 경우 컬럼명이 숫자인 경우 워링이 발생한다. (FutureWarning: Feature names only support names that are all strings. Got feature names with dtypes: ['int64'])
        • 경우의 따라 숫자 컬럼명을 문자로 변경해주면 되긴하지만, 그것은 모델의 학습에 대해 의도한 바가 아니다. 
    • 3. scikit-learn의 onehot-encoding과 pandas의 get_dummies()
      • 겉으로 보기엔 둘은 똑같은 역할을 하는 것처럼 보이지만 둘은 공통점과 명확한 차이가 존재한다.
      • 우선 공통점을 보자면 두 메서드는 각 값에 해당하는 컬럼을 새로 생성한 후 컬럼에 해당하는 값일 경우 1 아닐 경우 0으로 둔다. (참고링크)
      •  
      • 히지만 명확한 차이는 모델 학습에서 들어난다. 
        • 판다스의 get_dummies()는 그저 위의 공통사항을 분류만 할 뿐이지만,
        • 사이킷 런의 원핫인코딩은 train 데이터 셋에 맞게 test 셋을 맞추는 것이다. 
        • 이 차이를 다시 풀자면, train 셋에는 해당하는 컬럼이 없지만 test 셋에는 컬럼이 있을 경우 모델은 학습을 할 수 없어 에러가 나게된다. 
        • 하지만 원핫인코딩의 경우 데이터 뿐만이 아니라, 컬럼명까지 참조하기 때문에 get_dummies()와 같은 문제가 발생하지 않는다. (단 encoding시 handle_unknown='ignore' 파라미터 추가 필요)
        • 하지만 get_dummies()도 위의 문제를 해결하고 사용할 수 있는 방법이 있는데, 파이프라인을 구축하여 encoding시 원핫인코딩의 handle_unknown 기능을 간접적으로 구현하는 방법이 있다. (참고링크)
        • (이외의 다른 차이도 존재하지만 생략)
  • 따라 본 v1.0.1 버전에선 다음과 같은 계획을 갖고 비교해가며 모델 학습을 진행한다. 
    • 인코딩 방법간의 차이를 살펴보기 위해 모델은 RandomForest로 고정한다.
    • 1. 앞의 (3-2) 데이터 정리 포스트에서 변수를 숫자 범주형(레이블 인코더)으로 변환한 데이터를 그대로 학습 후 제출 
    • 2. 숫자 범주형을 사용하지 않고 범주형 데이터를 원핫인코딩 후 제출 

 

위의 1번의 경우 데이터 셋을 불러온다. 

1
2
3
4
5
6
import pandas as pd
import numpy as np
 
noNaTrain = pd.read_csv("../input/noNa_train - v1.0.1.csv")
noNaTest = pd.read_csv("../input/noNa_test - v1.0.1.csv")
gender = pd.read_csv("../input/gender_submission.csv")
cs

 

1
2
3
4
5
feature_1 = ['Survived','Pclass','ca_Sex','ca_Age''SibSp''Parch','ca_designation']
 
train_data = noNaTrain[feature_1[1:]].copy()
target = noNaTrain['Survived'].copy().values
test_data = noNaTest[feature_1[1:]].copy()
cs
  • Fare 컬럼을 사용하지 않고 위와 같이 featrue_1을 구성한다.
  • 모델에 학습시킬 train_data는 Survived를 제외, target은 Survived를 사용

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
 
X_train, X_test, y_train, y_test = train_test_split(train_data, target, test_size = 0.2, random_state=42)
 
model = RandomForestClassifier()
model.fit(X_train, y_train)
accuracy = round(model.score(X_test, y_test) * 1002)
print(f"accuracy of {type(model).__name__}: {str(accuracy)}%")
 
predict = model.predict(test_data)
submission_df = pd.DataFrame({'PassengerId':noNaTest['PassengerId'],
                            'Survived':predict})
# submission_df.head()
submission_df.to_csv(f'sbn_{type(model).__name__ }_labeling.csv', index=False)
print(f"name of export submission : sbn_{type(model).__name__}_labeling.csv")
cs

 

  • 분류모델에 별다른 하이퍼 파라미터 수정하지 않고,
  • 위의 train_data는 변수들을 라벨링한 후의 결과 값으로 80.45%의 정확도를 보였다. 

  • 하지만 실제 스코어는 0.5933으로 매우 낮았다. 

 

이제 2번의 계획인 onehot encoding을 사용해 본다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
# test for one hot encoding
feature_2 = ['Survived''Pclass''Sex''ca_Age''SibSp''Parch''designation']
 
train_data2 = noNaTrain[feature_2[1:]].copy()
target = noNaTrain['Survived'].copy().values
test_data2 = noNaTest[feature_2[1:]].copy()
 
 
for df in [train_data2, test_data2]: 
    for col in ['Pclass', 'ca_Age', 'SibSp''Parch']:
        for index, item in enumerate(df[col]):
            df.loc[index, col] = col+ '_' + str(item)
 
cs
  • 우선 feature의 경우 기존 feature_1 = ['Survived','Pclass','ca_Sex','ca_Age''SibSp''Parch','ca_designation'] 에서
  • ca_Sex를 Sex로, ca_designation은 designation으로 사용하고 나머지 컬럼은 앞의 prefix를 붙혀 데이터를 변환한다. 
  • 데이터 변환 결과는 아래와 같다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# https://towardsdatascience.com/stop-using-pandas-get-dummies-for-feature-encoding-5d2cd07cb4fc 
# https://pythonsimplified.com/difference-between-onehotencoder-and-get_dummies/
 
from sklearn.preprocessing import OneHotEncoder
# one hot encoding
enc = OneHotEncoder(sparse=False, handle_unknown='ignore')
onehot = enc.fit_transform(train_data2[feature_2[1:]])
columns = list([j for i in enc.categories_ for j in i])
#to print the encoded features for train data
trainOneHotDf = pd.DataFrame(onehot, columns=columns)
train_data2 = trainOneHotDf.copy()
 
# tranform encoding for test data
test_onehot = enc.transform(test_data2[feature_2[1:]])
#to print the encoded features for train data
testOneHotDf = pd.DataFrame(test_onehot, columns=columns)
test_data2 = testOneHotDf.copy()
cs
  • 위 코드를 살펴보면 enc를 통해 train_data가 fit_transform되었고 , onehot array를 실제 columns와 매핑하여 trainOneHotDf를 생성하였다.
  • testOneHotDf의 경우는 train_data2의 컬럼을 기준으로 학습된 enc를 적용하여 transform해서 test셋에는 있지만 train 셋에는 없어서 학습할 수 없는 경우가 발생하지 않도록 unknown 컬럼은 ignore 시키도록 파라미터를 추가하였다.

  • 위를 바탕으로 feature들이 onehot-encoding된 것을 확인할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
X_train, X_test, y_train, y_test = train_test_split(train_data2, target, test_size = 0.2, random_state=42)
 
model = RandomForestClassifier()
model.fit(X_train, y_train)
accuracy = round(model.score(X_test, y_test) * 1002)
print(f"accuracy of {type(model).__name__}: {str(accuracy)}%")
 
predict = model.predict(test_data2)
submission_df = pd.DataFrame({'PassengerId':noNaTest['PassengerId'],
                            'Survived':predict})
# submission_df.head()
submission_df.to_csv(f'sbn_{type(model).__name__ }_onehot.csv', index=False)
print(f"name of export submission : sbn_{type(model).__name__}_onehot.csv")
cs

 

  • 분류모델에 별다른 하이퍼 파라미터 수정하지 않고,
  • 위의 train_data는 변수들을 onehot-encoding한 후의 결과 값으로 82.68%의 정확도를 보였다. 

  • 단지 onehot-encoding만 실시하였지만 score가 확연히 차이나는 것을 확인할 수 있다.
  • 이는 모델이 학습시에 데이터를 올바르게 읽지 못하고 연속된 값이 연관이 있다고 판단하여 생기는 문제이다.
  • 따라 데이터 전처리의 경우 각 데이터의 특성에 따른 적절한 전처리를 할 필요가 있다.

 

 

 

 

 

다음 포스트

 

 

TBW

 
 
 
 
 
 
 
 
 

 

 

이전 포스트

 

 


 

 

1. 데이터 정리

주어진 데이터 셋을 모델링하기 이전에 테스트를 진행할 test셋 또한 데이터 정제가 필요하다.

따라 이전 포스트들에서 했던 내용들 중 코드만 모아서 train과 test 각 데이터셋을 처음부터 다시 정리하도록 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
train = pd.read_csv("../input/train.csv")
test = pd.read_csv("../input/test.csv")
gender = pd.read_csv("../input/gender_submission.csv")
 
dfs = { 
       'train': {
          'data' : train
        }, 
        'test': {
         'data' : test
        } 
      }
cs
  • 우선 데이터 셋을 새로 다시 불러온다. 
  • 이후 dict를 생성하여 object로 관리한다. 

 

1. 나이 범주화

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
for dfName in dfs.keys():
    for index, item in enumerate(dfs[dfName]['data']['Age']):
        if item < 10:
            result = 0
        elif item >= 10 and item < 20:
            result = 1
        elif item >= 20 and item < 30:
            result = 2
        elif item >= 30 and item < 40:
            result = 3
        elif item >= 40 and item < 50:
            result = 4
        elif item >= 50 and item < 60:
            result = 5
        elif item >= 60 and item < 70:
            result = 6
        elif item >= 70 and item <= 80:
            result = 7
        elif item > 80 and item < 90:
            result = 8
        else:
            result = 99
        
        dfs[dfName]['data'].loc[index, 'ca_Age'= result
 
cs

 

2. 성별,탑승지,존칭,성 범주화

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
for dfName in dfs.keys():
    df = dfs[dfName]['data']
    
    # 성별 범주화
    df.loc[ df['Sex'== 'male''ca_Sex'= 0
    df.loc[ df['Sex'== 'female''ca_Sex'= 1
    
    # 탑승지 범주화
    df.loc[ df['Embarked'== 'C''ca_Embarked'= 0
    df.loc[ df['Embarked'== 'Q''ca_Embarked'= 1
    df.loc[ df['Embarked'== 'S''ca_Embarked'= 2
    
    # 존칭, 성 출력
    df['designation'= df['Name'].str.extract(' (\w*)\.')
    df['lastName'= df['Name'].str.extract('\.+ \(?([\w]*)')
    
    encoder = LabelEncoder()
    encoder.fit(df['designation'])
    labels = encoder.transform(df['designation'])
 
    df['ca_designation'= labels
cs
  • 보기 쉽게 하기 위해 for문을 나눠서 사용했다.

3. 나이 대체

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
for dfName in dfs.keys():
    gTrain = dfs[dfName]['data'].groupby(['designation''Sex''Pclass''SibSp''Parch'])
    # gTrain.describe()[['Age','Survived']]
    dfs[dfName]['gAge'= gTrain.describe()['Age']
    gAge = dfs[dfName]['gAge']
    
    for item in dfs[dfName]['data'].iterrows():
        temp = item[1]
        guessAge = gAge.loc[temp['designation'],
                            temp['Sex'],
                            temp['Pclass'],
                            temp['SibSp'],
                            temp['Parch']]
 
        if (math.isnan(guessAge['mean'])):
            guessAge = gAge.loc[temp['designation'],
                                temp['Sex']].mean()
# 테스트셋에 예측한 카테고리별 평균이 없는 경우 트레인셋에서 카테고리의 평균을 사용
            if (math.isnan(guessAge['mean'])):
                guessAge = dfs['train']['gAge'].loc[temp['designation'], 
                                                    temp['Sex']].mean()
                
        
        dfs[dfName]['data'].loc[item[0], 'guessAgeMean'= round(guessAge['mean'], 2)
        dfs[dfName]['data'].loc[item[0], 'guessAgeMedian'= round(guessAge['50%'], 2)
cs
  • 평균으로 대체하려했지만 테스트셋에 예측한 카테고리별 평균이 없는 경우 트레인셋에서의 카테고리로 평균을 대체 사용하였다.

  • 파생변수의 결측치가 없음을 확인하고
  • 기존 나이와 요금을 결측치 처리할 예정이다.

 

1
2
3
4
5
6
for dfName in dfs.keys():
    df = dfs[dfName]['data']
    for index, item in enumerate(df['Age']):
        if(math.isnan(item)):
            df.loc[index, 'Age'= df.loc[index, 'guessAgeMean']
    
cs
  • 나이 중 결측치 대체

 

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
for dfName in dfs.keys():
    df = dfs[dfName]['data']
    for index, item in enumerate(df['ca_Age']):
        if(item == 99):
            age = df.loc[index, 'Age']
            if age < 10:
                result = 0
            elif age >= 10 and age < 20:
                result = 1
            elif age >= 20 and age < 30:
                result = 2
            elif age >= 30 and age < 40:
                result = 3
            elif age >= 40 and age < 50:
                result = 4
            elif age >= 50 and age < 60:
                result = 5
            elif age >= 60 and age < 70:
                result = 6
            elif age >= 70 and age <= 80:
                result = 7
            elif age > 80 and age < 90:
                result = 8
                
            df.loc[index, 'ca_Age'= result
        
cs
  • 범주화한 나이에 결측치를 99로 처리했던 값들을 다시 새로 범주화한다.

4. 다른 컬럼의 결측치 처리

1
2
3
4
dfs['train']['data']['Embarked'].fillna("S", inplace=True)
dfs['train']['data']['ca_Embarked'].fillna("2", inplace=True)
 
test.loc[test['Fare'].isna(), 'Fare'= test['Fare'].median()
cs
  • train 데이터셋과 test 데이터셋에 있는 각각의 결측치를 최빈값과 중앙값으로 대체한다.

 

  • 결측치를 다시 한번 확인한 후 

5. 내보내기

  • Cabin 컬럼을 drop하고 noNa_train.csv, noNa_test.csv로 저장한다.

 

 

 

 

다음 포스트

 

 

2023.01.18 - [Kaggle/타이타닉] - 타이타닉 생존자 예측 분석 (3-3) 데이터 인코딩 - v.1.0.1

 

타이타닉 생존자 예측 분석 (3-3) 데이터 인코딩 - v.1.0.1

이전 포스트 2023.01.12 - [분류 전체보기] - 타이타닉 생존자 예측 분석 (3-2) 데이터 정리 - v.1.0.1 타이타닉 생존자 예측 분석 (3-2) 데이터 정리 - v.1.0.1 이전 포스트 2023.01.09 - [Kaggle/타이타닉] - 타이

drewvvv.tistory.com

 
 
 
 
 
 
 
 
 
 
 

 

이전 포스트

 

 

 

2023.01.09 - [분류 전체보기] - 타이타닉 생존자 예측 분석 (2-3) EDA(상관관계 및 기타) - v.1.0.1

 

타이타닉 생존자 예측 분석 (2-3) EDA(상관관계 및 기타) - v.1.0.1

이전 포스트 타이타닉 생존자 예측 분석 (2-2) EDA(연령대별) - v.1.0.1 이전 포스트 타이타닉 생존자 예측 분석 (2-1) EDA(성별) - v.1.0.1 - 개요 우선 데이터 사이언티스트 (이하 DS)의 꿈을 가장 크게 꾸

drewvvv.tistory.com

 

 

 

 

 

 
 

1. 결측치 처리

 

우선 타이타닉 데이터셋에서 결측치를 확인해보았다.

  • 나이의 경우 전체 891개 (트레인 데이터셋) 에서 177개의 결측치를 보였다.
  • 따라 다양한 결측치 처리 법 중 대체를 통해 나이의 결측치를  채우도록 하겠다.
  • 우선 정렬을 해가며 데이터를 확인해보았을 때,

 

  • 나이 순으로 정렬했을 때 이름에 Master, Miss가 들어가는 경우 16세 이하임을 확인할 수 있다.
  • 따라 이름에 Master나 Miss가 들어가는 경우 ca_Age를 0또는 1로 대부분 대체할 생각이다. (Miss가 들어가는데 나이가 많은 경우도 있기 때문)
1
2
3
train['designation'= train['Name'].str.extract(' (\w*)\.')
train['lastName'= train['Name'].str.extract('\.+ \(?([\w]*)')
train[['Name''designation''lastName']]
cs

 

 

  • 정규식을 활용해서 존칭과 성을 분류해보았다. 

  • 존칭에서 위의 패턴을 사용하지 않는 사람들은 전체 중 24명이었다.
  • 대부분의 경우 위의 경우를 other로 하나로 취급해서 처리했지만, 우선 존칭을 그대로 활용해볼 생각이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
orders = train['designation'].value_counts().index.tolist()
 
fig = plt.figure(figsize=(16,7))
 
area01 = fig.add_subplot(1,2,1)
area02 = fig.add_subplot(1,2,2)
 
ax1 = sns.countplot(data=train, x = "designation", hue="Survived", order=orders, ax = area01)
ax1.set_title('존칭별 인원(명)')
ax2 = sns.barplot(data = train, x = "designation", y = "Survived", hue="Sex", order=orders, errwidth = 0, ax = area02)
ax2.set_title('존칭별/성별 생존률')
 
plt.show()
cs
 
 
  • 존칭별 인원 카운트에선 큰 범주 4개를 제외하곤 갯수가 많이 없음을 확인하였고,
  • 존칭별/성별 생존율을 살펴보면 Dr를 제외하곤 각 막대가 없는 것을 볼 수 있는데, 당연히 성별에 따라 존칭을 사용하기 때문이다. 또한, 여성용 존칭과 남성용 존칭에 따라 생존을 볼 수 있다. 

 

존칭에 따른 성별표

  • 존칭과 성별로 groupby를하고 나이와 생존에 대해 describe()를 조회하여 존칭별/성별 나이의 기술통계값을 확인할 수 있었다. 
  • 위의 size()함수와는 다르게 Age에서는 count가 줄어든 것을 볼 수 있는데 이는 describe() 함수가 결측치를 제거하고 통계를 내기 때문이다. 

 

1
2
3
4
5
6
7
8
from sklearn.preprocessing import LabelEncoder
 
encoder = LabelEncoder()
encoder.fit(train['designation'])
labels = encoder.transform(train['designation'])
 
train['ca_designation'= labels
 
cs
  • LabelEncoder로 존칭을 숫자로 변환하였다. 
  • 이후 corr()함수를 사용하여 상관관계를 확인했을 때 

  • 특정 조건에서 나이와 존칭의 상관계수가 0.75까지 올라가는 경우가 존재한다.

 

 

2. 나이 예측

 

1
2
3
4
5
gTrain = train.groupby(['designation''Sex''Pclass''SibSp''Parch'])
gAge = gTrain.describe()['Age']
gAge['mDiff'= gAge['mean'- gAge['50%']
print(f"max value : \n{gAge[['mean','50%', 'mDiff']].max()}")
gAge[['mean','50%''mDiff']]
cs

 

  • 존칭, 성별, Pclass, SibSp, ParCh에 따라 분류한 후, 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
for item in train.iterrows():
    temp = item[1]
    guessAge = gAge.loc[temp['designation'],
                        temp['Sex'],
                        temp['Pclass'],
                        temp['SibSp'],
                        temp['Parch']]
 
    if (math.isnan(guessAge['mean'])):
        guessAge = gAge.loc[temp['designation'],
                            temp['Sex']].mean()
        
    train.loc[item[0], 'guessAgeMean'= round(guessAge['mean'], 2)
    train.loc[item[0], 'guessAgeMedian'= round(guessAge['50%'], 2)
cs

 

  • 각 row 데이터에 따른 평균 나이와 중앙값을 구하였다. 
  • 결측치가 없음을 확인하고

  • 실제 나이와 예측한 나이가 편차가 크지 않은 것으로 보인다.

 

  • 기술통계에서도 실제 나이와 추론한 나이가 비슷하고 오히려 편차는 낮게 나타났다. 

 

1
2
3
4
for index, item in enumerate(train['Age']):
    if(math.isnan(item)):
        train.loc[index, 'Age'= train.loc[index, 'guessAgeMean']
    
cs
  • 따라 추론한 평균값으로 171개의 NaN값을 대체한다. 
  • (현재는 Pclass까지 포함했지만 제외할 예정)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
for index, item in enumerate(train['ca_Age']):
    if(item == 99):
        age = train.loc[index, 'Age']
        if age < 10:
            result = 0
        elif age >= 10 and age < 20:
            result = 1
        elif age >= 20 and age < 30:
            result = 2
        elif age >= 30 and age < 40:
            result = 3
        elif age >= 40 and age < 50:
            result = 4
        elif age >= 50 and age < 60:
            result = 5
        elif age >= 60 and age < 70:
            result = 6
        elif age >= 70 and age <= 80:
            result = 7
        elif age > 80 and age < 90:
            result = 8
            
        train.loc[index, 'ca_Age'= result
    
cs
  • 이후 ca_Age 컬럼에서 99로 분류하였던 NaN값을 각 평균값으로 다시 대체한다.
1
2
3
train['Embarked'].fillna("S", inplace=True)
train['ca_Embarked'].fillna("2", inplace=True)
train = train.drop('Cabin', axis=1)
cs
  • Embarked의 경우 대부분이 S이므로 S로 대체하고 ca_Embared 값은 S의 숫자값인 2로 대체한다.
  • Cabin 컬럼은 결측치가 대부분이어서 이번 전처리에선 제거 처리하였다.

  • 결측치가 제거됐음을 확인하고 

 

1
train.to_csv('../input/noNaTrain.csv', index=False)
cs
  • noNa 데이터로 내보낸다. 

 

 

 

 

 

 

다음 포스트

 

2023.01.12 - [분류 전체보기] - 타이타닉 생존자 예측 분석 (3-2) 데이터 정리 - v.1.0.1

 

타이타닉 생존자 예측 분석 (3-2) 데이터 정리 - v.1.0.1

이전 포스트 2023.01.09 - [Kaggle/타이타닉] - 타이타닉 생존자 예측 분석 (3-1) 전처리 (나이예측) - v.1.0.1 타이타닉 생존자 예측 분석 (3-1) 전처리 (나이예측) - v.1.0.1 이전 포스트 2023.01.09 - [분류 전체

drewvvv.tistory.com

 
 
 
 

 

이전 포스트

 

 

2023.01.05 - [Kaggle/타이타닉] - 타이타닉 생존자 예측 분석 (2-2) EDA(연령대별) - v.1.0.1

 

타이타닉 생존자 예측 분석 (2-2) EDA(연령대별) - v.1.0.1

이전 포스트 타이타닉 생존자 예측 분석 (2-1) EDA(성별) - v.1.0.1 - 개요 우선 데이터 사이언티스트 (이하 DS)의 꿈을 가장 크게 꾸게 해준 타이타닉은 매우 흥미로웠다. 타이타닉에 탑승했던 사람들

drewvvv.tistory.com


 

1. 상관관계 분석 

 

파이썬 데이터테이블에서 상관관계를 분석하는 함수로 pandas의 corr()이 있다. 

df.corr()을 하면 상관관계를 분석해주는데 타이타닉 데이터를 토대로 상관관계를 확인해보면 아래와 같다.

  • 대각선을 기준으로 대칭이어서 한쪽만 살펴보면된다. 
  • pandas의 corr 함수에는 methods라는 파라미터를 입력해서 
    • 연속형 - 연속형 (모수적) 자료간의 선형관계를 나타내는 피어슨 상관계수 (methods="pearson")
    • 연속형 - 연속형 (비모수적) 자료간의 비선형관계를 나타내는 스피어만 순위 상관계수 (methods="spearman")
      • 순위를 사용하기 때문에 연속형 변수가 아닌 순서형 변수에서도 사용 가능(*)
    • 연속형 - 연속형 (비모수적) 자료간의 켄달의 타우 순위 상관계수 (method="kendall") 이 존재한다. 
  • 하지만 사용시 주의할 점이 있는데, corr 함수의 메서드들은 연속형 자료에 대해서 상관관계를 분석한다. 
  • 따라, 범주형 변수의 상관관계를 분석할 경우
    • 명목형(이분형) & 연속형 - Point biserial correlation coefficient
    • 명목형(이분형) & 명목형(이분형) - Phi coefficient 를 사용해야 한다. [참고링크]
    • 또한 상관관계가 인과관계를 설명하지 않는 것의 유의한다. [참링크]

단, scipy의 Point biserial correlation을 활용하여 명목형과 연속형에 대한 상관분석을 하였을 때, 

  • df의 corr에서 해당 컬럼의 값과 동일하게 나오는 것을 확인할 수 있는데, 이는 corr함수가 같이 분석해주는 것으로 보인다. 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
df = train.corr(numeric_only=True)
# 그림 사이즈 지정
fig, ax = plt.subplots( figsize=(7,7) )
 
# 삼각형 마스크를 만든다(위 쪽 삼각형에 True, 아래 삼각형에 False)
mask = np.zeros_like(df, dtype=bool)
mask[np.triu_indices_from(mask)] = True
 
# 히트맵을 그린다
sns.heatmap(df, 
            cmap = 'RdYlBu_r'
            annot = True,   # 실제 값을 표시한다
            mask=mask,      # 표시하지 않을 마스크 부분을 지정한다
            linewidths=.5,  # 경계면 실선으로 구분하기
            cbar_kws={"shrink": .5},# 컬러바 크기 절반으로 줄이기
            vmin = -1,vmax = 1   # 컬러바 범위 -1 ~ 1
           )  
plt.show()
cs

 

  • sns heatmap 함수를 이용하여 corr을 시각화한다.  
1
2
train.loc[ train['Sex'== 'male''ca_Sex'= 0
train.loc[ train['Sex'== 'female''ca_Sex'= 1
cs
  • 데이터 분석 모형은 숫자만 입력으로 받을 수 있기 때문에 성별을 숫자로 변환한다. 

 

  • 이후 pandas의 crosstab을 활용하여 요인별로 교차분석을 하여, 행 및 열 요인 기준별로 빈도를 세어서 도수분포표, 교차표를 만들어 시각화할 수 있다. [참조링크]

 

 

2. 기타 EDA 분석  (선착장별, 객실등급별, 요금별)

 

- 선착장별 EDA

1
2
3
4
5
train.loc[ train['Embarked'== 'C''ca_Embarked'= 0
train.loc[ train['Embarked'== 'Q''ca_Embarked'= 1
train.loc[ train['Embarked'== 'S''ca_Embarked'= 2
 
pd.crosstab(train.ca_Embarked,train.Survived,margins=True).style.background_gradient(cmap='summer_r')
cs

 

  • 선착장을  숫자 변환하고 도수분포표를 확인해본 경우, 선착장별 생존자 차이는 뚜렷한 차이를 보긴 어렵다.

  • 선착장별 요금분포

 

- 객실등급별 EDA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fig = plt.figure(figsize=(10,10))
 
area01 = fig.add_subplot(2,2,1)
area02 = fig.add_subplot(2,2,2)
area03 = fig.add_subplot(2,2,3)
 
sns.countplot(data = train, x='Pclass', ax=area01)
area01.set_title('객실별 count')
sns.barplot(data = train, x="Pclass", y="Survived", errwidth=0, ax=area02)
area02.set_title('객실별 생존자 count')
sns.countplot(data = train, x='Pclass', hue='Survived', ax=area03)
area03.set_title('객실별 생존 여부 count')
 
plt.show()
cs

 

  • 객실등급이 높을 수록 생존율이 높음을 확인할 수 있다.

 

 

- 객실등급별, Pclass별 요금분포

  • 선착장 위치에 상관없이 1등 객실에 탑승한 고객은 비싼 요금을 지불했다.

 

 
 
 
 
 

 

 

다음 포스트

 

 

2023.01.09 - [분류 전체보기] - 타이타닉 생존자 예측 분석 (3-1) 전처리 (나이예측) - v.1.0.1

 

타이타닉 생존자 예측 분석 (3-1) 전처리 (나이예측) - v.1.0.1

이전 포스트 2023.01.09 - [분류 전체보기] - 타이타닉 생존자 예측 분석 (2-3) EDA(상관관계 및 기타) - v.1.0.1 타이타닉 생존자 예측 분석 (2-3) EDA(상관관계 및 기타) - v.1.0.1 이전 포스트 타이타닉 생존

drewvvv.tistory.com

 

 

 

 
 
 
 
 
 
 
 
 

이전 포스트

2023.01.04 - [Kaggle/타이타닉] - 타이타닉 생존자 예측 분석 (2-1) EDA(성별) v.1.0.1

 

타이타닉 생존자 예측 분석 (2-1) EDA(성별) - v.1.0.1

- 개요 우선 데이터 사이언티스트 (이하 DS)의 꿈을 가장 크게 꾸게 해준 타이타닉은 매우 흥미로웠다. 타이타닉에 탑승했던 사람들의 일련의 데이터들을 토대로 정보로 이끌어 내어 분석하여 그

drewvvv.tistory.com


 

 

3. 연령대별 - EDA 

age: Age is fractional if less than 1. If the age is estimated, is it in the form of xx.5
# 나이가 1 미만인 경우 1살 미만의 영유아의 개월 수 이다.

  • 연령대별 분포를 확인하기 이전에 위의 설명에서 연령이 1미만인 경우를 확인해본다.
  • 데이터는 몇개 없으나 영유아의 경우 Parch가 1이상인 것을 확인할 수 있다. 

 

  • 트레인 셋의 결측치를 확인해보니 Age에서 177개가 확인되었다. 
  • 결측치를 처리하는 방법은 대체/제외가 있고, 다른 버전의 EDA에서는 성능에 따른 다양한 결측치 처리 방법을 시도해 볼 것이다.

 

1
2
3
4
5
6
# rugplot
# rug는 rugplot이라고도 불리우며, 데이터 위치를 x축 위에 **작은 선분(rug)으로 나타내어
# 데이터들의 위치 및 분포**를 보여준다.
sns.set(rc = {'figure.figsize':(10,8)})
sns.distplot(train['Age'], rug=True, hist=True, kde=True# histplot 사용 권장 
plt.show()
cs
  • 우선 Age의 분포를 그래프로 확인해본다. 

  • 현재 distplot을 사용하면 histplot을 사용하라고 경고가 뜨지만 한번 distplot의 옵셥을 주어 표현해 보았다. 
  • 연령의 분포는 어느정도 정규분포를 그리고 있으며 20~30대 구간이 많은 것을 볼 수 있다.
  • 분포가 정규분포를 그리는 것으로 보아 Age 컬럼의 구간을 나눌 때 표준편차를 활용하는 것이 좋을 것이라 판단되나, 임의로 10의 크기로 구간을 분할해보도록 한다. 

 

 

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
# 연령대를 임의의 10의 크기로 분할 
for index, item in enumerate(train['Age']):
    if item < 10:
        result = 0
    elif item >= 10 and item < 20:
        result = 1
    elif item >= 20 and item < 30:
        result = 2
    elif item >= 30 and item < 40:
        result = 3
    elif item >= 40 and item < 50:
        result = 4
    elif item >= 50 and item < 60:
        result = 5
    elif item >= 60 and item < 70:
        result = 6
    elif item >= 70 and item <= 80:
        result = 7
    elif item > 80 and item < 90:
        result = 8
    else:
        result = 99
    
    train.loc[index, 'ca_Age'= result
train['ca_Age']
cs

 

  • 연령대 범주별 분포

 

 

 
  • 연령대 범주별 생존율
  • 연령대별/성별로 구분한 경우 모든 연령대에서 여성의 생존이 남성보다 높음을 확인할 수 있다.
 
 
1
2
3
4
5
6
7
8
9
10
fig = plt.figure(figsize=(10,5))
 
area01 = fig.add_subplot(1,2,1)
area02 = fig.add_subplot(1,2,2)
 
sns.countplot(data = train, x = 'ca_Age', hue = "Sex", ax = area01)
sns.barplot(data = train, x = "ca_Age", y = "Survived", hue = "Sex", errwidth = 0, ax = area02)
 
plt.show()
# 전체 각 연령대에서 남성이 여성보다 많지만 생존의 경우는 ca_Age 0을 제외하고 모두 여성이 높음
cs
  • 전체 각 연령대에서 남성이 여성보다 많지만 생존의 경우는 ca_Age == 0은 나름 비등하지만 나머지 모두에선 여성이 높음을 확인할 수 있다.

 

 

1
2
3
4
5
6
7
8
from statsmodels.graphics.mosaicplot import mosaic
 
 
mosaic(train.sort_values('Pclass'), ['Survived''ca_Age''Sex'], gap=0.002)
plt.title('Survivor of Titanic', fontsize=20)
 
plt.show()
# 대부분의 연령대별 사망자는 남자가 지배적
cs

  • 대부분의 연령대별 사망자는 남자가 지배적인 것을 확인할 수 있다.

 

 

 

 

 

다음 포스트

 

2023.01.09 - [분류 전체보기] - 타이타닉 생존자 예측 분석 (2-3) EDA(상관관계 및 기타) - v.1.0.1

 

타이타닉 생존자 예측 분석 (2-3) EDA(상관관계 및 기타) - v.1.0.1

이전 포스트 타이타닉 생존자 예측 분석 (2-2) EDA(연령대별) - v.1.0.1 이전 포스트 타이타닉 생존자 예측 분석 (2-1) EDA(성별) - v.1.0.1 - 개요 우선 데이터 사이언티스트 (이하 DS)의 꿈을 가장 크게 꾸

drewvvv.tistory.com

 

 
 
 

- 개요

 

우선 데이터 사이언티스트 (이하 DS)의 꿈을 가장 크게 꾸게 해준 타이타닉은 매우 흥미로웠다.

타이타닉에 탑승했던 사람들의 일련의 데이터들을 토대로 정보로 이끌어 내어 분석하여 그 사람이 살았을지 죽었을지를 예측할 수 있다는 것이 말이다. 


 

타이타닉은 생존 여부를 예측하는 이진 분류(binary claasification) 이다.

 

일례로 들면, 이는 고양이와 강아지 사진들 속에서 이 사진 속 동물이 고양이인지 강아지인지 맞추는 것과 같다.

단, 그 사진 속 특징 정보들이 각기 상이한 정보이고, 데이터로서 분산되어 있어서 그 분산된 데이터를 토대로 정보를 도출하여 사진 속 동물을 정확히 분류하는데 목표한다. 

 

따라 아래에서 타이타닉의 데이터들을 토대로 정보를 도출하고 생존자 분류를 시작한다.

 


 

 

 

 

- EDA (탐색적 분석)

1. 데이터 획득

  • 데이터는 아래 캐글 사이트 내의 타이타닉 컴피티션에서 획득할 수 있다.
https://www.kaggle.com/competitions/titanic/data

 

 

 

 

데이터를 다운받은 후

EDA에 기본적으로 필요한 라이브러리를 import하고 데이터를 불러온다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
 
# 한글 폰트 적용
plt.rc('font', family='Malgun Gothic'
# 캔버스 사이즈 적용
plt.rcParams["figure.figsize"= (129)
 
train = pd.read_csv("../input/train.csv")
test = pd.read_csv("../input/test.csv")
gender = pd.read_csv("../input/gender_submission.csv")
cs
 
 
 
변수 설명 : 캐글 내에 있는 데이터 설명
survival    Survival    0 = No, 1 = Yes # 생존 여부 - 우리의 target 데이터이다.
pclass  Ticket class    1 = 1st, 2 = 2nd, 3 = 3rd # 승객의 티켓 등급이다. (객실 등급)
sex Sex
Age Age in years    
sibsp   # of siblings / spouses aboard the Titanic # 같이 탑승한 형제자매, 배우자 수
parch   # of parents / children aboard the Titanic # 같이 탑승한 부모, 자식 수
ticket  Ticket number
fare    Passenger fare  
cabin   Cabin number # 승객의 객실 번호   
embarked    Port of Embarkation # 승객이 탑승한 항구 

pclass: A proxy for socio-economic status (SES)
1st = Upper
2nd = Middle
3rd = Lower

age: Age is fractional if less than 1. If the age is estimated, is it in the form of xx.5
# 나이가 1 미만인 경우 1살 미만의 영유아의 개월 수 이다.

 

우리가 모델을 학습시키기 위해 사용할 데이터셋은 train 데이터셋이고 먼저 train에 대한 기술 통계를 확인해본다. 
train 데이터셋의 기술통계
  • 사실 describe() 함수는 각 컬럼의 갯수 평균과 표준편차 그리고 사분위 수를 표시해 주는데 해당 자료가 필요한 이터는 연속형 변수인 Age와 Fare 뿐이다. 
  • passengerId와 Pclass, SibSp, Parch는 명목형 변수이다.
  • 하지만 python의 pandas에서는 해당 자료형들이 아래와 같이 int64이기 때문에 위와 같이 분류하게 된다.

  • 연속형으로 분류된 column들을 명목형으로 다시 분류하려면 형변환을하면 된다.
  • 명목형 변수의 describe를 보려면 train.describe(include=['O']) 와 같이 파라미터를 넣어주면 된다. 

 

 

 

2. 성별 - EDA (탐색적 현황 분석)

성별 구분에 따른 분포

  • 성별에 따른 생존확률을 보면, 각각 여성의 경우 0.74, 남성의 경우 0.19의 평균을 가진다. 
  • 이는 평균적으로 여성이 남성보다 높은 생존 확률을 가지는 것을 볼 수 있다.
관련된 내용을 막대 그래프를 이용해서 나타내면 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fig = plt.figure(figsize=(10,10))
 
area01 = fig.add_subplot(2,2,1)
area02 = fig.add_subplot(2,2,2)
area03 = fig.add_subplot(2,2,3)
 
 
sns.countplot(data = train, x='Sex', ax=area01)
area01.set_title('성별 count')
sns.barplot(data = train, x="Sex", y="Survived", errwidth=0, ax=area02) # hue로 그룹화가 된다.
area02.set_title('성별 생존자 count')
sns.countplot(data = train, x='Sex', hue='Survived', ax=area03)
area03.set_title('성별 생존 여부 count')
 
plt.show()
cs
  • (1,1) 차트를 보면 전체 중에서 남성이 여성보다 2배 가량 많다. 
  • 하지만 (1,2) 차트를 보면 위의 group by 조회 결과 값을 시각적으로 확인할 수 있고, 남성의 생존율은 전체 남성 수에 비해 0.2 미만으로 낮은 것을 볼 수 있다.
  • (2,1) 차트는 각 생존 여부에 따라 count한 것인데, 남성은 생존자가 대략 5:1, 여성은 1:3 정도의 수치를 보이고 있다.
 

성별/Pclass별 구분에 따른 분포

 

  • 이번엔 성별과 Pclass별 구분에 따른 분포를 확인해보았는데, 일차적으로 여성이 남성보다 압도적으로 높은 편이고, 객실 등급에 따라 생존율도 차이나는 것을 볼 수 있다. 
1
2
3
4
5
6
7
8
9
fig = plt.figure(figsize=(10,5))
 
area01 = fig.add_subplot(1,2,1)
area02 = fig.add_subplot(1,2,2)
 
sns.countplot(data = train, x='Sex', hue="Pclass", ax=area01)
sns.barplot(data = train, x = "Sex", y = "Survived", hue="Pclass", errwidth=0, ax=area02)
 
plt.show()
cs

  • 성별/Pclass별 count와 생존율을 시각적으로 비교한다.

 


 

 

 

 

 

다음 포스트

 

타이타닉 생존자 예측 분석 (2-2) EDA(연령대별) - v.1.0.1

이전 포스트 2023.01.04 - [Kaggle/타이타닉] - 타이타닉 생존자 예측 분석 (2-1) EDA v.1.0.1 타이타닉 생존자 예측 분석 (2) EDA v.1.0.1 - 개요 우선 데이터 사이언티스트 (이하 DS)의 꿈을 가장 크게 꾸게 해준

drewvvv.tistory.com

 
 
 

 

 

최근 데이터 분석을 다시 시작하게 되었다.

원래 목표로 삼았던 직업인 데이터 사이언티스트가 되기 위한 준비를 해오고 있었고, 이제 다시 캐글을 시작할 때가 되었다. 

 

데이터 분석의 Hello World!인 타이타닉을 처음부터 다시 분석해보면서 이전에 내 부족한 부분은 무엇이었고,

지금 나의 부족한 부분은 무엇인지를 알고 더 나아가 훌륭한 데이터 분석가가 되기 위한 준비를 이를 통해 지금부터 다시 해나가겠다.

 

 

 

 


지금부터 써내려가는 이 블로그의 타이타닉 타임랩스는 매 순간순간의 시행착오들을 기록할 것이다.

그 과정이 비록 길고 오답일 수 있지만, 반복되는 시행착오 끝에 그 의미를 정확히 해석할 줄 아는 분석가가 되기 위함이다.

 

 

매 시행착오를 통해 발전될 때마다 게시글 제목에 버전이 업그레이드 될 것이다.

 

 

 

 

 

 
 
 

 

 

 

 

 

 

 

 

 


이후 포스트

 
 
 
 
 
 
 
 
 
 
 
 

+ Recent posts