1. Introdução à Regressão Logística

A Regressão Logística é um modelo de Machine Learning usado quando a variável resposta é binária — ou seja, tem apenas dois resultados possíveis, como sim/não, 0/1, sobreviveu/não sobreviveu.

Diferente da regressão linear, que prevê valores contínuos, a regressão logística prevê a probabilidade de um evento ocorrer, sempre entre 0 e 1.

A fórmula central do modelo é a função sigmoide:

\[P(Y=1) = \frac{1}{1 + e^{-(\beta_0 + \beta_1 X_1 + \cdots + \beta_n X_n)}}\]

Quando usar? - Diagnóstico médico (doente ou saudável) - Detecção de spam (spam ou não spam) - Previsão de sobrevivência (como faremos aqui)


2. O Dataset: Titanic

Usaremos o famoso dataset Titanic, que contém informações sobre os passageiros do naufrágio de 1912. Nosso objetivo é prever quem sobreviveu com base em características como sexo, idade e classe.

if (!require(tidyverse))  install.packages("tidyverse")
if (!require(caret))      install.packages("caret")
if (!require(pROC))       install.packages("pROC")
if (!require(titanic))    install.packages("titanic")

library(tidyverse)
library(caret)
library(pROC)
library(titanic)
dados <- titanic_train

cat("Dimensões:", dim(dados), "\n")
## Dimensões: 891 12
cat("Variáveis:", names(dados), "\n")
## Variáveis: PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
glimpse(dados)
## Rows: 891
## Columns: 12
## $ PassengerId <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ Survived    <int> 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1…
## $ Pclass      <int> 3, 1, 3, 1, 3, 3, 1, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3, 2, 3, 3…
## $ Name        <chr> "Braund, Mr. Owen Harris", "Cumings, Mrs. John Bradley (Fl…
## $ Sex         <chr> "male", "female", "female", "female", "male", "male", "mal…
## $ Age         <dbl> 22, 38, 26, 35, 35, NA, 54, 2, 27, 14, 4, 58, 20, 39, 14, …
## $ SibSp       <int> 1, 1, 0, 1, 0, 0, 0, 3, 0, 1, 1, 0, 0, 1, 0, 0, 4, 0, 1, 0…
## $ Parch       <int> 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 5, 0, 0, 1, 0, 0, 0…
## $ Ticket      <chr> "A/5 21171", "PC 17599", "STON/O2. 3101282", "113803", "37…
## $ Fare        <dbl> 7.2500, 71.2833, 7.9250, 53.1000, 8.0500, 8.4583, 51.8625,…
## $ Cabin       <chr> "", "C85", "", "C123", "", "", "E46", "", "", "", "G6", "C…
## $ Embarked    <chr> "S", "C", "S", "S", "S", "Q", "S", "S", "S", "C", "S", "S"…

3. Pré-processamento

Selecionamos as variáveis mais relevantes e removemos valores ausentes.

dados_limpo <- dados %>%
  select(Survived, Pclass, Sex, Age, SibSp, Parch, Fare) %>%
  mutate(
    Survived = factor(Survived, levels = c(0, 1),
                      labels = c("Nao", "Sim")),
    Sex      = factor(Sex),
    Pclass   = factor(Pclass)
  ) %>%
  drop_na()

cat("Linhas após limpeza:", nrow(dados_limpo), "\n")
## Linhas após limpeza: 714
cat("\nDistribuição da variável alvo:\n")
## 
## Distribuição da variável alvo:
print(prop.table(table(dados_limpo$Survived)))
## 
##       Nao       Sim 
## 0.5938375 0.4061625

4. Divisão Treino e Teste

set.seed(42)
indice_treino <- createDataPartition(dados_limpo$Survived,
                                     p = 0.8, list = FALSE)
treino <- dados_limpo[ indice_treino, ]
teste  <- dados_limpo[-indice_treino, ]

cat("Treino:", nrow(treino), "linhas\n")
## Treino: 572 linhas
cat("Teste: ", nrow(teste),  "linhas\n")
## Teste:  142 linhas

5. Treinamento do Modelo

modelo <- glm(Survived ~ .,
              data   = treino,
              family = binomial(link = "logit"))

summary(modelo)
## 
## Call:
## glm(formula = Survived ~ ., family = binomial(link = "logit"), 
##     data = treino)
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  4.0113173  0.5511054   7.279 3.37e-13 ***
## Pclass2     -1.2290410  0.3550867  -3.461 0.000538 ***
## Pclass3     -2.4036981  0.3740668  -6.426 1.31e-10 ***
## Sexmale     -2.4713014  0.2412303 -10.245  < 2e-16 ***
## Age         -0.0431636  0.0089913  -4.801 1.58e-06 ***
## SibSp       -0.3360601  0.1429036  -2.352 0.018690 *  
## Parch       -0.0794852  0.1327182  -0.599 0.549238    
## Fare         0.0009956  0.0029938   0.333 0.739479    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 772.45  on 571  degrees of freedom
## Residual deviance: 532.71  on 564  degrees of freedom
## AIC: 548.71
## 
## Number of Fisher Scoring iterations: 4

Interpretação: Odds Ratio

