1. Contextualización

Librerías utilizadas:

library(readr)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ purrr     1.1.0
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ── 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(dplyr)
library(kableExtra)
## 
## Attaching package: 'kableExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     group_rows
library(Amelia)
## Loading required package: Rcpp
## ## 
## ## Amelia II: Multiple Imputation
## ## (Version 1.8.3, built: 2024-11-07)
## ## Copyright (C) 2005-2025 James Honaker, Gary King and Matthew Blackwell
## ## Refer to http://gking.harvard.edu/amelia/ for more information
## ##
library(mice)
## 
## Attaching package: 'mice'
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## The following objects are masked from 'package:base':
## 
##     cbind, rbind

1.Carga de datos

A través de readr se importan la base de datos *diabetes.csv:

datos <- 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.
#View(datos)

2. Caracterización de variables y resumen estadístico

A manera de descriptivo se genera una tabla con los primeros 10 registros de la base de datos:

head(datos, 10)
## # A tibble: 10 × 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
##  7           3      78            50            32      88  31  
##  8          10     115             0             0       0  35.3
##  9           2     197            70            45     543  30.5
## 10           8     125            96             0       0   0  
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

Gracias a la función dim(), es posible verificar el número total de filas y columnas del dataframe:

dim(datos)
## [1] 768   9

Este dataset está compuesto por un total de 768 filas y 9 columnas.

Haciendo uso de la función kableExtra(), se construye una tabla de caracterización de variables:

descripciones <- c(
  "Número de embarazos que ha tenido la paciente",
  "Concentración de glucosa en la sangre",
  "Presión arterial",
  "Espesor del pliegue cutáneo",
  "Nivel de insulina",
  "Índice de masa corporal",
  "Función de pedigrí de diabetes",
  "Edad de la paciente (años)",
  "Variable objetivo: 1 = diagnóstico positivo de diabetes, 0 = diagnóstico negativo"
)

tipo_variable <- c(
  "Cuantitativa discreta",   # Pregnancies
  "Cuantitativa continua",   # Glucose
  "Cuantitativa continua",   # BloodPressure
  "Cuantitativa continua",   # SkinThickness
  "Cuantitativa continua",   # Insulin
  "Cuantitativa continua",   # BMI
  "Cuantitativa continua",   # DiabetesPedigreeFunction
  "Cuantitativa discreta",   # Age
  "Categórica dicotómica"    # Outcome
)

tabla_vars <- data.frame(
  `Nombre de Variable` = names(datos),
  `Tipo de Variable` = tipo_variable,
  `Descripción` = descripciones
)

tabla_vars %>%
  kable("html", caption = "Caracterización de Variables") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed", "responsive"))
Caracterización de Variables
Nombre.de.Variable Tipo.de.Variable Descripción
Pregnancies Cuantitativa discreta Número de embarazos que ha tenido la paciente
Glucose Cuantitativa continua Concentración de glucosa en la sangre
BloodPressure Cuantitativa continua Presión arterial
SkinThickness Cuantitativa continua Espesor del pliegue cutáneo
Insulin Cuantitativa continua Nivel de insulina
BMI Cuantitativa continua Índice de masa corporal
DiabetesPedigreeFunction Cuantitativa continua Función de pedigrí de diabetes
Age Cuantitativa discreta Edad de la paciente (años)
Outcome Categórica dicotómica Variable objetivo: 1 = diagnóstico positivo de diabetes, 0 = diagnóstico negativo

Luego, utilizando la función summary(), se hace un resumen general de las varibles presentes en el dataset:

