# Enfermedad cardiaca y frecuencia cardiaca
# https://rpubs.com/JOJOQUIMO/Heart_Disease_and_thalach
#C:\Users\HP\Downloads\Heart_Disease_Prediction.xlsx
datos <- read.csv("Heart_Disease_Prediction.csv", head=T, sep=";")
str(datos)
## 'data.frame': 270 obs. of 14 variables:
## $ Age : int 70 67 57 64 74 65 56 59 60 63 ...
## $ Sex : int 1 0 1 1 0 1 1 1 1 0 ...
## $ cp : int 4 3 2 4 2 4 3 4 4 4 ...
## $ trestbps : int 130 115 124 128 120 120 130 110 140 150 ...
## $ Chol : int 322 564 261 263 269 177 256 239 293 407 ...
## $ fbs : int 0 0 0 0 0 0 1 0 0 0 ...
## $ EKG : int 2 2 0 0 2 0 2 2 2 2 ...
## $ thalach : int 109 160 141 105 121 140 142 142 170 154 ...
## $ exang : int 0 0 0 1 1 0 1 1 0 0 ...
## $ oldpeak : chr "2,4" "1,6" "0,3" "0,2" ...
## $ Slope : int 2 2 1 2 1 1 2 2 2 2 ...
## $ ca : int 3 0 0 1 1 0 1 1 2 3 ...
## $ Thal : int 3 7 7 7 3 7 6 7 7 7 ...
## $ Heart.Disease: int 1 0 1 0 0 0 1 1 1 1 ...
# convirtiendo a factor la variable target y etiquetando
datos$Heart.Disease <- factor(datos$Heart.Disease,levels = c("0","1"),
labels=c("no", "yes"))
str(datos)
## 'data.frame': 270 obs. of 14 variables:
## $ Age : int 70 67 57 64 74 65 56 59 60 63 ...
## $ Sex : int 1 0 1 1 0 1 1 1 1 0 ...
## $ cp : int 4 3 2 4 2 4 3 4 4 4 ...
## $ trestbps : int 130 115 124 128 120 120 130 110 140 150 ...
## $ Chol : int 322 564 261 263 269 177 256 239 293 407 ...
## $ fbs : int 0 0 0 0 0 0 1 0 0 0 ...
## $ EKG : int 2 2 0 0 2 0 2 2 2 2 ...
## $ thalach : int 109 160 141 105 121 140 142 142 170 154 ...
## $ exang : int 0 0 0 1 1 0 1 1 0 0 ...
## $ oldpeak : chr "2,4" "1,6" "0,3" "0,2" ...
## $ Slope : int 2 2 1 2 1 1 2 2 2 2 ...
## $ ca : int 3 0 0 1 1 0 1 1 2 3 ...
## $ Thal : int 3 7 7 7 3 7 6 7 7 7 ...
## $ Heart.Disease: Factor w/ 2 levels "no","yes": 2 1 2 1 1 1 2 2 2 2 ...
# Representación de las observaciones
require(ggplot2)
## Cargando paquete requerido: ggplot2
require(gridExtra)
## Cargando paquete requerido: gridExtra
require(caret)
## Cargando paquete requerido: caret
## Cargando paquete requerido: lattice
require(tidyverse)
## Cargando paquete requerido: tidyverse
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ lubridate 1.9.3 ✔ tibble 3.2.1
## ✔ purrr 1.0.2 ✔ tidyr 1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::combine() masks gridExtra::combine()
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ✖ purrr::lift() masks caret::lift()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
datos %>%
group_by(datos$Heart.Disease) %>%
summarise( numero_casos = n(),
porcentaje = numero_casos / nrow(datos)
)
## # A tibble: 2 × 3
## `datos$Heart.Disease` numero_casos porcentaje
## <fct> <int> <dbl>
## 1 no 150 0.556
## 2 yes 120 0.444
qplot(Heart.Disease, data = datos, col = Heart.Disease,
main = " ",geom = "bar") +
geom_text(aes(label=scales::percent(..count../sum(..count..))),
stat='count',position=position_stack(0.5))+
geom_text(aes(label=..count..),
stat="count",position=position_stack())
## Warning: `qplot()` was deprecated in ggplot2 3.4.0.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

# Histograma de la variable cuantitativa
ggplot(datos, aes(x=thalach))+
geom_histogram(color="steelblue", fill="lightblue")+
ggtitle("Histograma de notas de matemáticas")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

