Modulo: Analísis Exploratorio de Datos
Docente: Sebastian Moreno Rodriguez
Soy Jeiner Velandia, licenciado en Psicología y Pedagogía, con formación en maestría en TI aplicadas a la educación. Actualmente curso un doctorado en Gestión de la Innovación Tecnológica y una especialización en Métodos Estadísticos para el Análisis de Datos.
En este documento presento los 7 pasos de la actividad: instalación de paquetes, selección y limpieza de datos, creación de data frames, y el trabajo con variables, nominales, ordinales, discretas y continuas. Este data set se obtiene de kaggle https://www.kaggle.com/datasets/redwankarimsony/heart-disease-data, denominada UCI Heart Disease Data, con 16 variables, seis (6) integer, 6 string, 2 boleanas y 2 de tipo numerico.
Instalo y cargo las librerías necesarias: dplyr
,
ggplot2
, DescTools
, readr
,
patchwork
, e1071
y opcionalmente
plotly
.
#====================== 1. INSTALO PAQUETES CARGO LIBRERIAS ===========
install.packages("dplyr") # Pauete de manipulacion de datos
install.packages("ggplot2") # Liberia para usar gráficos
install.packages("e1071") # skewness y kurtosis (Fisher-Pearson)
install.packages("DescTools") # Skew(), Kurt(), BowleySkew()
install.packages("readr") #importar/exportar CSV
library(dplyr)
library(ggplot2)
library(e1071)
library(DescTools)
library(readr)
library(patchwork) # USAR FRAMES PARA GRAFICOS
Leo el archivo heart.csv
, reviso su estructura,
estandarizo los nombres de las variables, elimino duplicados, verifico
valores ausentes y reemplazo los ceros de cholesterol
por
NA
.
#====================== 2. LIMPIEZA Y ALISTAMIENTO BASE DE DATOS
# 1. Creo el objeto heart para que cargue el Data set de heart
# 2. Visualizo el data frame en crudo 10 head / 10 tail
# 3 Str - reviso la estructura de los datos y el tipo
# 4. tolower() → convierte todos los nombres a minusculas
# 5. gsub("[^0-9a-zA-Z]+", "_", names(heart)) → reemplaza cualquier símbolo,
heart <- read_csv("heart.csv")
## Rows: 918 Columns: 12
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): Sex, ChestPainType, RestingECG, ExerciseAngina, ST_Slope
## dbl (7): Age, RestingBP, Cholesterol, FastingBS, MaxHR, Oldpeak, HeartDisease
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(heart,10)
## # A tibble: 10 × 12
## Age Sex ChestPainType RestingBP Cholesterol FastingBS RestingECG MaxHR
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <chr> <dbl>
## 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
## # ℹ 4 more variables: ExerciseAngina <chr>, Oldpeak <dbl>, ST_Slope <chr>,
## # HeartDisease <dbl>
tail(heart,10)
## # A tibble: 10 × 12
## Age Sex ChestPainType RestingBP Cholesterol FastingBS RestingECG MaxHR
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <chr> <dbl>
## 1 63 M ASY 140 187 0 LVH 144
## 2 63 F ASY 124 197 0 Normal 136
## 3 41 M ATA 120 157 0 Normal 182
## 4 59 M ASY 164 176 1 LVH 90
## 5 57 F ASY 140 241 0 Normal 123
## 6 45 M TA 110 264 0 Normal 132
## 7 68 M ASY 144 193 1 Normal 141
## 8 57 M ASY 130 131 0 Normal 115
## 9 57 F ATA 130 236 0 LVH 174
## 10 38 M NAP 138 175 0 Normal 173
## # ℹ 4 more variables: ExerciseAngina <chr>, Oldpeak <dbl>, ST_Slope <chr>,
## # HeartDisease <dbl>
str(heart)
## spc_tbl_ [918 × 12] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ Age : num [1:918] 40 49 37 48 54 39 45 54 37 48 ...
## $ Sex : chr [1:918] "M" "F" "M" "F" ...
## $ ChestPainType : chr [1:918] "ATA" "NAP" "ATA" "ASY" ...
## $ RestingBP : num [1:918] 140 160 130 138 150 120 130 110 140 120 ...
## $ Cholesterol : num [1:918] 289 180 283 214 195 339 237 208 207 284 ...
## $ FastingBS : num [1:918] 0 0 0 0 0 0 0 0 0 0 ...
## $ RestingECG : chr [1:918] "Normal" "Normal" "ST" "Normal" ...
## $ MaxHR : num [1:918] 172 156 98 108 122 170 170 142 130 120 ...
## $ ExerciseAngina: chr [1:918] "N" "N" "N" "Y" ...
## $ Oldpeak : num [1:918] 0 1 0 1.5 0 0 0 0 1.5 0 ...
## $ ST_Slope : chr [1:918] "Up" "Flat" "Up" "Flat" ...
## $ HeartDisease : num [1:918] 0 1 0 1 0 0 0 0 1 0 ...
## - attr(*, "spec")=
## .. cols(
## .. Age = col_double(),
## .. Sex = col_character(),
## .. ChestPainType = col_character(),
## .. RestingBP = col_double(),
## .. Cholesterol = col_double(),
## .. FastingBS = col_double(),
## .. RestingECG = col_character(),
## .. MaxHR = col_double(),
## .. ExerciseAngina = col_character(),
## .. Oldpeak = col_double(),
## .. ST_Slope = col_character(),
## .. HeartDisease = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
names(heart) <- tolower(names(heart)) # Todo Minuscula
names(heart) <- gsub("[^0-9a-zA-Z]+", "_", names(heart)) # Elimina caracteres especiales
### IMPORTANTE uso el pipe para revisar que no haya filas repeditas
heart <- heart %>% distinct() # Distinct() elimina filas repetidas
colSums(is.na(heart)) # reviso columas con NA
## 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
#Como no hay datos NA en ninguna variable no debemos eliminar o remplazar datos======
Clasifico las variables en nominales, ordinales, discretas y continuas. Convierto las nominales en factores y las ordinales en factores ordenados.
#============================ 3. Categorizar las variables
# Voy a crear los objetos en forma de vector para guardar las variables según el tipo
nominales <- c("sex", "fastingbs", "exerciseangina", "heartdisease")
ordinales <- c("chestpaintype", "restingecg", "st_slope")
discretas <- c("age", "restingbp", "maxhr")
continuas <- c("cholesterol", "oldpeak")
Genero tablas básicas de frecuencias absolutas para variables
nominales y ordinales con lapply()
y
table()
.
#========================= 4. Convierto en factor Nominales y ordinales ===================
# mutate() permite agregar nuevas columnas a un data frame (o modificar las existentes)
# across() aplicar la misma transformación a todas columnas con datos similares
heart <- heart %>%
mutate(across(all_of(nominales), as.factor)) %>% # Aca le indico dentro del across que tome todas nomnales y las tome como factor
mutate(across(all_of(ordinales), ~factor(.x, # aca le digo que transfome en factor pero que ademas les de un nivel y las ordene (ORDINAL)
levels = sort(unique(.x)),
ordered = TRUE)))
#========== Verifico
#Verifico cuales son las variables nominales y ordinales y si son de factor y ordenada
sapply(heart[nominales], class)
## sex fastingbs exerciseangina heartdisease
## "factor" "factor" "factor" "factor"
sapply(heart[ordinales], class)
## chestpaintype restingecg st_slope
## [1,] "ordered" "ordered" "ordered"
## [2,] "factor" "factor" "factor"
#=========== vamos a crear las tablas de frecuenia y las vamos a guardar
#Lappy() es una función mas avanzada, como un For aplica la misma función a todas las variables
# table() cuenta frecuencias absolutas por defecto no requiero agregar una función pra las freceucias
Fre_Nominal <- lapply(heart[nominales], table)
Fre_Ordinal <- lapply(heart[ordinales], table)
# Reviso las tablas de cada variable
Fre_Nominal
## $sex
##
## F M
## 193 725
##
## $fastingbs
##
## 0 1
## 704 214
##
## $exerciseangina
##
## N Y
## 547 371
##
## $heartdisease
##
## 0 1
## 410 508
Fre_Ordinal
## $chestpaintype
##
## ASY ATA NAP TA
## 496 173 203 46
##
## $restingecg
##
## LVH Normal ST
## 188 552 178
##
## $st_slope
##
## Down Flat Up
## 63 460 395
Creo una función que calcula frecuencias absolutas, relativas,
porcentuales y acumuladas. La aplico con lapply()
para
generar tablas completas por tipo de variable.
# 1. Creo un función que es mas eficiente, por que muchas variables
tb_frecuencia <- function(x) { # abro función tb_frecuencia
x <- droplevels(x) # 1. Elimino los niveles no usados
fre_abs <- table(x,useNA="no") # Creo un objeto para guardar la frecuencia absoluta y le elimino los NA
fre_rela <- prop.table(fre_abs) # Creo un objeto para guardar la frecuencia relativa
n <- sum(fre_abs) # para que recorra las varibles de la función
#Dentro de la misma función creo el data frame con las variables y las frecuencias
df_fre <- data.frame(
categorias = names(fre_abs),
Frecuencia = as.numeric(fre_abs),
Frec_Relativa = round(as.numeric(fre_rela), 4),
Porcentaje = round(as.numeric(fre_rela) * 100, 2),
Frec_Acumulada = cumsum(as.numeric(fre_abs)),
Porc_Acumulado = round(cumsum(as.numeric(fre_abs)) / n * 100, 2),
row.names = NULL
)
# cierro el data frame
df_fre
} # ciero función Tb_frecuencia
#====================== ahora paso las variables a la función y lo guardo en el data frame======
tabla_nominal <- lapply(heart[nominales], tb_frecuencia) # Aplico Lappy a el df heart variables nominales, y aplico la función creada tb_frecuencia
tabla_ordinal <- lapply(heart[ordinales], tb_frecuencia)
tabla_nominal # Veo en la consola la tabla
## $sex
## categorias Frecuencia Frec_Relativa Porcentaje Frec_Acumulada Porc_Acumulado
## 1 F 193 0.2102 21.02 193 21.02
## 2 M 725 0.7898 78.98 918 100.00
##
## $fastingbs
## categorias Frecuencia Frec_Relativa Porcentaje Frec_Acumulada Porc_Acumulado
## 1 0 704 0.7669 76.69 704 76.69
## 2 1 214 0.2331 23.31 918 100.00
##
## $exerciseangina
## categorias Frecuencia Frec_Relativa Porcentaje Frec_Acumulada Porc_Acumulado
## 1 N 547 0.5959 59.59 547 59.59
## 2 Y 371 0.4041 40.41 918 100.00
##
## $heartdisease
## categorias Frecuencia Frec_Relativa Porcentaje Frec_Acumulada Porc_Acumulado
## 1 0 410 0.4466 44.66 410 44.66
## 2 1 508 0.5534 55.34 918 100.00
tabla_ordinal # Veo en la consola la tabla
## $chestpaintype
## categorias Frecuencia Frec_Relativa Porcentaje Frec_Acumulada Porc_Acumulado
## 1 ASY 496 0.5403 54.03 496 54.03
## 2 ATA 173 0.1885 18.85 669 72.88
## 3 NAP 203 0.2211 22.11 872 94.99
## 4 TA 46 0.0501 5.01 918 100.00
##
## $restingecg
## categorias Frecuencia Frec_Relativa Porcentaje Frec_Acumulada Porc_Acumulado
## 1 LVH 188 0.2048 20.48 188 20.48
## 2 Normal 552 0.6013 60.13 740 80.61
## 3 ST 178 0.1939 19.39 918 100.00
##
## $st_slope
## categorias Frecuencia Frec_Relativa Porcentaje Frec_Acumulada Porc_Acumulado
## 1 Down 63 0.0686 6.86 63 6.86
## 2 Flat 460 0.5011 50.11 523 56.97
## 3 Up 395 0.4303 43.03 918 100.00
Defino una función para calcular medidas de localización, dispersión y forma (media, mediana, moda, varianza, desviación estándar, asimetría, curtosis) y la aplico a las variables cuantitativas.
#=================== 6. Función para sacar Centralidad de Dispersión y de Forma ============
# 1) Función simple: centralidad, dispersión y forma
analizar <- function(x, nom) {
x <- as.numeric(x)
x <- x[!is.na(x)]
data.frame(
# LOCALIZACIÓN
Variable = nom,
n = length(x),
Media = mean(x),
Mediana = median(x),
Moda = as.numeric(names(which.max(table(x)))), # moda simple y rápida
#DISPERSIÓN
Varianza = var(x),
Desv_Est = sd(x),
Rango = max(x) - min(x),
IQR = IQR(x),
CV = ifelse(mean(x) == 0, NA_real_, sd(x) / mean(x) * 100),
#FORMA
Asimetria = e1071::skewness(x, type = 2), # Fisher–Pearson
Curtosis = e1071::kurtosis(x, type = 2), # exceso de curtosis (normal = 0)
row.names = NULL
)
}
#===========. Aplico la función a las variables discretas y continuas==========
# Aplico la función a cada variables y los pongo en una tabla
heart$cholesterol[heart$cholesterol == 0] <- NA #### Encontre valores de 0 que en la variable colesterol no es normal
# Verificar cuántos fueron reemplazados
sum(is.na(heart$cholesterol))
## [1] 172
# ================== VARIABLES DISCRETAS ==================
edad <- analizar(heart$age, "Edad")
presion <- analizar(heart$restingbp, "Presión en reposo")
frecuencia <- analizar(heart$maxhr, "Frecuencia Max")
# ================== VARIABLES CONTINUAS ==================
colesterol <- analizar(heart$cholesterol, "Colesterol")
oldpeak <- analizar(heart$oldpeak, "Oldpeak") # Pico de enfermedad
# ================== CONSTRUIR TABLA FINAL ==================
EstadisticosHeart <- rbind(edad, presion, frecuencia, colesterol, oldpeak)
#EstadisticosHeart
knitr::kable (EstadisticosHeart)
Variable | n | Media | Mediana | Moda | Varianza | Desv_Est | Rango | IQR | CV | Asimetria | Curtosis |
---|---|---|---|---|---|---|---|---|---|---|---|
Edad | 918 | 53.5108932 | 54.0 | 54 | 88.974254 | 9.432616 | 49.0 | 13.00 | 17.62747 | -0.1959330 | -0.3861396 |
Presión en reposo | 918 | 132.3965142 | 130.0 | 120 | 342.773903 | 18.514154 | 200.0 | 20.00 | 13.98387 | 0.1798393 | 3.2712509 |
Frecuencia Max | 918 | 136.8093682 | 138.0 | 150 | 648.228614 | 25.460334 | 142.0 | 36.00 | 18.61008 | -0.1443594 | -0.4482478 |
Colesterol | 746 | 244.6353887 | 237.0 | 254 | 3499.139363 | 59.153524 | 518.0 | 67.75 | 24.18028 | 1.2385344 | 4.5285924 |
Oldpeak | 918 | 0.8873638 | 0.6 | 0 | 1.137572 | 1.066570 | 8.8 | 1.50 | 120.19536 | 1.0228720 | 1.2030637 |
Genero gráficos con ggplot2
:
- Cualitativas → geom_bar()
- Ordinales → gráficos de frecuencia y porcentaje combinados con
patchwork
- Cuantitativas → histogramas, líneas de media y boxplots
- Bivariadas → relaciones entre variables continuas y categóricas con
geom_point()
, geom_boxplot()
y
geom_smooth()
.
#==============================***************** NOMINALES==========********************
# ➤ Variables nominales:
# sex → Sexo del paciente (Male/Female)
# fastingbs → Glucosa en ayunas >120 mg/dl (0 = No, 1 = Sí)
# exerciseangina → Angina inducida por ejercicio (Yes/No)
# heartdisease → Diagnóstico de enfermedad cardíaca (0 = No, 1 = Sí)
#SEXO
ggplot(heart, aes(x = sex)) +
geom_bar(color = "black", fill = "steelblue") + # barras por categoría
labs(title = "Distribución: Sex", x = "", y = "Frecuencia") +
theme_minimal()
# EXERCISEANGINA / angina inducida por ejercicio
ggplot(heart, aes(x = exerciseangina)) +
geom_bar(color = "black", fill = "cyan") +
labs(title = "Distribución: Exercise Angina", x = "", y = "Frecuencia") +
theme_minimal()
# FASTINGBS / glucosa en ayunas
ggplot(heart, aes(x = fastingbs)) +
geom_bar(color = "black", fill = "magenta") +
labs(title = "Distribución: FastingBS", x = "", y = "Frecuencia") +
theme_minimal()
# HEARTDISEASE ÷ ENFERMEDAD CARDIACA
ggplot(heart, aes(x = factor(heartdisease,
levels = c(0, 1),
labels = c("No", "Sí")))) +
geom_bar(color = "black", fill = "blue") +
labs(title = "Distribución: Heart Disease",
x = "Presencia de enfermedad cardíaca",
y = "Frecuencia") +
theme_minimal()
Analizo variables cualitativas ordinales:
chestpaintype
, restingecg
y
st_slope
. Para cada una presento dos
paneles:
1) Frecuencia absoluta (conteos), y
2) Porcentaje (proporciones, usando
after_stat(prop*100)
).
Esta dupla facilita comparar volumen y peso
relativo de cada nivel.
El orden de niveles respeta la semántica clínica
(p. ej., Up < Flat < Down
en st_slope
),
lo que mejora la interpretación: se observan gradientes
y tendencias coherentes con riesgo. Las
leyendas/captions aclaran siglas clínicas (TA
,
ATA
, NAP
, ASY
).
Puntos de lectura: identifique niveles predominantes,
diferencias porcentuales significativas y
patrones consistentes entre paneles (si un nivel domina
en frecuencia, debería reflejarse también en el porcentaje).
#==============================***************** ORDINALES==========********************
# chestpaintype → Tipo de dolor en el pecho (Typical, Atypical, Non-anginal, Asymptomatic)
# restingecg → Resultados del ECG en reposo (Normal, ST-T abnormality, Left ventricular hypertrophy)
# st_slope → Pendiente del segmento ST (Up- BUENO, Flat- RIESGO, Down- MUY MALO)
### POR SER ORDINAL CREO LOS FACTORES Y LOS ORDENO Y LUEGO GRAFICO
###### DOLOR DE PECHO
# 1) Frecuencia- como voy a usar el frame creo un objeto para guardar el grafico
p_freq <- ggplot(heart, aes(x = chestpaintype)) +
geom_bar(color = "black", fill = "tomato") +
labs(title = "Frecuencia", x = "Tipo de dolor torácico", y = "Frecuencia") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 20, hjust = 1))
# 2) Porcentaje
p_pct <- ggplot(heart, aes(x = chestpaintype)) +
geom_bar(aes(y = after_stat(prop * 100), group = 1),
color = "black", fill = "pink") +
labs(title = "Porcentaje", x = "Tipo de dolor torácico", y = "%" ,
caption = "TA: Typical Angina | ATA: Atypical Angina | NAP: Non-Anginal Pain | ASY: Asymptomatic") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 20, hjust = 1))
# 3) En un solo frame
p_freq + p_pct + plot_annotation(title = "Chest Pain Type — Frecuencia y %")
#### =============================REPOSO restingecg=============
#Frecuencia
p_freq <- ggplot(heart, aes(x = restingecg)) +
geom_bar(color = "black", fill = "royalblue") +
labs(
title = "Frecuencia",
x = "Resultados del ECG en reposo",
y = "Frecuencia")
theme_minimal() +
theme(
axis.text.x = element_text(angle = 20, hjust = 1),
plot.caption = element_text(hjust = 0, size = 9, face = "italic")
)
# procentaje
p_pct <- ggplot(heart, aes(x = restingecg)) +
geom_bar(aes(y = after_stat(prop * 100), group = 1),
color = "black", fill = "cyan") +
labs(
title = "Porcentaje",
x = "Resultados del ECG en reposo",
y = "%",
caption = "Normal: sin alteraciones | ST-T: Anomalías del segmento ST o de la onda T | LVH: Hipertrofia ventricular izquierda"
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 20, hjust = 1),
plot.caption = element_text(hjust = 0, size = 9, face = "italic")
)
#### Frame
p_freq + p_pct + plot_annotation(
title = "RestingECG — Frecuencia y Porcentaje"
)
######============== st_slope
# frecuencias
p_freq <- ggplot(heart, aes(x = st_slope)) +
geom_bar(color = "black", fill = "yellow") +
labs(
title = "Frecuencia",
x = "Pendiente del segmento ST",
y = "Frecuencia",
caption = "Down: Mayor Riesgo | Flat:Riesgo intermedio | Up: Menor Riesgo(Normal)"
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 20, hjust = 1),
plot.caption = element_text(hjust = 0, size = 9, face = "italic")
)
# Porcentajes
p_pct <- ggplot(heart, aes(x = st_slope)) +
geom_bar(aes(y = after_stat(prop * 100), group = 1),
color = "black", fill = "darkorange") +
labs(
title = "Porcentaje",
x = "Pendiente del segmento ST",
y = "%",
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 20, hjust = 1),
plot.caption = element_text(hjust = 0, size = 9, face = "italic")
)
# Unir graficos
p_freq + p_pct + plot_annotation(title = "ST Slope — Frecuencia y Porcentaje")
Exploro las variables cuantitativas discretas
age
, restingbp
y maxhr
con
histogramas (forma de la distribución) y
boxplots (dispersión y outliers), combinados en cada
caso para una lectura integral.
- En los histogramas, trazo una línea discontinua en la
media para contrastarla con la mediana implícita del
boxplot (asimetría si difieren).
- Los binwidths (p. ej., 5 años para age
,
10 mmHg para restingbp
) equilibran detalle
y suavidad.
- Los boxplots evidencian IQR,
mediana y valores atípicos, útiles
para detectar dispersión elevada o colas
pesadas.
Lectura sugerida: compare la concentración de barras y
la posición de la media; verifique si el boxplot
sugiere asimetría (mediana desplazada) y si existen
outliers que podrían requerir análisis robustos o
reportes diferenciados.
#==============================***************** DISCRETAS==========********************
# Variables
# Edad paciente,
# restingbp : Frecuencia cardiaca en reposo
# maxhr: Max heart rate / Maxima frecuencia cardiaca alcanzada.
# EDAe - Histograma
p_hist <- ggplot(heart, aes(x = age)) +
geom_histogram(binwidth = 5, fill = "cyan", color = "black") +
geom_vline(aes(xintercept = mean(age, na.rm = TRUE)),
color = "red", linetype = "dashed", linewidth = 1) +
labs(
title = "Distribución de la edad",
x = "Edad (años)",
y = "Frecuencia",
caption = "La línea roja discontinua indica la media"
) +
theme_minimal()
# Boxplot - caja bigotes
p_box <- ggplot(heart, aes(y = age)) +
geom_boxplot(fill = "cyan", color = "black") +
labs(
title = "Dispersión de la edad",
x = "",
y = "Edad (años)"
) +
theme_minimal()
# Frame
p_hist + p_box + plot_annotation(title = "Edad del paciente — Histograma y Boxplot")
########. PResión Arterial reposo
# Histograma
p_hist <- ggplot(heart, aes(x = restingbp)) +
geom_histogram(binwidth = 10, fill = "khaki", color = "black") +
geom_vline(aes(xintercept = mean(restingbp, na.rm = TRUE)),
color = "red", linetype = "dashed", linewidth = 1) +
labs(
title = "Distribución Resting HR",
x = "Presión arterial (mm Hg)",
y = "Frecuencia",
caption = "Línea roja discontinua: media"
) +
theme_minimal()
# Casjas bibotes
p_box <- ggplot(heart, aes(y = restingbp)) +
geom_boxplot(fill = "khaki", color = "black") +
labs(
title = "Dispersión Resting HR",
x = "",
y = "Presión (mm Hg)"
) +
theme_minimal()
# Frame
p_hist + p_box + plot_annotation(title = "Presión arterial en reposo — Histograma y Boxplot")
# Frecuencia Cardica MÁXIMA
p_hist <- ggplot(heart, aes(x = maxhr)) +
geom_histogram(binwidth = 5, fill = "ivory", color = "black") +
geom_vline(aes(xintercept = mean(maxhr, na.rm = TRUE)),
color = "red", linetype = "dashed", linewidth = 1) +
labs(
title = "Distribución Max HR",
x = "Frecuencia cardíaca (lpm)",
y = "Frecuencia",
caption = "Línea roja discontinua: media"
) +
theme_minimal()
# Dispersinón
# Boxplot
p_box <- ggplot(heart, aes(y = maxhr)) +
geom_boxplot(fill = "ivory", color = "black") +
labs(
title = "Dispersión Max HR",
x = "",
y = "Frecuencia cardíaca (lpm)"
) +
theme_minimal()
# Frame combinado
p_hist + p_box + plot_annotation(title = "Frecuencia cardíaca máxima — Histograma y Boxplot")
Trabajo con las variables cuantitativas continuas
cholesterol
y oldpeak
.
- En cholesterol
, sustituyo ceros por
NA
(regla de negocio) para evitar sesgos. Presento
histograma + media y boxplot para
evaluar forma, tendencia central y
dispersión; el caption recuerda que se excluyen
ceros.
- En oldpeak
(depresión del segmento ST), uso un
binwidth fino (0.2) para apreciar variaciones
clínicas sutiles; acompaño con boxplot para
identificar asimetría y
outliers.
La combinación histograma–boxplot permite verificar si hay
colas (curtosis), sesgos (skewness) y
diferencias entre media y mediana que sugieran
no normalidad o la necesidad de
transformaciones en análisis posteriores.
#==============================***************** CONTINUAS ==========*******************
# ========COLESTEROL
heart$cholesterol[heart$cholesterol == 0] <- NA ### Quitar los 0 por que afectan la data
p_hist <- ggplot(heart, aes(x = cholesterol)) +
geom_histogram(binwidth = 10, fill = "aquamarine", color = "black", na.rm = TRUE) +
geom_vline(aes(xintercept = mean(cholesterol, na.rm = TRUE)),
color = "red", linetype = "dashed", linewidth = 1) +
labs(
title = "Distribución de colesterol",
x = "Colesterol (mg/dl)",
y = "Frecuencia",
caption = "La línea roja discontinua indica la media (valores 0 excluidos)"
) +
theme_minimal()
######============== Blox pot
p_box <- ggplot(heart, aes(y = cholesterol)) +
geom_boxplot(fill = "aquamarine", color = "black", na.rm = TRUE) +
labs(
title = "Dispersión de colesterol",
x = "",
y = "Colesterol (mg/dl)"
) +
theme_minimal()
p_hist + p_box + plot_annotation(title = "Colesterol — Histograma y Boxplot")
#####OLd Peak
# Histograma
p_hist <- ggplot(heart, aes(x = oldpeak)) +
geom_histogram(binwidth = 0.2, fill = "steelblue", color = "black") +
geom_vline(aes(xintercept = mean(oldpeak, na.rm = TRUE)),
color = "red", linetype = "dashed", linewidth = 1) +
labs(
title = "Distribución de Oldpeak",
x = "Oldpeak (depresión ST)",
y = "Frecuencia",
caption = "La línea roja discontinua indica la media"
) +
theme_minimal()
# Boxplot
p_box <- ggplot(heart, aes(y = oldpeak)) +
geom_boxplot(fill = "seagreen", color = "black") +
labs(
title = "Dispersión de Oldpeak",
x = "",
y = "Oldpeak (depresión ST)"
) +
theme_minimal()
# Frame combinado
p_hist + p_box + plot_annotation(title = "Oldpeak — Histograma y Boxplot")
rofundizo en relaciones bivariadas (numérica–numérica y categórica–numérica), incorporando color/fill para una tercera dimensión categórica:
Edad vs. Colesterol (color =
heartdisease):
Dispersión con tendencia lineal. Interesa la
pendiente (¿aumenta colesterol con la edad?) y si la
nube se estratifica por enfermedad
(patrones diferenciales).
Edad vs. MaxHR (color = sex):
Dispersión + tendencia. Se espera descenso de
maxhr
con la edad; el color permite ver si existe
diferencia sistemática por sexo.
Oldpeak vs. ST Slope (color =
heartdisease):
Jitter (para evitar solapamiento) y
boxplot por nivel de st_slope
. Compara
medianas, IQR y
outliers entre pendientes y según enfermedad, útil para
perfil de riesgo.
Colesterol por tipo de dolor torácico (fill =
heartdisease):
Boxplots por chestpaintype
con fill de
enfermedad. Evalúa si ciertos síntomas se asocian con
mayor colesterol y si la composición
por enfermedad cambia entre niveles.
Presión en reposo vs. Edad (color =
heartdisease):
Dispersión + tendencia. Explora si la presión aumenta
con la edad y si el patrón difiere en personas con/sin
enfermedad.
En todos los casos, interpreto la pendiente (dirección/fuerza de relación), la dispersión residual (variabilidad no explicada) y la separación por color (posibles interacciones o diferencias de subgrupos). Esta capa visual orienta decisiones sobre modelos o estratificaciones en análisis posteriores.
#En esta sección probaré hacer graficos entre variables cualis y cuantis serán 5 graficos mas uno con 4 variables en 2d
library(ggplot2)
install.packages("plotly") # la neceisto para el 2d de 4 variables
library(plotly)
# Prepato algunas variables para trabajar quito los 0 De colesterol, pongo si o no en enferdad cardiaca, sexo hombre / mujern
heart$cholesterol[heart$cholesterol == 0] <- NA
heart$heartdisease <- factor(heart$heartdisease, levels = c(0, 1), labels = c("No", "Sí"))
heart$sex <- factor(heart$sex, levels = c("M", "F"), labels = c("Hombre", "Mujer"))
########### Grafico 1: Colesterol vs Edad
# x= edad
# y= colesterol
# ttercer elemento será coloreado con el heart diases (Bicolor )
ggplot(heart, aes(x = age, y = cholesterol, color = heartdisease)) +
geom_point(alpha = 0.7, size = 2) +
geom_smooth(method = "lm", se = FALSE, color = "black", linetype = "dashed") +
labs(
title = "Edad vs Colesterol / Enfermedad Cardíaca",
x = "Edad (años)", y = "Colesterol (mg/dl)",
color = "Enfermedad cardíaca"
) +
theme_minimal()
# ===================Grafico 2 : Max HR + Edad + Sexo
# x edad
#y = Maxima frecuencia cardiacac
# Color = Sexo
ggplot(heart, aes(x = age, y = maxhr, color = sex)) +
geom_point(alpha = 0.7, size = 2) +
geom_smooth(method = "lm", se = FALSE, color = "black", linetype = "dashed") +
labs(
title = "Frecuencia cardíaca máxima según edad y sexo",
x = "Edad (años)", y = "Frecuencia máxima (lpm)",
color = "Sexo"
) +
theme_minimal()
# ===================Grafico 3 : Depresión segmento + Tipo de pendiente / enfermedad cardiaca
#
ggplot(heart, aes(x = st_slope, y = oldpeak, color = heartdisease)) +
geom_jitter(width = 0.2, alpha = 0.7, size = 2) +
geom_boxplot(alpha = 0.3, outlier.shape = NA) +
labs(
title = "Oldpeak según tipo de ST Slope",
x = "Tipo de pendiente ST", y = "Oldpeak (depresión ST)",
color = "Enfermedad cardíaca",
caption = "Down: Mayor Riesgo | Flat:Riesgo intermedio | Up: Menor Riesgo"
) +
theme_minimal()
#======================= Grafico 4: Dolor torx + Colesterol + enfermedaad cardiaca
# x= dolor pecho
# y= colesterol
# Color= Enfermedad cardiaca
ggplot(heart, aes(x = chestpaintype, y = cholesterol, fill = heartdisease)) +
geom_boxplot(alpha = 0.8, color = "black", na.rm = TRUE) +
labs(
title = "Colesterol según tipo de dolor torácico",
x = "Tipo de dolor torácico", y = "Colesterol (mg/dl)",
fill = "Enfermedad cardíaca",
caption = "TA: Typical Angina | ATA: Atypical Angina | NAP: Non-Anginal Pain | ASY: Asymptomatic"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 20, hjust = 1),
plot.caption = element_text(size = 6, face = "italic", hjust = 0.5)) # con estta linea puedo moficar el tamañao de las siglas
#======================= Grafico 5: Edad+ Resting PR + Enferdad cardiaca
# x= Edad
#y= presion arterial en reposo
# Color= Enfermedad cardicaa
ggplot(heart, aes(x = age, y = restingbp, color = heartdisease)) +
geom_point(alpha = 0.7, size = 2) +
geom_smooth(method = "lm", se = FALSE, linetype = "dashed", color = "black") +
labs(
title = "Presión Arterial en reposo + edad y enfermedad cardíaca",
x = "Edad (años)", y = "Presión en reposo (mmHg)",
color = "Enfermedad cardíaca"
) +
theme_minimal()