summary(datos)
##   Pregnancies        Glucose      BloodPressure    SkinThickness  
##  Min.   : 0.000   Min.   :  0.0   Min.   :  0.00   Min.   : 0.00  
##  1st Qu.: 1.000   1st Qu.: 99.0   1st Qu.: 62.00   1st Qu.: 0.00  
##  Median : 3.000   Median :117.0   Median : 72.00   Median :23.00  
##  Mean   : 3.845   Mean   :120.9   Mean   : 69.11   Mean   :20.54  
##  3rd Qu.: 6.000   3rd Qu.:140.2   3rd Qu.: 80.00   3rd Qu.:32.00  
##  Max.   :17.000   Max.   :199.0   Max.   :122.00   Max.   :99.00  
##     Insulin           BMI        DiabetesPedigreeFunction      Age       
##  Min.   :  0.0   Min.   : 0.00   Min.   :0.0780           Min.   :21.00  
##  1st Qu.:  0.0   1st Qu.:27.30   1st Qu.:0.2437           1st Qu.:24.00  
##  Median : 30.5   Median :32.00   Median :0.3725           Median :29.00  
##  Mean   : 79.8   Mean   :31.99   Mean   :0.4719           Mean   :33.24  
##  3rd Qu.:127.2   3rd Qu.:36.60   3rd Qu.:0.6262           3rd Qu.:41.00  
##  Max.   :846.0   Max.   :67.10   Max.   :2.4200           Max.   :81.00  
##     Outcome     
##  Min.   :0.000  
##  1st Qu.:0.000  
##  Median :0.000  
##  Mean   :0.349  
##  3rd Qu.:1.000  
##  Max.   :1.000

De este resumen se infiere que todas las bases de datos son cuantitativas como se mencionó en la tabla construida anteriormente. En el caso de la variable Outcome, R asume que es numérica debido a que el contenido de sus celdas está compuesto por números. Sin embargo, debido a que sus opciones de respuesta son 1 para diagnósticos positivos y 0 para negativos; se clasifica como una variable cualitativa dicotómica, tal como se mencionó en la tabla.

3. Simulación de valores faltantes(NA)

Cuando se cumplan las condiciones establecidas ((bmi > 40) o charges == 0 (en el caso de que existan)), las celdas serán tomadas como valores faltantes

datos$BMI[datos$BMI > 40] <- NA

if ("Charges" %in% names(datos)) {
  datos$Charges[datos$Charges == 0] <- NA
}

4. Visualización de valores faltantes(NA)

Luego, a través de Amelia(), se gener aun missingness map para visualizar el % de valores NA:

missmap(datos, col = c("red", "blue"), legend = TRUE)
## Warning: Unknown or uninitialised column: `arguments`.
## Unknown or uninitialised column: `arguments`.
## Warning: Unknown or uninitialised column: `imputations`.

Esto significa, que el dataset contiene un 1% de valores faltantes. Por supuesto, esto ocurre bajo las condiciones establecidas en la Actividad Evalutativa #3. Pues siguiendo las condiciones de 14.1 Actividad Práctica, el porcentaje de valores faltantes era del 9%.

5. Detección de valores atípicos

En este inciso, se aplicarán 3 métodos de detección de valores atípicos a cada variable numérica presente en la base de datos.

5.1 Pregnancies

5.1.1 Regla del IQR

Primero se genera un diagrama de caja y bigotes para visualizar posibles outliers:

boxplot(datos$Pregnancies,
        main = "Boxplot de Pregnancies",
        ylab = "Pregnancies",
        col = "steelblue",
        border = "black")

A simple vista, se evidencia presencia de posibles datos atípicos. Por lo tanto, es necesario aplicar la regla del IQR para detectarlos con mayor precisión.

out <- boxplot.stats(datos$Pregnancies)$out

out_ind <- which(datos$Pregnancies %in% c(out))

out_ind
## [1]  89 160 299 456
datos[out_ind, ]
## # A tibble: 4 × 9
##   Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##         <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
## 1          15     136            70            32     110  37.1
## 2          17     163            72            41     114  NA  
## 3          14     100            78            25     184  36.6
## 4          14     175            62            30       0  33.6
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

El primer output arroja el índice de cada fila que contiene un posible outlier.

El segundo output genera una tabla en la que aparecen el valor real que es considera atípico.

Luego, los valores obtenidos en la tabla coinciden con los que se aprecian a simple vista en el diagrama de caja y bigotes. Por lo tanto, hasta ahora, se confirma la presencia de 4 posibles valores atípicos en la pvariable Pregnancies.

5.1.3 Percentiles