# grafico de cajas
p1 <- ggplot(data = datos, aes(x = thalach, y = Heart.Disease)) +
geom_point(aes(color = Heart.Disease)) +
theme_bw() + theme(legend.position = "null")
p2 <- ggplot(data = datos, aes(x = Heart.Disease, y = thalach , color=Heart.Disease)) +
geom_boxplot(outlier.shape=NA) +
geom_jitter (width=0.1) +
theme_bw() +
theme(legend.position = "null")
grid.arrange(p1, p2, nrow = 1)

# Estadísticos descriptivos
datos %>% filter(!is.na(thalach)) %>% group_by(Heart.Disease) %>%
summarise(media = mean(thalach),
mediana = median(thalach),
min = min(thalach),
max = max(thalach))
## # A tibble: 2 × 5
## Heart.Disease media mediana min max
## <fct> <dbl> <dbl> <int> <int>
## 1 no 158. 161 96 202
## 2 yes 139. 142. 71 195
# estimación del modelo
modelo <- glm(Heart.Disease ~ thalach, data = datos, family = "binomial")
summary(modelo)
##
## Call:
## glm(formula = Heart.Disease ~ thalach, family = "binomial", data = datos)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 6.159089 1.020518 6.035 1.59e-09 ***
## thalach -0.042753 0.006767 -6.318 2.64e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 370.96 on 269 degrees of freedom
## Residual deviance: 320.04 on 268 degrees of freedom
## AIC: 324.04
##
## Number of Fisher Scoring iterations: 4
# exponenciando para facilitar la interpretación de los coeficientes
round(exp(coefficients(modelo)),6)
## (Intercept) thalach
## 472.996901 0.958148
# Intervalos de Confianza para los coeficientes
round(confint(modelo, level=0.95),5)
## Waiting for profiling to be done...
## 2.5 % 97.5 %
## (Intercept) 4.23925 8.25190
## thalach -0.05662 -0.03002
# Tasa de ventajas e IC 95%
round(exp(cbind(OR = coef(modelo), confint(modelo,level=0.95))),5)
## Waiting for profiling to be done...
## OR 2.5 % 97.5 %
## (Intercept) 472.99690 69.35609 3834.91473
## thalach 0.95815 0.94496 0.97043
# El mismo calculo se puede obtener directamente con:
anova(modelo, test = "Chisq")
## Analysis of Deviance Table
##
## Model: binomial, link: logit
##
## Response: Heart.Disease
##
## Terms added sequentially (first to last)
##
##
## Df Deviance Resid. Df Resid. Dev Pr(>Chi)
## NULL 269 370.96
## thalach 1 50.919 268 320.04 9.627e-13 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
library(ResourceSelection)
## ResourceSelection 0.3-6 2023-06-27
hoslem.test(datos$Heart.Disease, fitted(modelo))
## Warning in Ops.factor(1, y): '-' no es significativo para factores
##
## Hosmer and Lemeshow goodness of fit (GOF) test
##
## data: datos$Heart.Disease, fitted(modelo)
## X-squared = NA, df = 8, p-value = NA
# medidas tipo R2
# Pseudo R2 de McFadden
library(pscl)
## Classes and Methods for R originally developed in the
## Political Science Computational Laboratory
## Department of Political Science
## Stanford University (2002-2015),
## by and under the direction of Simon Jackman.
## hurdle and zeroinfl functions by Achim Zeileis.
pscl :: pR2 (modelo) ["McFadden"]
## fitting null model for pseudo-r2
## McFadden
## 0.1372624
# medidas tipo R2
# Pseudo R2 de McFadden
(RsqrMcFadden <- 1 - modelo$deviance/modelo$null.deviance)
## [1] 0.1372624
# Pseudo R2 de Cox y Snell
LR <- modelo$null.deviance - modelo$deviance
N <- sum(weights(modelo))
(RsqrCN <- 1 - exp(-LR/N))
## [1] 0.1718724
# Pseudo R2de Nlkerke
L0.adj <- exp(-modelo$null.deviance/N)
(RsqrNal <- RsqrCN/(1 - L0.adj))
## [1] 0.230118
# criterio AIC (para comparar modelos)
AIC(modelo)
## [1] 324.0405
# BIC (para comparar modelos) criterio de inform, bayesiana
BIC(modelo)
## [1] 331.2373
# CURVA ROC Y AREA BAJO LA CURVA
library(stats)
library(pROC)
## Type 'citation("pROC")' for a citation.
##
## Adjuntando el paquete: 'pROC'
##
## The following objects are masked from 'package:stats':
##
## cov, smooth, var
prob=predict(modelo,type="response")
rocA1C <- with(datos,roc(Heart.Disease, prob))
## Setting levels: control = no, case = yes
## Setting direction: controls < cases
plot(rocA1C, col="red", print.auc=TRUE)

