library(caret)
library(mice)
library(dplyr)
library(ggplot2)
library(stringr)
library(purrr)
library(corrplot)
library(viridis)
library(RColorBrewer)
1.Tratamento inicial dos dados
Unindo o conjunto de treino e teste para estimar pseudo valores. Existem muitos dados faltantes, e para estimar um valor mais próximo do real para os mesmos, precisaremos do máximo de informação possível.
setwd('/home/davi/Desktop/titanic')
train <- read.csv(file='train.csv', sep=',', stringsAsFactors = FALSE)
train$dataTag <- 'train'
test <- read.csv(file='test.csv', sep=',', stringsAsFactors = FALSE)
test$dataTag <- 'test'
test$Survived <- NA
concat_data <- train %>% bind_rows(test)
head(concat_data)
## PassengerId Survived Pclass
## 1 1 0 3
## 2 2 1 1
## 3 3 1 3
## 4 4 1 1
## 5 5 0 3
## 6 6 0 3
## Name Sex Age SibSp
## 1 Braund, Mr. Owen Harris male 22 1
## 2 Cumings, Mrs. John Bradley (Florence Briggs Thayer) female 38 1
## 3 Heikkinen, Miss. Laina female 26 0
## 4 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35 1
## 5 Allen, Mr. William Henry male 35 0
## 6 Moran, Mr. James male NA 0
## Parch Ticket Fare Cabin Embarked dataTag
## 1 0 A/5 21171 7.2500 S train
## 2 0 PC 17599 71.2833 C85 C train
## 3 0 STON/O2. 3101282 7.9250 S train
## 4 0 113803 53.1000 C123 S train
## 5 0 373450 8.0500 S train
## 6 0 330877 8.4583 Q train
summary(concat_data)
## PassengerId Survived Pclass Name
## Min. : 1 Min. :0.0000 Min. :1.000 Length:1309
## 1st Qu.: 328 1st Qu.:0.0000 1st Qu.:2.000 Class :character
## Median : 655 Median :0.0000 Median :3.000 Mode :character
## Mean : 655 Mean :0.3838 Mean :2.295
## 3rd Qu.: 982 3rd Qu.:1.0000 3rd Qu.:3.000
## Max. :1309 Max. :1.0000 Max. :3.000
## NA's :418
## Sex Age SibSp Parch
## Length:1309 Min. : 0.17 Min. :0.0000 Min. :0.000
## Class :character 1st Qu.:21.00 1st Qu.:0.0000 1st Qu.:0.000
## Mode :character Median :28.00 Median :0.0000 Median :0.000
## Mean :29.88 Mean :0.4989 Mean :0.385
## 3rd Qu.:39.00 3rd Qu.:1.0000 3rd Qu.:0.000
## Max. :80.00 Max. :8.0000 Max. :9.000
## NA's :263
## Ticket Fare Cabin
## Length:1309 Min. : 0.000 Length:1309
## Class :character 1st Qu.: 7.896 Class :character
## Mode :character Median : 14.454 Mode :character
## Mean : 33.295
## 3rd Qu.: 31.275
## Max. :512.329
## NA's :1
## Embarked dataTag
## Length:1309 Length:1309
## Class :character Class :character
## Mode :character Mode :character
##
##
##
##
Formatando os dados. Precisamos marcas as regiões com dados faltantes e também definir os tipos de cada variável (categórico, numérico ou string) do nosso conjunto de dados.
concat_data$Cabin[which(concat_data$Cabin == '')] <- NA
concat_data$Embarked[which(concat_data$Embarked == '')] <- NA
concat_data$Survived <- as.factor(concat_data$Survived)
concat_data$Pclass <- as.factor(concat_data$Pclass)
concat_data$Sex <- as.factor(concat_data$Sex)
concat_data$Ticket <- as.factor(concat_data$Ticket)
concat_data$Cabin <- as.factor(concat_data$Cabin)
concat_data$Embarked <- as.factor(concat_data$Embarked)
concat_data$dataTag <- as.factor(concat_data$dataTag)
# Tem um NA tambem, pertence a Pclass 3, vou substituir pela media dessa classe
which(is.na(concat_data$Fare))
## [1] 1044
concat_data[1044, ]
## PassengerId Survived Pclass Name Sex Age SibSp Parch
## 1044 1044 <NA> 3 Storey, Mr. Thomas male 60.5 0 0
## Ticket Fare Cabin Embarked dataTag
## 1044 3701 NA <NA> S test
# Substituindo
concat_data$Fare[1044] <- mean( concat_data$Fare[which(concat_data$Pclass == 3)], na.rm = TRUE)
# Observei 2 NA's em Embarked
concat_data[ which(is.na(concat_data$Embarked)), ]
## PassengerId Survived Pclass Name
## 62 62 1 1 Icard, Miss. Amelie
## 830 830 1 1 Stone, Mrs. George Nelson (Martha Evelyn)
## Sex Age SibSp Parch Ticket Fare Cabin Embarked dataTag
## 62 female 38 0 0 113572 80 B28 <NA> train
## 830 female 62 0 0 113572 80 B28 <NA> train
# Onde sera que eles embarcaram?
# Por serem classe 1, provavelmente embarcaram em S ou C
table(concat_data$Embarked, concat_data$Pclass)
##
## 1 2 3
## C 141 28 101
## Q 3 7 113
## S 177 242 495
concat_data$Embarked[ which(is.na(concat_data$Embarked)) ] <- factor("S")
2.Definindo novas variáveis
A partir dos nomes, irei separar o sobrenome e o pronome de tratamento para ajudar a indentificar melhor todos os subgrupos de passageiros.
length(concat_data$Name) # n nomes
## [1] 1309
# preciso tratar melhor, fazer split na virgula e no ponto para capturar o sobrenome e o pronome de tratamento
names <- strsplit(concat_data$Name, ',')
names_no_space <- lapply(names, function(x) gsub(' ', '',x))
# Sobrenpome
surname <- lapply(names_no_space , function(x) x[[1]] )
surname <- unlist(surname)
treat_pron <- lapply(names_no_space , function(x) x[[2]])
treat_pron <- lapply(treat_pron, function(x) strsplit(x, '\\.', perl = TRUE)[[1]])
treat_pron <- map(treat_pron, 1) %>% unlist()
# Adicionando as novas colunas
concat_data$surname <- as.factor(surname)
concat_data$treat_pron <- as.factor(treat_pron)
head(concat_data)
## PassengerId Survived Pclass
## 1 1 0 3
## 2 2 1 1
## 3 3 1 3
## 4 4 1 1
## 5 5 0 3
## 6 6 0 3
## Name Sex Age SibSp
## 1 Braund, Mr. Owen Harris male 22 1
## 2 Cumings, Mrs. John Bradley (Florence Briggs Thayer) female 38 1
## 3 Heikkinen, Miss. Laina female 26 0
## 4 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35 1
## 5 Allen, Mr. William Henry male 35 0
## 6 Moran, Mr. James male NA 0
## Parch Ticket Fare Cabin Embarked dataTag surname
## 1 0 A/5 21171 7.2500 <NA> S train Braund
## 2 0 PC 17599 71.2833 C85 C train Cumings
## 3 0 STON/O2. 3101282 7.9250 <NA> S train Heikkinen
## 4 0 113803 53.1000 C123 S train Futrelle
## 5 0 373450 8.0500 <NA> S train Allen
## 6 0 330877 8.4583 <NA> Q train Moran
## treat_pron
## 1 Mr
## 2 Mrs
## 3 Miss
## 4 Mrs
## 5 Mr
## 6 Mr
Vamos ver a distribuição da idade. Menores de idade possuem o pronome de tratamento Master.
colorRampPalette(brewer.pal(9, "Set1"))
## function (n)
## {
## x <- ramp(seq.int(0, 1, length.out = n))
## if (ncol(x) == 4L)
## rgb(x[, 1L], x[, 2L], x[, 3L], x[, 4L], maxColorValue = 255)
## else rgb(x[, 1L], x[, 2L], x[, 3L], maxColorValue = 255)
## }
## <bytecode: 0x7546de8>
## <environment: 0x7545ad0>
concat_data %>%
select(Age, treat_pron) %>%
na.omit() %>%
ggplot(aes(y=Age, x=treat_pron)) +
geom_boxplot( fill= 'deepskyblue3') +
theme(legend.text = element_text(size=12),
axis.text.x = element_text(size=10, angle = 0),
axis.title=element_text(size=12),
axis.text = element_text(size=12),
axis.line = element_line(colour = "black"),
panel.border = element_blank()) +
theme_bw()
3.Estimando pseudo valores para idade
3.1.Método II
Minha primeira estrategia será utilizar a idade média de cada classe para preencher os dados faltantes, ao invés de simplesmente usar uma média global.
class_with_na <- concat_data$treat_pron[is.na(concat_data$Age)]
as.character(class_with_na) %>% table()
## .
## Dr Master Miss Mr Mrs Ms
## 1 8 50 176 27 1
mean_age_by_class <- concat_data %>%
filter(str_detect(treat_pron, "Dr|Miss|Mr|Mrs|Ms|Master")) %>%
dplyr::group_by(treat_pron) %>%
dplyr::summarize(mean_age = mean(Age, na.rm = TRUE))
mean_age_by_class
## # A tibble: 6 x 2
## treat_pron mean_age
## <fctr> <dbl>
## 1 Dr 43.6
## 2 Master 5.48
## 3 Miss 21.8
## 4 Mr 32.3
## 5 Mrs 37.0
## 6 Ms 28.0
for(i in 1:nrow(concat_data)){
if(is.na(concat_data$Age[i]) & concat_data$treat_pron[i] == 'Dr'){
concat_data$Age[i] <- 43.6
}
if(is.na(concat_data$Age[i]) & concat_data$treat_pron[i] == 'Miss'){
concat_data$Age[i] <- 21.8
}
if(is.na(concat_data$Age[i]) & concat_data$treat_pron[i] == 'Mr'){
concat_data$Age[i] <- 32.3
}
if(is.na(concat_data$Age[i]) & concat_data$treat_pron[i] == 'Mrs'){
concat_data$Age[i] <- 37.0
}
if(is.na(concat_data$Age[i]) & concat_data$treat_pron[i] == 'Ms'){
concat_data$Age[i] <- 28.0
}
if(is.na(concat_data$Age[i]) & concat_data$treat_pron[i] == 'Master'){
concat_data$Age[i] <- 5.48
}
}
summary(concat_data)
## PassengerId Survived Pclass Name Sex
## Min. : 1 0 :549 1:323 Length:1309 female:466
## 1st Qu.: 328 1 :342 2:277 Class :character male :843
## Median : 655 NA's:418 3:709 Mode :character
## Mean : 655
## 3rd Qu.: 982
## Max. :1309
##
## Age SibSp Parch Ticket
## Min. : 0.17 Min. :0.0000 Min. :0.000 CA. 2343: 11
## 1st Qu.:21.80 1st Qu.:0.0000 1st Qu.:0.000 1601 : 8
## Median :30.00 Median :0.0000 Median :0.000 CA 2144 : 8
## Mean :29.90 Mean :0.4989 Mean :0.385 3101295 : 7
## 3rd Qu.:36.00 3rd Qu.:1.0000 3rd Qu.:0.000 347077 : 7
## Max. :80.00 Max. :8.0000 Max. :9.000 347082 : 7
## (Other) :1261
## Fare Cabin Embarked dataTag
## Min. : 0.000 C23 C25 C27 : 6 C:270 test :418
## 1st Qu.: 7.896 B57 B59 B63 B66: 5 Q:123 train:891
## Median : 14.454 G6 : 5 S:916
## Mean : 33.280 B96 B98 : 4
## 3rd Qu.: 31.275 C22 C26 : 4
## Max. :512.329 (Other) : 271
## NA's :1014
## surname treat_pron
## Andersson: 11 Mr :757
## Sage : 11 Miss :260
## Asplund : 8 Mrs :197
## Goodwin : 8 Master : 61
## Davies : 7 Dr : 8
## Brown : 6 Rev : 8
## (Other) :1258 (Other): 18
3.2.Método II
A segunda estratégia consiste em realizar uma regressão da idade usando random forest.
# Ordenar por sobrenome
concat_data <- concat_data %>%
arrange(surname)
train_na <- concat_data %>%
select(Age, Pclass, SibSp, Parch, Fare, treat_pron,Sex) %>%
na.omit()
age_model <- train(Age ~ Pclass + Sex + SibSp + Parch +
Fare + surname + treat_pron ,
data = train_na,
method = 'rf',
tuneGrid = data.frame(mtry = c(2,3,4,5,6,8,10)),
trControl = trainControl(method = 'cv', number = 10,
verboseIter = TRUE))
plot(age_model)
print(age_model)
summary(age_model)
pred_age <- predict(age_model, concat_data)
concat_data$Age[which(is.na(concat_data$Age))] <- pred_age[which(is.na(concat_data$Age))]
3.3.Método III
Terceira tentativa: fazer regressão linear multipla
age_model2 <- lm(Age ~ Pclass + SibSp + Parch + treat_pron,
data = train_na)
summary(age_model2)
plot(age)
pred_age <- predict(age_model2, concat_data)
concat_data$Age[which(is.na(concat_data$Age))] <- pred_age[which(is.na(concat_data$Age))]
4.Explorando os dados
Com os valores faltantes estimados, podemos analisar melhor os dados.
Idade x morte x sexo
Do total de homens, a maioria morreu. Do total de mulheres, a maioria sobreviveu.
concat_data %>%
select(Age, Sex, Survived) %>%
na.omit()%>%
ggplot(aes(y=Age, x=Sex , fill=Survived, size= Age)) +
geom_boxplot(alpha = 1) +
scale_fill_manual(values = c('#330033', '#008080')) +
theme_bw()
Dos que sobreviveram, boa parte tinha entre 20 a 30 anos, seja homem ou mulher
concat_data %>%
select(Age, Sex, Survived, treat_pron) %>%
na.omit()%>%
ggplot(aes(y=Age, x=treat_pron , fill=Survived)) +
geom_boxplot(alpha = 0.8)+
geom_hline(yintercept = c(20, 30, 40), color = 'red', linetype= 3) +
scale_fill_manual(values = c('#330033', '#008080')) +
theme_bw()
concat_data %>%
select(Age, Sex, Survived, treat_pron) %>%
na.omit()%>%
ggplot(aes(y=Age, x=treat_pron , col=Survived) ) +
geom_point(alpha = 0.5, size=3, position = position_jitterdodge()) +
scale_color_manual(values = c('#330033', '#008080')) +
theme_bw()
# Classe vs Tarifa
# A classe 1 ten yna tarifa consideravelmente mais cara (sao os passageiros ricos)
concat_data %>%
ggplot(aes(x=Pclass, y=Fare)) +
geom_boxplot(fill='cornflowerblue') +
theme_bw()
Os mais privilegiados morreram menos que os demais?
Com os dados observados até então, parece que as pessoas da classe 3 morreram mais (50%). Seria uma questao de localizacao do navio?
concat_data %>%
na.omit() %>%
ggplot(aes(x=Pclass, fill=Survived)) +
geom_bar(position = 'fill', alpha=0.8) +
scale_fill_manual(values = c('#330033', '#008080')) +
theme_bw()
Conforme a figura, podemos ver que a classe 1, se situa mais ao centro das classes 2 e 3. A classe 3 se situa na ponta traseira do navio.
Será que o navio afundou primeiramente pela parte traseira? Se sim, essa observação parece ser bem plausível.
Vamos ver se existe alguma relação linear entre os dados
dist_df_vars <- concat_data %>%
select(Pclass, Age, SibSp, Parch, Fare)
dist_df_vars$Pclass <- as.numeric(dist_df_vars$Pclass)
vars_cor <- cor(dist_df_vars, method = 'pearson')
corrplot(vars_cor, method = 'ellipse', type="upper", order="hclust", addCoef.col = "black")
5.Treinando e classificando os dados
Hora de aplicar os métodos de classificação sobre os dados de treinamento. Antes disso, precisamos separar novamente os dados em duas partes, a parte de treinamento e a de teste.
Faremos testes usando Random forest e Regressão logística para classificar quem sobreviveu e quem não sobreviveu ao desastre.
train <- concat_data %>% filter(dataTag == 'train')
test <- concat_data %>% filter(dataTag == 'test')
5.1.Random Forest
final_model <- train(Survived ~ Pclass + Sex + Age + SibSp + Parch +
Fare + surname + treat_pron,
data= train,
method= 'rf',
tuneGrid= data.frame(mtry= c(2,3,4,5,6,8,10)),
trControl= trainControl(method= 'cv', number= 10,
verboseIter= TRUE))
pred_surv <- predict(final_model, test)
to_submit <- data.frame(PassengerId = test$PassengerId, Survived = pred_surv)
write.csv(file='to_submit.csv', to_submit, row.names = F)
print(final_model)
plot(final_model)
5.2.Regressão logística
logist_regression <- glm(Survived ~ Pclass + Sex + Age + SibSp + Fare,
data = train, family = 'binomial')
pred_surv <- predict(logist_regression, test, type = 'response')
pred_cutoff_90 <- ifelse(pred_surv >= 0.90, 1, 0)
pred_cutoff_60 <- ifelse(pred_surv >= 0.60, 1, 0)
to_submit <- data.frame(PassengerId = test$PassengerId, Survived = pred_cutoff_60)
write.csv(file='to_submit.csv', to_submit, row.names = F)
summary(logist_regression)
##
## Call:
## glm(formula = Survived ~ Pclass + Sex + Age + SibSp + Fare, family = "binomial",
## data = train)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.7782 -0.5967 -0.4199 0.6163 2.5135
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 3.959967 0.446512 8.869 < 2e-16 ***
## Pclass2 -1.100803 0.292908 -3.758 0.000171 ***
## Pclass3 -2.262839 0.290373 -7.793 6.55e-15 ***
## Sexmale -2.695011 0.194883 -13.829 < 2e-16 ***
## Age -0.042567 0.007844 -5.427 5.74e-08 ***
## SibSp -0.399149 0.106066 -3.763 0.000168 ***
## Fare 0.002389 0.002328 1.026 0.304703
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 1186.66 on 890 degrees of freedom
## Residual deviance: 784.86 on 884 degrees of freedom
## AIC: 798.86
##
## Number of Fisher Scoring iterations: 5
6.Resultados da submissão no Kaggle
A métrica de avaliação de desemenho usada foi a acurácia. Que mede o total de acertos sobre o total de observações. Após diversos testes e diferentes combinações de parâmetros de classificação, o melhor resultado obtido foi de 0.78468
Score obtido