Se fijan ambos límites y los datos que los sobrepasen se consideran atípicos:

lower_bound <- quantile(datos$Pregnancies, 0.025, na.rm = TRUE)
upper_bound <- quantile(datos$Pregnancies, 0.975, na.rm = TRUE)

outlier_ind <- which(datos$Pregnancies < lower_bound | datos$Pregnancies > upper_bound)

cat("Límite inferior (2.5%):", lower_bound, "\n")
## Límite inferior (2.5%): 0
cat("Límite superior (97.5%):", upper_bound, "\n")
## Límite superior (97.5%): 12
cat("Filas con outliers:", if (length(outlier_ind)) outlier_ind else "ninguna", "\n\n")
## Filas con outliers: 29 73 87 89 160 275 299 324 358 456 519 636 692 745
datos[outlier_ind, ]
## # A tibble: 14 × 9
##    Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##          <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
##  1          13     145            82            19     110  22.2
##  2          13     126            90             0       0  NA  
##  3          13     106            72            54       0  36.6
##  4          15     136            70            32     110  37.1
##  5          17     163            72            41     114  NA  
##  6          13     106            70             0       0  34.2
##  7          14     100            78            25     184  36.6
##  8          13     152            90            33      29  26.8
##  9          13     129             0            30       0  39.9
## 10          14     175            62            30       0  33.6
## 11          13      76            60             0       0  32.8
## 12          13     104            72             0       0  31.2
## 13          13     158           114             0       0  NA  
## 14          13     153            88            37     140  NA  
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

El método de percentiles arroja un límite superior que es menor que el límite del bigote positivo de los boxplots. Por lo tanto, el número de datos atípicos es mayor al obtenido mediante el método de la regla del IQR.

5.1.3 Filtro de Hampel

lower_bound <- median(datos$Pregnancies, na.rm = TRUE) - 
  3 * mad(datos$Pregnancies, constant = 1, na.rm = TRUE)

upper_bound <- median(datos$Pregnancies, na.rm = TRUE) + 
  3 * mad(datos$Pregnancies, constant = 1, na.rm = TRUE)

outlier_ind <- which(datos$Pregnancies < lower_bound | datos$Pregnancies > upper_bound)

cat("Límite inferior:", lower_bound, "\n")
## Límite inferior: -3
cat("Límite superior:", upper_bound, "\n")
## Límite superior: 9
cat("Filas con outliers:", outlier_ind, "\n\n")
## Filas con outliers: 8 12 13 25 26 29 35 37 73 87 89 144 160 194 216 247 255 260 271 275 282 299 307 324 328 334 358 359 376 437 456 459 465 506 511 519 543 559 560 579 583 591 615 635 636 649 659 661 668 673 692 707 713 718 741 745 746 764
datos[outlier_ind, ]
## # A tibble: 58 × 9
##    Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##          <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
##  1          10     115             0             0       0  35.3
##  2          10     168            74             0       0  38  
##  3          10     139            80             0       0  27.1
##  4          11     143            94            33     146  36.6
##  5          10     125            70            26     115  31.1
##  6          13     145            82            19     110  22.2
##  7          10     122            78            31       0  27.6
##  8          11     138            76             0       0  33.2
##  9          13     126            90             0       0  NA  
## 10          13     106            72            54       0  36.6
## # ℹ 48 more rows
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

En este caso, el límite superior es menor que el obtenido en el método de Percntiles. Por consiguiente, el número de datos atípicos es más elevado todavía.

5.2 Glucose

5.2.1 Regla del IQR (boxplot)

boxplot(datos$Glucose,
        main = "Boxplot de Glucose",
        ylab = "Glucose",
        col = "steelblue",
        border = "black")

out <- boxplot.stats(datos$Glucose)$out

out_ind <- which(datos$Glucose %in% c(out))

out_ind
## [1]  76 183 343 350 503
datos[out_ind, ]
## # A tibble: 5 × 9
##   Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##         <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
## 1           1       0            48            20       0  24.7
## 2           1       0            74            20      23  27.7
## 3           1       0            68            35       0  32  
## 4           5       0            80            32       0  NA  
## 5           6       0            68            41       0  39  
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

