Código
setwd("\\Users\\paulo\\OneDrive\\Área de Trabalho\\ESTATÍSTICA\\UFPB\\8º PERÍODO\\ANÁLISE MULTIVARIADA II\\PROVA")
<- read.csv2("heart.csv", header = T, sep = ",") banco
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")
<- read.csv2("heart.csv", header = T, sep = ",") banco
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…
$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) banco
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:
::vis_miss(banco) visdat
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:
::vis_dat(banco) visdat
skim
do pacote skimr
skim(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
<- banco %>%
desc_idade 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.
<- banco %>%
desc_MAXHR 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.
<- banco %>%
desc_restingBP 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.
<- banco %>%
desc_Choles 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.
<- banco %>%
desc_old_peak 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
<- banco %>%
rho ::select(where(is.numeric)) %>%
dplyrcor()
::corrplot(rho, method = "square", type = "full") corrplot
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.
<- round(rho,2)
rho 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)
<- banco %>%
desc_heart ::group_by(HeartDisease) %>%
dplyr::summarise(quantidade = n(),
dplyr= round(n()/dim(banco)[1]*100,2))
proporção
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::tab_header(
gttitle = html("<b> Situação dos pacientes quanto a presença de doença cardíaca</b>"),
subtitle = "Com relação ao eletrocardiograma em repouso"
%>%
) ::cols_label(
gtRestingECG = "ECG em Repouso",
n = "Frequência",
percent = "Percentual"
%>%
) ::fmt_number(
gtcolumns = 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::tab_header(
gttitle = 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"
%>%
) ::cols_label(
gtST_Slope = "Inclinação do Segmento",
n = "Frequência",
percent = "Percentual"
%>%
) ::fmt_number(
gtcolumns = 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::tab_header(
gttitle = html("<b> Situação dos pacientes quanto a presença de doença cardíaca</b>"),
subtitle = "Com relação ao Sexo"
%>%
) ::cols_label(
gtSex = "Sexo",
n = "Frequência",
percent = "Percentual"
%>%
) ::fmt_number(
gtcolumns = 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::tab_header(
gttitle = html("<b> Situação dos pacientes quanto a presença de doença cardíaca </b>"),
subtitle = "Com relação a dor no peito"
%>%
) ::cols_label(
gtChestPainType = "Tipo de dor no peito",
n = "Frequência",
percent = "Percentual"
%>%
) ::fmt_number(
gtcolumns = 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::tab_header(
gttitle = 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"
%>%
) ::cols_label(
gtFastingBS = "Açucar no Sangue",
n = "Frequência",
percent = "Percentual"
%>%
) ::fmt_number(
gtcolumns = 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)
<- initial_split(banco, prop = 0.75, strata = HeartDisease)
split_inicial <- training(split_inicial)
banco_treino <- testing(split_inicial) banco_teste
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.
<- recipe(HeartDisease ~ ., data = banco_treino) %>%
banco_receita # 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 %>% # usa a receita
banco_receita prep() %>% # aplica a receita no conjunto de treinamento
juice() # extrai apenas o dataframe preprocessado
Quantidade de Observações no banco de Treinamento, de acordo com a variável destino, ou feature target, que é a Heart Disease
<- banco_preparado %>%
tabela group_by(HeartDisease) %>%
summarise(quantidade = n(),
percentual = n()/dim(banco_preparado)[1])
colnames(tabela) <- c("Heart Disease","Quantidade", "Proporção")
%>%
tabela ::gt() %>%
gttab_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")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração Própria")
gt%>%
) 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.
<- vfold_cv(banco_treino,
cv_folds 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.
<- nearest_neighbor(neighbors = tune()) %>% # K-NN
knn_spec set_mode("classification") %>%
set_engine("kknn")
<- naive_Bayes() %>% # Naive Bayes
nbayes_spec set_engine("naivebayes") %>%
set_mode("classification")
<- discrim_linear() %>% # Linear discriminant analysis
lda_spec set_engine("MASS") %>%
set_mode("classification")
<- discrim_quad() %>% # Quadratic discriminant analysis
qda_spec set_engine("MASS") %>%
set_mode("classification")
<- logistic_reg(penalty = tune(), mixture = tune()) %>% # RL
reg_log_spec set_engine(engine = "glmnet", standardize = FALSE) %>%
set_mode("classification")
<- decision_tree(cost_complexity = tune(),
dec_tree min_n = tune(),
tree_depth = tune()) %>% # Decision tree
set_engine(engine = "rpart") %>%
set_mode("classification")
<- bag_tree(cost_complexity = tune(),
bagg_tree min_n = tune(),
tree_depth = tune()) %>% # Bagged tree
set_engine(engine = "rpart") %>%
set_mode("classification")
<- rand_forest(mtry = tune(),
random_forest min_n = tune(),
trees = tune()) %>% # Random Forest
set_engine(engine = "ranger", importance = "impurity") %>%
set_mode("classification")
<- boost_tree(tree_depth = tune(),
xgboost 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")
<- svm_linear(cost = tune(), # SVM Linear
lsvm margin = tune()) %>%
set_engine(engine = "kernlab") %>%
set_mode("classification")
<- svm_rbf(cost = tune(), # RBF/Gaussian Kernel SVM
rsvm_spec rbf_sigma = tune(),
margin = tune()) %>%
set_engine(engine = "kernlab") %>%
set_mode("classification")
<- svm_poly(cost = tune(), # Polynomial Kernel SVM
psvm_spec degree = tune(),
scale_factor = tune(),
margin = tune()) %>%
set_engine(engine = "kernlab") %>%
set_mode("classification")
<- mlp(hidden_units = tune(), # MLP - Perceptron com multi camadas
rede epochs = tune(),
activation = "softmax",
penalty = tune(),
mode = "classification",
engine = "nnet")
<- mlp(hidden_units = tune(),
rede2 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.= workflow_set(
wf 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))
<- Sys.time()
tempo_inicial = control_grid(
grid_ctrl save_pred = TRUE,
parallel_over = "resamples",
save_workflow = TRUE
)
= wf %>%
grid_results workflow_map(
seed = 2024,
resamples = cv_folds,
grid = 5,
control = grid_ctrl)
<- Sys.time()
tempo_final
<- as.numeric(difftime(tempo_final, tempo_inicial, units = "secs")) tempo
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")
<- workflowsets::rank_results(grid_results,
results select_best = TRUE,
rank_metric = "roc_auc") %>%
filter(.metric == "roc_auc") %>%
::select(wflow_id, mean, std_err, model, rank)
dplyr
colnames(results) <- c("Método", "Média", "Desvio Padrão", "Modelo", "Ranking")
$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() %>%
results ::tab_header(
gttitle = gt::html("<b> Resultado do Treinamento dos Modelos</b>"),
subtitle = glue::glue("De acordo com a métrica da curva Roc")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) ::data_color(
gtcolumns = 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")
<- workflowsets::rank_results(grid_results,
results_acc select_best = TRUE,
rank_metric = "accuracy") %>%
filter(.metric == "accuracy") %>%
::select(wflow_id, mean, std_err, model, rank)
dplyr
colnames(results_acc) <- c("Método", "Média", "Desvio Padrão", "Modelo", "Ranking")
$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() %>%
results_acc ::tab_header(
gttitle = gt::html("<b> Resultado do Treinamento dos Modelos</b>"),
subtitle = glue::glue("De acordo com a métrica da Acurácia")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) ::data_color(
gtcolumns = 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.
= grid_results %>%
best_set_linear extract_workflow_set_result("Reg_log") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_knn extract_workflow_set_result("KNN") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_nbayes extract_workflow_set_result("Nayve_Bayes") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_lda extract_workflow_set_result("LDA") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_qda extract_workflow_set_result("QDA") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_rand_fore extract_workflow_set_result("random_fore") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_decision extract_workflow_set_result("decision") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_bag extract_workflow_set_result("bag_treeee") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_xbost extract_workflow_set_result("xgbost") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_svmlin extract_workflow_set_result("svm_lin") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_svmrf extract_workflow_set_result("svm_rf") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_psvm extract_workflow_set_result("psvm") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_mlp extract_workflow_set_result("rede_neural") %>%
select_best(metric = "accuracy")
= grid_results %>%
best_set_mlp2 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() %>%
::tab_header(
gttitle = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de Regressão Logística")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) ::cols_label(
gtpenalty = "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() %>%
::tab_header(
gttitle = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de Árvore de Decisão")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) 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() %>%
::tab_header(
gttitle = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de Bageed Tree")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) 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() %>%
::tab_header(
gttitle = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de Floresta Aleatória")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) 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() %>%
::tab_header(
gttitle = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo XGBOOST")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) 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() %>%
::tab_header(
gttitle = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de SVM Linear")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) 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() %>%
::tab_header(
gttitle = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de SVM RBF")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) 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() %>%
::tab_header(
gttitle = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste</b>"),
subtitle = glue::glue("Do modelo de SVM Polinomial")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) 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() %>%
::tab_header(
gttitle = gt::html("<b> Resultado dos hiperparâmetros do melhor ajuste </b>"),
subtitle = glue::glue("Do modelo de Rede Neural Perceptron com Multi Camadas")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) 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() %>%
::tab_header(
gttitle = 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")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) 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.
<- function(rc_rslts, fit_obj, par_set, split_obj) {
resultado_teste <- rc_rslts %>%
res 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(grid_results, "Reg_log", best_set_linear, split_inicial)
resultado_teste_reg_log <- resultado_teste(grid_results, "KNN", best_set_knn, split_inicial)
resultado_teste_knn <- resultado_teste(grid_results, "LDA", best_set_lda, split_inicial)
resultado_teste_lda <- resultado_teste(grid_results, "QDA", best_set_qda, split_inicial)
resultado_teste_qda <- resultado_teste(grid_results, "Nayve_Bayes", best_set_nbayes, split_inicial)
resultado_teste_naive <- resultado_teste(grid_results, "decision", best_set_decision, split_inicial)
resultado_teste_decision <- resultado_teste(grid_results, "bag_treeee", best_set_bag, split_inicial)
resultado_teste_bag <- resultado_teste(grid_results, "random_fore", best_set_rand_fore, split_inicial)
resultado_teste_rand_fore <- resultado_teste(grid_results, "xgbost", best_set_xbost, split_inicial)
resultado_teste_xgbost <- resultado_teste(grid_results, "svm_lin", best_set_svmlin, split_inicial)
resultado_teste_svmlin <- resultado_teste(grid_results, "svm_rf", best_set_svmrf, split_inicial)
resultado_teste_svmrf <- resultado_teste(grid_results, "psvm", best_set_psvm, split_inicial)
resultado_teste_psvm <- resultado_teste(grid_results, "rede_neural", best_set_mlp, split_inicial)
resultado_teste_mlp <- resultado_teste(grid_results, "rede_neural2", best_set_mlp2, split_inicial) resultado_teste_mlp2
<- rbind(collect_metrics(resultado_teste_reg_log)$.estimate,
metrics_table 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
<- round(metrics_table, 4)
metrics_table
<- 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")
row_names
<- cbind(row_names, metrics_table)
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),
= as.numeric(Precisão),
Precisão Recall = as.numeric(Recall),
= as.numeric(Específicidade),
Específicidade Kappa = as.numeric(Kappa)) %>%
arrange(desc(Acurácia), desc(`Curva Roc`), desc(f_means), desc(Kappa))
%>%
metrics_table ::gt() %>%
gt::tab_header(
gttitle = gt::html("<b> Resultado dos modelos nos dados de teste</b>"),
subtitle = glue::glue("De acordo com algumas métricas")) %>%
::tab_source_note(
gt::html("<b> Fonte:</b> Elaboração própria")
gt%>%
) ::data_color(
gtcolumns = 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
)%>%
) ::data_color(
gtcolumns = `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 |
= grid_results %>%
test_results_rf1 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.
= grid_results %>%
test_results_psvm 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) )
<- test_results_psvm %>%
psvm_pred collect_predictions()
%>%
psvm_pred conf_mat(HeartDisease, .pred_class) %>%
autoplot(type = "heatmap")
%>%
psvm_pred roc_curve(HeartDisease, .pred_Normal) %>%
autoplot()
= grid_results %>%
test_results_mlp 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) )
<- test_results_mlp %>%
rede_pred collect_predictions()
%>%
rede_pred conf_mat(HeartDisease, .pred_class) %>%
autoplot(type = "heatmap")
%>%
rede_pred roc_curve(HeartDisease, .pred_Normal) %>%
autoplot()
= grid_results %>%
test_results_rf 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) )
<- test_results_rf %>%
rf_pred 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.