El objetivo principal de esta actividad es que aprendas a manejar y tratar datos incompletos o problemáticos en un conjunto de datos real, con el fin de poder realizar análisis posteriores confiables. Buscando:
El conjunto de datos contiene información médica de pacientes y se utiliza para predecir un resultado de interés de salud (variable dependiente). Incluye variables independientes como:
| Variable | Tipo | Descripción |
|---|---|---|
| Pregnancies | Numérica | Número de embarazos de la paciente |
| Glucose | Numérica | Nivel de glucosa en sangre (mg/dL) |
| BloodPressure | Numérica | Presión arterial diastólica (mm Hg) |
| SkinThickness | Numérica | Espesor del pliegue cutáneo (mm) |
| Insulin | Numérica | Nivel de insulina en sangre (mu U/mL) |
| BMI | Numérica | Índice de masa corporal (peso en kg / altura en m²) |
| DiabetesPedigreeFunction | Numérica | Función de predisposición genética a la diabetes (herencia) |
| Age | Numérica | Edad de la paciente (años) |
| Outcome | Categórica | Resultado de interés (1 = diabetes, 0 = no diabetes) |
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.4.3
## Warning: package 'ggplot2' was built under R version 4.4.3
## Warning: package 'readr' was built under R version 4.4.3
## Warning: package 'dplyr' was built under R version 4.4.2
## Warning: package 'forcats' was built under R version 4.4.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.2 ✔ tibble 3.2.1
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.1
## ✔ purrr 1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(mice)
## Warning: package 'mice' was built under R version 4.4.3
##
## Adjuntando el paquete: 'mice'
##
## The following object is masked from 'package:stats':
##
## filter
##
## The following objects are masked from 'package:base':
##
## cbind, rbind
library(ggplot2)
library(dplyr)
library(tidyr)
library(readr)
library(reshape2)
##
## Adjuntando el paquete: 'reshape2'
##
## The following object is masked from 'package:tidyr':
##
## smiths
library(visdat)
## Warning: package 'visdat' was built under R version 4.4.3
library(gridExtra)
## Warning: package 'gridExtra' was built under R version 4.4.3
##
## Adjuntando el paquete: 'gridExtra'
##
## The following object is masked from 'package:dplyr':
##
## combine
library(skimr)
## Warning: package 'skimr' was built under R version 4.4.3
library(ggcorrplot)
## Warning: package 'ggcorrplot' was built under R version 4.4.3
library(patchwork)
## Warning: package 'patchwork' was built under R version 4.4.2
library(GGally)
## Warning: package 'GGally' was built under R version 4.4.3
df <- read_csv("diabetes.csv")
## Rows: 768 Columns: 9
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (9): Pregnancies, Glucose, BloodPressure, SkinThickness, Insulin, BMI, D...
##
## ℹ 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.
print(paste("Número de filas:",nrow(df) ) ) # Número de filas
## [1] "Número de filas: 768"
print(paste("Número de columnas:",ncol(df))) # Número de columnas
## [1] "Número de columnas: 9"
head(df)
## # A tibble: 6 × 9
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 6 148 72 35 0 33.6
## 2 1 85 66 29 0 26.6
## 3 8 183 64 0 0 23.3
## 4 1 89 66 23 94 28.1
## 5 0 137 40 35 168 43.1
## 6 5 116 74 0 0 25.6
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>
Obseervación inicial: Aparentemente no hay valores faltantes ni atípicos dentro de la definición o el contexto de cada variable observado.
str(df) # Tipo de cada variable y primeros valores
## spc_tbl_ [768 × 9] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ Pregnancies : num [1:768] 6 1 8 1 0 5 3 10 2 8 ...
## $ Glucose : num [1:768] 148 85 183 89 137 116 78 115 197 125 ...
## $ BloodPressure : num [1:768] 72 66 64 66 40 74 50 0 70 96 ...
## $ SkinThickness : num [1:768] 35 29 0 23 35 0 32 0 45 0 ...
## $ Insulin : num [1:768] 0 0 0 94 168 0 88 0 543 0 ...
## $ BMI : num [1:768] 33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 0 ...
## $ DiabetesPedigreeFunction: num [1:768] 0.627 0.351 0.672 0.167 2.288 ...
## $ Age : num [1:768] 50 31 32 21 33 30 26 29 53 54 ...
## $ Outcome : num [1:768] 1 0 1 0 1 0 1 0 1 1 ...
## - attr(*, "spec")=
## .. cols(
## .. Pregnancies = col_double(),
## .. Glucose = col_double(),
## .. BloodPressure = col_double(),
## .. SkinThickness = col_double(),
## .. Insulin = col_double(),
## .. BMI = col_double(),
## .. DiabetesPedigreeFunction = col_double(),
## .. Age = col_double(),
## .. Outcome = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
Observación: Podemos ver que nuestras 9 variables
son tomadas como numéricas; sin embargo, la variable
Outcome es categórica y debemos denotarla como tal.
df$Outcome <- as.factor(df$Outcome)
str(df)
## spc_tbl_ [768 × 9] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ Pregnancies : num [1:768] 6 1 8 1 0 5 3 10 2 8 ...
## $ Glucose : num [1:768] 148 85 183 89 137 116 78 115 197 125 ...
## $ BloodPressure : num [1:768] 72 66 64 66 40 74 50 0 70 96 ...
## $ SkinThickness : num [1:768] 35 29 0 23 35 0 32 0 45 0 ...
## $ Insulin : num [1:768] 0 0 0 94 168 0 88 0 543 0 ...
## $ BMI : num [1:768] 33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 0 ...
## $ DiabetesPedigreeFunction: num [1:768] 0.627 0.351 0.672 0.167 2.288 ...
## $ Age : num [1:768] 50 31 32 21 33 30 26 29 53 54 ...
## $ Outcome : Factor w/ 2 levels "0","1": 2 1 2 1 2 1 2 1 2 2 ...
## - attr(*, "spec")=
## .. cols(
## .. Pregnancies = col_double(),
## .. Glucose = col_double(),
## .. BloodPressure = col_double(),
## .. SkinThickness = col_double(),
## .. Insulin = col_double(),
## .. BMI = col_double(),
## .. DiabetesPedigreeFunction = col_double(),
## .. Age = col_double(),
## .. Outcome = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
Como parte del ejercicio se plantea el ejercicio de sustituir todos
los valores ausentes o nulos por NAN y realice el análisis
de estos datos faltantes.
cols <- c("Glucose","BloodPressure","SkinThickness","Insulin","BMI")
for (col in cols) {
df[[col]][df[[col]] == 0] <- NA
}
vis_miss(df)
Note que el porcentaje general en el dataset de valores faltantes es el 9.4% sin embargo es necesario revisar la proporción en cada variable para saber como tratarlos.
colSums(is.na(df))
## Pregnancies Glucose BloodPressure
## 0 5 35
## SkinThickness Insulin BMI
## 227 374 11
## DiabetesPedigreeFunction Age Outcome
## 0 0 0
Luego de hacer el reemplazo, podemos ver la cantidad de valores
faltantes por variable, encontrando una gran proporción en las variables
Insulin y SkinThickness.
na_summary <- tibble(
variable = names(df),
n_na = sapply(df, function(x) sum(is.na(x))),
pct_na = round(sapply(df, function(x) mean(is.na(x)) * 100), 2)
) %>%
arrange(desc(pct_na))
print(na_summary)
## # A tibble: 9 × 3
## variable n_na pct_na
## <chr> <int> <dbl>
## 1 Insulin 374 48.7
## 2 SkinThickness 227 29.6
## 3 BloodPressure 35 4.56
## 4 BMI 11 1.43
## 5 Glucose 5 0.65
## 6 Pregnancies 0 0
## 7 DiabetesPedigreeFunction 0 0
## 8 Age 0 0
## 9 Outcome 0 0
Dado los porcentajes de Na por variable, encontramos que es necesario
tratar con mayor cuidado los Na de las variables Insulin y
SkinThickness, dado su alto porcentaje.
# --- Imputación con método pmm ---
imp_pmm <- mice(df, method = "pmm", m = 5, seed = 123)
##
## iter imp variable
## 1 1 Glucose BloodPressure SkinThickness Insulin BMI
## 1 2 Glucose BloodPressure SkinThickness Insulin BMI
## 1 3 Glucose BloodPressure SkinThickness Insulin BMI
## 1 4 Glucose BloodPressure SkinThickness Insulin BMI
## 1 5 Glucose BloodPressure SkinThickness Insulin BMI
## 2 1 Glucose BloodPressure SkinThickness Insulin BMI
## 2 2 Glucose BloodPressure SkinThickness Insulin BMI
## 2 3 Glucose BloodPressure SkinThickness Insulin BMI
## 2 4 Glucose BloodPressure SkinThickness Insulin BMI
## 2 5 Glucose BloodPressure SkinThickness Insulin BMI
## 3 1 Glucose BloodPressure SkinThickness Insulin BMI
## 3 2 Glucose BloodPressure SkinThickness Insulin BMI
## 3 3 Glucose BloodPressure SkinThickness Insulin BMI
## 3 4 Glucose BloodPressure SkinThickness Insulin BMI
## 3 5 Glucose BloodPressure SkinThickness Insulin BMI
## 4 1 Glucose BloodPressure SkinThickness Insulin BMI
## 4 2 Glucose BloodPressure SkinThickness Insulin BMI
## 4 3 Glucose BloodPressure SkinThickness Insulin BMI
## 4 4 Glucose BloodPressure SkinThickness Insulin BMI
## 4 5 Glucose BloodPressure SkinThickness Insulin BMI
## 5 1 Glucose BloodPressure SkinThickness Insulin BMI
## 5 2 Glucose BloodPressure SkinThickness Insulin BMI
## 5 3 Glucose BloodPressure SkinThickness Insulin BMI
## 5 4 Glucose BloodPressure SkinThickness Insulin BMI
## 5 5 Glucose BloodPressure SkinThickness Insulin BMI
df_pmm <- complete(imp_pmm)
# --- Imputación con método norm.predict ---
imp_norm_predict <- mice(df, method = "norm.predict", m = 5, seed = 123)
##
## iter imp variable
## 1 1 Glucose BloodPressure SkinThickness Insulin BMI
## 1 2 Glucose BloodPressure SkinThickness Insulin BMI
## 1 3 Glucose BloodPressure SkinThickness Insulin BMI
## 1 4 Glucose BloodPressure SkinThickness Insulin BMI
## 1 5 Glucose BloodPressure SkinThickness Insulin BMI
## 2 1 Glucose BloodPressure SkinThickness Insulin BMI
## 2 2 Glucose BloodPressure SkinThickness Insulin BMI
## 2 3 Glucose BloodPressure SkinThickness Insulin BMI
## 2 4 Glucose BloodPressure SkinThickness Insulin BMI
## 2 5 Glucose BloodPressure SkinThickness Insulin BMI
## 3 1 Glucose BloodPressure SkinThickness Insulin BMI
## 3 2 Glucose BloodPressure SkinThickness Insulin BMI
## 3 3 Glucose BloodPressure SkinThickness Insulin BMI
## 3 4 Glucose BloodPressure SkinThickness Insulin BMI
## 3 5 Glucose BloodPressure SkinThickness Insulin BMI
## 4 1 Glucose BloodPressure SkinThickness Insulin BMI
## 4 2 Glucose BloodPressure SkinThickness Insulin BMI
## 4 3 Glucose BloodPressure SkinThickness Insulin BMI
## 4 4 Glucose BloodPressure SkinThickness Insulin BMI
## 4 5 Glucose BloodPressure SkinThickness Insulin BMI
## 5 1 Glucose BloodPressure SkinThickness Insulin BMI
## 5 2 Glucose BloodPressure SkinThickness Insulin BMI
## 5 3 Glucose BloodPressure SkinThickness Insulin BMI
## 5 4 Glucose BloodPressure SkinThickness Insulin BMI
## 5 5 Glucose BloodPressure SkinThickness Insulin BMI
df_norm_predict <- complete(imp_norm_predict)
# --- Imputación con método norm.nob ---
imp_norm_nob <- mice(df, method = "norm.nob", m = 5, seed = 123)
##
## iter imp variable
## 1 1 Glucose BloodPressure SkinThickness Insulin BMI
## 1 2 Glucose BloodPressure SkinThickness Insulin BMI
## 1 3 Glucose BloodPressure SkinThickness Insulin BMI
## 1 4 Glucose BloodPressure SkinThickness Insulin BMI
## 1 5 Glucose BloodPressure SkinThickness Insulin BMI
## 2 1 Glucose BloodPressure SkinThickness Insulin BMI
## 2 2 Glucose BloodPressure SkinThickness Insulin BMI
## 2 3 Glucose BloodPressure SkinThickness Insulin BMI
## 2 4 Glucose BloodPressure SkinThickness Insulin BMI
## 2 5 Glucose BloodPressure SkinThickness Insulin BMI
## 3 1 Glucose BloodPressure SkinThickness Insulin BMI
## 3 2 Glucose BloodPressure SkinThickness Insulin BMI
## 3 3 Glucose BloodPressure SkinThickness Insulin BMI
## 3 4 Glucose BloodPressure SkinThickness Insulin BMI
## 3 5 Glucose BloodPressure SkinThickness Insulin BMI
## 4 1 Glucose BloodPressure SkinThickness Insulin BMI
## 4 2 Glucose BloodPressure SkinThickness Insulin BMI
## 4 3 Glucose BloodPressure SkinThickness Insulin BMI
## 4 4 Glucose BloodPressure SkinThickness Insulin BMI
## 4 5 Glucose BloodPressure SkinThickness Insulin BMI
## 5 1 Glucose BloodPressure SkinThickness Insulin BMI
## 5 2 Glucose BloodPressure SkinThickness Insulin BMI
## 5 3 Glucose BloodPressure SkinThickness Insulin BMI
## 5 4 Glucose BloodPressure SkinThickness Insulin BMI
## 5 5 Glucose BloodPressure SkinThickness Insulin BMI
df_norm_nob <- complete(imp_norm_nob)
# --- Imputación con método norm ---
imp_norm <- mice(df, method = "norm", m = 5, seed = 123)
##
## iter imp variable
## 1 1 Glucose BloodPressure SkinThickness Insulin BMI
## 1 2 Glucose BloodPressure SkinThickness Insulin BMI
## 1 3 Glucose BloodPressure SkinThickness Insulin BMI
## 1 4 Glucose BloodPressure SkinThickness Insulin BMI
## 1 5 Glucose BloodPressure SkinThickness Insulin BMI
## 2 1 Glucose BloodPressure SkinThickness Insulin BMI
## 2 2 Glucose BloodPressure SkinThickness Insulin BMI
## 2 3 Glucose BloodPressure SkinThickness Insulin BMI
## 2 4 Glucose BloodPressure SkinThickness Insulin BMI
## 2 5 Glucose BloodPressure SkinThickness Insulin BMI
## 3 1 Glucose BloodPressure SkinThickness Insulin BMI
## 3 2 Glucose BloodPressure SkinThickness Insulin BMI
## 3 3 Glucose BloodPressure SkinThickness Insulin BMI
## 3 4 Glucose BloodPressure SkinThickness Insulin BMI
## 3 5 Glucose BloodPressure SkinThickness Insulin BMI
## 4 1 Glucose BloodPressure SkinThickness Insulin BMI
## 4 2 Glucose BloodPressure SkinThickness Insulin BMI
## 4 3 Glucose BloodPressure SkinThickness Insulin BMI
## 4 4 Glucose BloodPressure SkinThickness Insulin BMI
## 4 5 Glucose BloodPressure SkinThickness Insulin BMI
## 5 1 Glucose BloodPressure SkinThickness Insulin BMI
## 5 2 Glucose BloodPressure SkinThickness Insulin BMI
## 5 3 Glucose BloodPressure SkinThickness Insulin BMI
## 5 4 Glucose BloodPressure SkinThickness Insulin BMI
## 5 5 Glucose BloodPressure SkinThickness Insulin BMI
df_norm <- complete(imp_norm)
Estadísticos:
# Mostrar resumen de imputaciones realizadas
print("Original")
## [1] "Original"
summary(df[cols])
## Glucose BloodPressure SkinThickness Insulin
## Min. : 44.0 Min. : 24.00 Min. : 7.00 Min. : 14.00
## 1st Qu.: 99.0 1st Qu.: 64.00 1st Qu.:22.00 1st Qu.: 76.25
## Median :117.0 Median : 72.00 Median :29.00 Median :125.00
## Mean :121.7 Mean : 72.41 Mean :29.15 Mean :155.55
## 3rd Qu.:141.0 3rd Qu.: 80.00 3rd Qu.:36.00 3rd Qu.:190.00
## Max. :199.0 Max. :122.00 Max. :99.00 Max. :846.00
## NA's :5 NA's :35 NA's :227 NA's :374
## BMI
## Min. :18.20
## 1st Qu.:27.50
## Median :32.30
## Mean :32.46
## 3rd Qu.:36.60
## Max. :67.10
## NA's :11
print("Método pmm")
## [1] "Método pmm"
summary(df_pmm[cols])
## Glucose BloodPressure SkinThickness Insulin
## Min. : 44.0 Min. : 24.0 Min. : 7.00 Min. : 14.0
## 1st Qu.: 99.0 1st Qu.: 64.0 1st Qu.:21.00 1st Qu.: 71.0
## Median :117.0 Median : 72.0 Median :29.00 Median :115.0
## Mean :121.6 Mean : 72.3 Mean :28.74 Mean :146.6
## 3rd Qu.:141.0 3rd Qu.: 80.0 3rd Qu.:36.00 3rd Qu.:182.0
## Max. :199.0 Max. :122.0 Max. :99.00 Max. :846.0
## BMI
## Min. :18.20
## 1st Qu.:27.48
## Median :32.15
## Mean :32.41
## 3rd Qu.:36.60
## Max. :67.10
Glucose
BloodPressure
SkinThickness
Insulin
BMI
Histogramas
# Crear un dataframe combinado con una columna que indique el origen
df_comb <- bind_rows(
df %>% select(all_of(cols)) %>% mutate(Origen = "df"),
df_pmm %>% select(all_of(cols)) %>% mutate(Origen = "df_pmm")
)
# Pivot largo para ggplot
df_long <- df_comb %>%
pivot_longer(cols = all_of(cols), names_to = "Variable", values_to = "Valor")
# Histograma comparativo
ggplot(df_long, aes(x = Valor, fill = Origen)) +
geom_histogram(alpha = 0.5, position = "identity", bins = 30, color = "black") +
facet_wrap(~ Variable, scales = "free") +
scale_fill_manual(values = c("black", "red")) +
theme_minimal() +
ggtitle("Comparación de histogramas: df vs df_pmm")
## Warning: Removed 652 rows containing non-finite outside the scale range
## (`stat_bin()`).
Graficamente, en variables como BloodPressure,
BMI y Glucose no se observan cambios radicales
luego de imputar por el método PMM.
Sin embargo, para las variables Insulin
y SkinThickness se observan cambios
notables en las distribuciones, por lo que es recomendable evaluar si
estos cambios son estadísticamente significativos.
Pruebas
for (col in cols) {
cat("\nVariable:", col, "\n")
print(wilcox.test(df[[col]], df_pmm[[col]]))
}
##
## Variable: Glucose
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_pmm[[col]]
## W = 293526, p-value = 0.9508
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: BloodPressure
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_pmm[[col]]
## W = 283013, p-value = 0.8543
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: SkinThickness
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_pmm[[col]]
## W = 212193, p-value = 0.5087
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: Insulin
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_pmm[[col]]
## W = 159530, p-value = 0.1284
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: BMI
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_pmm[[col]]
## W = 291828, p-value = 0.8946
## alternative hypothesis: true location shift is not equal to 0
Se observa que el p-valor de cada prueba es mayor a 0.05, por lo que no se rechaza la hipótesis nula. Esto indica que no existen diferencias estadísticamente significativas entre las medianas de las variables antes y después de la imputación mediante el método PMM. Por lo que, este método es una buena opción para la imputación de nuestros datos.
Estadísticos:
# Mostrar resumen de imputaciones realizadas
print("Original")
## [1] "Original"
summary(df[cols])
## Glucose BloodPressure SkinThickness Insulin
## Min. : 44.0 Min. : 24.00 Min. : 7.00 Min. : 14.00
## 1st Qu.: 99.0 1st Qu.: 64.00 1st Qu.:22.00 1st Qu.: 76.25
## Median :117.0 Median : 72.00 Median :29.00 Median :125.00
## Mean :121.7 Mean : 72.41 Mean :29.15 Mean :155.55
## 3rd Qu.:141.0 3rd Qu.: 80.00 3rd Qu.:36.00 3rd Qu.:190.00
## Max. :199.0 Max. :122.00 Max. :99.00 Max. :846.00
## NA's :5 NA's :35 NA's :227 NA's :374
## BMI
## Min. :18.20
## 1st Qu.:27.50
## Median :32.30
## Mean :32.46
## 3rd Qu.:36.60
## Max. :67.10
## NA's :11
print("Método norm_predict")
## [1] "Método norm_predict"
summary(df_norm_predict[cols])
## Glucose BloodPressure SkinThickness Insulin
## Min. : 44.0 Min. : 24.00 Min. : 7.00 Min. :-22.18
## 1st Qu.: 99.0 1st Qu.: 64.00 1st Qu.:22.00 1st Qu.: 88.00
## Median :117.0 Median : 72.00 Median :28.50 Median :130.00
## Mean :121.7 Mean : 72.35 Mean :28.89 Mean :151.74
## 3rd Qu.:141.0 3rd Qu.: 80.00 3rd Qu.:35.00 3rd Qu.:190.47
## Max. :199.0 Max. :122.00 Max. :99.00 Max. :846.00
## BMI
## Min. :18.20
## 1st Qu.:27.50
## Median :32.05
## Mean :32.44
## 3rd Qu.:36.60
## Max. :67.10
Se comparan las variables originales con los datos imputados mediante los métodos PMM y Norm_predict:
Glucose
BloodPressure
SkinThickness
Insulin
BMI
Histogramas
# Crear un dataframe combinado con una columna que indique el origen
df_comb <- bind_rows(
df %>% select(all_of(cols)) %>% mutate(Origen = "df"),
df_norm_predict %>% select(all_of(cols)) %>% mutate(Origen = "df_norm_predict")
)
# Pivot largo para ggplot
df_long <- df_comb %>%
pivot_longer(cols = all_of(cols), names_to = "Variable", values_to = "Valor")
# Histograma comparativo
ggplot(df_long, aes(x = Valor, fill = Origen)) +
geom_histogram(alpha = 0.5, position = "identity", bins = 30, color = "black") +
facet_wrap(~ Variable, scales = "free") +
scale_fill_manual(values = c("black", "red")) +
theme_minimal() +
ggtitle("Comparación de histogramas: df vs df_norm_predict")
## Warning: Removed 652 rows containing non-finite outside the scale range
## (`stat_bin()`).
En general, se observa que las distribuciones antes y después de la
imputación no presentan cambios radicales a simple vista, a excepción de
las variables Insulin y SkinThickness, donde
sí se notan diferencias más importantes que analizaremos con
pruebas.
Pruebas
for (col in cols) {
cat("\nVariable:", col, "\n")
print(wilcox.test(df[[col]], df_norm_predict[[col]]))
}
##
## Variable: Glucose
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm_predict[[col]]
## W = 293105, p-value = 0.9897
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: BloodPressure
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm_predict[[col]]
## W = 282272, p-value = 0.9241
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: SkinThickness
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm_predict[[col]]
## W = 210911, p-value = 0.6382
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: Insulin
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm_predict[[col]]
## W = 145705, p-value = 0.3019
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: BMI
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm_predict[[col]]
## W = 291082, p-value = 0.9635
## alternative hypothesis: true location shift is not equal to 0
No hay diferencias estadísticamente significativas entre las
distribuciones de las variables antes y después de la imputación por el
método Norm_predict. Esto indica que, aunque algunas variables como
Insulin y SkinThickness presentan cambios
visuales, dichos cambios no son suficientes para considerarlos
estadísticamente relevantes.
Sin embargo, cabe destacar que para la variable Insulin,
este método generó valores extremos erróneos; por lo tanto, no se
considerará Norm_predict como alternativa de imputación para
Insulin.
Estadísticos:
# Mostrar resumen de imputaciones realizadas
print("Original")
## [1] "Original"
summary(df[cols])
## Glucose BloodPressure SkinThickness Insulin
## Min. : 44.0 Min. : 24.00 Min. : 7.00 Min. : 14.00
## 1st Qu.: 99.0 1st Qu.: 64.00 1st Qu.:22.00 1st Qu.: 76.25
## Median :117.0 Median : 72.00 Median :29.00 Median :125.00
## Mean :121.7 Mean : 72.41 Mean :29.15 Mean :155.55
## 3rd Qu.:141.0 3rd Qu.: 80.00 3rd Qu.:36.00 3rd Qu.:190.00
## Max. :199.0 Max. :122.00 Max. :99.00 Max. :846.00
## NA's :5 NA's :35 NA's :227 NA's :374
## BMI
## Min. :18.20
## 1st Qu.:27.50
## Median :32.30
## Mean :32.46
## 3rd Qu.:36.60
## Max. :67.10
## NA's :11
print("Método norm_nob")
## [1] "Método norm_nob"
summary(df_norm_nob[cols])
## Glucose BloodPressure SkinThickness Insulin
## Min. : 44.0 Min. : 24.00 Min. :-0.1049 Min. :-280.72
## 1st Qu.: 99.0 1st Qu.: 64.00 1st Qu.:21.0000 1st Qu.: 71.75
## Median :117.0 Median : 72.00 Median :28.3273 Median : 127.57
## Mean :121.6 Mean : 72.42 Mean :28.5705 Mean : 149.95
## 3rd Qu.:140.2 3rd Qu.: 80.00 3rd Qu.:35.6374 3rd Qu.: 201.28
## Max. :199.0 Max. :122.00 Max. :99.0000 Max. : 846.00
## BMI
## Min. :18.20
## 1st Qu.:27.50
## Median :32.35
## Mean :32.44
## 3rd Qu.:36.60
## Max. :67.10
Glucose y BloodPressure: Los valores mínimos, máximos, medianas y medias son prácticamente iguales a los originales. Esto indica que la imputación no afecta significativamente la distribución de estas variables.
SkinThickness: La media y mediana son similares a los originales, aunque aparece un valor mínimo negativo (-0.1049). La imputación no altera la tendencia central, pero genera un valor atípico poco realista.
Insulin: Se observan valores mínimos extremadamente negativos (-280.72), mientras que la mediana y media son similares a los originales. Esto indica que la imputación introduce valores erróneos extremos, por lo que este método no es recomendable para Insulin.
BMI: Cambios mínimos, la distribución se mantiene estable. La imputación no altera la variable de manera relevante.
En general, el método Norm_nob mantiene relativamente
estables las distribuciones de Glucose, BloodPressure, SkinThickness y
BMI, pero genera valores extremos no válidos para SkinThickness e
Insulin, lo que limita su uso para estas variables.
Histogramas
# Crear un dataframe combinado con una columna que indique el origen
df_comb <- bind_rows(
df %>% select(all_of(cols)) %>% mutate(Origen = "df"),
df_norm_nob %>% select(all_of(cols)) %>% mutate(Origen = "df_norm_nob")
)
# Pivot largo para ggplot
df_long <- df_comb %>%
pivot_longer(cols = all_of(cols), names_to = "Variable", values_to = "Valor")
# Histograma comparativo
ggplot(df_long, aes(x = Valor, fill = Origen)) +
geom_histogram(alpha = 0.5, position = "identity", bins = 30, color = "black") +
facet_wrap(~ Variable, scales = "free") +
scale_fill_manual(values = c("blue", "red")) +
theme_minimal() +
ggtitle("Comparación de histogramas: df vs df_norm_nob")
## Warning: Removed 652 rows containing non-finite outside the scale range
## (`stat_bin()`).
Para Glucose, BloodPressure y BMI, la imputación mantiene la distribución original.Para Insulin y SkinThickness, la imputación introduce valores atípicos y negativos, por lo que no es recomendable usar Norm_nob en estas variables.
Pruebas
for (col in cols) {
cat("\nVariable:", col, "\n")
print(wilcox.test(df[[col]], df_norm_nob[[col]]))
}
##
## Variable: Glucose
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm_nob[[col]]
## W = 293396, p-value = 0.9628
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: BloodPressure
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm_nob[[col]]
## W = 281102, p-value = 0.9648
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: SkinThickness
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm_nob[[col]]
## W = 213540, p-value = 0.3894
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: Insulin
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm_nob[[col]]
## W = 152569, p-value = 0.8142
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: BMI
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm_nob[[col]]
## W = 290750, p-value = 0.9943
## alternative hypothesis: true location shift is not equal to 0
En todas las variables, el p-valor > 0.05 indica que no existe evidencia estadísticamente significativa para afirmar que la imputación con norm.nob haya alterado la distribución de los datos originales.
Sin embargo, cabe destacar que para la variable Insulin
y SkinThickness, este método generó valores extremos
erróneos; por lo tanto, no es un método adecuado para estas
variables.
Estadísticos:
# Mostrar resumen de imputaciones realizadas
print("Original")
## [1] "Original"
summary(df[cols])
## Glucose BloodPressure SkinThickness Insulin
## Min. : 44.0 Min. : 24.00 Min. : 7.00 Min. : 14.00
## 1st Qu.: 99.0 1st Qu.: 64.00 1st Qu.:22.00 1st Qu.: 76.25
## Median :117.0 Median : 72.00 Median :29.00 Median :125.00
## Mean :121.7 Mean : 72.41 Mean :29.15 Mean :155.55
## 3rd Qu.:141.0 3rd Qu.: 80.00 3rd Qu.:36.00 3rd Qu.:190.00
## Max. :199.0 Max. :122.00 Max. :99.00 Max. :846.00
## NA's :5 NA's :35 NA's :227 NA's :374
## BMI
## Min. :18.20
## 1st Qu.:27.50
## Median :32.30
## Mean :32.46
## 3rd Qu.:36.60
## Max. :67.10
## NA's :11
print("Método norm")
## [1] "Método norm"
summary(df_norm[cols])
## Glucose BloodPressure SkinThickness Insulin
## Min. : 44.0 Min. : 24.00 Min. : 6.209 Min. :-196.00
## 1st Qu.: 99.0 1st Qu.: 64.00 1st Qu.:21.954 1st Qu.: 73.75
## Median :117.0 Median : 72.00 Median :29.195 Median : 136.09
## Mean :121.6 Mean : 72.44 Mean :29.260 Mean : 156.85
## 3rd Qu.:141.0 3rd Qu.: 80.00 3rd Qu.:36.000 3rd Qu.: 219.30
## Max. :199.0 Max. :122.00 Max. :99.000 Max. : 846.00
## BMI
## Min. :18.20
## 1st Qu.:27.48
## Median :32.25
## Mean :32.44
## 3rd Qu.:36.60
## Max. :67.10
Glucose
Mínimo, cuartiles, mediana, media y máximo prácticamente idénticos antes
y después. Norm mantuvo muy bien la distribución.
BloodPressure
Estadísticos idénticos (mín, Q1, mediana, media, Q3, máx). No hay
alteración perceptible.
SkinThickness
Muy parecido, pero el mínimo cambió de 7.0 → 6.209 (ligero
descenso).
Esto sugiere que el método generó un valor imputado menor que el mínimo
original.
Insulin
Mínimo cambió drásticamente de 14.0 → -196.0
Esto es importante: el método norm puede generar valores fuera del rango
plausible, incluso negativos (algo no fisiológico para insulina).
Mediana, media y máximo son similares, pero este valor atípico negativo
es una señal de alerta. Riesgo de valores no realistas.
BMI
Muy estable: diferencias de redondeo mínimas. Conservada la
estructura.
Histogramas
# Crear un dataframe combinado con una columna que indique el origen
df_comb <- bind_rows(
df %>% select(all_of(cols)) %>% mutate(Origen = "df"),
df_norm %>% select(all_of(cols)) %>% mutate(Origen = "df_norm")
)
df_long <- df_comb %>%
pivot_longer(cols = all_of(cols), names_to = "Variable", values_to = "Valor")
# Histograma comparativo
ggplot(df_long, aes(x = Valor, fill = Origen)) +
geom_histogram(alpha = 0.5, position = "identity", bins = 30, color = "black") +
facet_wrap(~ Variable, scales = "free") +
scale_fill_manual(values = c("blue", "red")) +
theme_minimal() +
ggtitle("Comparación de histogramas: df vs df_norm")
## Warning: Removed 652 rows containing non-finite outside the scale range
## (`stat_bin()`).
El método norm preserva bastante bien las distribuciones, excepto en Insulin, donde genera valores no fisiológicos que no deberían aceptarse sin corrección posterior.
Pruebas
for (col in cols) {
cat("\nVariable:", col, "\n")
print(wilcox.test(df[[col]], df_norm[[col]]))
}
##
## Variable: Glucose
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm[[col]]
## W = 293225, p-value = 0.9786
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: BloodPressure
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm[[col]]
## W = 281325, p-value = 0.986
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: SkinThickness
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm[[col]]
## W = 206068, p-value = 0.8034
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: Insulin
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm[[col]]
## W = 146395, p-value = 0.3655
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: BMI
##
## Wilcoxon rank sum test with continuity correction
##
## data: df[[col]] and df_norm[[col]]
## W = 291126, p-value = 0.9595
## alternative hypothesis: true location shift is not equal to 0
En general: el método norm conserva muy bien la forma y posición de las distribuciones, pero en variables como Insulin puede introducir valores fuera del rango plausible, por lo que es recomendable considerar otro método.
imp_pmm <- mice(df[cols], method = "pmm", m = 1, seed = 123)
##
## iter imp variable
## 1 1 Glucose BloodPressure SkinThickness Insulin BMI
## 2 1 Glucose BloodPressure SkinThickness Insulin BMI
## 3 1 Glucose BloodPressure SkinThickness Insulin BMI
## 4 1 Glucose BloodPressure SkinThickness Insulin BMI
## 5 1 Glucose BloodPressure SkinThickness Insulin BMI
df_pmm <- complete(imp_pmm)
for (col in cols) {
na_idx <- is.na(df[[col]])
df[na_idx, col] <- df_pmm[na_idx, col]
}
vis_miss(df)
Iniciaremos de forma clasica realizando Boxplot para cada variable.
# Seleccionar columnas numéricas
df_num <- df[sapply(df, is.numeric)]
# Calcular límites de outliers por variable usando la regla de Tukey
outliers_df <- df_num %>%
pivot_longer(cols = everything(), names_to = "variable", values_to = "value") %>%
group_by(variable) %>%
mutate(
Q1 = quantile(value, 0.25, na.rm = TRUE),
Q3 = quantile(value, 0.75, na.rm = TRUE),
IQR = Q3 - Q1,
Lower = Q1 - 1.5 * IQR,
Upper = Q3 + 1.5 * IQR,
is_outlier = value < Lower | value > Upper
)
# 📌 Tabla con los outliers detectados
tabla_outliers <- outliers_df %>%
filter(is_outlier) %>%
select(variable, value) %>%
arrange(variable, value)
# Mostrar tabla en consola
print(tabla_outliers)
## # A tibble: 115 × 2
## # Groups: variable [7]
## variable value
## <chr> <dbl>
## 1 Age 67
## 2 Age 67
## 3 Age 67
## 4 Age 68
## 5 Age 69
## 6 Age 69
## 7 Age 70
## 8 Age 72
## 9 Age 81
## 10 BMI 52.3
## # ℹ 105 more rows
# 📌 Boxplots con outliers etiquetados
ggplot(outliers_df, aes(x = "", y = value)) +
geom_boxplot(outlier.shape = NA) + # Evitar que el boxplot dibuje puntos duplicados
geom_point(
data = filter(outliers_df, is_outlier),
aes(color = "Outlier"),
size = 2
) +
geom_text(
data = filter(outliers_df, is_outlier),
aes(label = round(value, 1)),
vjust = -0.5,
size = 2.5
) +
facet_wrap(~ variable, scales = "free", ncol = 3) +
theme_minimal() +
labs(
title = "Boxplots con valores atípicos identificados",
x = "",
y = "Valor"
) +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
legend.position = "none"
)
De los boxplots en cuanto a outliers vemos que todas las variables
los presentan a excepción de la variable Glucose. Entre las
variables que mas valores atípicos tienen, destacan Insulin
y DiabetesPedigreeFunction
# --- Función Hampel ---
hampel_filter <- function(x, t0 = 3) {
med <- median(x, na.rm = TRUE)
mad_val <- mad(x, constant = 1.4826, na.rm = TRUE)
outliers <- abs(x - med) > (t0 * mad_val)
x[outliers] <- med
return(list(
vector_imputado = x,
indices_outliers = which(outliers),
cantidad_outliers = sum(outliers)
))
}
# --- Dataset de ejemplo ---
# df <- tu_dataframe
# Copia de seguridad
df_original <- df
df_imputado <- df
resumen_outliers <- data.frame(Variable = character(),
Cantidad = integer(),
stringsAsFactors = FALSE)
# --- Aplicar Hampel a todas las variables numéricas ---
for (var in names(df_imputado)) {
if (is.numeric(df_imputado[[var]])) {
res <- hampel_filter(df_imputado[[var]])
df_imputado[[var]] <- res$vector_imputado
resumen_outliers <- rbind(resumen_outliers,
data.frame(Variable = var,
Cantidad = res$cantidad_outliers))
}
}
print(resumen_outliers)
## Variable Cantidad
## 1 Pregnancies 23
## 2 Glucose 0
## 3 BloodPressure 11
## 4 SkinThickness 2
## 5 Insulin 43
## 6 BMI 4
## 7 DiabetesPedigreeFunction 40
## 8 Age 27
# --- Preparar datos para gráficos ---
df_original_long <- df_original %>%
pivot_longer(cols = where(is.numeric), names_to = "Variable", values_to = "Valor") %>%
mutate(Tipo = "Antes")
df_imputado_long <- df_imputado %>%
pivot_longer(cols = where(is.numeric), names_to = "Variable", values_to = "Valor") %>%
mutate(Tipo = "Después")
df_comparacion <- bind_rows(df_original_long, df_imputado_long)
# --- Generar un gráfico por variable ---
graficos <- df_comparacion %>%
group_split(Variable) %>%
lapply(function(data_var) {
ggplot(data_var, aes(x = Tipo, y = Valor, fill = Tipo)) +
geom_boxplot(outlier.shape = 16, outlier.size = 1.5, alpha = 0.6) +
labs(title = unique(data_var$Variable), x = "", y = "Valor") +
theme_minimal() +
theme(legend.position = "none",
plot.title = element_text(size = 14, face = "bold"))
})
# --- Combinar en una cuadrícula ---
wrap_plots(graficos) + plot_annotation(title = "Comparación Antes vs Después (Método Hampel)")
Análisis y tratamiento de outliers con el método de Hampel
En general,
Variable por variable
Age
- Antes: valores de hasta ~81 años.
- Después: tope reducido a ~60, los casos muy extremos fueron
reemplazados.
BMI
- Antes: valores de hasta ~67.1.
- Después: máximo reducido a ~50, outliers severos eliminados.
BloodPressure
- Antes: varios outliers altos (~122) y bajos (~24).
- Después: reducción en los valores extremos, especialmente los muy
bajos.
DiabetesPedigreeFunction
- Antes: valores hasta ~2.5.
- Después: máximo cercano a ~1.0, gran limpieza de valores atípicos.
Glucose
- Antes: extremos cerca de ~200.
- Después: rango más controlado, pero aún hay algunos puntos que Hampel
no considera outliers.
Insulin
- Antes: outliers muy extremos (hasta ~846).
- Después: máximo reducido a ~400, pero quedan algunos puntos altos que
para Hampel no son atípicos bajo su umbral.
Pregnancies
- Antes: valores hasta 17 embarazos.
- Después: máximo alrededor de 10, reduciendo casos extremos.
SkinThickness
- Antes: valores hasta ~99 mm.
- Después: máximo cerca de ~60 mm, disminución importante de
extremos.
A primera vista, podemos decir que el Filtro de Hampel logró detectar e imputar los outliers (utilizando la mediana) de forma exitosa, ya que gráficamente no se observan cambios drásticos en la distribución de los datos. Sin embargo, esto lo confirmaremos a continuación mediante una prueba no paramétrica (para datos que no siguen una distribución Normal), y así verificar si realmente no ha habido cambios estadísticamente significativos.
Prueba de Wilcox
cols <- c("Glucose","BloodPressure","SkinThickness","Insulin","BMI", "Age", "DiabetesPedigreeFunction","Pregnancies")
for (col in cols) {
cat("\nVariable:", col, "\n")
print(wilcox.test(df_original[[col]], df_imputado[[col]]))
}
##
## Variable: Glucose
##
## Wilcoxon rank sum test with continuity correction
##
## data: df_original[[col]] and df_imputado[[col]]
## W = 294912, p-value = 1
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: BloodPressure
##
## Wilcoxon rank sum test with continuity correction
##
## data: df_original[[col]] and df_imputado[[col]]
## W = 296843, p-value = 0.824
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: SkinThickness
##
## Wilcoxon rank sum test with continuity correction
##
## data: df_original[[col]] and df_imputado[[col]]
## W = 295666, p-value = 0.9309
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: Insulin
##
## Wilcoxon rank sum test with continuity correction
##
## data: df_original[[col]] and df_imputado[[col]]
## W = 310457, p-value = 0.07369
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: BMI
##
## Wilcoxon rank sum test with continuity correction
##
## data: df_original[[col]] and df_imputado[[col]]
## W = 296438, p-value = 0.8607
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: Age
##
## Wilcoxon rank sum test with continuity correction
##
## data: df_original[[col]] and df_imputado[[col]]
## W = 304983, p-value = 0.246
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: DiabetesPedigreeFunction
##
## Wilcoxon rank sum test with continuity correction
##
## data: df_original[[col]] and df_imputado[[col]]
## W = 309472, p-value = 0.09391
## alternative hypothesis: true location shift is not equal to 0
##
##
## Variable: Pregnancies
##
## Wilcoxon rank sum test with continuity correction
##
## data: df_original[[col]] and df_imputado[[col]]
## W = 303422, p-value = 0.3241
## alternative hypothesis: true location shift is not equal to 0
Nótese que, para cada una de nuestras variables, el p-valor es mayor que 0.05; por lo tanto, no se rechaza la hipótesis nula y se concluye que no existen diferencias estadísticamente significativas entre las distribuciones de las variables antes y después del tratamiento de los outliers. En consecuencia, podemos afirmar que, desde el punto de vista estadístico, la imputación realizada fue adecuada.
skim(df_imputado)
| Name | df_imputado |
| Number of rows | 768 |
| Number of columns | 9 |
| _______________________ | |
| Column type frequency: | |
| factor | 1 |
| numeric | 8 |
| ________________________ | |
| Group variables | None |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| Outcome | 0 | 1 | FALSE | 2 | 0: 500, 1: 268 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| Pregnancies | 0 | 1 | 3.55 | 2.96 | 0.00 | 1.00 | 3.00 | 6.00 | 11.00 | ▇▃▂▂▂ |
| Glucose | 0 | 1 | 121.54 | 30.51 | 44.00 | 99.00 | 117.00 | 140.25 | 199.00 | ▁▇▇▃▂ |
| BloodPressure | 0 | 1 | 72.22 | 11.42 | 38.00 | 64.00 | 72.00 | 80.00 | 106.00 | ▁▅▇▅▁ |
| SkinThickness | 0 | 1 | 28.61 | 10.10 | 7.00 | 20.00 | 29.00 | 36.00 | 63.00 | ▅▇▇▃▁ |
| Insulin | 0 | 1 | 130.27 | 71.52 | 14.00 | 75.00 | 125.00 | 168.00 | 360.00 | ▆▇▅▂▁ |
| BMI | 0 | 1 | 32.31 | 6.67 | 18.20 | 27.48 | 32.30 | 36.52 | 53.20 | ▃▇▇▃▁ |
| DiabetesPedigreeFunction | 0 | 1 | 0.42 | 0.23 | 0.08 | 0.24 | 0.37 | 0.56 | 1.11 | ▇▇▅▂▁ |
| Age | 0 | 1 | 31.95 | 9.97 | 21.00 | 24.00 | 29.00 | 38.25 | 60.00 | ▇▃▂▂▁ |
Pregnancies (Embarazos):
La media es de 3.55 embarazos con desviación estándar de 2.96, lo que
indica variabilidad en la cantidad de embarazos. La mayoría de los casos
se encuentra entre 1 y 6 embarazos, con mediana en 3, y pocos casos en 0
o valores más altos.
Glucose (Glucosa):
Presenta una media de 121.54 mg/dL y desviación estándar de 30.51. La
mayoría de los valores se concentra entre 99 y 140, aunque hay algunos
valores más bajos (44) y más altos (140), mostrando cierta dispersión
algo tipico en la población.
BloodPressure (Presión arterial):
Con media de 72.22 mmHg y desviación estándar de 11.42, la mayoría de
los individuos se encuentra dentro de rangos normales, aunque existen
casos con presión más baja (38), que podrían ser considerados
atípicos.
SkinThickness (Grosor de piel):
La media es de 28.61 mm y desviación estándar de 10.10, indicando
dispersión moderada. La mediana es 29 mm, y la mayoría se concentra
entre 20 y 36 mm, con algunos valores bajos (7 mm) que podrían
considerarse outliers leves.
Insulin (Insulina):
Con una media de 130.27 µU/mL y desviación estándar de 71.52, los
niveles de insulina muestran alta variabilidad. Los valores se
encuentran entre 14 y 168, con la mediana en 125, reflejando
heterogeneidad fisiológica y algunos posibles outliers.
BMI (Índice de masa corporal):
La media es 32.31 kg/m² con desviación estándar de 6.67. La mayoría de
los individuos tiene sobrepeso u obesidad, concentrándose entre 27.48 y
36.53, con la mediana en 32.30, mientras que valores más bajos (18.2)
son menos frecuentes.
DiabetesPedigreeFunction (Función de pedigree
diabético):
Presenta media 0.416 y desviación estándar 0.229. La mayoría de los
casos tiene riesgo genético bajo a moderado, con percentiles 25% en
0.244, mediana 0.372 y 75% en 0.560, existiendo algunos individuos con
mayor predisposición.
Age (Edad):
La media es 31.95 años con desviación estándar 9.97. La población es
relativamente joven, concentrándose entre 24 y 38 años, con mediana en
29 y pocos casos mayores.
for (var in names(df_imputado)) {
if (is.numeric(df_imputado[[var]])) {
# Histograma + densidad
p1 <- ggplot(df_imputado, aes(x = .data[[var]])) +
geom_histogram(aes(y = ..density..), bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
geom_density(color = "red", size = 1) +
labs(title = paste("Histograma y densidad de", var), x = var, y = "Densidad") +
theme_minimal()
# Boxplot
p2 <- ggplot(df_imputado, aes(y = .data[[var]])) +
geom_boxplot(fill = "lightgreen", color = "black", alpha = 0.7) +
labs(title = paste("Boxplot de", var), y = var) +
theme_minimal()
graficos[[var]] <- grid.arrange(p1, p2, ncol = 2)
}
}
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## 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 (`..density..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(density)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
Pregnancies: En el histograma se observa que los datos presentan un sesgo positivo, evidenciado por una cola más extendida hacia la derecha. Esto se confirma en el boxplot, donde la mediana aparece ligeramente desplazada hacia la izquierda y el bigote correspondiente al tercer cuartil es más largo, reflejando el mismo sesgo identificado en el histograma.
Glucose: El histograma muestra una distribución con sesgo positivo, evidenciado por una cola más alargada hacia la derecha. Esto coincide con el boxplot, donde la mediana se encuentra centrada pero el bigote superior es más extenso que el inferior, lo que confirma la presencia del sesgo observado en el histograma.
BloodPressure: En el histograma observamos que la distribución es aproximadamente simétrica, con una ligera concentración de valores alrededor de la media (~70–75). La curva de densidad confirma esta tendencia, mostrando una forma cercana a la normalidad. En el boxplot, la mediana se encuentra centrada dentro de la caja, y los bigotes son relativamente equilibrados, lo que refuerza la idea de simetría. Sin embargo, se identifican algunos valores atípicos tanto por encima como por debajo de los límites, que podrían corresponder a casos clínicamente relevantes de presión arterial inusualmente alta o baja.
SkinThickness: El histograma muestra una distribución con ligera asimetría positiva, evidenciada por una cola más extendida hacia la derecha. En el boxplot, la mediana se encuentra centrada, pero se observa un valor atípico en el extremo superior, lo que refuerza la presencia de algunos registros elevados.
Insulin: Presenta una marcada asimetría positiva, con una cola larga hacia valores altos. El boxplot confirma esta observación, mostrando múltiples valores atípicos por encima del tercer cuartil, lo que indica que existen casos con niveles de insulina considerablemente más altos que el resto.
BMI: La distribución presenta una ligera asimetría positiva, con la mayoría de los valores concentrados alrededor de la media y una cola hacia la derecha. El boxplot muestra algunos valores atípicos en el extremo superior, aunque en menor cantidad que en la variable Insulin.
DiabetesPedigreeFunction: La variable tiene una asimetría positiva notable, con una gran concentración de observaciones en valores bajos y una cola extendida hacia la derecha. El boxplot refleja la presencia de varios valores atípicos superiores, lo que sugiere que en algunos casos el historial familiar de diabetes influye de manera excepcionalmente elevada.
Age: Tiene una asimetría positiva muy clara, con datos concentrados entre los 20-30 años y una cola que se extiende hacia la derecha. EL boxplot confirma lo observado en el histograma pues es claro como la mediana se encuentra mas hacia la izquierda y asi mismo el bigote superior es bastante alargado.
library(ggplot2)
# Suponiendo que tu dataframe se llama df
ggplot(df_imputado, aes(x = factor(Outcome), fill = factor(Outcome))) +
geom_bar() +
labs(title = "Distribución de Outcome",
x = "Outcome",
y = "Cantidad") +
scale_fill_manual(values = c("lightblue", "salmon")) +
theme_minimal()
En el caso de la variable
Outcome, se observa que la clase
más prevalente es 0 (no tener diabetes), con aproximadamente 500
observaciones, mientras que la clase 1 (tener diabetes) presenta
alrededor de 260–270 observaciones. Esto indica un desequilibrio de
clases, ya que hay casi el doble de casos sin diabetes que con
diabetes.
Correlación:
cor_matrix <- cor(df_imputado[,cols], use = "complete.obs")
ggcorrplot(cor_matrix, hc.order = TRUE, type = "lower",
lab = TRUE, lab_size = 3, colors = c("blue", "white", "red"))
El análisis de la matriz de correlación revela varias relaciones
destacables entre las variables estudiadas. Se observa que BMI y
SkinThickness presentan una correlación positiva fuerte (r = 0.69), lo
que indica que a medida que aumenta el índice de masa corporal, también
tiende a incrementarse el grosor de la piel. Por otra parte, la variable
Insulin muestra una correlación positiva moderada con Glucose (r =
0.47), sugiriendo una relación directa entre los niveles de insulina y
glucosa en sangre. Asimismo, se identifica una correlación positiva
moderada-fuerte entre Pregnancies y Age, lo que refleja que,
generalmente, a mayor edad, se registra un mayor número de embarazos. En
contraste, las demás variables presentan, en términos generales,
correlaciones positivas débiles, indicando relaciones poco marcadas
entre ellas.
Graficos de Dispersión:
pairs(df_imputado[,cols],
main = "Matriz de Gráficos de Dispersión",
pch = 19, # tipo de punto
col = "red") # color de puntos
# Seleccionar variables numéricas
numeric_vars <- c("Pregnancies", "Glucose", "BloodPressure",
"SkinThickness", "Insulin", "BMI",
"DiabetesPedigreeFunction", "Age")
# Convertir a formato largo
df_long <- df %>%
select(all_of(numeric_vars), Outcome) %>%
pivot_longer(cols = all_of(numeric_vars),
names_to = "Variable",
values_to = "Valor")
# Crear matriz de boxplots
ggplot(df_long, aes(x = factor(Outcome), y = Valor, fill = factor(Outcome))) +
geom_boxplot() +
facet_wrap(~Variable, scales = "free_y") + # un boxplot por variable
labs(title = "Distribución de variables numéricas según Outcome",
x = "Outcome",
y = "Valor") +
scale_fill_manual(values = c("lightblue", "salmon")) +
theme_minimal()
El análisis de la matriz de boxplots muestra diferencias notables entre
los grupos de Outcome 0 y 1. La variable Glucose presenta valores
significativamente más altos en los individuos con diabetes (Outcome 1),
confirmando que la glucosa elevada es un fuerte indicador de la
enfermedad. De manera similar, el BMI es ligeramente mayor en este
grupo, lo que sugiere que un mayor índice de masa corporal puede estar
asociado a un mayor riesgo de diabetes. La edad también muestra una
mediana superior en los individuos con diabetes, indicando que la
probabilidad de presentar la enfermedad aumenta con la edad. En cuanto a
Insulin, se observa una tendencia a valores más altos en Outcome 1,
aunque con gran dispersión, lo que podría reflejar resistencia a la
insulina; sin embargo, la presencia de muchos valores atípicos limita la
interpretación precisa. SkinThickness es ligeramente mayor en los
individuos con diabetes, posiblemente relacionado con adiposidad
subcutánea, mientras que BloodPressure muestra diferencias mínimas entre
los grupos, sugiriendo que no es un factor diferenciador relevante en
este dataset. La DiabetesPedigreeFunction es algo mayor en Outcome 1, lo
que indica una posible influencia genética, y finalmente, los individuos
con diabetes tienden a haber tenido un mayor número de embarazos,
reflejando que, en este conjunto de datos, más embarazos se asocian a un
mayor riesgo de diabetes.