Esta regla, detecta 5 posibles datos atípicos que coinciden con los visualizados en el boxplot, es decir, cuando la glucosa es 0.

5.2.2 Percentiles

lower_bound <- quantile(datos$Glucose, 0.025, na.rm = TRUE)
upper_bound <- quantile(datos$Glucose, 0.975, na.rm = TRUE)

outlier_ind <- which(datos$Glucose < lower_bound | datos$Glucose > upper_bound)

cat("Límite inferior (2.5%):", lower_bound, "\n")
## Límite inferior (2.5%): 71.175
cat("Límite superior (97.5%):", upper_bound, "\n")
## Límite superior (97.5%): 189
cat("Filas con outliers:", if (length(outlier_ind)) outlier_ind else "ninguna", "\n\n")
## Filas con outliers: 9 23 48 63 76 77 98 147 183 186 207 229 259 261 274 320 343 350 353 360 400 409 462 490 499 503 521 538 562 580 597 618 662 673 676 681 738 760
datos[outlier_ind, ]
## # A tibble: 38 × 9
##    Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##          <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
##  1           2     197            70            45     543  30.5
##  2           7     196            90             0       0  39.8
##  3           2      71            70            27       0  28  
##  4           5      44            62             0       0  25  
##  5           1       0            48            20       0  24.7
##  6           7      62            78             0       0  32.6
##  7           1      71            48            18      76  20.4
##  8           9      57            80            37       0  32.8
##  9           1       0            74            20      23  27.7
## 10           7     194            68            28       0  35.9
## # ℹ 28 more rows
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

En este caso, como ambos límites comprenden valores mucho más grandes que 0, el número de valores atípicos aumenta, pues los diagramas de caja y bigotes solo tomaban el 0 como outlier.

5.2.3 Filtro de Hampel

lower_bound <- median(datos$Glucose, na.rm = TRUE) - 
  3 * mad(datos$Glucose, constant = 1, na.rm = TRUE)

upper_bound <- median(datos$Glucose, na.rm = TRUE) + 
  3 * mad(datos$Glucose, constant = 1, na.rm = TRUE)

outlier_ind <- which(datos$Glucose < lower_bound | datos$Glucose > upper_bound)

cat("Límite inferior:", lower_bound, "\n")
## Límite inferior: 57
cat("Límite superior:", upper_bound, "\n")
## Límite superior: 177
cat("Filas con outliers:", outlier_ind, "\n\n")
## Filas con outliers: 3 9 14 23 41 46 57 63 76 155 176 183 186 187 207 210 213 229 237 238 246 259 261 318 320 328 333 340 343 350 360 361 400 409 426 428 441 446 490 499 503 507 546 547 550 562 580 596 605 607 623 648 662 676 681 716 749 754 760
datos[outlier_ind, ]
## # A tibble: 59 × 9
##    Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##          <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
##  1           8     183            64             0       0  23.3
##  2           2     197            70            45     543  30.5
##  3           1     189            60            23     846  30.1
##  4           7     196            90             0       0  39.8
##  5           3     180            64            25      70  34  
##  6           0     180            66            39       0  NA  
##  7           7     187            68            39     304  37.7
##  8           5      44            62             0       0  25  
##  9           1       0            48            20       0  24.7
## 10           8     188            78             0       0  NA  
## # ℹ 49 more rows
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

Ahora, como el límite superior obtenido es menor que el límite superior resultante tras aplicar el método de percentiles. Es claro que el número de valores atípicos será aún mayor.

5.3 BloodPressure

5.3.1 Regla del IQR (boxplot)

boxplot(datos$BloodPressure,
        main = "Boxplot de BloodPressure",
        ylab = "BloodPressure",
        col = "steelblue",
        border = "black")

out <- boxplot.stats(datos$BloodPressure)$out

out_ind <- which(datos$BloodPressure %in% c(out))