O Odds Ratio indica o quanto cada variável aumenta ou reduz a chance de sobrevivência.

odds_ratio <- exp(cbind(OR = coef(modelo), confint(modelo)))
round(odds_ratio, 3)
##                 OR  2.5 %  97.5 %
## (Intercept) 55.220 19.213 167.612
## Pclass2      0.293  0.145   0.584
## Pclass3      0.090  0.043   0.187
## Sexmale      0.084  0.052   0.134
## Age          0.958  0.941   0.974
## SibSp        0.715  0.535   0.938
## Parch        0.924  0.705   1.192
## Fare         1.001  0.995   1.007

Como interpretar: - OR > 1 → aumenta a chance de sobreviver - OR < 1 → reduz a chance de sobreviver - OR = 1 → sem efeito


6. Avaliação do Modelo

prob_pred   <- predict(modelo, newdata = teste, type = "response")
classe_pred <- factor(ifelse(prob_pred >= 0.5, "Sim", "Nao"),
                      levels = c("Nao", "Sim"))

mat_conf <- confusionMatrix(classe_pred, teste$Survived,
                             positive = "Sim")
print(mat_conf)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction Nao Sim
##        Nao  76  11
##        Sim   8  47
##                                          
##                Accuracy : 0.8662         
##                  95% CI : (0.799, 0.9175)
##     No Information Rate : 0.5915         
##     P-Value [Acc > NIR] : 8.601e-13      
##                                          
##                   Kappa : 0.7209         
##                                          
##  Mcnemar's Test P-Value : 0.6464         
##                                          
##             Sensitivity : 0.8103         
##             Specificity : 0.9048         
##          Pos Pred Value : 0.8545         
##          Neg Pred Value : 0.8736         
##              Prevalence : 0.4085         
##          Detection Rate : 0.3310         
##    Detection Prevalence : 0.3873         
##       Balanced Accuracy : 0.8576         
##                                          
##        'Positive' Class : Sim            
## 

7. Curva ROC

roc_obj <- roc(teste$Survived, prob_pred, levels = c("Nao", "Sim"))
auc_val <- auc(roc_obj)

plot(roc_obj,
     main      = paste("Curva ROC - AUC =", round(auc_val, 3)),
     col       = "steelblue",
     lwd       = 2,
     print.auc = TRUE)
abline(a = 0, b = 1, lty = 2, col = "gray50")

Gráfico de linha mostrando a Curva ROC do modelo. O eixo X representa a Taxa de Falsos Positivos (1 - Especificidade) de 0 a 1, e o eixo Y representa a Taxa de Verdadeiros Positivos (Sensibilidade) de 0 a 1. A curva do modelo aparece em azul, bem acima da linha diagonal tracejada cinza que representa um classificador aleatório. Quanto mais a curva se aproxima do canto superior esquerdo, melhor o modelo. O valor de AUC é exibido no gráfico, indicando a qualidade geral da classificação.

Descrição do gráfico: A curva ROC mostra a relação entre sensibilidade e especificidade do modelo. A curva azul está bem acima da diagonal cinza tracejada (que representa um modelo aleatório), indicando boa capacidade preditiva. O AUC (Área sob a Curva) próximo de 1 indica excelente desempenho.


8. Visualizações Exploratórias

Gráfico 1 — Sobrevivência por Sexo

dados_limpo %>%
  group_by(Sex, Survived) %>%
  summarise(n = n(), .groups = "drop") %>%
  ggplot(aes(x = Sex, y = n, fill = Survived)) +
  geom_col(position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = c("Nao" = "#e74c3c", "Sim" = "#2ecc71")) +
  labs(title = "Taxa de Sobrevivência por Sexo",
       x = "Sexo", y = "Proporção", fill = "Sobreviveu?") +
  theme_minimal()

Gráfico de barras empilhadas com duas barras verticais lado a lado. A barra da esquerda representa passageiras do sexo feminino e a da direita do sexo masculino. Cada barra está dividida em duas cores: vermelho representando quem não sobreviveu e verde representando quem sobreviveu. A barra feminina mostra uma grande proporção verde no topo, indicando alta taxa de sobrevivência para mulheres. A barra masculina mostra predominância de vermelho, indicando baixa taxa de sobrevivência para homens. O eixo Y vai de 0 a 100 por cento.

Descrição: Mulheres tiveram uma taxa de sobrevivência muito maior que os homens — cerca de 74% das mulheres sobreviveram, contra apenas 19% dos homens. Isso reflete a política histórica de “mulheres e crianças primeiro” adotada durante o naufrágio.


Gráfico 2 — Sobrevivência por Classe

dados_limpo %>%
  group_by(Pclass, Survived) %>%
  summarise(n = n(), .groups = "drop") %>%
  ggplot(aes(x = Pclass, y = n, fill = Survived)) +
  geom_col(position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = c("Nao" = "#e74c3c", "Sim" = "#2ecc71")) +
  labs(title = "Taxa de Sobrevivência por Classe",
       x = "Classe", y = "Proporção", fill = "Sobreviveu?") +
  theme_minimal()