closest <- coords(rocA1C,"b",ret=c("threshold","specificity","sensitivity","npv","ppv"),
best.method="closest.topleft")
closest
## threshold specificity sensitivity npv ppv
## threshold 0.4633215 0.7666667 0.6333333 0.7232704 0.6846847
#Calculando area bajo la curva
areaROC<-auc(roc(datos$Heart.Disease,prob))
## Setting levels: control = no, case = yes
## Setting direction: controls < cases
ROC<-plot.roc(datos$Heart.Disease,prob, xlab="1- Especificidad", ylab="Sensibilidad",
main = paste('Area Bajo la Curva =',round(areaROC,4)), col="blue")
## Setting levels: control = no, case = yes
## Setting direction: controls < cases

#library(InformationValue)
#plotROC(sjlabelled::as_numeric(datos$Heart.Disease,start.at = 0), prob)
# probabilidades y grupos estimados
prob=predict(modelo,type="response")
head(prob,20)
## 1 2 3 4 5 6 7 8
## 0.8174253 0.3359470 0.5326784 0.8415775 0.7282897 0.5433046 0.5220226 0.5220226
## 9 10 11 12 13 14 15 16
## 0.2480686 0.3953472 0.3264772 0.8043164 0.1770489 0.4899733 0.3455505 0.6931633
## 17 18 19 20
## 0.7366668 0.3851742 0.5006603 0.1898531
library(vcd)
## Cargando paquete requerido: grid
predicciones <- ifelse(test = modelo$fitted.values > 0.5, yes = 1, no = 0)
head(predicciones,20)
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
## 1 0 1 1 1 1 1 1 0 0 0 1 0 0 0 1 1 0 1 0
matriz_confusion <- table(modelo$model$Heart.Disease, predicciones,
dnn = c("observaciones", "predicciones"))
matriz_confusion
## predicciones
## observaciones 0 1
## no 119 31
## yes 52 68
# Convertir Heart.Disease a factor con niveles 0 y 1
if (!is.factor(datos$Heart.Disease)) {
datos$Heart.Disease <- as.factor(datos$Heart.Disease)
}
levels(datos$Heart.Disease) <- c(0, 1) # Asegurar los niveles correctos
# Convertir las predicciones a factor con los mismos niveles
predicciones <- factor(predicciones, levels = c(0, 1))
# Crear la matriz de confusión usando la función confusionMatrix
matriz_confusion <- confusionMatrix(predicciones, datos$Heart.Disease)
# Extraer la tabla de contingencia de la matriz de confusión
tabla_contingencia <- matriz_confusion$table
# Calcular el error 1
error1 <- sum(tabla_contingencia[1, 2], tabla_contingencia[2, 1]) / sum(tabla_contingencia)
print(error1)
## [1] 0.3074074
# Preparar la base de datos para guardar valores predichos y probabilidades
prob <- predict(modelo, type = "response")
finaldata <- cbind(datos, prob, predicciones)
head(finaldata, 20)
## Age Sex cp trestbps Chol fbs EKG thalach exang oldpeak Slope ca Thal
## 1 70 1 4 130 322 0 2 109 0 2,4 2 3 3
## 2 67 0 3 115 564 0 2 160 0 1,6 2 0 7
## 3 57 1 2 124 261 0 0 141 0 0,3 1 0 7
## 4 64 1 4 128 263 0 0 105 1 0,2 2 1 7
## 5 74 0 2 120 269 0 2 121 1 0,2 1 1 3
## 6 65 1 4 120 177 0 0 140 0 0,4 1 0 7
## 7 56 1 3 130 256 1 2 142 1 0,6 2 1 6
## 8 59 1 4 110 239 0 2 142 1 1,2 2 1 7
## 9 60 1 4 140 293 0 2 170 0 1,2 2 2 7
## 10 63 0 4 150 407 0 2 154 0 4 2 3 7
## 11 59 1 4 135 234 0 0 161 0 0,5 2 0 7
## 12 53 1 4 142 226 0 2 111 1 0 1 0 7
## 13 44 1 3 140 235 0 2 180 0 0 1 0 3
## 14 61 1 1 134 234 0 0 145 0 2,6 2 2 3
## 15 57 0 4 128 303 0 2 159 0 0 1 1 3
## 16 71 0 4 112 149 0 0 125 0 1,6 2 0 3
## 17 46 1 4 140 311 0 0 120 1 1,8 2 2 7
## 18 53 1 4 140 203 1 2 155 1 3,1 3 0 7
## 19 64 1 1 110 211 0 2 144 1 1,8 2 0 3
## 20 40 1 1 140 199 0 0 178 1 1,4 1 0 7
## Heart.Disease prob predicciones
## 1 1 0.8174253 1
## 2 0 0.3359470 0
## 3 1 0.5326784 1
## 4 0 0.8415775 1
## 5 0 0.7282897 1
## 6 0 0.5433046 1
## 7 1 0.5220226 1
## 8 1 0.5220226 1
## 9 1 0.2480686 0
## 10 1 0.3953472 0
## 11 0 0.3264772 0
## 12 0 0.8043164 1
## 13 0 0.1770489 0
## 14 1 0.4899733 0
## 15 0 0.3455505 0
## 16 0 0.6931633 1
## 17 1 0.7366668 1
## 18 1 0.3851742 0
## 19 0 0.5006603 1
## 20 0 0.1898531 0
# Predicción para nuevos individuos
nuevo1 <- data.frame(thalach = 60)
print(predict(modelo, newdata = nuevo1, type = "response"))
## 1
## 0.9732447
nuevo1 <- data.frame(thalach = 20)
print(predict(modelo, newdata = nuevo1, type = "response"))
## 1
## 0.9950531
nuevo1 <- data.frame(thalach = 63)
print(predict(modelo, newdata = nuevo1, type = "response"))
## 1
## 0.9696942
# Gráfico del modelo: SIN INTERVALOS DE CONFIANZA
# Convirtiendo en numerico (0,1) la variable respuesta
datos$Heart.Disease <- as.character(datos$Heart.Disease)
datos$Heart.Disease <- as.numeric(datos$Heart.Disease)
plot(Heart.Disease ~ thalach, datos, col = "darkblue",
main = "Modelo regresion logistica Heart.Disease ~ nota thalach",
ylab = "P(Heart.Disease = 1 | thalach)", xlab = "thalach", pch = 19)
# type = 'response' devuelve las predicciones en forma de probabilidad en
# lugar de en log_ODDs
curve(predict(modelo, data.frame(thalach = x), type = "response"),
col = "firebrick", lwd = 2.5, add = TRUE)

