O naufrágio do RMS Titanic, ocorrido em 1912, é considerado um dos maiores desastres marítimos da história moderna. Além de sua relevância histórica, o conjunto de dados do Titanic tornou-se amplamente utilizado em estudos de Ciência de Dados e Aprendizado de Máquina.
O dataset contém informações demográficas, econômicas e sociais dos passageiros, permitindo investigar quais fatores influenciaram diretamente as chances de sobrevivência.
Neste relatório, será utilizada uma abordagem de aprendizado supervisionado baseada em Árvores de Decisão para prever a sobrevivência dos passageiros.
Os principais objetivos deste trabalho são:
Árvores de Decisão são modelos supervisionados utilizados para tarefas de classificação e regressão. Esses algoritmos realizam divisões sucessivas nos dados, criando subconjuntos mais homogêneos.
Neste trabalho, foi utilizado o algoritmo CART (Classification
and Regression Trees), implementado no pacote
rpart.
Os principais critérios utilizados em Árvores de Decisão incluem:
O pacote rpart utiliza o Índice Gini como critério
padrão para seleção das divisões.
df <- titanic_train
kable(
head(df),
caption = "Primeiras Linhas do Dataset Titanic"
) |>
kable_styling(
bootstrap_options = c(
"striped",
"hover",
"condensed"
),
full_width = FALSE
)
| PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 0 | 3 | Braund, Mr. Owen Harris | male | 22 | 1 | 0 | A/5 21171 | 7.2500 | S | |
| 2 | 1 | 1 | Cumings, Mrs. John Bradley (Florence Briggs Thayer) | female | 38 | 1 | 0 | PC 17599 | 71.2833 | C85 | C |
| 3 | 1 | 3 | Heikkinen, Miss. Laina | female | 26 | 0 | 0 | STON/O2. 3101282 | 7.9250 | S | |
| 4 | 1 | 1 | Futrelle, Mrs. Jacques Heath (Lily May Peel) | female | 35 | 1 | 0 | 113803 | 53.1000 | C123 | S |
| 5 | 0 | 3 | Allen, Mr. William Henry | male | 35 | 0 | 0 | 373450 | 8.0500 | S | |
| 6 | 0 | 3 | Moran, Mr. James | male | NA | 0 | 0 | 330877 | 8.4583 | Q |
O conjunto de dados possui:
df <- df %>%
mutate(
Survived = factor(
Survived,
levels = c(0, 1),
labels = c("Morreu", "Sobreviveu")
),
Pclass = factor(Pclass),
Sex = factor(
Sex,
labels = c("Feminino", "Masculino")
)
)
str(df)
## 'data.frame': 891 obs. of 12 variables:
## $ PassengerId: int 1 2 3 4 5 6 7 8 9 10 ...
## $ Survived : Factor w/ 2 levels "Morreu","Sobreviveu": 1 2 2 2 1 1 1 1 2 2 ...
## $ Pclass : Factor w/ 3 levels "1","2","3": 3 1 3 1 3 3 1 3 3 2 ...
## $ Name : chr "Braund, Mr. Owen Harris" "Cumings, Mrs. John Bradley (Florence Briggs Thayer)" "Heikkinen, Miss. Laina" "Futrelle, Mrs. Jacques Heath (Lily May Peel)" ...
## $ Sex : Factor w/ 2 levels "Feminino","Masculino": 2 1 1 1 2 2 2 2 1 1 ...
## $ Age : num 22 38 26 35 35 NA 54 2 27 14 ...
## $ SibSp : int 1 1 0 1 0 0 0 3 0 1 ...
## $ Parch : int 0 0 0 0 0 0 0 1 2 0 ...
## $ Ticket : chr "A/5 21171" "PC 17599" "STON/O2. 3101282" "113803" ...
## $ Fare : num 7.25 71.28 7.92 53.1 8.05 ...
## $ Cabin : chr "" "C85" "" "C123" ...
## $ Embarked : chr "S" "C" "S" "S" ...
As variáveis categóricas foram convertidas para o tipo
factor, permitindo melhor interpretação e modelagem
estatística.
df_filtrado <- df %>%
filter(!is.na(Age)) %>%
mutate(
FaixaEtaria = case_when(
Age < 12 ~ "Criança",
Age < 18 ~ "Adolescente",
Age < 60 ~ "Adulto",
TRUE ~ "Idoso"
),
FaixaEtaria = factor(
FaixaEtaria,
levels = c(
"Criança",
"Adolescente",
"Adulto",
"Idoso"
)
),
TamanhoFamilia = SibSp + Parch + 1
)
cat(
"Quantidade de registros removidos:",
nrow(df) - nrow(df_filtrado)
)
## Quantidade de registros removidos: 177
Foram removidos registros com idade ausente (NA) e
criadas novas variáveis derivadas para enriquecer a análise.
O gráfico abaixo apresenta a relação entre sexo e taxa de sobrevivência.
ggplot(
df_filtrado,
aes(x = Sex, fill = Survived)
) +
geom_bar(position = "fill") +
scale_y_continuous(
labels = percent
) +
labs(
title = "Taxa de Sobrevivência por Sexo",
x = "Sexo",
y = "Proporção"
) +
theme_minimal()
Observa-se que passageiros do sexo feminino apresentaram probabilidade significativamente maior de sobrevivência.
ggplot(
df_filtrado,
aes(x = Pclass, fill = Survived)
) +
geom_bar(position = "fill") +
scale_y_continuous(
labels = percent
) +
labs(
title = "Taxa de Sobrevivência por Classe",
x = "Classe",
y = "Proporção"
) +
theme_minimal()
Passageiros da primeira classe apresentaram maiores taxas de sobrevivência.
ggplot(
df_filtrado,
aes(x = Age, fill = Survived)
) +
geom_histogram(
bins = 30,
alpha = 0.7,
position = "identity"
) +
labs(
title = "Distribuição das Idades",
x = "Idade",
y = "Frequência"
) +
theme_minimal()
Crianças apresentaram maior proporção de sobrevivência.
ggplot(
df_filtrado,
aes(x = FaixaEtaria, fill = Survived)
) +
geom_bar(position = "fill") +
scale_y_continuous(labels = percent) +
labs(
title = "Sobrevivência por Faixa Etária",
x = "Faixa Etária",
y = "Proporção"
) +
theme_minimal()
ggplot(
df_filtrado,
aes(
x = Pclass,
y = Fare,
fill = Pclass
)
) +
geom_boxplot() +
labs(
title = "Distribuição da Tarifa por Classe",
x = "Classe",
y = "Tarifa"
) +
theme_minimal()
dados_cor <- df_filtrado %>%
select(
Age,
Fare,
SibSp,
Parch,
TamanhoFamilia
)
matriz_cor <- cor(dados_cor)
corrplot(
matriz_cor,
method = "color",
type = "upper"
)
resumo <- df_filtrado %>%
group_by(
FaixaEtaria,
Survived
) %>%
summarise(
Quantidade = n(),
IdadeMedia = round(mean(Age), 1),
TarifaMedia = round(mean(Fare), 2),
.groups = "drop"
)
kable(
resumo,
caption = "Resumo Estatístico"
) |>
kable_styling(
bootstrap_options = c(
"striped",
"hover"
),
full_width = FALSE
)
| FaixaEtaria | Survived | Quantidade | IdadeMedia | TarifaMedia |
|---|---|---|---|---|
| Criança | Morreu | 29 | 6.0 | 34.26 |
| Criança | Sobreviveu | 39 | 3.7 | 30.03 |
| Adolescente | Morreu | 23 | 15.9 | 17.75 |
| Adolescente | Sobreviveu | 22 | 15.4 | 43.41 |
| Adulto | Morreu | 353 | 31.7 | 21.44 |
| Adulto | Sobreviveu | 222 | 32.8 | 56.51 |
| Idoso | Morreu | 19 | 65.4 | 40.40 |
| Idoso | Sobreviveu | 7 | 64.3 | 51.79 |
set.seed(123)
indices <- createDataPartition(
df_filtrado$Survived,
p = 0.7,
list = FALSE
)
train_data <- df_filtrado[indices, ]
test_data <- df_filtrado[-indices, ]
cat(
"Treino:", nrow(train_data),
"\nTeste:", nrow(test_data)
)
## Treino: 500
## Teste: 214
Foi utilizada divisão estratificada 70/30 para preservar a proporção das classes.
tree_model_baseline <- rpart(
Survived ~ Pclass + Sex + Age,
data = train_data,
method = "class",
control = rpart.control(
maxdepth = 4,
minsplit = 20,
cp = 0.01
)
)
tree_model_fare <- rpart(
Survived ~ Pclass + Sex + Age + Fare,
data = train_data,
method = "class",
control = rpart.control(
maxdepth = 4,
minsplit = 20,
cp = 0.01
)
)
A profundidade máxima da árvore foi limitada para reduzir o risco de overfitting.
rpart.plot(
tree_model_fare,
type = 4,
extra = 104,
fallen.leaves = TRUE,
shadow.col = "gray",
box.palette = "RdYlGn",
branch.lty = 3,
tweak = 1.2,
main = "Árvore de Decisão - Modelo com Fare"
)
A variável Sex foi selecionada como nó raiz por
apresentar maior capacidade de separação entre as classes.
importancia <- data.frame(
Variavel = names(
tree_model_fare$variable.importance
),
Importancia = tree_model_fare$variable.importance
)
kable(
importancia,
caption = "Importância das Variáveis"
) |>
kable_styling(
bootstrap_options = c(
"striped",
"hover"
),
full_width = FALSE
)
| Variavel | Importancia | |
|---|---|---|
| Sex | Sex | 65.72925 |
| Pclass | Pclass | 28.89049 |
| Fare | Fare | 24.18973 |
| Age | Age | 23.14598 |
pred_baseline <- predict(
tree_model_baseline,
test_data,
type = "class"
)
pred_fare <- predict(
tree_model_fare,
test_data,
type = "class"
)
matriz_baseline <- confusionMatrix(
pred_baseline,
test_data$Survived,
positive = "Sobreviveu"
)
matriz_fare <- confusionMatrix(
pred_fare,
test_data$Survived,
positive = "Sobreviveu"
)
matriz_fare
## Confusion Matrix and Statistics
##
## Reference
## Prediction Morreu Sobreviveu
## Morreu 103 22
## Sobreviveu 24 65
##
## Accuracy : 0.785
## 95% CI : (0.7239, 0.8381)
## No Information Rate : 0.5935
## P-Value [Acc > NIR] : 0.000000002262
##
## Kappa : 0.5561
##
## Mcnemar's Test P-Value : 0.8828
##
## Sensitivity : 0.7471
## Specificity : 0.8110
## Pos Pred Value : 0.7303
## Neg Pred Value : 0.8240
## Prevalence : 0.4065
## Detection Rate : 0.3037
## Detection Prevalence : 0.4159
## Balanced Accuracy : 0.7791
##
## 'Positive' Class : Sobreviveu
##
metricas <- data.frame(
Metrica = c(
"Acurácia",
"Recall",
"Precisão",
"F1-Score"
),
Baseline = round(c(
matriz_baseline$overall["Accuracy"],
matriz_baseline$byClass["Sensitivity"],
matriz_baseline$byClass["Precision"],
matriz_baseline$byClass["F1"]
), 4),
Com_Fare = round(c(
matriz_fare$overall["Accuracy"],
matriz_fare$byClass["Sensitivity"],
matriz_fare$byClass["Precision"],
matriz_fare$byClass["F1"]
), 4)
)
kable(
metricas,
caption = "Comparação entre os Modelos"
) |>
kable_styling(
bootstrap_options = c(
"striped",
"hover"
),
full_width = FALSE
)
| Metrica | Baseline | Com_Fare | |
|---|---|---|---|
| Accuracy | Acurácia | 0.7757 | 0.7850 |
| Sensitivity | Recall | 0.5977 | 0.7471 |
| Precision | Precisão | 0.8000 | 0.7303 |
| F1 | F1-Score | 0.6842 | 0.7386 |
O modelo com a variável Fare apresentou desempenho
superior ao modelo baseline.
probabilidades <- predict(
tree_model_fare,
test_data,
type = "prob"
)[,2]
roc_obj <- roc(
test_data$Survived,
probabilidades,
levels = c(
"Morreu",
"Sobreviveu"
)
)
plot(
roc_obj,
main = "Curva ROC"
)
auc(roc_obj)
## Area under the curve: 0.8385
A Curva ROC avalia a capacidade discriminativa do modelo.
tabela <- df_filtrado %>%
select(
Nome = Name,
Sexo = Sex,
Idade = Age,
Classe = Pclass,
Tarifa = Fare,
Sobreviveu = Survived
)
datatable(
tabela,
extensions = c("Buttons"),
options = list(
dom = "Bfrtip",
buttons = c(
"copy",
"csv",
"excel",
"pdf"
),
pageLength = 10,
scrollX = TRUE,
language = list(
search = "Buscar:",
lengthMenu = "Mostrar _MENU_ registros",
info = "Exibindo _START_ a _END_ de _TOTAL_ registros"
)
),
filter = "top",
rownames = FALSE,
class = "table table-striped table-hover"
)
A Entropia de Shannon mede o nível de desordem presente em um conjunto de dados.
\[ H(S) = -\sum_{i=1}^{c} p_i \log_2(p_i) \]
O Ganho de Informação mede a redução da entropia após uma divisão.
\[ IG(S,A)=H(S)-\sum_{v\in valores(A)} \frac{|S_v|}{|S|}H(S_v) \]
O Índice Gini é utilizado pelo algoritmo CART para selecionar as melhores divisões.
\[ Gini(S)=1-\sum_{i=1}^{c}p_i^2 \]
O F1-Score representa a média harmônica entre Precisão e Recall.
\[ F_1 = 2 \cdot \frac{Precisao \times Recall} {Precisao + Recall} \]
Os resultados demonstraram que o sexo foi a variável mais relevante para prever a sobrevivência dos passageiros.
Além disso, passageiros das classes superiores apresentaram maiores taxas de sobrevivência, indicando forte influência socioeconômica.
A inclusão da variável Fare melhorou levemente o
desempenho do modelo, sugerindo correlação entre valor da passagem e
chance de sobrevivência.
As Árvores de Decisão apresentaram elevada interpretabilidade, permitindo compreender claramente os critérios utilizados pelo modelo.
O presente trabalho demonstrou a aplicação de Árvores de Decisão na previsão da sobrevivência dos passageiros do Titanic.
Os resultados mostraram que variáveis como sexo, classe social, idade e tarifa paga tiveram influência significativa na sobrevivência.
O modelo apresentou desempenho satisfatório e elevada interpretabilidade.
Como trabalhos futuros, recomenda-se:
BREIMAN, L. et al. Classification and Regression Trees. CRC Press, 1984.
KUHN, M.; JOHNSON, K. Applied Predictive Modeling. Springer, 2013.
WICKHAM, H. ggplot2: Elegant Graphics for Data Analysis. Springer, 2016.
XIE, Y. R Markdown: The Definitive Guide. CRC Press, 2018.