Gráfico de barras empilhadas com três barras verticais representando as classes 1, 2 e 3 do navio. Cada barra está dividida em vermelho (não sobreviveu) e verde (sobreviveu). A barra da classe 1 tem a maior proporção verde, indicando alta sobrevivência. A classe 2 tem proporção intermediária. A classe 3 tem a maior proporção vermelha, indicando baixa taxa de sobrevivência. O eixo Y vai de 0 a 100 por cento.

Descrição: Passageiros da 1ª classe tiveram a maior taxa de sobrevivência (cerca de 63%), seguidos pela 2ª classe (47%) e 3ª classe (24%). Isso mostra que a posição socioeconômica teve forte impacto nas chances de sobrevivência.


Gráfico 3 — Distribuição de Idade

ggplot(dados_limpo, aes(x = Age, fill = Survived)) +
  geom_histogram(bins = 30, alpha = 0.7, position = "identity") +
  scale_fill_manual(values = c("Nao" = "#e74c3c", "Sim" = "#2ecc71")) +
  labs(title = "Distribuição de Idade por Sobrevivência",
       x = "Idade", y = "Contagem", fill = "Sobreviveu?") +
  theme_minimal()

Histograma com barras sobrepostas em duas cores: vermelho para quem não sobreviveu e verde para quem sobreviveu. O eixo X representa a idade dos passageiros de 0 a 80 anos, e o eixo Y representa a contagem de passageiros. As barras verdes são mais frequentes nas idades muito jovens (crianças até 10 anos), indicando prioridade dada às crianças. Nas idades adultas entre 20 e 40 anos, predominam as barras vermelhas, especialmente para homens. A distribuição é mais concentrada entre 20 e 40 anos para ambos os grupos.

Descrição: Crianças pequenas (até 10 anos) tiveram maior proporção de sobrevivência. A faixa etária de 20 a 40 anos concentra o maior número de passageiros, com mais mortes do que sobrevivências. Idosos acima de 60 anos tiveram baixa taxa de sobrevivência.


Gráfico 4 — Probabilidade Predita por Classe e Sexo

teste %>%
  mutate(prob = prob_pred) %>%
  ggplot(aes(x = Pclass, y = prob, fill = Sex)) +
  geom_boxplot(alpha = 0.7) +
  scale_fill_manual(values = c("female" = "#9b59b6", "male" = "#3498db")) +
  labs(title = "Probabilidade Predita de Sobrevivência por Classe e Sexo",
       x = "Classe", y = "Probabilidade", fill = "Sexo") +
  theme_minimal()

Gráfico de caixas (boxplot) com três grupos no eixo X representando as classes 1, 2 e 3. Dentro de cada grupo há duas caixas lado a lado: uma roxa para passageiras do sexo feminino e uma azul para passageiros do sexo masculino. O eixo Y mostra a probabilidade predita de sobrevivência de 0 a 1. As caixas roxas (feminino) estão consistentemente mais altas que as azuis (masculino) em todas as classes. Na classe 1 feminino, a mediana da probabilidade predita é próxima de 0,95. Na classe 3 masculino, a mediana é próxima de 0,10.

Descrição: O modelo prevê probabilidades muito distintas conforme sexo e classe. Mulheres da 1ª classe têm probabilidade próxima de 95% de sobreviver, enquanto homens da 3ª classe têm probabilidade de apenas 10%. Isso confirma que sexo e classe são os preditores mais fortes do modelo.


9. Exercício para os Colegas

Use o modelo treinado para prever a probabilidade de sobrevivência dos passageiros abaixo:

novos <- data.frame(
  Pclass = factor(c(1, 3, 2), levels = c(1, 2, 3)),
  Sex    = factor(c("female", "male", "female")),
  Age    = c(25, 40, 30),
  SibSp  = c(0, 1, 0),
  Parch  = c(0, 0, 1),
  Fare   = c(100, 10, 50)
)

print(novos)
##   Pclass    Sex Age SibSp Parch Fare
## 1      1 female  25     0     0  100
## 2      3   male  40     1     0   10
## 3      2 female  30     0     1   50
prob_novos <- predict(modelo, newdata = novos, type = "response")

for (i in seq_along(prob_novos)) {
  cat(sprintf("Passageiro %d: %.1f%% de chance de sobreviver\n",
              i, prob_novos[i] * 100))
}
## Passageiro 1: 95.4% de chance de sobreviver
## Passageiro 2: 5.1% de chance de sobreviver
## Passageiro 3: 81.1% de chance de sobreviver

Pergunta: Qual passageiro tem maior chance de sobreviver? Por quê? Discuta com base nos coeficientes do modelo.


10. Conclusão

A Regressão Logística mostrou-se um modelo eficiente para prever sobrevivência no Titanic. Os principais achados foram:

  • Sexo foi o preditor mais forte: mulheres tinham muito mais chance de sobreviver
  • Classe também foi determinante: passageiros de 1ª classe sobreviveram mais
  • Idade teve efeito menor, mas crianças tiveram prioridade
  • O modelo alcançou boa acurácia e AUC acima de 0,85

A Regressão Logística é uma excelente escolha para problemas binários por ser simples, interpretável e eficiente.