# MEDIANTE GGPLOT2 INCLUYENDO INTERVALOS DE CONFIANZA
nuevos_puntos <- seq(from = min(datos$thalach), to = max(datos$thalach), by = 0.5)
# Predicciones de los nuevos puntos segun el modelo
predicciones <- predict(modelo, data.frame(thalach = nuevos_puntos), se.fit = TRUE)
# Mediante la funcion logit se transforman los log_ODDs a probabilidades
predicciones_logit <- exp(predicciones$fit) / (1 + exp(predicciones$fit))
# Se calcula el limite inferior y superior del IC del 95%
limite_inferior <- predicciones$fit - 1.96 * predicciones$se.fit
limite_inferior_logit <- exp(limite_inferior) / (1 + exp(limite_inferior))
limite_superior <- predicciones$fit + 1.96 * predicciones$se.fit
limite_superior_logit <- exp(limite_superior) / (1 + exp(limite_superior))
# Se crea un data frame con los nuevos puntos y sus predicciones
datos_curva <- data.frame(thalach = nuevos_puntos,
probabilidad_Heart.Disease = predicciones_logit,
limite_inferior_logit = limite_inferior_logit,
limite_superior_logit = limite_superior_logit)
ggplot(datos, aes(x = thalach, y = Heart.Disease)) +
geom_point(aes(color = as.factor(Heart.Disease)), shape = 19, size = 3) +
geom_line(data = datos_curva, aes(y = probabilidad_Heart.Disease), color = "firebrick") +
geom_line(data = datos_curva, aes(y = limite_inferior_logit), linetype = "dashed") +
geom_line(data = datos_curva, aes(y = limite_superior_logit), linetype = "dashed") +
theme_bw() +
labs(title = "Modelo regresion logistica Heart.Disease ~ nota thalach",
y = "P(Heart.Disease = 1 | thalach)", x = "thalach") +
theme(legend.position = "none") +
theme(plot.title = element_text(hjust = 0.5))

# Modelo gausiano
modelo_gaussiano <- glm(Heart.Disease ~ thalach, data = datos, family = "gaussian")
summary(modelo_gaussiano)
##
## Call:
## glm(formula = Heart.Disease ~ thalach, family = "gaussian", data = datos)
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.790614 0.180564 9.917 < 2e-16 ***
## thalach -0.008994 0.001192 -7.544 7.12e-13 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for gaussian family taken to be 0.2051856)
##
## Null deviance: 66.667 on 269 degrees of freedom
## Residual deviance: 54.990 on 268 degrees of freedom
## AIC: 342.58
##
## Number of Fisher Scoring iterations: 2