out_ind
##  [1]   8  16  19  44  50  61  79  82  85 107 126 173 178 194 223 262 267 270 301
## [20] 333 337 348 358 363 427 431 436 454 469 485 495 523 534 536 550 590 598 602
## [39] 605 620 644 692 698 704 707
datos[out_ind, ]
## # A tibble: 45 × 9
##    Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##          <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
##  1          10     115             0             0       0  35.3
##  2           7     100             0             0       0  30  
##  3           1     103            30            38      83  NA  
##  4           9     171           110            24     240  NA  
##  5           7     105             0             0       0   0  
##  6           2      84             0             0       0   0  
##  7           0     131             0             0       0  NA  
##  8           2      74             0             0       0   0  
##  9           5     137           108             0       0  NA  
## 10           1      96           122             0       0  22.4
## # ℹ 35 more rows
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

En este caso, se visualizan outliers fuera de ambos bigotes del diagrama.

5.3.2 Percentiles

lower_bound <- quantile(datos$BloodPressure, 0.025, na.rm = TRUE)
upper_bound <- quantile(datos$BloodPressure, 0.975, na.rm = TRUE)

outlier_ind <- which(datos$BloodPressure < lower_bound | datos$BloodPressure > upper_bound)

cat("Límite inferior (2.5%):", lower_bound, "\n")
## Límite inferior (2.5%): 0
cat("Límite superior (97.5%):", upper_bound, "\n")
## Límite superior (97.5%): 96
cat("Filas con outliers:", if (length(outlier_ind)) outlier_ind else "ninguna", "\n\n")
## Filas con outliers: 44 85 107 178 188 208 304 363 370 380 388 441 465 550 659 663 673 674 692
datos[outlier_ind, ]
## # A tibble: 19 × 9
##    Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##          <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
##  1           9     171           110            24     240  NA  
##  2           5     137           108             0       0  NA  
##  3           1      96           122             0       0  22.4
##  4           0     129           110            46     130  NA  
##  5           1     128            98            41      58  32  
##  6           5     162           104             0       0  37.7
##  7           5     115            98             0       0  NA  
##  8           5     103           108            37       0  39.2
##  9           1     133           102            28     140  32.8
## 10           0      93           100            39      72  NA  
## 11           8     105           100            36       0  NA  
## 12           0     189           104            25       0  34.3
## 13          10     115            98             0       0  24  
## 14           4     189           110            31       0  28.5
## 15          11     127           106             0       0  39  
## 16           8     167           106            46     231  37.6
## 17          10      68           106            23      49  35.5
## 18           3     123           100            35     240  NA  
## 19          13     158           114             0       0  NA  
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

Nuevamente, el método de percentiles muestra una mayor cantidad de valores atípicos a comparación de la regla del IQR. En este caso, ambos límites tienen sentido médico, pues no es nada común encontrar pacientes con una presión sanguína menor o igual a cero, ni mayor que 96.

5.3.3 Filtro de Hampel

lower_bound <- median(datos$BloodPressure, na.rm = TRUE) - 
  3 * mad(datos$BloodPressure, constant = 1, na.rm = TRUE)

upper_bound <- median(datos$BloodPressure, na.rm = TRUE) + 
  3 * mad(datos$BloodPressure, constant = 1, na.rm = TRUE)

outlier_ind <- which(datos$BloodPressure < lower_bound | datos$BloodPressure > upper_bound)

cat("Límite inferior:", lower_bound, "\n")
## Límite inferior: 48
cat("Límite superior:", upper_bound, "\n")
## Límite superior: 96
cat("Filas con outliers:", outlier_ind, "\n\n")
## Filas con outliers: 5 8 16 19 44 50 61 79 81 82 85 107 126 173 178 188 194 208 223 262 267 270 301 304 333 337 347 348 358 363 370 380 388 427 431 436 441 454 465 469 485 495 523 534 536 550 576 577 590 598 600 602 605 620 644 659 663 673 674 692 698 704 707 708 742
datos[outlier_ind, ]
## # A tibble: 65 × 9
##    Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##          <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
##  1           0     137            40            35     168  NA  
##  2          10     115             0             0       0  35.3
##  3           7     100             0             0       0  30  
##  4           1     103            30            38      83  NA  
##  5           9     171           110            24     240  NA  
##  6           7     105             0             0       0   0  
##  7           2      84             0             0       0   0  
##  8           0     131             0             0       0  NA  
##  9           3     113            44            13       0  22.4
## 10           2      74             0             0       0   0  
## # ℹ 55 more rows
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

