Código
setwd("\\Users\\paulo\\OneDrive\\Área de Trabalho\\ESTATÍSTICA\\UFPB\\8º PERÍODO\\ANÁLISE MULTIVARIADA II\\PROVA")
banco <- read.csv2("heart.csv", header = T, sep = ",")Aplicar a aprendizagem supervisionada para um problema de classificação, e nesse exemplo incorporando os modelos baseados em árvores, bem como o SVM (Suport Vector Machines)
Utilizando tidymodels para treinar os modelos e junto com isso utilizando workflows.
Com a adição de uma rede neural perceptron com multi camadas.
O pacote tidymodels é um meta-pacote que consiste de algumas bibliotecas, tais como:
rsample: funções para particionamento e reamostragem eficiente de dados;
parsnip: interface unificada para um amplo conjunto de modelos que podem ser testados sem que o usuário se preocupe com diferenças de sintaxe;
recipes: pré-processamento e feature engineering;
workflows: junta pré-processamento, modelagem (treinamento) e pós-processamento; tune: otimização de hiperparâmetros;
yardstick: funções para avaliar a efetividade de modelos através de medidas de performance; broom: converte a informação contida em objetos comuns de R para o formato tidy;
dials: cria e gerencia hiperparâmetros de ajuste e grids de hiperparâmetros.
Outras bibliotecas serão também utilizadas no processo, como a finetune, que permite um processo de otimização de hiperparâmetros mais eficiente.
O conjunto de dados pode ser encontrado em: conjunto de dados
Mais sobre o banco de dados
As doenças cardiovasculares (DCVs) são a causa número 1 de morte no mundo, levando cerca de 17,9 milhões de vidas a cada ano, o que representa 31% de todas as mortes em todo o mundo. Quatro em cada 5 mortes por DCV são devidas a ataques cardíacos e derrames, e um terço dessas mortes ocorre prematuramente em pessoas com menos de 70 anos de idade. A insuficiência cardíaca é um evento comum causado por DCVs e este conjunto de dados contém 11 recursos que podem ser usados para prever uma possível doença cardíaca.
Pessoas com doenças cardiovasculares ou com alto risco cardiovascular (devido à presença de um ou mais fatores de risco, como hipertensão, diabetes, hiperlipidemia ou doença já estabelecida) precisam de detecção e gerenciamento precoces, em que um modelo de aprendizado de máquina pode ser de grande ajuda.
Sobre as variáveis dependentes:
Idade: idade do paciente em anos
Sexo: sexo do paciente [M: Masculino, F: Feminino]
ChestPainType: tipo de dor no peito [TA: Angina Típica, ATA: Angina Atípica, NAP: Dor Não Anginosa, ASY: Assintomática]
RestingBP: pressão arterial em repouso [mm Hg]
Colesterol: colesterol sérico [mm/dl]
JejumBS: açúcar no sangue em jejum [1: se JejumBS > 120 mg/dl, 0: caso contrário]
ECG em repouso: resultados do eletrocardiograma em repouso [Normal: Normal, ST: com anormalidade da onda ST-T (inversões da onda T e/ou elevação ou depressão do ST > 0,05 mV), HVE: mostrando hipertrofia ventricular esquerda provável ou definitiva pelos critérios de Estes]
MaxHR: frequência cardíaca máxima alcançada [Valor numérico entre 60 e 202]
ExerciseAngina: angina induzida por exercício [S: Sim, N: Não]
Oldpeak: oldpeak = ST [Valor numérico medido na depressão]
ST_Slope: a inclinação do segmento ST do exercício de pico [Up: ascendente, Flat: plano, Down: descendente]
Sobre a variável resposta:
HeartDisease: classe de saída [1: doença cardíaca, 0: normal]
setwd("\\Users\\paulo\\OneDrive\\Área de Trabalho\\ESTATÍSTICA\\UFPB\\8º PERÍODO\\ANÁLISE MULTIVARIADA II\\PROVA")
banco <- read.csv2("heart.csv", header = T, sep = ",")library(tidyverse) # Framework do tidyverse
library(tidymodels) # Framework de modelagem
library(skimr) # Estatística descritiva rápida
library(DataExplorer) # Exploração do conjunto de dados
library(corrplot) # Gráfico de correlação
library(GGally) # Gráficos adicionais com estrutura ggplot2
library(stringr) # Para lidar com strings
library(glmnet) # LASSO, Ridge e Rede Elástica
library(MASS) # Discriminante Linear (LDA) e Quadrático (RL)
library(recipes) # Pré-processamento dos dados
library(class) #knn
library(themis) # Balanceamento de dados
library(discrim) # lda, qda
library(kknn) # (Kernel) K-NN
library(finetune) # Otimização fina de hiperparâmetros
library(gt) # Para tabelas de maneira melhor visualmente
library(dplyr) # Para tratamento dos dados
library(plotly) # Para gráficos de melhor qualidade
library(stringr) # Para tratamentos com strings de maneira melhor
library(rpart) # Biblioteca para a engine de método de árvore de decisão
library(parsnip)
library(ranger)
library(baguette)
library(kernlab)
library(xgboost) # Bibilioteca para o xgboost
library(qgraph) # Biblioteca para um novo modo de visualizar a matriz de correlação
library(lubridate)
library(nnet) # Engine para a rede neural - perceptron com multi camadas
#library(keras) # Engine para a rede neural - perceptron com multi camadas
library(brulee)banco %>%
head(n = 10) Age Sex ChestPainType RestingBP Cholesterol FastingBS RestingECG MaxHR
1 40 M ATA 140 289 0 Normal 172
2 49 F NAP 160 180 0 Normal 156
3 37 M ATA 130 283 0 ST 98
4 48 F ASY 138 214 0 Normal 108
5 54 M NAP 150 195 0 Normal 122
6 39 M NAP 120 339 0 Normal 170
7 45 F ATA 130 237 0 Normal 170
8 54 M ATA 110 208 0 Normal 142
9 37 M ASY 140 207 0 Normal 130
10 48 F ATA 120 284 0 Normal 120
ExerciseAngina Oldpeak ST_Slope HeartDisease
1 N 0 Up 0
2 N 1 Flat 1
3 N 0 Up 0
4 Y 1.5 Flat 1
5 N 0 Up 0
6 N 0 Up 0
7 N 0 Up 0
8 N 0 Up 0
9 Y 1.5 Flat 1
10 N 0 Up 0
glimpse(banco)Rows: 918
Columns: 12
$ Age <int> 40, 49, 37, 48, 54, 39, 45, 54, 37, 48, 37, 58, 39, 49,…
$ Sex <chr> "M", "F", "M", "F", "M", "M", "F", "M", "M", "F", "F", …
$ ChestPainType <chr> "ATA", "NAP", "ATA", "ASY", "NAP", "NAP", "ATA", "ATA",…
$ RestingBP <int> 140, 160, 130, 138, 150, 120, 130, 110, 140, 120, 130, …
$ Cholesterol <int> 289, 180, 283, 214, 195, 339, 237, 208, 207, 284, 211, …
$ FastingBS <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ RestingECG <chr> "Normal", "Normal", "ST", "Normal", "Normal", "Normal",…
$ MaxHR <int> 172, 156, 98, 108, 122, 170, 170, 142, 130, 120, 142, 9…
$ ExerciseAngina <chr> "N", "N", "N", "Y", "N", "N", "N", "N", "Y", "N", "N", …
$ Oldpeak <chr> "0", "1", "0", "1.5", "0", "0", "0", "0", "1.5", "0", "…
$ ST_Slope <chr> "Up", "Flat", "Up", "Flat", "Up", "Up", "Up", "Up", "Fl…
$ HeartDisease <int> 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1…
banco$Sex <- factor(banco$Sex, levels = c("M","F"), labels = c("Masculino", "Feminino"))
banco$ChestPainType <- factor(banco$ChestPainType, levels = c("TA", "ATA", "NAP", "ASY"), labels = c("Angina Típica", "Angina Atípica", "Dor Não Anginosa", "Assintomática"))
banco$FastingBS <- factor(banco$FastingBS, levels = c(0,1),labels = c("C.C", "JejumBS > 120 mg/dl"))
banco$RestingECG <- factor(banco$RestingECG, levels = c("Normal", "ST", "LVH"), labels = c("Normal", "anormalidade da onda", "hipertrofia ventricular"))
banco$ExerciseAngina <- factor(banco$ExerciseAngina, levels = c("N", "Y"), labels = c("Não", "Sim"))
banco$ST_Slope <- factor(banco$ST_Slope, levels = c("Up", "Flat", "Down"), labels = c("Ascendente", "Plano", "Descendente"))
banco$HeartDisease <- factor(banco$HeartDisease, levels = c(0,1),labels = c("Normal", "Doença cardiaca"))
banco$Oldpeak <- as.numeric(banco$Oldpeak)glimpse(banco)Rows: 918
Columns: 12
$ Age <int> 40, 49, 37, 48, 54, 39, 45, 54, 37, 48, 37, 58, 39, 49,…
$ Sex <fct> Masculino, Feminino, Masculino, Feminino, Masculino, Ma…
$ ChestPainType <fct> Angina Atípica, Dor Não Anginosa, Angina Atípica, Assin…
$ RestingBP <int> 140, 160, 130, 138, 150, 120, 130, 110, 140, 120, 130, …
$ Cholesterol <int> 289, 180, 283, 214, 195, 339, 237, 208, 207, 284, 211, …
$ FastingBS <fct> C.C, C.C, C.C, C.C, C.C, C.C, C.C, C.C, C.C, C.C, C.C, …
$ RestingECG <fct> Normal, Normal, anormalidade da onda, Normal, Normal, N…
$ MaxHR <int> 172, 156, 98, 108, 122, 170, 170, 142, 130, 120, 142, 9…
$ ExerciseAngina <fct> Não, Não, Não, Sim, Não, Não, Não, Não, Sim, Não, Não, …
$ Oldpeak <dbl> 0.0, 1.0, 0.0, 1.5, 0.0, 0.0, 0.0, 0.0, 1.5, 0.0, 0.0, …
$ ST_Slope <fct> Ascendente, Plano, Ascendente, Plano, Ascendente, Ascen…
$ HeartDisease <fct> Normal, Doença cardiaca, Normal, Doença cardiaca, Norma…
Comentário: Com o gráfico abaixo, podemos analisar se existe informação ausente, como NA e em quais variáveis encontra-se a observação ausente, se assim existir:
visdat::vis_miss(banco)De maneira analítica, temos a seguinte quantidade de informações ausentes no banco de dados:
is.na(banco) %>%
colSums() Age Sex ChestPainType RestingBP Cholesterol
0 0 0 0 0
FastingBS RestingECG MaxHR ExerciseAngina Oldpeak
0 0 0 0 0
ST_Slope HeartDisease
0 0
E visualmente, temos um banco de dados dessa maneira:
visdat::vis_dat(banco)skim do pacote skimrskim(banco)| Name | banco |
| Number of rows | 918 |
| Number of columns | 12 |
| _______________________ | |
| Column type frequency: | |
| factor | 7 |
| numeric | 5 |
| ________________________ | |
| Group variables | None |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| Sex | 0 | 1 | FALSE | 2 | Mas: 725, Fem: 193 |
| ChestPainType | 0 | 1 | FALSE | 4 | Ass: 496, Dor: 203, Ang: 173, Ang: 46 |
| FastingBS | 0 | 1 | FALSE | 2 | C.C: 704, Jej: 214 |
| RestingECG | 0 | 1 | FALSE | 3 | Nor: 552, hip: 188, ano: 178 |
| ExerciseAngina | 0 | 1 | FALSE | 2 | Não: 547, Sim: 371 |
| ST_Slope | 0 | 1 | FALSE | 3 | Pla: 460, Asc: 395, Des: 63 |
| HeartDisease | 0 | 1 | FALSE | 2 | Doe: 508, Nor: 410 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| Age | 0 | 1 | 53.51 | 9.43 | 28.0 | 47.00 | 54.0 | 60.0 | 77.0 | ▁▅▇▆▁ |
| RestingBP | 0 | 1 | 132.40 | 18.51 | 0.0 | 120.00 | 130.0 | 140.0 | 200.0 | ▁▁▃▇▁ |
| Cholesterol | 0 | 1 | 198.80 | 109.38 | 0.0 | 173.25 | 223.0 | 267.0 | 603.0 | ▃▇▇▁▁ |
| MaxHR | 0 | 1 | 136.81 | 25.46 | 60.0 | 120.00 | 138.0 | 156.0 | 202.0 | ▁▃▇▆▂ |
| Oldpeak | 0 | 1 | 0.89 | 1.07 | -2.6 | 0.00 | 0.6 | 1.5 | 6.2 | ▁▇▆▁▁ |
As medidas de posição e dispersão que serão utilizadas serão:
Média
Mediana
1º Quartil
3º Quartil
Mínimo
Máximo
Desvio Padrão
Coeficiente de Variação
desc_idade <- banco %>%
group_by(HeartDisease) %>%
summarise(media = mean(Age, na.rm = T),
mediana = median(Age, na.rm = T),
quartil_1 = quantile(Age, 0.25, na.rm = T),
quartil_3 = quantile(Age, 0.75, na.rm = T),
minimo = min(Age, na.rm = T),
maximo = max(Age, na.rm = T),
desvio = sd(Age, na.rm = T),
coeficiente = round(sd(Age, na.rm = T)/mean(Age, na.rm = T)*100,2))
colnames(desc_idade) <- c("Grupo","Média", "Mediana", "1º Quartil","3º Quartil", "Mínimo", "Máximo", "Desvio Padrão", "Coeficiente de Variação")
desc_idade %>%
gt() %>%
fmt_percent(
columns = `Coeficiente de Variação`,
decimals = 2,
scale_values = FALSE,
sep_mark = ".",
dec_mark = ","
) %>%
tab_header(title = html("<b> Estatística Descritiva da Pressão Arterial em Repouso</b>")) %>%
tab_source_note(html("<b> Fonte: </b> Elaboração Própria")) %>%
data_color(
columns = Média,
colors = scales::col_numeric(
palette = colorspace::sequential_hcl(n = 5, palette = "gray"),
domain = c(min(desc_idade$Média), max(desc_idade$Média)),
reverse = TRUE
)
) %>%
cols_align(align = "center", columns = vars(everything())) %>%
fmt_number(
columns = c(Média, `Desvio Padrão`),
decimals = 3
)| Estatística Descritiva da Pressão Arterial em Repouso | ||||||||
| Grupo | Média | Mediana | 1º Quartil | 3º Quartil | Mínimo | Máximo | Desvio Padrão | Coeficiente de Variação |
|---|---|---|---|---|---|---|---|---|
| Normal | 50.551 | 51 | 43 | 57 | 28 | 76 | 9.445 | 18,68% |
| Doença cardiaca | 55.900 | 57 | 51 | 62 | 31 | 77 | 8.727 | 15,61% |
| Fonte: Elaboração Própria | ||||||||
plot_ly(banco, x = banco$Age, color = banco$HeartDisease, type = "box") %>%
layout(title = "Boxplot da Idade de acordo com a presença ou ausência de doença cardíaca")No boxplot acima, pacientes que apresentaram doenças cardíacas possuem uma variabilidade menor de idade quando comparado com pacientes que não apresentaram. Além disso nota-se a presença de 4 outliers referentes a pacientes que apresentaram doenças cardiovasculares antes dos 35 anos.
Também podemos observar 75% dos pacientes que não apresentaram doenças cardiovasculares são mais novos que a idade mediana dos pacientes que apresentaram doenças cardiovasculares. Isto é, pode-se levantar a hipótese de que a idade talvez tenha uma contribuição significativa para o desenvolvimento de doenças cardíacas.
desc_MAXHR <- banco %>%
group_by(HeartDisease) %>%
summarise(media = mean(MaxHR, na.rm = T),
mediana = median(MaxHR, na.rm = T),
quartil_1 = quantile(MaxHR, 0.25, na.rm = T),
quartil_3 = quantile(MaxHR, 0.75, na.rm = T),
minimo = min(MaxHR, na.rm = T),
maximo = max(MaxHR, na.rm = T),
desvio = sd(MaxHR, na.rm = T),
coeficiente = round(sd(MaxHR, na.rm = T)/mean(MaxHR, na.rm = T)*100,2))
colnames(desc_MAXHR) <- c("Grupo","Média", "Mediana", "1º Quartil","3º Quartil", "Mínimo", "Máximo", "Desvio Padrão", "Coeficiente de Variação")
desc_MAXHR %>%
gt() %>%
fmt_percent(
columns = `Coeficiente de Variação`,
decimals = 2,
scale_values = FALSE,
sep_mark = ".",
dec_mark = ","
) %>%
tab_header(title = html("<b> Estatística Descritiva da Pressão Arterial em Repouso</b>")) %>%
tab_source_note(html("<b> Fonte: </b> Elaboração Própria")) %>%
data_color(
columns = Média,
colors = scales::col_numeric(
palette = colorspace::sequential_hcl(n = 5, palette = "green"),
domain = c(min(desc_MAXHR$Média), max(desc_MAXHR$Média)),
reverse = TRUE
)
) %>%
fmt_number(columns = c(Média, `Desvio Padrão`),
decimals = 3) %>%
cols_align(align = "center", columns = everything())| Estatística Descritiva da Pressão Arterial em Repouso | ||||||||
| Grupo | Média | Mediana | 1º Quartil | 3º Quartil | Mínimo | Máximo | Desvio Padrão | Coeficiente de Variação |
|---|---|---|---|---|---|---|---|---|
| Normal | 148.151 | 150 | 134 | 165.00 | 69 | 202 | 23.288 | 15,72% |
| Doença cardiaca | 127.656 | 126 | 112 | 144.25 | 60 | 195 | 23.387 | 18,32% |
| Fonte: Elaboração Própria | ||||||||
plot_ly(banco, x = banco$MaxHR, color = banco$HeartDisease, type = "box") %>%
layout(title = "Boxplot da Frequência Máxima Cardíaca de acordo com a presença ou ausência de doença cardíaca")Como o coração de pessoas com doenças cardíacas não estão funcionando de maneira adequada, quando se é necessária uma carga maior de trabalho o coração desse indivíduo não consegue trabalhar de forma tão eficiente quanto o coração de uma pessoa saudável. Por conta disso, 75% dos indíviduos doentes possuem a frequência máxima cardíaca abaixo da mediana da frequência máxima cardíaca do grupo de pessoas saudáveis.
desc_restingBP <- banco %>%
group_by(HeartDisease) %>%
summarise(media = mean(RestingBP, na.rm = T),
mediana = median(RestingBP, na.rm = T),
quartil_1 = quantile(RestingBP, 0.25, na.rm = T),
quartil_3 = quantile(RestingBP, 0.75, na.rm = T),
minimo = min(RestingBP, na.rm = T),
maximo = max(RestingBP, na.rm = T),
desvio = sd(RestingBP, na.rm = T),
coeficiente = round(sd(RestingBP, na.rm = T)/mean(RestingBP, na.rm = T)*100,2))
colnames(desc_restingBP) <- c("Grupo","Média", "Mediana", "1º Quartil","3º Quartil", "Mínimo", "Máximo", "Desvio Padrão", "Coeficiente de Variação")
desc_restingBP %>%
gt() %>%
fmt_percent(
columns = `Coeficiente de Variação`,
decimals = 2,
scale_values = FALSE,
sep_mark = ".",
dec_mark = ","
) %>%
tab_header(title = html("<b> Estatística Descritiva da Pressão Arterial em Repouso</b>")) %>%
tab_source_note(html("<b> Fonte: </b> Elaboração Própria")) %>%
data_color(
columns = Média,
colors = scales::col_numeric(
palette = colorspace::sequential_hcl(n = 5, palette = "green"),
domain = c(min(desc_restingBP$Média), max(desc_restingBP$Média)),
reverse = TRUE
)
) %>%
cols_align(align = "center", columns = everything()) %>%
fmt_number(columns = c(Média, `Desvio Padrão`),
decimals = 4)| Estatística Descritiva da Pressão Arterial em Repouso | ||||||||
| Grupo | Média | Mediana | 1º Quartil | 3º Quartil | Mínimo | Máximo | Desvio Padrão | Coeficiente de Variação |
|---|---|---|---|---|---|---|---|---|
| Normal | 130.1805 | 130 | 120 | 140 | 80 | 190 | 16.4996 | 12,67% |
| Doença cardiaca | 134.1850 | 132 | 120 | 145 | 0 | 200 | 19.8287 | 14,78% |
| Fonte: Elaboração Própria | ||||||||
plot_ly(banco, x = banco$RestingBP, color = banco$HeartDisease, type = "box") %>%
layout(title = "Boxplot da Pressão Arterial em Repouso de acordo com a presença ou ausência de doença cardíaca")Apesar de ambos os grupos apresentarem dados semelhantes, o grupo que possui doenças cardiovasculares é ligeiramente maior que o grupo de indivíduos saudáveis. Afinal, o coração doente por não apresentar os batimentos tão eficientes quanto um coração saudável, as artérias tendem a compensar esses batimentos cardíacos aumentando sua pressão.
desc_Choles <- banco %>%
group_by(HeartDisease) %>%
summarise(media = mean(Cholesterol, na.rm = T),
mediana = median(Cholesterol, na.rm = T),
quartil_1 = quantile(Cholesterol, 0.25, na.rm = T),
quartil_3 = quantile(Cholesterol, 0.75, na.rm = T),
minimo = min(Cholesterol, na.rm = T),
maximo = max(Cholesterol, na.rm = T),
desvio = sd(Cholesterol, na.rm = T),
coeficiente = round(sd(Cholesterol, na.rm = T)/mean(Cholesterol, na.rm = T)*100,2))
colnames(desc_Choles) <- c("Grupo","Média", "Mediana", "1º Quartil","3º Quartil", "Mínimo", "Máximo", "Desvio Padrão", "Coeficiente de Variação")
desc_Choles %>%
gt() %>%
fmt_percent(
columns = `Coeficiente de Variação`,
decimals = 2,
scale_values = FALSE,
sep_mark = ".",
dec_mark = ","
) %>%
tab_header(title = html("<b> Estatística Descritiva da Pressão Arterial em Repouso</b>")) %>%
tab_source_note(html("<b> Fonte: </b> Elaboração Própria")) %>%
data_color(
columns = Média,
colors = scales::col_numeric(
palette = colorspace::sequential_hcl(n = 5, palette = "Reds 2"),
domain = c(min(desc_Choles$Média), max(desc_Choles$Média)),
reverse = TRUE
)
) %>%
cols_align("center", columns = everything()) %>%
fmt_number(columns = c(Média, `Desvio Padrão`),
decimals = 2)| Estatística Descritiva da Pressão Arterial em Repouso | ||||||||
| Grupo | Média | Mediana | 1º Quartil | 3º Quartil | Mínimo | Máximo | Desvio Padrão | Coeficiente de Variação |
|---|---|---|---|---|---|---|---|---|
| Normal | 227.12 | 227 | 197.25 | 266.75 | 0 | 564 | 74.63 | 32,86% |
| Doença cardiaca | 175.94 | 217 | 0.00 | 267.00 | 0 | 603 | 126.39 | 71,84% |
| Fonte: Elaboração Própria | ||||||||
plot_ly(banco, x = banco$Cholesterol, color = banco$HeartDisease, type = "box") %>%
layout(title = "Boxplot do Colesterol Sérico de acordo \ncom a presença ou ausência de doença cardíaca")Devem existir inúmeros fatores que podem implicar uma maior variabilidade do colesterol no grupo de pessoas que possuem doenças cardíacas, uma delas pode estar relacionada com o fato do uso de medicações para diminuir e tentar controlar esse nível do colesterol.
Após a visualização gráfica do boxplot, observamos uma diferença significativa na variação dos dados do colesterol de acordo com a presença ou ausência de doença cardíaca.
desc_old_peak <- banco %>%
group_by(HeartDisease) %>%
summarise(media = mean(Oldpeak, na.rm = T),
mediana = median(Oldpeak, na.rm = T),
quartil_1 = quantile(Oldpeak, 0.25, na.rm = T),
quartil_3 = quantile(Oldpeak, 0.75, na.rm = T),
minimo = min(Oldpeak, na.rm = T),
maximo = max(Oldpeak, na.rm = T),
desvio = sd(Oldpeak, na.rm = T),
coeficiente = round(sd(Oldpeak, na.rm = T)/mean(Oldpeak, na.rm = T)*100,2))
colnames(desc_old_peak) <- c("Grupo","Média", "Mediana", "1º Quartil","3º Quartil", "Mínimo", "Máximo", "Desvio Padrão", "Coeficiente de Variação")
desc_old_peak %>%
gt() %>%
fmt_percent(
columns = `Coeficiente de Variação`,
decimals = 2,
scale_values = FALSE,
sep_mark = ".",
dec_mark = ","
) %>%
tab_header(title = html("<b> Estatística Descritiva da Pressão Arterial em Repouso</b>")) %>%
tab_source_note(html("<b> Fonte: </b> Elaboração Própria")) %>%
data_color(
columns = Média,
colors = scales::col_numeric(
palette = colorspace::sequential_hcl(n = 5, palette = "Reds 2"),
domain = c(min(desc_old_peak$Média), max(desc_old_peak$Média)),
reverse = TRUE
)
) %>%
cols_align(align = "center", columns = everything()) %>%
fmt_number(columns = c(Média, `Desvio Padrão`),
decimals = 4)| Estatística Descritiva da Pressão Arterial em Repouso | ||||||||
| Grupo | Média | Mediana | 1º Quartil | 3º Quartil | Mínimo | Máximo | Desvio Padrão | Coeficiente de Variação |
|---|---|---|---|---|---|---|---|---|
| Normal | 0.4080 | 0.0 | 0 | 0.6 | -1.1 | 4.2 | 0.6997 | 171,48% |
| Doença cardiaca | 1.2742 | 1.2 | 0 | 2.0 | -2.6 | 6.2 | 1.1519 | 90,40% |
| Fonte: Elaboração Própria | ||||||||
plot_ly(banco, x = banco$Oldpeak, color = banco$HeartDisease, type = "box") %>%
layout(title = "Boxplot do banco do valor númerico medido \nna depressão de acordo com a presença ou ausência de doença cardíaca")Podemos observar que no grupo normal, temos a presença de muitos outliers, já no grupo de doença cardíaca, entre o primeiro e terceiro quartil podemos enxergar uma variabilidade maior do que comparando esses mesmos quartis.
Agora, vamos visualizar a matriz de correlação das variáveis preditoras
rho <- banco %>%
dplyr::select(where(is.numeric)) %>%
cor()
corrplot::corrplot(rho, method = "square", type = "full")Observando a correlação de outra maneira mais gráfica, através de um gráfico que vai mostrar as ligações e força da correlação se existir
No gráfico abaixo, correlações positivas podem ser vistas com a ligação azul e correlações negativas com a vermelha.
rho <- round(rho,2)
qgraph(rho, shape = "diamond",
posCol = "darkblue",
negCol = "darkred",
layout = "groups",
size = 20,
vTrans = 100,
borders = F,
edge.labels = rho,
color = "skyblue",
edge.label.position = 0.5,
edge.label.color = "black",
edge.label.margin = 0.01)desc_heart <- banco %>%
dplyr::group_by(HeartDisease) %>%
dplyr::summarise(quantidade = n(),
proporção = round(n()/dim(banco)[1]*100,2))
colnames(desc_heart) <- c("Doença Cardíaca", "Frequência", "Percentual")
desc_heart %>%
gt() %>%
fmt_percent(
columns = Percentual,
decimals = 2,
scale_values = FALSE,
sep_mark = ".",
dec_mark = ","
) %>%
tab_header(title = html("<b> Estatística Descritiva da variável de interesse </b>")) %>%
tab_source_note(html("<b>Fonte: </b> Elaboração Própria")) %>%
cols_align(align = "center", columns = everything())| Estatística Descritiva da variável de interesse | ||
| Doença Cardíaca | Frequência | Percentual |
|---|---|---|
| Normal | 410 | 44,66% |
| Doença cardiaca | 508 | 55,34% |
| Fonte: Elaboração Própria | ||
banco %>%
count(HeartDisease, RestingECG) %>%
group_by(HeartDisease) %>%
mutate(percent = n / sum(n) *100,
percent = round(percent, 2)) %>%
gt::gt() %>%
gt::tab_header(
title = html("<b> Situação dos pacientes quanto a presença de doença cardíaca</b>"),
subtitle = "Com relação ao eletrocardiograma em repouso"
) %>%
gt::cols_label(
RestingECG = "ECG em Repouso",
n = "Frequência",
percent = "Percentual"
) %>%
gt::fmt_number(
columns = vars(n),
suffixing = T,
decimals = 0
) %>%
fmt_percent(columns = percent,
decimals = 2,
scale_values = F,
sep_mark = ".",
dec_mark = ",") %>%
tab_source_note(html("<b> Fonte: </b> Elaboração Própria")) %>%
cols_align(align = "center", columns = everything())| Situação dos pacientes quanto a presença de doença cardíaca | ||
| Com relação ao eletrocardiograma em repouso | ||
| ECG em Repouso | Frequência | Percentual |
|---|---|---|
| Normal | ||
| Normal | 267 | 65,12% |
| anormalidade da onda | 61 | 14,88% |
| hipertrofia ventricular | 82 | 20,00% |
| Doença cardiaca | ||
| Normal | 285 | 56,10% |
| anormalidade da onda | 117 | 23,03% |
| hipertrofia ventricular | 106 | 20,87% |
| Fonte: Elaboração Própria | ||
banco %>%
count(HeartDisease, ST_Slope) %>%
group_by(HeartDisease) %>%
mutate(percent = n / sum(n) *100,
percent = round(percent, 2)) %>%
gt::gt() %>%
gt::tab_header(
title = html("<b> Situação dos pacientes quanto a presença de doença cardíaca</b>"),
subtitle = "Com relação a inclinação do Segmento"
) %>%
gt::cols_label(
ST_Slope = "Inclinação do Segmento",
n = "Frequência",
percent = "Percentual"
) %>%
gt::fmt_number(
columns = vars(n),
suffixing = TRUE,
decimals = 0
) %>%
fmt_percent(
columns = percent,
decimals = 2,
dec_mark = ",",
sep_mark = ".",
scale_values = F
) %>%
tab_source_note(html("<b> Fonte: </b> Elaboração Própria")) %>%
cols_align(align = "center", columns = everything())| Situação dos pacientes quanto a presença de doença cardíaca | ||
| Com relação a inclinação do Segmento | ||
| Inclinação do Segmento | Frequência | Percentual |
|---|---|---|
| Normal | ||
| Ascendente | 317 | 77,32% |
| Plano | 79 | 19,27% |
| Descendente | 14 | 3,41% |
| Doença cardiaca | ||
| Ascendente | 78 | 15,35% |
| Plano | 381 | 75,00% |
| Descendente | 49 | 9,65% |
| Fonte: Elaboração Própria | ||
banco %>%
count(HeartDisease, Sex) %>%
group_by(HeartDisease) %>%
mutate(percent = n / sum(n) *100,
percent = round(percent, 2)) %>%
gt::gt() %>%
gt::tab_header(
title = html("<b> Situação dos pacientes quanto a presença de doença cardíaca</b>"),
subtitle = "Com relação ao Sexo"
) %>%
gt::cols_label(
Sex = "Sexo",
n = "Frequência",
percent = "Percentual"
) %>%
gt::fmt_number(
columns = vars(n),
suffixing = TRUE,
decimals = 0
) %>%
fmt_percent(
columns = percent,
decimals = 2,
scale_values = F,
dec_mark = ",",
sep_mark = "."
) %>%
tab_source_note(html("<b> Fonte:</b> Elaboração Própria")) %>%
cols_align(align = "center", columns = everything())| Situação dos pacientes quanto a presença de doença cardíaca | ||
| Com relação ao Sexo | ||
| Sexo | Frequência | Percentual |
|---|---|---|
| Normal | ||
| Masculino | 267 | 65,12% |
| Feminino | 143 | 34,88% |
| Doença cardiaca | ||
| Masculino | 458 | 90,16% |
| Feminino | 50 | 9,84% |
| Fonte: Elaboração Própria | ||
banco %>%
count(HeartDisease, ChestPainType) %>%
group_by(HeartDisease) %>%
mutate(percent = n / sum(n) *100,
percent = round(percent, 2)) %>%
gt::gt() %>%
gt::tab_header(
title = html("<b> Situação dos pacientes quanto a presença de doença cardíaca </b>"),
subtitle = "Com relação a dor no peito"
) %>%
gt::cols_label(
ChestPainType = "Tipo de dor no peito",
n = "Frequência",
percent = "Percentual"
) %>%
gt::fmt_number(
columns = vars(n),
suffixing = TRUE,
decimals = 0
) %>%
fmt_percent(
columns = percent,
decimals = 2,
scale_values = F,
dec_mark = ",",
sep_mark = "."
) %>%
tab_source_note(html("<b> Fonte:</b> Elaboração Própria")) %>%
cols_align(align = "center", columns = everything())| Situação dos pacientes quanto a presença de doença cardíaca | ||
| Com relação a dor no peito | ||
| Tipo de dor no peito | Frequência | Percentual |
|---|---|---|
| Normal | ||
| Angina Típica | 26 | 6,34% |
| Angina Atípica | 149 | 36,34% |
| Dor Não Anginosa | 131 | 31,95% |
| Assintomática | 104 | 25,37% |
| Doença cardiaca | ||
| Angina Típica | 20 | 3,94% |
| Angina Atípica | 24 | 4,72% |
| Dor Não Anginosa | 72 | 14,17% |
| Assintomática | 392 | 77,17% |
| Fonte: Elaboração Própria | ||
banco %>%
count(HeartDisease, FastingBS) %>%
group_by(HeartDisease) %>%
mutate(percent = n / sum(n) *100,
percent = round(percent, 2)) %>%
gt::gt() %>%
gt::tab_header(
title = html("<b> Situação dos pacientes quanto a presença de doença cardíaca</b>"),
subtitle = "Com relação ao Açúcar no Sengue em Jejum"
) %>%
gt::cols_label(
FastingBS = "Açucar no Sangue",
n = "Frequência",
percent = "Percentual"
) %>%
gt::fmt_number(
columns = vars(n),
suffixing = TRUE,
decimals = 0
) %>%
fmt_percent(
columns = percent,
decimals = 2,
scale_values = F,
dec_mark = ",",
sep_mark = "."
) %>%
tab_source_note(html("<b> Fonte:</b> Elaboração Própria")) %>%
cols_align(align = "center", columns = everything()) %>%
tab_style(
style = list(
cell_text(weight = "bolder")
),
locations = cells_body(columns = percent)
)| Situação dos pacientes quanto a presença de doença cardíaca | ||
| Com relação ao Açúcar no Sengue em Jejum | ||
| Açucar no Sangue | Frequência | Percentual |
|---|---|---|
| Normal | ||
| C.C | 366 | 89,27% |
| JejumBS > 120 mg/dl | 44 | 10,73% |
| Doença cardiaca | ||
| C.C | 338 | 66,54% |
| JejumBS > 120 mg/dl | 170 | 33,46% |
| Fonte: Elaboração Própria | ||
Para esse banco de dados a proporção que será utilizada no particionamento é de 75%.
set.seed(2024)
split_inicial <- initial_split(banco, prop = 0.75, strata = HeartDisease)
banco_treino <- training(split_inicial)
banco_teste <- testing(split_inicial)Para o pré-processamento é a parte de aplicarmos algumas receitas para melhorar os dados, com isso utilizamos a biblioteca recipes, e aplicamos as receitas de acordo com as necessidades que possuímos.
banco_receita <- recipe(HeartDisease ~ ., data = banco_treino) %>%
# step_impute_knn(all_predictors(), neighbors = 5) %>% # imputa valores ausentes por K-NN
# step_impute_bag(all_predictors()) %>% # imputa valores ausentes por bagged trees
# step_impute_mean(all_numeric_predictors()) %>% # imputa valores ausentes pela média
# step_impute_median(all_numeric_predictors()) %>% # imputa valores ausentes pela mediana
# step_impute_mode(all_predictors()) %>% # imputa valores ausentes pela mediana
# step_naomit(everything(), skip = TRUE) %>% # remove linhas que contém NA ou NaN
# step_interact(terms = ~ all_numeric_predictors():all_numeric_predictors()) %>%
# step_log( # Transformação log: y = log(x)
# ) %>%
step_YeoJohnson( # Transformação Yeo-Johnson
all_numeric_predictors()
) %>%
step_poly(all_numeric_predictors(), degree = 2) %>%
step_normalize( # normaliza variáveis numéricas para terem média 0 e variância 1
all_numeric_predictors()
) %>%
# step_range( # normaliza variáveis numéricas para pertencerem ao intervalo [0,1]
# all_numeric_predictors()
# ) %>%
step_dummy(all_nominal_predictors()) %>% # converte variáveis qualitativas em variáveis dummy
# step_smote(HeartDisease, over_ratio = 1) %>% # Balanceamento de classes usando SMOTE
# step_upsample(diagnosis) %>% # Balanceamento de classes usando upsample
# step_downsample(diagnosis) %>% # Balanceamento de classes usando downsample
step_nzv(all_numeric_predictors()) %>% # remove variáveis que têm variância próxima de zero
step_corr( # remove preditores que tenham alta correlação com algum outro preditor
all_numeric_predictors(),
threshold = 0.8,
method = "spearman"
)Na aplicação desse banco de dados as receitas utilizadas foram:
Transformação de Yeo-Johnson nas variáveis numéricas;
Polinômios de Grau dois, ou seja, elevando as variáveis numéricas para termos talvez uma classificação melhor;
Normalização das variáveis númericas;
Transformação das variáveis categóricas em variáveis Dummy;
Remoção de variáveis preditoras altamente correlacionadas, estabelecido como ponto de corte uma correlação através do método de spearman, não sensível apenas a correlação linear, foi de 0.80.
Depois de aplicar as receitas precisamos continuar para extrair o banco aplicando as receitas.
# Dessa vez, não usaremos os "dados preparados" explicitamente, mas criarei esse objeto para verificarmos o efeito do smote e da imputação dos valores ausentes
set.seed(2024)
banco_preparado <-
banco_receita %>% # usa a receita
prep() %>% # aplica a receita no conjunto de treinamento
juice() # extrai apenas o dataframe preprocessadoQuantidade de Observações no banco de Treinamento, de acordo com a variável destino, ou feature target, que é a Heart Disease
tabela <- banco_preparado %>%
group_by(HeartDisease) %>%
summarise(quantidade = n(),
percentual = n()/dim(banco_preparado)[1])
colnames(tabela) <- c("Heart Disease","Quantidade", "Proporção")
tabela %>%
gt::gt() %>%
tab_header(
title = gt::html("<b> Quantidade de Observações de acordo com as classes da variável dependente
</b>"),
subtitle = glue::glue("No banco de Treinamento")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração Própria")
) %>%
fmt_number(columns = Quantidade,
suffixing = TRUE,
decimals = 0) %>%
fmt_percent(columns = Proporção,
decimals = 2,
scale_values = T,
dec_mark = ",",
sep_mark = ".") %>%
cols_align(align = "center", columns = everything()) %>%
tab_style(
style = list(
cell_text(weight = "bolder")
),
locations = cells_body(columns = everything())
)| Quantidade de Observações de acordo com as classes da variável dependente | ||
| No banco de Treinamento | ||
| Heart Disease | Quantidade | Proporção |
|---|---|---|
| Normal | 307 | 44,62% |
| Doença cardiaca | 381 | 55,38% |
| Fonte: Elaboração Própria | ||
Utilizamos o método \(k\)-fold cross-validation para construir um conjunto de validação com \(k\) folds. Consideramos um procedimento com \(k = 10\) folds. Os dados são particionados em 10 partes utilizando amostragem estratificada e, em cada iteração, os modelos são ajustados em um conjunto de treinamento com composto por 9 dessas partes e avaliado em um conjunto de teste composto por 1 dessas partes. Esse procedimento foi utilizado para avaliar o modelo e obter os valores ótimos dos hiperparâmetros dos modelos.
cv_folds <- vfold_cv(banco_treino,
v = 10,
strata = HeartDisease)Os hiperparâmetros dos modelos foram otimizados no processo de validação cruzada. A busca pelos valores ótimos dos hiperparâmetros se deu através de um processo de busca em uma grade aleatória de valores definida através de um esquema de hipercubo latino, visando preencher adequadamente o espaço de valores dos hiperparâmetros.
knn_spec <- nearest_neighbor(neighbors = tune()) %>% # K-NN
set_mode("classification") %>%
set_engine("kknn")
nbayes_spec <- naive_Bayes() %>% # Naive Bayes
set_engine("naivebayes") %>%
set_mode("classification")
lda_spec <- discrim_linear() %>% # Linear discriminant analysis
set_engine("MASS") %>%
set_mode("classification")
qda_spec <- discrim_quad() %>% # Quadratic discriminant analysis
set_engine("MASS") %>%
set_mode("classification")
reg_log_spec <- logistic_reg(penalty = tune(), mixture = tune()) %>% # RL
set_engine(engine = "glmnet", standardize = FALSE) %>%
set_mode("classification")
dec_tree <- decision_tree(cost_complexity = tune(),
min_n = tune(),
tree_depth = tune()) %>% # Decision tree
set_engine(engine = "rpart") %>%
set_mode("classification")
bagg_tree <- bag_tree(cost_complexity = tune(),
min_n = tune(),
tree_depth = tune()) %>% # Bagged tree
set_engine(engine = "rpart") %>%
set_mode("classification")
random_forest <- rand_forest(mtry = tune(),
min_n = tune(),
trees = tune()) %>% # Random Forest
set_engine(engine = "ranger", importance = "impurity") %>%
set_mode("classification")
xgboost <- boost_tree(tree_depth = tune(),
learn_rate = tune(),
loss_reduction = tune(),
min_n = tune(),
sample_size = tune(),
trees = tune(),
mtry = tune()) %>% # Boosted trees
set_engine(engine = "xgboost") %>%
set_mode("classification")
lsvm <- svm_linear(cost = tune(), # SVM Linear
margin = tune()) %>%
set_engine(engine = "kernlab") %>%
set_mode("classification")
rsvm_spec <- svm_rbf(cost = tune(), # RBF/Gaussian Kernel SVM
rbf_sigma = tune(),
margin = tune()) %>%
set_engine(engine = "kernlab") %>%
set_mode("classification")
psvm_spec <- svm_poly(cost = tune(), # Polynomial Kernel SVM
degree = tune(),
scale_factor = tune(),
margin = tune()) %>%
set_engine(engine = "kernlab") %>%
set_mode("classification")
rede <- mlp(hidden_units = tune(), # MLP - Perceptron com multi camadas
epochs = tune(),
activation = "softmax",
penalty = tune(),
mode = "classification",
engine = "nnet")
rede2 <- mlp(hidden_units = tune(),
penalty = tune(),
epochs = tune(),
activation = "relu",
learn_rate = 0.1,
mode = "classification",
engine = "brulee")workflow, ou seja, prepando um fluxo de tarefas para ele treinar os modelos ao mesmo tempo.wf = workflow_set(
preproc = list(banco_receita),
models = list(
KNN = knn_spec,
Nayve_Bayes = nbayes_spec,
LDA = lda_spec,
QDA = qda_spec,
Reg_log = reg_log_spec,
decision = dec_tree,
bag_treeee = bagg_tree,
random_fore = random_forest,
xgbost = xgboost,
svm_lin = lsvm,
svm_rf = rsvm_spec,
psvm = psvm_spec,
rede_neural = rede,
rede_neural2 = rede2
)
) %>%
mutate(wflow_id = gsub("(recipe_)", "", wflow_id))tempo_inicial <- Sys.time()
grid_ctrl = control_grid(
save_pred = TRUE,
parallel_over = "resamples",
save_workflow = TRUE
)
grid_results = wf %>%
workflow_map(
seed = 2024,
resamples = cv_folds,
grid = 5,
control = grid_ctrl)
tempo_final <- Sys.time()
tempo <- as.numeric(difftime(tempo_final, tempo_inicial, units = "secs"))O tempo total de treinamento do modelo foi 0 horas, 2 minutos e 33 segundos.
Curva Roc e a Acurácia.autoplot(grid_results)Support Vector Machines e também a Rede Neural com a função de ativação Relu; com base na curva ROC e na Acurácia. Vamos continuar observando qual o melhor modelo e vendo os valores de algumas métricas.autoplot(grid_results, select_best = TRUE, metric = "roc_auc")results <- workflowsets::rank_results(grid_results,
select_best = TRUE,
rank_metric = "roc_auc") %>%
filter(.metric == "roc_auc") %>%
dplyr::select(wflow_id, mean, std_err, model, rank)
colnames(results) <- c("Método", "Média", "Desvio Padrão", "Modelo", "Ranking")
results$Método[which(results$Método=="Reg_log")] <- "Regressão Logística"
results$Método[which(results$Método=="LDA")] <- "Discriminante Linear"
results$Método[which(results$Método=="QDA")] <- "Discriminante Quadrática"
results$Método[which(results$Método=="KNN")] <- "Knn - mais próximos"
results$Método[which(results$Método=="Nayve_Bayes")] <- "Nayve Bayes"
results$Método[which(results$Método=="random_fore")] <- "Floresta Aleatória"
results$Método[which(results$Método=="bag_treeee")] <- "Bagged Trees"
results$Método[which(results$Método=="decision")] <- "Decision Tree"
results$Método[which(results$Método=="xgbost")] <- "XG Boost"
results$Método[which(results$Método=="svm_lin")] <- "SVM Linear"
results$Método[which(results$Método=="svm_rf")] <- "SVM RBF"
results$Método[which(results$Método=="psvm")] <- "SVM Polinomial"
results$Método[which(results$Método=="rede_neural")] <- "Rede Neural"
results$Método[which(results$Método=="rede_neural2")] <- "Rede Neural engine Brulee"
results$Média <- round(results$Média, 4)
results$`Desvio Padrão` <- round(results$`Desvio Padrão`, 4)
results %>% gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado do Treinamento dos Modelos</b>"),
subtitle = glue::glue("De acordo com a métrica da curva Roc")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
gt::data_color(
columns = Média,
colors = scales::col_numeric(
palette = colorspace::sequential_hcl(n = 5, palette = "Green"),
domain = c(min(results$Média), max(results$Média)),
reverse = TRUE
)
) %>%
cols_align(align = "center", columns = everything())| Resultado do Treinamento dos Modelos | ||||
| De acordo com a métrica da curva Roc | ||||
| Método | Média | Desvio Padrão | Modelo | Ranking |
|---|---|---|---|---|
| Rede Neural engine Brulee | 0.9422 | 0.0139 | mlp | 1 |
| Floresta Aleatória | 0.9315 | 0.0122 | rand_forest | 2 |
| Regressão Logística | 0.9289 | 0.0126 | logistic_reg | 3 |
| XG Boost | 0.9285 | 0.0133 | boost_tree | 4 |
| SVM Linear | 0.9278 | 0.0119 | svm_linear | 5 |
| Discriminante Linear | 0.9265 | 0.0128 | discrim_linear | 6 |
| SVM RBF | 0.9260 | 0.0124 | svm_rbf | 7 |
| SVM Polinomial | 0.9243 | 0.0135 | svm_poly | 8 |
| Bagged Trees | 0.9197 | 0.0116 | bag_tree | 9 |
| Knn - mais próximos | 0.9132 | 0.0163 | nearest_neighbor | 10 |
| Discriminante Quadrática | 0.8980 | 0.0134 | discrim_quad | 11 |
| Decision Tree | 0.8937 | 0.0115 | decision_tree | 12 |
| Rede Neural | 0.8913 | 0.0107 | mlp | 13 |
| Nayve Bayes | 0.8908 | 0.0147 | naive_Bayes | 14 |
| Fonte: Elaboração própria | ||||
autoplot(grid_results, select_best = TRUE, metric = "accuracy")results_acc <- workflowsets::rank_results(grid_results,
select_best = TRUE,
rank_metric = "accuracy") %>%
filter(.metric == "accuracy") %>%
dplyr::select(wflow_id, mean, std_err, model, rank)
colnames(results_acc) <- c("Método", "Média", "Desvio Padrão", "Modelo", "Ranking")
results_acc$Método[which(results_acc$Método=="Reg_log")] <- "Regressão Logística"
results_acc$Método[which(results_acc$Método=="LDA")] <- "Discriminante Linear"
results_acc$Método[which(results_acc$Método=="QDA")] <- "Discriminante Quadrática"
results_acc$Método[which(results_acc$Método=="KNN")] <- "Knn - mais próximos"
results_acc$Método[which(results_acc$Método=="Nayve_Bayes")] <- "Nayve Bayes"
results_acc$Método[which(results_acc$Método=="random_fore")] <- "Floresta Aleatória"
results_acc$Método[which(results_acc$Método=="bag_treeee")] <- "Bagged Trees"
results_acc$Método[which(results_acc$Método=="decision")] <- "Decision Tree"
results_acc$Método[which(results_acc$Método=="xgbost")] <- "XG Boost"
results_acc$Método[which(results_acc$Método=="svm_lin")] <- "SVM Linear"
results_acc$Método[which(results_acc$Método=="svm_rf")] <- "SVM RBF"
results_acc$Método[which(results_acc$Método=="psvm")] <- "SVM Polinomial"
results_acc$Método[which(results_acc$Método=="rede_neural")] <- "Rede Neural"
results_acc$Método[which(results_acc$Método=="rede_neural2")] <- "Rede Neural engine Brulee"
results_acc$Média <- round(results_acc$Média, 4)
results_acc$`Desvio Padrão` <- round(results_acc$`Desvio Padrão`, 4)
results_acc %>% gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado do Treinamento dos Modelos</b>"),
subtitle = glue::glue("De acordo com a métrica da Acurácia")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
gt::data_color(
columns = Média,
colors = scales::col_numeric(
palette = colorspace::sequential_hcl(n = 5, palette = "Green"),
domain = c(min(results_acc$Média), max(results_acc$Média)),
reverse = TRUE
)
) %>%
cols_align(align = "center", columns = everything())| Resultado do Treinamento dos Modelos | ||||
| De acordo com a métrica da Acurácia | ||||
| Método | Média | Desvio Padrão | Modelo | Ranking |
|---|---|---|---|---|
| SVM Linear | 0.8763 | 0.0169 | svm_linear | 1 |
| SVM Polinomial | 0.8735 | 0.0163 | svm_poly | 2 |
| Rede Neural engine Brulee | 0.8733 | 0.0259 | mlp | 3 |
| Regressão Logística | 0.8720 | 0.0149 | logistic_reg | 4 |
| Discriminante Linear | 0.8690 | 0.0164 | discrim_linear | 5 |
| Knn - mais próximos | 0.8677 | 0.0171 | nearest_neighbor | 6 |
| SVM RBF | 0.8675 | 0.0165 | svm_rbf | 7 |
| Floresta Aleatória | 0.8662 | 0.0130 | rand_forest | 8 |
| XG Boost | 0.8633 | 0.0174 | boost_tree | 9 |
| Rede Neural | 0.8545 | 0.0158 | mlp | 10 |
| Bagged Trees | 0.8473 | 0.0129 | bag_tree | 11 |
| Decision Tree | 0.8415 | 0.0122 | decision_tree | 12 |
| Discriminante Quadrática | 0.8313 | 0.0158 | discrim_quad | 13 |
| Nayve Bayes | 0.8284 | 0.0200 | naive_Bayes | 14 |
| Fonte: Elaboração própria | ||||
Agora é necessário selecionar o melhor modelos dos que foram treinados (um de cada modelo), com base na estimação dos hiperparâmetros para aplicar os dados de teste.
best_set_linear = grid_results %>%
extract_workflow_set_result("Reg_log") %>%
select_best(metric = "accuracy")
best_set_knn = grid_results %>%
extract_workflow_set_result("KNN") %>%
select_best(metric = "accuracy")
best_set_nbayes = grid_results %>%
extract_workflow_set_result("Nayve_Bayes") %>%
select_best(metric = "accuracy")
best_set_lda = grid_results %>%
extract_workflow_set_result("LDA") %>%
select_best(metric = "accuracy")
best_set_qda = grid_results %>%
extract_workflow_set_result("QDA") %>%
select_best(metric = "accuracy")
best_set_rand_fore = grid_results %>%
extract_workflow_set_result("random_fore") %>%
select_best(metric = "accuracy")
best_set_decision = grid_results %>%
extract_workflow_set_result("decision") %>%
select_best(metric = "accuracy")
best_set_bag = grid_results %>%
extract_workflow_set_result("bag_treeee") %>%
select_best(metric = "accuracy")
best_set_xbost = grid_results %>%
extract_workflow_set_result("xgbost") %>%
select_best(metric = "accuracy")
best_set_svmlin = grid_results %>%
extract_workflow_set_result("svm_lin") %>%
select_best(metric = "accuracy")
best_set_svmrf = grid_results %>%
extract_workflow_set_result("svm_rf") %>%
select_best(metric = "accuracy")
best_set_psvm = grid_results %>%
extract_workflow_set_result("psvm") %>%
select_best(metric = "accuracy")
best_set_mlp = grid_results %>%
extract_workflow_set_result("rede_neural") %>%
select_best(metric = "accuracy")
best_set_mlp2 = grid_results %>%
extract_workflow_set_result("rede_neural2") %>%
select_best(metric = "accuracy")Os hiperparâmetros do melhor modelo de cada método foi
O modelo KNN que foi o melhor teve um número de 12 vizinhos.
best_set_linear %>%
gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de Regressão Logística")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
gt::cols_label(
penalty = "Penalização",
mixture = "Mistura",
.config = "Configuração"
) %>%
fmt_number(columns = c(penalty, mixture),
decimals = 4) %>%
cols_align(align = "center", columns = everything())| Resultado dos hiperparâmetros do melhor ajuste | ||
| Do modelo de Regressão Logística | ||
| Penalização | Mistura | Configuração |
|---|---|---|
| 0.0142 | 0.7454 | Preprocessor1_Model4 |
| Fonte: Elaboração própria | ||
best_set_decision %>%
gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de Árvore de Decisão")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
fmt_number(columns = c(cost_complexity),
decimals = 5) %>%
cols_align(align = "center", columns = everything())| Resultado dos hiperparâmetros do melhor ajuste | |||
| Do modelo de Árvore de Decisão | |||
| cost_complexity | tree_depth | min_n | .config |
|---|---|---|---|
| 0.00000 | 7 | 28 | Preprocessor1_Model5 |
| Fonte: Elaboração própria | |||
best_set_bag %>%
gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de Bageed Tree")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
fmt_number(columns = cost_complexity,
decimals = 7) %>%
cols_align(align = "center", columns = everything())| Resultado dos hiperparâmetros do melhor ajuste | |||
| Do modelo de Bageed Tree | |||
| cost_complexity | tree_depth | min_n | .config |
|---|---|---|---|
| 0.0000000 | 7 | 28 | Preprocessor1_Model5 |
| Fonte: Elaboração própria | |||
best_set_rand_fore %>%
gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de Floresta Aleatória")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
cols_align(align = "center",
columns = everything())| Resultado dos hiperparâmetros do melhor ajuste | |||
| Do modelo de Floresta Aleatória | |||
| mtry | trees | min_n | .config |
|---|---|---|---|
| 2 | 843 | 28 | Preprocessor1_Model5 |
| Fonte: Elaboração própria | |||
best_set_xbost %>%
gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo XGBOOST")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
cols_align(align = "center",
columns = everything()) %>%
fmt_number(columns = c(learn_rate, loss_reduction, sample_size),
decimals = 4)| Resultado dos hiperparâmetros do melhor ajuste | |||||||
| Do modelo XGBOOST | |||||||
| mtry | trees | min_n | tree_depth | learn_rate | loss_reduction | sample_size | .config |
|---|---|---|---|---|---|---|---|
| 1 | 1656 | 9 | 7 | 0.0195 | 5.8762 | 0.5222 | Preprocessor1_Model1 |
| Fonte: Elaboração própria | |||||||
best_set_svmlin %>%
gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de SVM Linear")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
cols_align(align = "center",
columns = everything()) %>%
fmt_number(columns = c(cost, margin),
decimals = 4)| Resultado dos hiperparâmetros do melhor ajuste | ||
| Do modelo de SVM Linear | ||
| cost | margin | .config |
|---|---|---|
| 0.0110 | 0.0043 | Preprocessor1_Model5 |
| Fonte: Elaboração própria | ||
best_set_svmrf %>%
gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de SVM RBF")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
cols_align(align = "center",
columns = everything()) %>%
fmt_number(columns = c(cost, margin, rbf_sigma),
decimals = 4)| Resultado dos hiperparâmetros do melhor ajuste | |||
| Do modelo de SVM RBF | |||
| cost | rbf_sigma | margin | .config |
|---|---|---|---|
| 25.1330 | 0.0018 | 0.1058 | Preprocessor1_Model2 |
| Fonte: Elaboração própria | |||
best_set_psvm %>%
gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de SVM Polinomial")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
cols_align(align = "center",
columns = everything()) %>%
fmt_number(columns = c(cost, margin, scale_factor),
decimals = 4) %>%
fmt_number(columns = degree,
decimals = 0)| Resultado dos hiperparâmetros do melhor ajuste | ||||
| Do modelo de SVM Polinomial | ||||
| cost | degree | scale_factor | margin | .config |
|---|---|---|---|---|
| 21.0375 | 3 | 0.0001 | 0.0413 | Preprocessor1_Model4 |
| Fonte: Elaboração própria | ||||
best_set_mlp %>%
gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste </b>"),
subtitle = glue::glue("Do modelo de Rede Neural Perceptron com Multi Camadas")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
cols_align(align = "center",
columns = everything()) %>%
fmt_number(columns = penalty,
decimals = 6) %>%
fmt_number(columns = c(hidden_units, epochs),
decimals = 0)| Resultado dos hiperparâmetros do melhor ajuste | |||
| Do modelo de Rede Neural Perceptron com Multi Camadas | |||
| penalty | epochs | .config | |
|---|---|---|---|
| 1 | 0.000002 | 698 | Preprocessor1_Model5 |
| Fonte: Elaboração própria | |||
A rede Neural Perceptron Multi Camada com a Engine Brulee e função de ativação relu
best_set_mlp2 %>%
gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste </b>"),
subtitle = glue::glue("Do modelo de Rede Neural Perceptron com Multi Camadas e função de ativação Relu")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
cols_align(align = "center",
columns = everything()) %>%
fmt_number(columns = penalty,
decimals = 8) %>%
fmt_number(columns = c(hidden_units, epochs),
decimals = 0)| Resultado dos hiperparâmetros do melhor ajuste | |||
| Do modelo de Rede Neural Perceptron com Multi Camadas e função de ativação Relu | |||
| penalty | epochs | .config | |
|---|---|---|---|
| 5 | 0.00000002 | 91 | Preprocessor1_Model3 |
| Fonte: Elaboração própria | |||
Esses conjuntos de hiperparâmetros ótimos foram utilizados para reajustar os modelos no conjunto de treinamento completo para, em seguida, obter predições das classes da variável alvo no conjunto de teste. Foram calculadas as seguintes medidas no conjunto de teste: acurácia, área sob a curva ROC, F-measure, precision, recall, especificidade e Kappa.
resultado_teste <- function(rc_rslts, fit_obj, par_set, split_obj) {
res <- rc_rslts %>%
extract_workflow(fit_obj) %>%
finalize_workflow(par_set) %>%
last_fit(split = split_obj,
metrics = metric_set(
accuracy,roc_auc,
f_meas,precision,
recall,spec,kap))
res
}resultado_teste_reg_log <- resultado_teste(grid_results, "Reg_log", best_set_linear, split_inicial)
resultado_teste_knn <- resultado_teste(grid_results, "KNN", best_set_knn, split_inicial)
resultado_teste_lda <- resultado_teste(grid_results, "LDA", best_set_lda, split_inicial)
resultado_teste_qda <- resultado_teste(grid_results, "QDA", best_set_qda, split_inicial)
resultado_teste_naive <- resultado_teste(grid_results, "Nayve_Bayes", best_set_nbayes, split_inicial)
resultado_teste_decision <- resultado_teste(grid_results, "decision", best_set_decision, split_inicial)
resultado_teste_bag <- resultado_teste(grid_results, "bag_treeee", best_set_bag, split_inicial)
resultado_teste_rand_fore <- resultado_teste(grid_results, "random_fore", best_set_rand_fore, split_inicial)
resultado_teste_xgbost <- resultado_teste(grid_results, "xgbost", best_set_xbost, split_inicial)
resultado_teste_svmlin <- resultado_teste(grid_results, "svm_lin", best_set_svmlin, split_inicial)
resultado_teste_svmrf <- resultado_teste(grid_results, "svm_rf", best_set_svmrf, split_inicial)
resultado_teste_psvm <- resultado_teste(grid_results, "psvm", best_set_psvm, split_inicial)
resultado_teste_mlp <- resultado_teste(grid_results, "rede_neural", best_set_mlp, split_inicial)
resultado_teste_mlp2 <- resultado_teste(grid_results, "rede_neural2", best_set_mlp2, split_inicial)metrics_table <- rbind(collect_metrics(resultado_teste_reg_log)$.estimate,
collect_metrics(resultado_teste_knn)$.estimate,
collect_metrics(resultado_teste_lda)$.estimate,
collect_metrics(resultado_teste_qda)$.estimate,
collect_metrics(resultado_teste_naive)$.estimate,
collect_metrics(resultado_teste_decision)$.estimate,
collect_metrics(resultado_teste_bag)$.estimate,
collect_metrics(resultado_teste_rand_fore)$.estimate,
collect_metrics(resultado_teste_xgbost)$.estimate,
collect_metrics(resultado_teste_svmlin)$.estimate,
collect_metrics(resultado_teste_svmrf)$.estimate,
collect_metrics(resultado_teste_psvm)$.estimate,
collect_metrics(resultado_teste_mlp)$.estimate,
collect_metrics(resultado_teste_mlp2)$.estimate)Ajustando a tabela de métricas
metrics_table <- round(metrics_table, 4)
row_names <- c("Regressão Logística", "KNN", "Discriminante Linear", "Discriminante Quadrático", "Naive Bayes", "Árvore de Decisão", "Bageed Tree", "Floresta Aleatória", "XG Boost", "SVM Linear", "SVM RBF", "SVM Polinomial", "Rede Neural - Multi Camada", "Rede Neural - Multi Camada - Relu")
metrics_table <- cbind(row_names, metrics_table)
metrics_table <- metrics_table %>%
as.tibble()O resultado da aplicação dos dados no conjunto de treinamento, as métricas para análise do melhor método, o qual deve ser aplicado nesses dados segue abaixo.
colnames(metrics_table) <- c("Método", "Acurácia", "Curva Roc", "f_means", "Precisão", "Recall", "Específicidade", "Kappa")
metrics_table <- metrics_table %>%
mutate(Acurácia = as.numeric(Acurácia),
`Curva Roc` = as.numeric(`Curva Roc`),
f_means = as.numeric(f_means),
Precisão = as.numeric(Precisão),
Recall = as.numeric(Recall),
Específicidade = as.numeric(Específicidade),
Kappa = as.numeric(Kappa)) %>%
arrange(desc(Acurácia), desc(`Curva Roc`), desc(f_means), desc(Kappa))
metrics_table %>%
gt::gt() %>%
gt::tab_header(
title = gt::html("<b> Resultado dos modelos nos dados de teste</b>"),
subtitle = glue::glue("De acordo com algumas métricas")) %>%
gt::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
) %>%
gt::data_color(
columns = Acurácia,
colors = scales::col_numeric(
palette = colorspace::sequential_hcl(n = 10, palette = "Green"),
domain = c(min(metrics_table$Acurácia), max(metrics_table$Acurácia)),
reverse = TRUE
)
) %>%
gt::data_color(
columns = `Curva Roc`,
colors = scales::col_numeric(
palette = colorspace::sequential_hcl(n = 10, palette = "Green"),
domain = c(min(metrics_table$`Curva Roc`), max(metrics_table$`Curva Roc`)),
reverse = TRUE
)
) %>%
cols_align(align = "center", columns = everything()) %>%
tab_style(
style = list(
cell_text(weight = "bold")
),
locations = cells_body(columns = c(Acurácia, `Curva Roc`, f_means, Kappa))
)| Resultado dos modelos nos dados de teste | |||||||
| De acordo com algumas métricas | |||||||
| Método | Acurácia | Curva Roc | f_means | Precisão | Recall | Específicidade | Kappa |
|---|---|---|---|---|---|---|---|
| SVM Polinomial | 0.8783 | 0.8600 | 0.8866 | 0.8350 | 0.9134 | 0.7525 | 0.9362 |
| SVM Linear | 0.8783 | 0.8600 | 0.8866 | 0.8350 | 0.9134 | 0.7525 | 0.9349 |
| Regressão Logística | 0.8739 | 0.8557 | 0.8776 | 0.8350 | 0.9055 | 0.7439 | 0.9311 |
| Floresta Aleatória | 0.8739 | 0.8528 | 0.8936 | 0.8155 | 0.9213 | 0.7429 | 0.9283 |
| Rede Neural - Multi Camada | 0.8696 | 0.8500 | 0.8763 | 0.8252 | 0.9055 | 0.7348 | 0.9317 |
| SVM RBF | 0.8696 | 0.8469 | 0.8925 | 0.8058 | 0.9213 | 0.7338 | 0.9310 |
| Discriminante Linear | 0.8652 | 0.8458 | 0.8673 | 0.8252 | 0.8976 | 0.7262 | 0.9329 |
| XG Boost | 0.8652 | 0.8458 | 0.8673 | 0.8252 | 0.8976 | 0.7262 | 0.9245 |
| Bageed Tree | 0.8565 | 0.8308 | 0.8804 | 0.7864 | 0.9134 | 0.7069 | 0.8960 |
| KNN | 0.8478 | 0.8259 | 0.8469 | 0.8058 | 0.8819 | 0.6909 | 0.9059 |
| Rede Neural - Multi Camada - Relu | 0.8435 | 0.8144 | 0.8681 | 0.7670 | 0.9055 | 0.6800 | 0.9220 |
| Naive Bayes | 0.8348 | 0.8173 | 0.8095 | 0.8252 | 0.8425 | 0.6665 | 0.8849 |
| Árvore de Decisão | 0.8348 | 0.8021 | 0.8652 | 0.7476 | 0.9055 | 0.6616 | 0.8486 |
| Discriminante Quadrático | 0.8261 | 0.8148 | 0.7788 | 0.8544 | 0.8031 | 0.6515 | 0.9288 |
| Fonte: Elaboração própria | |||||||
test_results_rf1 = grid_results %>%
extract_workflow("Reg_log") %>%
finalize_workflow(best_set_linear) %>%
last_fit(split = split_inicial,
metrics = metric_set(
recall, precision, f_meas,
accuracy, kap,
roc_auc, sens, spec)
)Visualizando a importância das variáveis para o problema de classificação com base no melhor modelo que foi selecionando, tendo esse modelo via método Regressão Logística, e com os seguintes parâmetros:
O valor da penalização do modelo escolhido de Regressão Logística foi de 0.0142;
O valor da mistura do modelo escolhido de Regressão Logística foi de 0.7454.
test_results_rf1 %>%
pluck(".workflow",1) %>%
extract_fit_parsnip() %>%
vip::vip()Comentário: Acima podemos analisar o grau de importância das variáveis.
test_results_psvm = grid_results %>%
extract_workflow("psvm") %>%
finalize_workflow(best_set_psvm) %>%
last_fit(split = split_inicial,
metrics = metric_set(
recall, precision, f_meas,
accuracy, kap,
roc_auc, sens, spec)
)psvm_pred <- test_results_psvm %>%
collect_predictions()psvm_pred %>%
conf_mat(HeartDisease, .pred_class) %>%
autoplot(type = "heatmap")psvm_pred %>%
roc_curve(HeartDisease, .pred_Normal) %>%
autoplot()test_results_mlp = grid_results %>%
extract_workflow("rede_neural2") %>%
finalize_workflow(best_set_mlp2) %>%
last_fit(split = split_inicial,
metrics = metric_set(
recall, precision, f_meas,
accuracy, kap,
roc_auc, sens, spec)
)rede_pred <- test_results_mlp %>%
collect_predictions()rede_pred %>%
conf_mat(HeartDisease, .pred_class) %>%
autoplot(type = "heatmap")rede_pred %>%
roc_curve(HeartDisease, .pred_Normal) %>%
autoplot()test_results_rf = grid_results %>%
extract_workflow("random_fore") %>%
finalize_workflow(best_set_rand_fore) %>%
last_fit(split = split_inicial,
metrics = metric_set(
recall, precision, f_meas,
accuracy, kap,
roc_auc, sens, spec)
)rf_pred <- test_results_rf %>%
collect_predictions()rf_pred %>%
conf_mat(HeartDisease, .pred_class) %>%
autoplot(type = "heatmap")rf_pred %>%
roc_curve(HeartDisease, .pred_Normal) %>%
autoplot()Na tarefa passada, sem aplicar alguns desses métodos e com menos variáveis, tivemos uma acurácia menor do que 80%, agora com a aplicação desses métodos e com a utilização de mais variáveis, podemos observar que através da acurácia o melhor modelo foi SVM Polinomial e a Acurácia foi 0.8783, e o pior método pela acurácia foi o método Discriminante Quadrático.