Data set de dos variables por tipo

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.

1. Instalación Paquetes: Instalo y cargo librerías

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

2. Alistamiento Dataset (UCI Heart Disease Data)

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======

3. Tipifico variables y convierto factores

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")

4. Frecuencias rápidas por tipo

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

5. Tablas de frecuencia completas

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

6. Descriptivos para variables cuantitativas

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

7. Visualizaciones univariadas y bivariadas

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().

7.1 Variables Nominales

#==============================***************** 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()

7.2 Variables Ordinales

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")

7.3 Variables Discretas

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")

7.4 Variables Continuas

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")

8. Graficos combinando variables

rofundizo en relaciones bivariadas (numérica–numérica y categórica–numérica), incorporando color/fill para una tercera dimensión categórica:

  1. 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).

  2. 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.

  3. 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.

  4. 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.

  5. 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()