Con el simple de hecho de obtener un límite inferior mayor que 0. Es evidente que el número de outliers aumenta drásticamente.

5.4 SkinThickness

5.4.1 Regla del IQR (boxplot)

boxplot(datos$SkinThickness,
        main = "Boxplot de SkinThickness",
        ylab = "SkinThickness",
        col = "steelblue",
        border = "black")

out <- boxplot.stats(datos$SkinThickness)$out

out_ind <- which(datos$SkinThickness %in% c(out))

out_ind
## [1] 580
datos[out_ind, ]
## # A tibble: 1 × 9
##   Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##         <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
## 1           2     197            70            99       0  34.7
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

La regla del IQR sólo detecta un outlier: Cuando el espesor del pliegue cutáneo es de 99. A su vez, es importante mencionar que en esta ocasión, el valor obtenido sí coincide con el visualizados en el diagrama de caja y bigotes.

5.4.3 Percentiles

lower_bound <- quantile(datos$SkinThickness, 0.025, na.rm = TRUE)
upper_bound <- quantile(datos$SkinThickness, 0.975, na.rm = TRUE)

outlier_ind <- which(datos$SkinThickness < lower_bound | datos$SkinThickness > upper_bound)

cat("Límite inferior (2.5%):", lower_bound, "\n")
## Límite inferior (2.5%): 0
cat("Límite superior (97.5%):", upper_bound, "\n")
## Límite superior (97.5%): 47
cat("Filas con outliers:", if (length(outlier_ind)) outlier_ind else "ninguna", "\n\n")
## Filas con outliers: 58 87 100 121 151 212 274 276 371 410 446 459 533 540 580 592 658 694 764
datos[outlier_ind, ]
## # A tibble: 19 × 9
##    Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##          <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
##  1           0     100            88            60     110  NA  
##  2          13     106            72            54       0  36.6
##  3           1     122            90            51     220  NA  
##  4           0     162            76            56     100  NA  
##  5           1     136            74            50     204  37.4
##  6           0     147            85            54       0  NA  
##  7           1      71            78            50      45  33.2
##  8           2     100            70            52      57  NA  
##  9           3     173            82            48     465  38.4
## 10           1     172            68            49     579  NA  
## 11           0     180            78            63      14  NA  
## 12          10     148            84            48     237  37.6
## 13           1      86            66            52      65  NA  
## 14           3     129            92            49     155  36.4
## 15           2     197            70            99       0  34.7
## 16           2     112            78            50     140  39.4
## 17           1     120            80            48     200  38.9
## 18           7     129            68            49     125  38.5
## 19          10     101            76            48     180  32.9
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

Al ser 47 el límite superior resultante, es claro que el número de valores atípicos aumentará drásticamente con respecto al total obtenido con la regla del IQR, que sólo tomaba el 99 como outlier.

5.4.3 Filtro de Hampel

lower_bound <- median(datos$SkinThickness, na.rm = TRUE) - 
  3 * mad(datos$SkinThickness, constant = 1, na.rm = TRUE)

upper_bound <- median(datos$SkinThickness, na.rm = TRUE) + 
  3 * mad(datos$SkinThickness, constant = 1, na.rm = TRUE)

outlier_ind <- which(datos$SkinThickness < lower_bound | datos$SkinThickness > upper_bound)

cat("Límite inferior:", lower_bound, "\n")
## Límite inferior: -13
cat("Límite superior:", upper_bound, "\n")
## Límite superior: 59
cat("Filas con outliers:", outlier_ind, "\n\n")
## Filas con outliers: 58 446 580
datos[outlier_ind, ]
## # A tibble: 3 × 9
##   Pregnancies Glucose BloodPressure SkinThickness Insulin   BMI
##         <dbl>   <dbl>         <dbl>         <dbl>   <dbl> <dbl>
## 1           0     100            88            60     110  NA  
## 2           0     180            78            63      14  NA  
## 3           2     197            70            99       0  34.7
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>

