Fatores de Risco para AVC: Uma Análise Exploratória
Stroke Prediction Dataset — Projeto Final
1 Introdução
1.1 Declaração do Problema
O Acidente Vascular Cerebral (AVC) é uma das principais causas de morte e incapacidade permanente no mundo. De acordo com a Organização Mundial da Saúde (OMS), o AVC é responsável por aproximadamente 11% de todas as mortes globais, sendo a segunda maior causa de óbito em nível mundial. No Brasil, o cenário é igualmente preocupante: o AVC representa a primeira causa de morte e incapacidade no país, gerando um enorme custo humano, social e econômico.
O problema central que este projeto aborda é: quais características clínicas e demográficas estão mais associadas à ocorrência de AVC? Compreender os perfis de risco é fundamental para que profissionais de saúde possam realizar triagens preventivas mais eficazes, identificando pacientes que demandam atenção prioritária antes que o evento ocorra.
A relevância deste problema vai além do âmbito clínico. Gestores de saúde pública precisam de dados para alocar recursos preventivos de maneira mais inteligente; seguradoras de saúde podem ajustar apólices com base em perfis de risco; e a própria população pode tomar decisões mais informadas sobre seu estilo de vida ao entender quais fatores são modificáveis.
1.2 Abordagem Adotada
Para abordar esta questão, utilizamos o Stroke Prediction Dataset, disponível publicamente na plataforma Kaggle. O conjunto de dados contém informações clínicas e demográficas de 5.110 pacientes, incluindo variáveis como idade, presença de hipertensão, doenças cardíacas, nível médio de glicose, índice de massa corporal (IMC) e hábito de fumar, além do diagnóstico de AVC.
A metodologia empregada segue três etapas principais:
- Preparação dos dados: importação, limpeza e transformação do conjunto de dados bruto em um formato adequado para análise;
- Análise exploratória: investigação das distribuições individuais das variáveis e das relações entre os fatores de risco e a ocorrência de AVC;
- Síntese de insights: extração de padrões e conclusões que possam orientar decisões práticas na área da saúde.
1.3 Técnica Proposta
A análise é conduzida inteiramente em R, utilizando o ecossistema
tidyverse para manipulação e visualização dos dados. Serão
empregadas as seguintes técnicas:
- Análise univariada: distribuição de cada variável individualmente, com tabelas de frequência para variáveis categóricas e estatísticas descritivas para variáveis numéricas.
- Análise bivariada: comparação das distribuições das variáveis explicativas entre os grupos com e sem AVC, por meio de gráficos e tabelas cruzadas.
- Criação de variáveis derivadas: construção de faixas etárias, categorias de IMC e níveis de glicose, enriquecendo a análise com novas perspectivas clínicas relevantes.
- Visualizações interativas: uso de pacotes como
plotlypara permitir exploração dinâmica dos dados.
Esta abordagem combinada de estatística descritiva com visualização é suficiente para revelar padrões importantes sem a necessidade de modelos preditivos complexos, tornando os resultados acessíveis a um público mais amplo.
1.4 Utilidade para os Clientes
Os resultados desta análise têm aplicações práticas diretas para diferentes públicos:
- Profissionais de saúde: identificar quais combinações de fatores de risco merecem atenção clínica prioritária, orientando protocolos de rastreamento e prevenção.
- Gestores de saúde pública: compreender o perfil epidemiológico da população em risco, subsidiando políticas de prevenção e campanhas de conscientização.
- Seguradoras de saúde: refinar modelos atuariais ao incorporar variáveis clínicas que demonstrarem forte associação com AVC.
- Pesquisadores e estudantes de saúde: dispor de uma análise exploratória documentada e reproduzível que pode servir de ponto de partida para estudos mais aprofundados, incluindo modelagem preditiva.
2 Pacotes Requeridos
Antes de iniciar a análise, carregamos todos os pacotes necessários.
A instalação pode ser feita com
install.packages("nome_do_pacote") para cada um deles. O
pacote rmdformats é necessário para a renderização deste
documento no formato readthedown; para instalá-lo, utilize
install.packages("rmdformats").
# ===================================================
# CARREGAMENTO DOS PACOTES
# ===================================================
# tidyverse: coleção de pacotes para ciência de dados em R.
# Inclui: dplyr (manipulação), ggplot2 (visualização),
# readr (importação), tidyr (transformação), forcats
# (fatores), stringr (strings) e purrr (programação funcional).
library(tidyverse)
# janitor: limpeza de dados e padronização de nomes de colunas.
# Utilizado principalmente para uniformizar os nomes das variáveis
# (snake_case) e para identificar entradas duplicadas.
library(janitor)
# skimr: geração de resumos estatísticos compactos e informativos,
# especialmente útil para uma visão geral rápida do dataset.
library(skimr)
# knitr: integração entre código R e o documento final,
# permitindo a criação de tabelas formatadas com kable().
library(knitr)
# kableExtra: extensão do kable() para formatação avançada
# de tabelas HTML, com suporte a cores, agrupamentos e estilos.
library(kableExtra)
# naniar: análise e visualização de dados faltantes (NA).
# Permite entender a estrutura dos valores ausentes no dataset.
library(naniar)3 Preparação dos Dados
3.1 Fonte dos Dados
O conjunto de dados utilizado neste projeto é o Stroke Prediction Dataset, disponível publicamente na plataforma Kaggle no seguinte endereço:
🔗 https://www.kaggle.com/datasets/jawairia123/stroke-prediction-dataset
O arquivo pode ser baixado diretamente da página acima e deve ser
salvo no diretório de trabalho do R antes de executar este documento. O
nome do arquivo é healthcare-dataset-stroke-data.csv.
# ===================================================
# IMPORTAÇÃO DOS DADOS BRUTOS
# ===================================================
# Leitura do CSV com read_csv() do pacote readr (tidyverse).
# Utilizamos read_csv() em vez de read.csv() pois ele infere
# os tipos de dados de forma mais inteligente e retorna um tibble.
#
# ATENÇÃO: os valores ausentes da coluna 'bmi' estão armazenados
# no CSV como a string literal "N/A" (e não como célula vazia).
# Sem o argumento 'na', o readr não reconheceria "N/A" como ausente
# e leria a coluna inteira como caractere, causando erros nas etapas
# seguintes. O argumento 'na' amplia a lista de símbolos reconhecidos
# como valores ausentes para incluir "N/A".
stroke_bruto <- read_csv(
"healthcare-dataset-stroke-data.csv",
na = c("", "NA", "N/A") # reconhece "N/A" como valor ausente
)
# Visualizando as primeiras linhas para uma inspeção inicial
glimpse(stroke_bruto)Rows: 5,110
Columns: 12
$ id <dbl> 9046, 51676, 31112, 60182, 1665, 56669, 53882, 10434…
$ gender <chr> "Male", "Female", "Male", "Female", "Female", "Male"…
$ age <dbl> 67, 61, 80, 49, 79, 81, 74, 69, 59, 78, 81, 61, 54, …
$ hypertension <dbl> 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1…
$ heart_disease <dbl> 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0…
$ ever_married <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "No…
$ work_type <chr> "Private", "Self-employed", "Private", "Private", "S…
$ Residence_type <chr> "Urban", "Rural", "Rural", "Urban", "Rural", "Urban"…
$ avg_glucose_level <dbl> 228.69, 202.21, 105.92, 171.23, 174.12, 186.21, 70.0…
$ bmi <dbl> 36.6, NA, 32.5, 34.4, 24.0, 29.0, 27.4, 22.8, NA, 24…
$ smoking_status <chr> "formerly smoked", "never smoked", "never smoked", "…
$ stroke <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
3.2 Descrição dos Dados Originais
O dataset foi publicado originalmente com o objetivo de treinar modelos de aprendizado de máquina para predição de AVC, sendo amplamente utilizado em benchmarks de classificação binária na comunidade de ciência de dados. Os dados foram coletados de registros médicos de múltiplos pacientes e disponibilizados de forma anonimizada.
O conjunto de dados original possui 5110 observações e 12 variáveis, descritas na tabela a seguir:
# ===================================================
# TABELA DESCRITIVA DAS VARIÁVEIS ORIGINAIS
# ===================================================
# Construímos manualmente a tabela de metadados para que
# a descrição seja informativa e contextualizada.
tibble(
Variável = names(stroke_bruto),
Tipo = map_chr(stroke_bruto, ~ class(.x)[1]),
Descrição = c(
"Identificador único do paciente",
"Sexo biológico: 'Male', 'Female' ou 'Other'",
"Idade em anos (contínua)",
"Presença de hipertensão: 0 = Não, 1 = Sim",
"Presença de doença cardíaca: 0 = Não, 1 = Sim",
"Estado civil: 'Yes' ou 'No'",
"Tipo de ocupação: Private, Self-employed, Govt_job, children, Never_worked",
"Tipo de residência: 'Urban' ou 'Rural'",
"Nível médio de glicose no sangue (mg/dL)",
"Índice de Massa Corporal (IMC)",
"Hábito de fumar: 'formerly smoked', 'never smoked', 'smokes', 'Unknown'",
"Diagnóstico de AVC: 0 = Não, 1 = Sim (variável-alvo)"
),
`Valores Ausentes` = map_chr(stroke_bruto, ~ as.character(sum(is.na(.x))))
) |>
kable(caption = "Descrição das variáveis do dataset original") |>
kable_styling(
bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE,
font_size = 13
) |>
row_spec(12, bold = TRUE, background = "#FFF3CD") # destaque na variável-alvo| Variável | Tipo | Descrição | Valores Ausentes |
|---|---|---|---|
| id | numeric | Identificador único do paciente | 0 |
| gender | character | Sexo biológico: ‘Male’, ‘Female’ ou ‘Other’ | 0 |
| age | numeric | Idade em anos (contínua) | 0 |
| hypertension | numeric | Presença de hipertensão: 0 = Não, 1 = Sim | 0 |
| heart_disease | numeric | Presença de doença cardíaca: 0 = Não, 1 = Sim | 0 |
| ever_married | character | Estado civil: ‘Yes’ ou ‘No’ | 0 |
| work_type | character | Tipo de ocupação: Private, Self-employed, Govt_job, children, Never_worked | 0 |
| Residence_type | character | Tipo de residência: ‘Urban’ ou ‘Rural’ | 0 |
| avg_glucose_level | numeric | Nível médio de glicose no sangue (mg/dL) | 0 |
| bmi | numeric | Índice de Massa Corporal (IMC) | 201 |
| smoking_status | character | Hábito de fumar: ‘formerly smoked’, ‘never smoked’, ‘smokes’, ‘Unknown’ | 0 |
| stroke | numeric | Diagnóstico de AVC: 0 = Não, 1 = Sim (variável-alvo) | 0 |
Algumas peculiaridades importantes identificadas nos dados originais:
- Representação dos valores ausentes: os valores
faltantes da variável
bmiestão armazenados no CSV como a string literal"N/A"— e não como célula em branco (padrão mais comum). Isso exigiu configuração explícita durante a importação, pois oread_csv()não reconhece"N/A"como ausente por padrão, lendo a coluna inteira como texto caso esse detalhe seja ignorado. - Proporção de ausentes em
bmi: as observações sem IMC representam 3.9% do total, sugerindo que o dado não foi coletado para parte dos pacientes — o que será tratado por imputação. - Categoria “Unknown” em
smoking_status: não se trata de umNAtécnico, mas de uma categoria explícita que indica que o histórico tabágico do paciente não foi informado. Esta distinção é importante na etapa de limpeza. - Inconsistência no nome da coluna:
Residence_typecomeça com letra maiúscula, ao contrário de todas as demais variáveis — uma irregularidade que será corrigida. - Categoria rara em
gender: existe apenas 1 observação comgender == "Other", o que a torna estatisticamente inviável para análises comparativas. work_type == "children": esta categoria está escrita em letras minúsculas, diferentemente das demais, o que indica uma inconsistência de digitação.- Variável-alvo desbalanceada: apenas 249 pacientes (4.9%) sofreram AVC, refletindo a prevalência real na população geral mas exigindo atenção nas análises.
3.3 Importação e Limpeza dos Dados
Nesta seção realizamos todas as transformações necessárias para converter o dataset bruto em um conjunto de dados limpo e pronto para análise. Cada etapa é explicada antes do código correspondente.
3.3.1 Etapa 1 — Padronização dos nomes das colunas
O pacote janitor oferece a função
clean_names(), que converte todos os nomes de colunas para
o padrão snake_case (minúsculas, palavras separadas por
_). Isso resolve a inconsistência de
Residence_type e garante uniformidade.
# Padronizar nomes das colunas para snake_case
stroke <- stroke_bruto |>
clean_names()
# Verificar se os nomes foram corrigidos
names(stroke) [1] "id" "gender" "age"
[4] "hypertension" "heart_disease" "ever_married"
[7] "work_type" "residence_type" "avg_glucose_level"
[10] "bmi" "smoking_status" "stroke"
3.3.2 Etapa 2 — Remoção da coluna identificadora
A variável id é apenas um identificador único de cada
paciente, sem qualquer informação analítica. Mantê-la poderia gerar
confusão em análises de correlação ou resumos, portanto é removida.
3.3.3 Etapa 3 — Remoção da categoria de gênero “Other”
Como identificamos na etapa anterior, há apenas 1 observação com
gender == "Other". Uma única observação não permite nenhuma
inferência comparativa e pode distorcer análises agregadas por gênero.
Portanto, essa linha é removida.
# Remover a observação com gender == "Other"
stroke <- stroke |>
filter(gender != "Other")
# Confirmar remoção
stroke |> count(gender, name = "n_depois")3.3.4 Etapa 4 — Correção
da capitalização em work_type
A categoria "children" está grafada em letras
minúsculas, diferente das demais ("Private",
"Self-employed", etc.). Corrigimos isso para manter
consistência.
# Corrigir: "children" -> "Children"
stroke <- stroke |>
mutate(work_type = if_else(work_type == "children", "Children", work_type))
# Confirmar correção
stroke |> count(work_type)3.3.5 Etapa 5 — Conversão de variáveis para fatores
As variáveis binárias (0/1) e as categóricas de texto devem ser
convertidas para o tipo factor em R. Isso é fundamental
para que funções de análise e visualização as tratem corretamente.
Aproveitamos a conversão para aplicar rótulos descritivos em português
nas variáveis binárias.
stroke <- stroke |>
mutate(
# Variáveis binárias: recodificar 0/1 com rótulos descritivos
hypertension = factor(hypertension,
levels = c(0, 1),
labels = c("Não", "Sim")),
heart_disease = factor(heart_disease,
levels = c(0, 1),
labels = c("Não", "Sim")),
stroke = factor(stroke,
levels = c(0, 1),
labels = c("Não", "Sim")),
# Variáveis de texto: converter para fator
gender = factor(gender),
ever_married = factor(ever_married,
levels = c("No", "Yes"),
labels = c("Não", "Sim")),
work_type = factor(work_type),
residence_type = factor(residence_type),
# smoking_status: "Unknown" é mantido como nível de fator
# (não é convertido para NA porque representa uma categoria
# real — pacientes que não informaram o hábito tabágico).
smoking_status = factor(smoking_status,
levels = c("never smoked", "formerly smoked",
"smokes", "Unknown"))
)
# Verificar os tipos após conversão
glimpse(stroke)Rows: 5,109
Columns: 11
$ gender <fct> Male, Female, Male, Female, Female, Male, Male, Fema…
$ age <dbl> 67, 61, 80, 49, 79, 81, 74, 69, 59, 78, 81, 61, 54, …
$ hypertension <fct> Não, Não, Não, Não, Sim, Não, Sim, Não, Não, Não, Si…
$ heart_disease <fct> Sim, Não, Sim, Não, Não, Não, Sim, Não, Não, Não, Nã…
$ ever_married <fct> Sim, Sim, Sim, Sim, Sim, Sim, Sim, Não, Sim, Sim, Si…
$ work_type <fct> Private, Self-employed, Private, Private, Self-emplo…
$ residence_type <fct> Urban, Rural, Rural, Urban, Rural, Urban, Rural, Urb…
$ avg_glucose_level <dbl> 228.69, 202.21, 105.92, 171.23, 174.12, 186.21, 70.0…
$ bmi <dbl> 36.6, NA, 32.5, 34.4, 24.0, 29.0, 27.4, 22.8, NA, 24…
$ smoking_status <fct> formerly smoked, never smoked, never smoked, smokes,…
$ stroke <fct> Sim, Sim, Sim, Sim, Sim, Sim, Sim, Sim, Sim, Sim, Si…
3.3.6 Etapa 6 — Tratamento dos valores ausentes no IMC
Como constatado anteriormente, 201 observações não possuem valor de IMC. Antes de decidir o método de imputação, visualizamos o padrão de ausência:
# Visualizar o padrão de valores faltantes
vis_miss(stroke) +
labs(title = "Mapa de valores ausentes",
subtitle = "Apenas a variável 'bmi' possui valores faltantes") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Padrão de valores ausentes no dataset
Os dados faltantes em bmi estão distribuídos de forma
relativamente aleatória (faltam 3.9% dos valores). Para a imputação,
optamos por substituir cada valor ausente pela mediana do IMC do
grupo correspondente de faixa etária e gênero. Esta estratégia
é preferível à mediana global porque preserva as variações naturais de
IMC que existem entre crianças, adultos jovens e idosos, além de
respeitar as diferenças fisiológicas entre homens e mulheres.
# Passo 6a: criar faixa etária temporária para guiar a imputação
stroke <- stroke |>
mutate(
faixa_etaria_temp = cut(
age,
breaks = c(-Inf, 12, 17, 29, 44, 59, Inf),
labels = c("Criança", "Adolescente", "Jovem Adulto",
"Adulto", "Meia-Idade", "Idoso"),
right = TRUE
)
)
# Passo 6b: imputar bmi pela mediana do grupo (faixa etária + gênero)
stroke <- stroke |>
group_by(faixa_etaria_temp, gender) |>
mutate(
bmi = if_else(is.na(bmi),
median(bmi, na.rm = TRUE),
bmi)
) |>
ungroup()
# Confirmar que não há mais NAs em bmi
cat("Valores ausentes em bmi após imputação:", sum(is.na(stroke$bmi)), "\n")Valores ausentes em bmi após imputação: 0
3.3.7 Etapa 7 — Criação de variáveis derivadas
Com os dados limpos, criamos três novas variáveis que enriquecem a análise com perspectivas clínicas relevantes.
7a. Faixa Etária — segmentação da idade em categorias clínicas, substituindo a variável temporária utilizada na imputação.
7b. Categoria de IMC — classificação do IMC segundo os critérios da OMS, permitindo comparações padronizadas entre grupos.
7c. Nível de Glicose — categorização clínica do nível médio de glicose com base nos limiares utilizados para diagnóstico de pré-diabetes e diabetes (segundo a American Diabetes Association).
stroke <- stroke |>
# Remover a coluna temporária de faixa etária
select(-faixa_etaria_temp) |>
mutate(
# 7a: Faixa etária (variável definitiva)
faixa_etaria = cut(
age,
breaks = c(-Inf, 12, 17, 29, 44, 59, Inf),
labels = c("Criança (≤12)",
"Adolescente (13–17)",
"Jovem Adulto (18–29)",
"Adulto (30–44)",
"Meia-Idade (45–59)",
"Idoso (≥60)"),
right = TRUE
),
# 7b: Categoria de IMC (classificação OMS)
categoria_imc = case_when(
bmi < 18.5 ~ "Abaixo do Peso",
bmi >= 18.5 & bmi < 25 ~ "Peso Normal",
bmi >= 25 & bmi < 30 ~ "Sobrepeso",
bmi >= 30 ~ "Obesidade"
),
categoria_imc = factor(
categoria_imc,
levels = c("Abaixo do Peso", "Peso Normal", "Sobrepeso", "Obesidade")
),
# 7c: Nível de glicose (limiares ADA: <100 normal, 100-125 pré-diabetes,
# >=126 diabetes)
nivel_glicose = case_when(
avg_glucose_level < 100 ~ "Normal (<100 mg/dL)",
avg_glucose_level >= 100 & avg_glucose_level < 126 ~ "Pré-Diabetes (100–125)",
avg_glucose_level >= 126 ~ "Diabetes (≥126 mg/dL)"
),
nivel_glicose = factor(
nivel_glicose,
levels = c("Normal (<100 mg/dL)", "Pré-Diabetes (100–125)",
"Diabetes (≥126 mg/dL)")
)
)
# Verificar a criação das novas variáveis
stroke |>
select(age, faixa_etaria, bmi, categoria_imc,
avg_glucose_level, nivel_glicose) |>
slice_sample(n = 10) |>
kable(caption = "Amostra das variáveis derivadas criadas") |>
kable_styling(bootstrap_options = c("striped", "hover"),
full_width = FALSE, font_size = 12)| age | faixa_etaria | bmi | categoria_imc | avg_glucose_level | nivel_glicose |
|---|---|---|---|---|---|
| 1 | Criança (≤12) | 15.1 | Abaixo do Peso | 123.21 | Pré-Diabetes (100–125) |
| 56 | Meia-Idade (45–59) | 24.8 | Peso Normal | 112.62 | Pré-Diabetes (100–125) |
| 40 | Adulto (30–44) | 24.1 | Peso Normal | 106.76 | Pré-Diabetes (100–125) |
| 30 | Adulto (30–44) | 29.7 | Sobrepeso | 118.62 | Pré-Diabetes (100–125) |
| 60 | Idoso (≥60) | 28.5 | Sobrepeso | 100.20 | Pré-Diabetes (100–125) |
| 20 | Jovem Adulto (18–29) | 45.9 | Obesidade | 100.80 | Pré-Diabetes (100–125) |
| 57 | Meia-Idade (45–59) | 31.0 | Obesidade | 62.20 | Normal (<100 mg/dL) |
| 53 | Meia-Idade (45–59) | 55.2 | Obesidade | 87.03 | Normal (<100 mg/dL) |
| 48 | Meia-Idade (45–59) | 38.7 | Obesidade | 216.70 | Diabetes (≥126 mg/dL) |
| 60 | Idoso (≥60) | 37.3 | Obesidade | 153.48 | Diabetes (≥126 mg/dL) |
3.4 Conjunto de Dados Final
Após todas as etapas de limpeza e transformação, o dataset final
possui 5109 observações e 14 variáveis
(as 11 originais — após remoção de id — mais as 3 variáveis
derivadas criadas).
Rows: 5,109
Columns: 14
$ gender <fct> Male, Female, Male, Female, Female, Male, Male, Fema…
$ age <dbl> 67, 61, 80, 49, 79, 81, 74, 69, 59, 78, 81, 61, 54, …
$ hypertension <fct> Não, Não, Não, Não, Sim, Não, Sim, Não, Não, Não, Si…
$ heart_disease <fct> Sim, Não, Sim, Não, Não, Não, Sim, Não, Não, Não, Nã…
$ ever_married <fct> Sim, Sim, Sim, Sim, Sim, Sim, Sim, Não, Sim, Sim, Si…
$ work_type <fct> Private, Self-employed, Private, Private, Self-emplo…
$ residence_type <fct> Urban, Rural, Rural, Urban, Rural, Urban, Rural, Urb…
$ avg_glucose_level <dbl> 228.69, 202.21, 105.92, 171.23, 174.12, 186.21, 70.0…
$ bmi <dbl> 36.60, 29.20, 32.50, 34.40, 24.00, 29.00, 27.40, 22.…
$ smoking_status <fct> formerly smoked, never smoked, never smoked, smokes,…
$ stroke <fct> Sim, Sim, Sim, Sim, Sim, Sim, Sim, Sim, Sim, Sim, Si…
$ faixa_etaria <fct> Idoso (≥60), Idoso (≥60), Idoso (≥60), Meia-Idade (4…
$ categoria_imc <fct> Obesidade, Sobrepeso, Obesidade, Obesidade, Peso Nor…
$ nivel_glicose <fct> Diabetes (≥126 mg/dL), Diabetes (≥126 mg/dL), Pré-Di…
A seguir, exibimos as 10 primeiras linhas do dataset final de forma condensada:
# Exibir amostra do dataset final (as 10 primeiras linhas)
stroke |>
head(10) |>
kable(caption = "Primeiras 10 linhas do dataset limpo") |>
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = TRUE, font_size = 11) |>
scroll_box(width = "100%")| gender | age | hypertension | heart_disease | ever_married | work_type | residence_type | avg_glucose_level | bmi | smoking_status | stroke | faixa_etaria | categoria_imc | nivel_glicose |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Male | 67 | Não | Sim | Sim | Private | Urban | 228.69 | 36.60 | formerly smoked | Sim | Idoso (≥60) | Obesidade | Diabetes (≥126 mg/dL) |
| Female | 61 | Não | Não | Sim | Self-employed | Rural | 202.21 | 29.20 | never smoked | Sim | Idoso (≥60) | Sobrepeso | Diabetes (≥126 mg/dL) |
| Male | 80 | Não | Sim | Sim | Private | Rural | 105.92 | 32.50 | never smoked | Sim | Idoso (≥60) | Obesidade | Pré-Diabetes (100–125) |
| Female | 49 | Não | Não | Sim | Private | Urban | 171.23 | 34.40 | smokes | Sim | Meia-Idade (45–59) | Obesidade | Diabetes (≥126 mg/dL) |
| Female | 79 | Sim | Não | Sim | Self-employed | Rural | 174.12 | 24.00 | never smoked | Sim | Idoso (≥60) | Peso Normal | Diabetes (≥126 mg/dL) |
| Male | 81 | Não | Não | Sim | Private | Urban | 186.21 | 29.00 | formerly smoked | Sim | Idoso (≥60) | Sobrepeso | Diabetes (≥126 mg/dL) |
| Male | 74 | Sim | Sim | Sim | Private | Rural | 70.09 | 27.40 | never smoked | Sim | Idoso (≥60) | Sobrepeso | Normal (<100 mg/dL) |
| Female | 69 | Não | Não | Não | Private | Urban | 94.39 | 22.80 | never smoked | Sim | Idoso (≥60) | Peso Normal | Normal (<100 mg/dL) |
| Female | 59 | Não | Não | Sim | Private | Rural | 76.15 | 29.75 | Unknown | Sim | Meia-Idade (45–59) | Sobrepeso | Normal (<100 mg/dL) |
| Female | 78 | Não | Não | Sim | Private | Urban | 58.57 | 24.20 | Unknown | Sim | Idoso (≥60) | Peso Normal | Normal (<100 mg/dL) |
3.5 Resumo das Variáveis de Interesse
A tabela a seguir consolida as informações descritivas de todas as variáveis do dataset limpo. Para variáveis numéricas, apresentamos a média, mediana, desvio padrão e amplitude. Para variáveis categóricas, apresentamos o número de categorias e a categoria mais frequente.
# ===================================================
# TABELA RESUMO CONSOLIDADA DAS VARIÁVEIS
# ===================================================
# --- Variáveis numéricas ---
resumo_num <- stroke |>
select(age, avg_glucose_level, bmi) |>
pivot_longer(everything(), names_to = "Variável", values_to = "valor") |>
group_by(Variável) |>
summarise(
Tipo = "Numérica",
# first() extrai o valor escalar do grupo — necessário porque dentro
# de summarise() a coluna agrupadora ainda é um vetor (todos iguais),
# e case_when() sobre um vetor devolveria N linhas em vez de 1.
Descrição = case_when(
first(Variável) == "age" ~ "Idade (anos)",
first(Variável) == "avg_glucose_level" ~ "Glicose média (mg/dL)",
first(Variável) == "bmi" ~ "IMC (kg/m²)"
),
Resumo = sprintf(
"Média: %.1f | Mediana: %.1f | DP: %.1f | Min–Max: %.1f–%.1f",
mean(valor), median(valor), sd(valor), min(valor), max(valor)
),
`N Ausentes` = "0",
.groups = "drop"
)
# --- Variáveis categóricas ---
resumo_cat <- stroke |>
select(gender, hypertension, heart_disease, ever_married,
work_type, residence_type, smoking_status, stroke,
faixa_etaria, categoria_imc, nivel_glicose) |>
pivot_longer(everything(), names_to = "Variável", values_to = "valor") |>
group_by(Variável) |>
summarise(
Tipo = "Categórica",
Descrição = case_when(
first(Variável) == "gender" ~ "Sexo biológico",
first(Variável) == "hypertension" ~ "Hipertensão (Sim/Não)",
first(Variável) == "heart_disease" ~ "Doença cardíaca (Sim/Não)",
first(Variável) == "ever_married" ~ "Já foi casado(a) (Sim/Não)",
first(Variável) == "work_type" ~ "Tipo de ocupação",
first(Variável) == "residence_type" ~ "Tipo de residência",
first(Variável) == "smoking_status" ~ "Hábito tabágico",
first(Variável) == "stroke" ~ "AVC (Sim/Não) — variável-alvo",
first(Variável) == "faixa_etaria" ~ "Faixa etária (derivada)",
first(Variável) == "categoria_imc" ~ "Categoria de IMC — OMS (derivada)",
first(Variável) == "nivel_glicose" ~ "Nível de glicose clínico (derivada)"
),
Resumo = sprintf(
"%d categorias | Mais frequente: '%s' (%s)",
n_distinct(valor),
names(sort(table(valor), decreasing = TRUE))[1],
scales::percent(
max(table(valor)) / sum(!is.na(valor)),
accuracy = 0.1
)
),
`N Ausentes` = as.character(sum(is.na(valor))),
.groups = "drop"
)
# Combinar e exibir
bind_rows(resumo_num, resumo_cat) |>
arrange(Tipo, Variável) |>
kable(caption = "Resumo consolidado das variáveis do dataset limpo") |>
kable_styling(
bootstrap_options = c("striped", "hover", "condensed"),
full_width = TRUE,
font_size = 12
) |>
row_spec(which(bind_rows(resumo_num, resumo_cat) |>
arrange(Tipo, Variável) |>
pull(Variável) == "stroke"),
bold = TRUE, background = "#FFF3CD") |>
pack_rows("Variáveis Categóricas", 1, 11) |>
pack_rows("Variáveis Numéricas", 12, 14)| Variável | Tipo | Descrição | Resumo | N Ausentes |
|---|---|---|---|---|
| Variáveis Categóricas | ||||
| categoria_imc | Categórica | Categoria de IMC — OMS (derivada) | 4 categorias | Mais frequente: ‘Obesidade’ (38.5%) | 0 |
| ever_married | Categórica | Já foi casado(a) (Sim/Não) | 2 categorias | Mais frequente: ‘Sim’ (65.6%) | 0 |
| faixa_etaria | Categórica | Faixa etária (derivada) | 6 categorias | Mais frequente: ‘Idoso (≥60)’ (26.9%) | 0 |
| gender | Categórica | Sexo biológico | 2 categorias | Mais frequente: ‘Female’ (58.6%) | 0 |
| heart_disease | Categórica | Doença cardíaca (Sim/Não) | 2 categorias | Mais frequente: ‘Não’ (94.6%) | 0 |
| hypertension | Categórica | Hipertensão (Sim/Não) | 2 categorias | Mais frequente: ‘Não’ (90.3%) | 0 |
| nivel_glicose | Categórica | Nível de glicose clínico (derivada) | 3 categorias | Mais frequente: ‘Normal (<100 mg/dL)’ (61.3%) | 0 |
| residence_type | Categórica | Tipo de residência | 2 categorias | Mais frequente: ‘Urban’ (50.8%) | 0 |
| smoking_status | Categórica | Hábito tabágico | 4 categorias | Mais frequente: ‘never smoked’ (37.0%) | 0 |
| stroke | Categórica | AVC (Sim/Não) — variável-alvo | 2 categorias | Mais frequente: ‘Não’ (95.1%) | 0 |
| work_type | Categórica | Tipo de ocupação | 5 categorias | Mais frequente: ‘Private’ (57.2%) | 0 |
| Variáveis Numéricas | ||||
| age | Numérica | Idade (anos) | Média: 43.2 | Mediana: 45.0 | DP: 22.6 | Min–Max: 0.1–82.0 | 0 |
| avg_glucose_level | Numérica | Glicose média (mg/dL) | Média: 106.1 | Mediana: 91.9 | DP: 45.3 | Min–Max: 55.1–271.7 | 0 |
| bmi | Numérica | IMC (kg/m²) | Média: 28.9 | Mediana: 28.3 | DP: 7.7 | Min–Max: 10.3–97.6 | 0 |
Destaques do resumo:
- O dataset limpo está completamente livre de valores ausentes após a imputação do IMC.
- A variável-alvo
strokeé altamente desbalanceada: apenas 4.9% dos pacientes sofreram AVC. Isso é importante e será considerado na etapa de análise exploratória. - A idade varia de 0.08 a 82 anos, incluindo crianças — a faixa etária criada auxilia na segmentação dessas análises.
- Cerca de 30.2% dos pacientes possuem hábito tabágico desconhecido (“Unknown”), o que deve ser levado em conta ao interpretar qualquer análise envolvendo tabagismo. ```