문제정의
데이터 수집
데이터 전처리
-PassengerId 삭제
-Embarked 결측치 채우기
-Fare 결측치 채우기
-Age 결측치 채우기
-Cabin 결측치 채우기
탐색적 데이터 분석
-Cabin 시각화
-Pclass 시각화
-Cabin과 Pclass 시각화
-Sex 시각화
-Embarked 시각화
-Sex, Age, Survived 시각화
-Sex, Fare, Survived 시각화
SibSp(형제자매수), Parch(부모자식수) 확인해보기
→ Family_Size, Family_Group 컬럼 생성(특성공학)
Name 컬럼 호칭만 뽑아오기 → Title 컬럼 생성
Ticket 확인해보기 → 특징이 없어서 삭제
타입 변경 : Name, Sex, Ticket, Cabin, Embarked
모델 선택 및 하이퍼 파라미터 튜닝
KNN, DecisionTree 모델 사용하기
새로운 모델 사용해보기(Ensemble 계열 모델)
→ RandomForest, AdaBoost
학습
평가 및 예측
*교차검증
타입 변경 : Name, Sex, Ticket, Cabin, Embarked
문제정의
타이타닉 데이터를 사용해서 생존자와 사망자 예측해보기
데이터 수집
kaggle 사이트로부터 train, test 데이터 다운로드
import pandas as pd
train = pd.read_csv('./titanic/train.csv')
test = pd.read_csv('./titanic/test.csv')
train.shape, test.shape
train.head()
결측치 처리
이상치 처리
train
# 결측치 : Age, Cabin, Embarked
# 타입 변경 : Name, Sex, Ticket, Cabin, Embarked
train.info()
# 결측치 : Age, Cabin, Fare
# 타입 변경 : Name, Sex, Ticket, Cabin, Embarked
test.info()
y_train = train['Survived']
train.head()
-train
# train = train.frop('PassengerId', axis = 1)
# inplace = True : 변경된 결과 저장
train.drop('PassengerId', axis=1, inplace=True)
-test
test.drop('PassengerId', axis=1, inplace=True)
train
탑승한 항구
train에 2개의 결측치 존재
value_counts() : 값의 갯수 세어주는 함수
train['Embarked'].value_counts()
train['Embarked'] = train['Embarked'].fillna('S')
train.info()
test['Fare']
test.info()
test['Fare'].describe()
test['Fare'].fillna(14.45, inplace=True)
test.info()
train.iloc[5]
# 피어슨 상관계수
# 두 변수간의 선형 상관관계를 수치적으로 표현
# 두 값이 비례관계면 양의 숫자(최대값 1) > 양의 상관관계
# 두 값이 반비례관계면 음의 숫자(최대값 -1) > 음의 상관관계
# 0에 가까울수록 관계가 없다
train.corr()
# Age에 결측치가 존재한다 => Pclass와 Sex를 확인하고 해당하는 값으로 결측치 채움
# Age에 결측치가 존재하지 않는다 => 그 값을 그대로 사용한다.
age_table = train[['Pclass', 'Sex','Age']].groupby(by=['Pclass', 'Sex']).median()
train.iloc[17]
# 행이나 열 단위로 복잡한 작업(커스텀 함수)을 할 때 사용
# 결측치 채우는 함수 만들기
import numpy as np
def fill_age(data): # data는 하나의 행 데이터(한 사람의 데이터)
if np.isnan(data['Age']): # 결측치면 True, 아니면 False
# 결측치라면 Pclasss와 Sex를 확인하고 해당 값으로 age_table에서 검색
# 나온 값으로 결측치 채우기
return age_table.loc[data['Pclass'], data['Sex']][0]
else:
# 값이 있기 때문에 그대로 사용
return data['Age']
fill_age() # 함수를 사용하겠다
fill_age # 함수를 가져오겠다
train['Age'] = train.apply(fill_age, axis=1)
test['Age'] = test.apply(fill_age, axis=1)
train.info()
train['Cabin'].unique()
train['Cabin'] = train['Cabin'].str[0]
test['Cabin'] = test['Cabin'].str[0]
train['Cabin'].unique()
train['Cabin'].fillna('N', inplace=True)
test['Cabin'].fillna('N', inplace=True)
train.info()
# matplolib : 사용하기 어려운 편, 자세하게 사용 할 수 있음
# seaborn : 사용하기 쉬운 편, 자세하게 사용하기는 힘듦
import seaborn as sns
# data = 사용 할 데이터
# x = x축에 사용 할 데이터
# hue = 데이터 분리 기준
# 결측치로 채운 N데이터가 생존과 사망이 분리가 되어있어서 데이터로 사용하자
sns.countplot(data = train, x = 'Cabin', hue = 'Survived')
# 1등급에 탑승한 사람은 많이 생존
# 3등급에 탑승한 사람은 많이 사망
sns.countplot(data = train, x = 'Pclass', hue = 'Survived')
# N구역은 사망자의 비율이 높고, 3등급의 객실 사람이 많다.
# A,B,C구역은 1등급만 존재(좋은, 비싼 구역이지 않을까?)
sns.countplot(data = train, x = 'Cabin', hue = 'Pclass')
sns.countplot(data = train, x = 'Sex', hue = 'Survived')
sns.countplot(data = train, x = 'Embarked', hue = 'Survived')
sns.countplot(data = train, x = 'Embarked', hue = 'Pclass')
sns.violinplot(data = train, x = 'Sex', y = 'Age', hue = 'Survived', split = True)
# 20대가 많이 탑승했다
# 남아는 많이 살았고 여아는 많이 죽었다 > 남아선호사상?
sns.violinplot(data = train, x = 'Sex', y = 'Fare', hue = 'Survived', split = True)
# 요금이 저렴할수록 죽은 사림이 더 많다
train['Family_Size'] = train['SibSp'] + train['Parch'] + 1
test['Family_Size'] = test['SibSp'] + test['Parch'] + 1
train.head()
sns.countplot(data = train, x = 'Family_Size', hue = 'Survived')
# 1명일때는 죽은 비율이 높다 > Alone
# 2~4명일때는 산 비율이 높다 > Small
# 5명 이상일때는 죽은 비율이 높다 > Large
# 수치형 > 범주형 : Binning
# cut : 구간정보, 구간에 대한 이름을 통해서 수치형 값을 범주형으로 변경
# bins : 구간 정보
bins = [0,1,4,20]
# labels : 구간에 대한 이름
labels = ['Alone', 'Small', 'Large']
train_cut = pd.cut(train['Family_Size'], bins = bins, labels = labels)
test_cut = pd.cut(test['Family_Size'], bins = bins, labels = labels)
train['Family_Group'] = train_cut
test['Family_Group'] = test_cut
train.head(10)
train['Name'][0]
# split : 매개변수값을 기준으로 앞과 뒤로 나눠준다.(리스트로 출력)
# strip : 문자열 맨 앞과 맨 뒤에 있는 공백 제거
train['Name'][0].split(',')[1].split('.')[0].strip()
# 이름을 하나씩 집어넣고 정해진 함수 사용
def split_title(name):
return name.split(',')[1].split('.')[0].strip()
train['Title'] = train['Name'].apply(split_title)
test['Title'] = test['Name'].apply(split_title)
train.head()
train.shape, test.shape
# 1 or 2개인 데이터들이 많다. → 과대적합 유발 → 처리를 해줘야함
# 1 or 2개인 데이터들이 많다. → Other로 합치기
train['Title'].value_counts()
convert_name_dic = {
'Mr' : 'Mr' ,
'Mrs' : 'Mrs',
'Miss' : 'Miss',
'Master' : 'Master',
'Don' : 'Other',
'Rev' : 'Rev',
'Dr': 'Dr',
'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_name_dic)
train['Title'].value_counts()
test['Title'] = test['Title'].map(convert_name_dic)
train.head()
# 문자열 끝엔 최소 4자리 숫자가 있다. → 한자리 숫자만 있는 경우도 존재
# 다른 탑승객과 똑같은 티켓을 가진 사람이 약 200명 → 공통된 데이터가 별로 없다.
# Ticket 데이터는 생존과 연관이 없어보임 → 삭제
len(train['Ticket'].unique())
train.drop('Ticket', axis = 1, inplace=True)
test.drop('Ticket', axis = 1, inplace=True)
train.shape, test.shape
train.columns
train.drop('Survived', axis=1, inplace=True)
train.drop('Name', axis=1, inplace=True)
test.drop('Name', axis=1, inplace=True)
train.shape, test.shape
combined = pd.concat([train, test], ignore_index=True)
combined
categorical_feature = ['Sex', 'Cabin', 'Embarked', 'Family_Group', 'Title']
one_hot = pd.get_dummies(combined[categorical_feature])
one_hot.shape
one_hot.columns
total = pd.concat([combined, one_hot], axis=1)
total.shape
total.drop(categorical_feature, axis=1, inplace=True)
total.shape
X_train = total.iloc[:891]
X_test = total.iloc[891:]
X_train.shape, X_test.shape
X_train.columns
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
knn = knn = KNeighborsClassifier()
tree = DecisionTreeClassifier()
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier()
rf = RandomForestClassifier(n_estimators=50, max_features=0.5)
knn.fit(X_train, y_train)
tree.fit(X_train, y_train)
rf.fit(X_train, y_train)
knn.score(X_train, y_train)
tree.score(X_train, y_train)
pre_knn = knn.predict(X_test)
pre_tree = tree.predict(X_test)
sub = pd.read_csv('./titanic/gender_submission.csv')
sub['Survived'] = pre_knn # 예측결과 집어넣기
sub.to_csv('knn_pre.csv', index=False) # csv파일로 출력하기
sub['Survived'] = pre_tree # 예측결과 집어넣기
sub.to_csv('tree_pre.csv', index=False) # csv파일로 출력하기
# 실제 점수와 유사한 결과를 출력
from sklearn.model_selection import cross_val_score
# train score : 0.81
# kaggle score : 0.6555
cross_val_score(knn, X_train, y_train, cv=5)
# train score : 0.98
# kaggle score : 0.70813
cross_val_score(tree, X_train, y_train, cv=5)
# 교차검증 score : 0.72
cross_val_score(knn, X_train, y_train, cv=5).mean()
# 교차검증 score : 0.78
cross_val_score(tree, X_train, y_train, cv=5).mean()
rf.score(X_train, y_train)
cross_val_score(rf, X_train, y_train, cv=5).mean()
pre_rf = rf.predict(X_test)
sub['Survived'] = pre_rf # 예측결과 집어넣기
sub.to_csv('rf_pre.csv', index=False) # csv파일로 출력하기