En este caso, el límite de outliers disminuyó, en comparación con el método de percentiles.

6. Tratamiento de valores atípicos

Tras aplicar múltiples técnicas de detección de valores atípicos a la mayoría de variables cuantitativas presentes en el dataset. Es evidente que es necesario aplicar técnicas de imputación para darles un tratamiento eficiente a cada una de ellas.

7. Imputación de valores atípicos

Se utiliza el método pmm para imputar las variables:

datos_out <- datos

vars_imputar <- c("Pregnancies", "BloodPressure", "SkinThickness", "Glucose")

for (v in vars_imputar) {
  lb <- median(datos_out[[v]], na.rm = TRUE) - 3 * mad(datos_out[[v]], constant = 1, na.rm = TRUE)
  ub <- median(datos_out[[v]], na.rm = TRUE) + 3 * mad(datos_out[[v]], constant = 1, na.rm = TRUE)
  idx <- which(datos_out[[v]] < lb | datos_out[[v]] > ub)
  datos_out[[v]][idx] <- NA
}

method <- make.method(datos_out)
method[] <- ""
method[vars_imputar] <- "pmm"

imp <- mice(datos_out, method = method, m = 5, seed = 123)
## 
##  iter imp variable
##   1   1  Pregnancies  Glucose  BloodPressure  SkinThickness
##   1   2  Pregnancies  Glucose  BloodPressure  SkinThickness
##   1   3  Pregnancies  Glucose  BloodPressure  SkinThickness
##   1   4  Pregnancies  Glucose  BloodPressure  SkinThickness
##   1   5  Pregnancies  Glucose  BloodPressure  SkinThickness
##   2   1  Pregnancies  Glucose  BloodPressure  SkinThickness
##   2   2  Pregnancies  Glucose  BloodPressure  SkinThickness
##   2   3  Pregnancies  Glucose  BloodPressure  SkinThickness
##   2   4  Pregnancies  Glucose  BloodPressure  SkinThickness
##   2   5  Pregnancies  Glucose  BloodPressure  SkinThickness
##   3   1  Pregnancies  Glucose  BloodPressure  SkinThickness
##   3   2  Pregnancies  Glucose  BloodPressure  SkinThickness
##   3   3  Pregnancies  Glucose  BloodPressure  SkinThickness
##   3   4  Pregnancies  Glucose  BloodPressure  SkinThickness
##   3   5  Pregnancies  Glucose  BloodPressure  SkinThickness
##   4   1  Pregnancies  Glucose  BloodPressure  SkinThickness
##   4   2  Pregnancies  Glucose  BloodPressure  SkinThickness
##   4   3  Pregnancies  Glucose  BloodPressure  SkinThickness
##   4   4  Pregnancies  Glucose  BloodPressure  SkinThickness
##   4   5  Pregnancies  Glucose  BloodPressure  SkinThickness
##   5   1  Pregnancies  Glucose  BloodPressure  SkinThickness
##   5   2  Pregnancies  Glucose  BloodPressure  SkinThickness
##   5   3  Pregnancies  Glucose  BloodPressure  SkinThickness
##   5   4  Pregnancies  Glucose  BloodPressure  SkinThickness
##   5   5  Pregnancies  Glucose  BloodPressure  SkinThickness
datos_pmm <- complete(imp) 

Con el fin de verificar visualmente la presencia de outliers post-imputación en la base de datos, se generan varios diagramas de caja y bigotes:

variables_imputadas <- c("Pregnancies", "BloodPressure", "SkinThickness", "Glucose")

for (var in variables_imputadas) {
  boxplot(datos_pmm[[var]],
          main = paste("Boxplot de", var, "después de imputar"),
          col = "steelblue",
          border = "black")
}

De esto, se infiere por simple inspección, que ya no hay presencia de outliers y por ende la imputación fue exitosa.