Funciones de Distribuciones en R

En R, las distribuciones de probabilidad tienen cuatro tipos de funciones:

  1. Funciones de densidad (d...): Devuelven la densidad de probabilidad en un punto.
  2. Funciones de distribución acumulada (p...): Devuelven la probabilidad acumulada hasta un punto.
  3. Funciones cuantil o inversas (q...): Devuelven el valor de la variable aleatoria correspondiente a un cuantil dado.
  4. Funciones generadoras de números aleatorios (r...): Se usan para generar números aleatorios.

Distribución Bernoulli

sample(c(0,1), prob = c(0.5, 0.5), size=1, replace = T)
## [1] 1

Distribución Binomial

# Probabilidad de obtener exactamente 3 éxitos en 10 ensayos con p = 0.8
n <- 10
k <- 3
p <- 0.8
combinatorio_n_k <- factorial(n)/(factorial(k)*factorial(n-k) )
funcion_masa_en_k <- combinatorio_n_k * p**k * (1-p)**(n-k)
print(funcion_masa_en_k)
## [1] 0.000786432
print(dbinom(3, size = 10, prob = 0.8))
## [1] 0.000786432
# Probabilidad acumulada P(X <= 4) en una binomial (12, 0.2)
probaTotalAcumulada <- 0
for (i in c(0:4)){
  probaTotalAcumulada <- probaTotalAcumulada + dbinom(i, size = 12, prob = 0.2)
}
print(probaTotalAcumulada)
## [1] 0.9274445
print(pbinom(4, size = 12, prob = 0.2))
## [1] 0.9274445
# Cuantil 80% de una binomial (10, 0.5)
qbinom(0.8, size = 10, prob = 0.5)
## [1] 6

Distribución Poisson

# Probabilidad de obtener exactamente 4 eventos con media lambda = 2
dpois(4, lambda = 2)
## [1] 0.09022352
# Probabilidad acumulada P(X <= 4) en una Poisson con lambda = 2
ppois(4, lambda = 2)
## [1] 0.947347
# Cuantil 90% de una Poisson con lambda = 2
qpois(0.90, lambda = 2)
## [1] 4

Distribución Exponencial

# Densidad en x = 1 de una distribución exponencial con tasa lambda = 2
dexp(1, rate = 2)
## [1] 0.2706706
# Probabilidad acumulada P(X <= 1) en una exponencial con lambda = 2
pexp(1, rate = 2)
## [1] 0.8646647
# Cuantil 75% de una exponencial con lambda = 2
qexp(0.75, rate = 2)
## [1] 0.6931472
curve(dexp(x, rate = 2), from = 0, to = 3, lwd = 2, col = "steelblue", 
      ylab = "Densidad", xlab = "x", main = "PDF de la Exponencial (λ = 2)")

Ejemplo con una Distribución Normal

# Densidad de una normal estándar en x = 1
dnorm(1, mean = 0, sd = 1)
## [1] 0.2419707
# Probabilidad acumulada P(X <= 1) en una normal estándar
pnorm(1, mean = 0, sd = 1)
## [1] 0.8413447
# Cuantil 90 de una normal con media 2 y desviación estándar 3
qnorm(0.90, mean = 2, sd = 3)
## [1] 5.844655
# Probabilidad de que X esté entre -1 y 1 en una normal estándar
pnorm(1, mean = 0, sd = 1) - pnorm(-1, mean = 0, sd = 1)
## [1] 0.6826895
lambda <- 1
com <- -2
fin <- lambda+5
x <- com:fin
xnorm <- seq(com, fin, 0.01)
poisson_vals <- dpois(x, lambda)
normal_vals <- dnorm(xnorm, mean = lambda, sd = sqrt(lambda))

plot(x, poisson_vals, type = "h", col = "blue", lwd = 2,
     main = "Poisson vs Normal",
     ylab = "Densidad", xlab = "x")
lines(xnorm, normal_vals, col = "red", lwd = 2)
legend("topright", legend = c("Poisson", "Normal"), col = c("blue", "red"), lwd = 2)

### Ley de los grandes numeros

set.seed(123)

# Simular 600 lanzamientos de una moneda cargada (Bernoulli con p = 0.7)
n <- 600
x <- rbinom(n, size = 1, prob = 0.7)

# Calcular media acumulada
media_acumulada <- cumsum(x) / (1:n)

# Graficar
plot(media_acumulada, type = "l", col = "blue", lwd = 2,
     main = "Ley de los Grandes Números (LGN)",
     xlab = "Número de observaciones",
     ylab = "Media acumulada")
abline(h = 0.7, col = "red", lty = 2)  # Línea de la media teórica
legend("bottomright", legend = c("Media acumulada", "Media verdadera"),
       col = c("blue", "red"), lwd = 2, lty = c(1, 2))

### Teorema central del limite

set.seed(220)

# Parámetros
n_muestras <- 100000    # cuántas muestras
tamaño_muestra <- 50   # tamaño de cada muestra

# Crear un vector para guardar las medias muestrales
medias <- numeric(n_muestras)

# Simular
for (i in 1:n_muestras) {
  muestra <- rexp(tamaño_muestra, rate = 1)  # Distribución exponencial (media = 1)
  medias[i] <- mean(muestra)
}

# Graficar histograma de las medias
hist(medias, breaks = 50, probability = TRUE, col = "skyblue",
     main = "Teorema Central del Límite (TCL)",
     xlab = "Medias muestrales")

# Agregar curva normal teórica
curve(dnorm(x, mean = 1, sd = 1/sqrt(tamaño_muestra)), add = TRUE, col = "red", lwd = 2)
legend("topright", legend = c("Medias muestrales", "Normal teórica"),
       col = c("skyblue", "red"), lwd = 2)

Distribución t de Student

# Densidad de una t de Student con 10 grados de libertad en x = 2
dt(2, df = 10)
## [1] 0.06114577
# Probabilidad acumulada P(X <= 2) en una t de Student con 10 grados de libertad
pt(2, df = 10)
## [1] 0.963306
# Cuantil 95% de una t de Student con 10 grados de libertad
qt(0.95, df = 10)
## [1] 1.812461
library(shiny)

ui <- fluidPage(
  titlePanel("Comparación: t de Student vs Normal estándar"),
  sidebarLayout(
    sidebarPanel(
      sliderInput("df", "Grados de libertad (df):",
                  min = 1, max = 100, value = 1, step = 1)
    ),
    mainPanel(
      plotOutput("distPlot")
    )
  )
)

server <- function(input, output) {
  output$distPlot <- renderPlot({
    curve(dt(x, df = input$df), from = -5, to = 5, col = "blue", lwd = 2,
          ylab = "Densidad", xlab = "x", main = paste("t de Student (df =", input$df, ") vs Normal"))
    curve(dnorm(x), from = -5, to = 5, col = "red", lwd = 2, add = TRUE, lty = 2)
    legend("topright", legend = c("t de Student", "Normal estándar"),
           col = c("blue", "red"), lwd = 2, lty = c(1, 2))
  })
}

shinyApp(ui = ui, server = server)
Shiny applications not supported in static R Markdown documents

Distribución Chi-cuadrado

Casos de uso:

Pruebas de independencia (Chi-cuadrado)

Análisis de varianza (ANOVA)

Evaluación de bondad de ajuste

# Densidad en x = 5 de una chi-cuadrado con 4 grados de libertad
dchisq(5, df = 4)
## [1] 0.1026062
# Probabilidad acumulada P(X <= 5) en una chi-cuadrado con 4 grados de libertad
pchisq(5, df = 4)
## [1] 0.7127025
# Cuantil 99% de una chi-cuadrado con 4 grados de libertad
qchisq(0.99, df = 4)
## [1] 13.2767
curve(dchisq(x, df = 4), from = 0, to = 15, main = "Distribución Chi-cuadrado (grados de libertad = 4)")

curve(dchisq(x, df = 1), from = 0, to = 15, main = "Distribución Chi-cuadrado (grados de libertad = 1)")

curve(dchisq(x, df = 10), from = 0, to = 25, main = "Distribución Chi-cuadrado (grados de libertad = 10)")

Distribución F de Snedecor

Casos de uso:

Comparación de varianzas

ANOVA (prueba F)

y otros..

# Densidad en x = 3 de una F con 5 y 10 grados de libertad
df(3, df1 = 5, df2 = 10)
## [1] 0.05582673
# Probabilidad acumulada P(X <= 3) en una F con 5 y 10 grados de libertad
pf(3, df1 = 5, df2 = 10)
## [1] 0.9344424
# Cuantil 95% de una F con 5 y 10 grados de libertad
qf(0.95, df1 = 5, df2 = 10)
## [1] 3.325835
curve(df(x, df1 = 5, df2 = 10), from = 0, to = 5, main = "Distribución F (df1 = 5, df2 = 10)")

# Generación de muestras de distribuciones conocidas

muestra.unif1=runif(100) # genera una muestra uniforme en [0,1] de 100 datos
muestra.unif1 # devuelve la muestra generada
##   [1] 0.74042917 0.06060387 0.44815931 0.70520033 0.14980901 0.70059005
##   [7] 0.94331055 0.74753949 0.22538930 0.36657710 0.95306480 0.88135497
##  [13] 0.35496980 0.63943374 0.31551702 0.95597580 0.75917776 0.78098909
##  [19] 0.80101753 0.10331994 0.51770831 0.09899885 0.80992561 0.79399644
##  [25] 0.71497625 0.56534870 0.90738863 0.83550352 0.76549823 0.91622815
##  [31] 0.20957039 0.35387827 0.10562600 0.11721563 0.99949867 0.82183209
##  [37] 0.66413885 0.30061668 0.90199878 0.63598207 0.70597580 0.74565152
##  [43] 0.60814978 0.46730330 0.99394922 0.70940463 0.93478809 0.67125895
##  [49] 0.45033733 0.69362545 0.37645711 0.01156165 0.84068528 0.20144150
##  [55] 0.06921584 0.47401665 0.66515178 0.29031024 0.82305299 0.60091102
##  [61] 0.30346513 0.40702847 0.34507134 0.28265412 0.43251592 0.10712020
##  [67] 0.21825061 0.49005801 0.65876810 0.01475468 0.40777074 0.35895701
##  [73] 0.02371050 0.20551534 0.98529712 0.94729525 0.66421386 0.59480097
##  [79] 0.79294093 0.26875167 0.55978508 0.12098553 0.21304076 0.40670725
##  [85] 0.68683903 0.98079129 0.78204748 0.11256118 0.64084680 0.05221190
##  [91] 0.61670265 0.98870831 0.74087994 0.45713742 0.55398697 0.81226586
##  [97] 0.59417271 0.65756812 0.89513997 0.95548542
muestra.unif2=runif(200,min=2,max=5) # genera una muestra uniforme en [2,5] de 200 datos
muestra.unif2 # devuelve la muestra generada
##   [1] 3.579399 4.571081 3.265408 2.078373 2.096941 2.352739 4.090955 3.812831
##   [9] 4.907075 4.308211 2.397673 2.247867 4.111123 4.012437 2.501061 4.221375
##  [17] 2.260264 2.648805 3.513249 4.442773 2.976490 3.664838 2.228200 4.590831
##  [25] 4.027013 3.195291 2.786817 3.675958 2.091530 4.905974 4.914610 2.115734
##  [33] 3.511480 2.390844 4.682109 2.836080 2.623192 3.904162 3.651448 4.228731
##  [41] 4.362340 2.820409 4.469565 3.205014 4.455499 2.293602 3.405958 3.918008
##  [49] 3.440164 3.011023 2.939242 4.337266 4.552475 4.013619 4.558456 4.036880
##  [57] 4.037275 4.086572 4.219177 3.227989 2.221861 3.728602 4.491770 2.119827
##  [65] 3.467433 2.917282 2.733925 3.399535 4.692520 2.700122 4.457819 4.216531
##  [73] 3.946205 2.995239 4.115602 4.880907 4.768666 3.500952 4.652364 3.305349
##  [81] 2.306639 2.903662 2.973551 4.126877 4.770191 4.478155 2.384397 2.171593
##  [89] 4.975327 4.746852 4.474165 4.677274 2.682309 4.827670 2.536487 2.812166
##  [97] 4.960047 4.701242 4.301109 3.609146 4.878997 3.989081 3.922237 2.323821
## [105] 2.484757 4.943961 4.846238 4.948866 2.374402 4.725156 4.910773 2.286091
## [113] 4.378342 2.001660 2.861195 3.703988 2.442162 4.129757 3.109064 2.044757
## [121] 4.862710 2.127956 2.603903 3.059018 4.639620 3.407775 2.211968 4.514156
## [129] 2.569993 3.315739 4.839088 2.961710 2.935697 2.400757 2.489231 4.176028
## [137] 4.856867 4.516277 3.672435 4.962107 2.685461 3.401775 4.333779 3.915034
## [145] 4.836480 4.827346 3.628800 4.713197 2.355867 4.330170 2.617845 3.755121
## [153] 2.605704 3.031690 3.192901 2.374146 2.484289 2.819595 4.924016 2.603762
## [161] 4.242884 4.599244 2.105177 2.388055 4.779116 4.702227 4.229962 3.697644
## [169] 3.038434 4.219772 4.662290 4.343123 2.858557 4.758197 4.749133 4.576582
## [177] 2.451825 2.049822 2.359585 2.350898 4.883602 4.473896 4.587783 2.309450
## [185] 2.448921 2.604292 2.333888 4.525389 4.494202 3.634131 4.938330 2.457503
## [193] 4.851168 2.332125 3.077755 2.226452 3.117209 4.086925 4.914893 2.651436
muestra.norm.est=rnorm(30) # genera una muestra normal estándar de 30 datos
muestra.norm.est # devuelve la muestra generada
##  [1]  0.37536456  0.61633444 -0.13207753 -0.84182534  0.64888415 -0.61988153
##  [7] -1.02143842  0.36716569  1.02678185 -0.15137749  0.23015338 -1.82897245
## [13] -0.42454906 -1.33004840  0.23188712  0.67897920  0.82312409  0.48332637
## [19] -1.76470658 -1.10769332 -0.62258009 -1.84438392  0.86542091 -0.02442294
## [25] -0.42052439 -0.06218155  0.77566964  0.29861208 -0.83071892  1.38690656
muestra.norm=rnorm(50,mean=10,sd=3) # genera una muestra normal(10,3) de 50 datos
muestra.norm # devuelve la muestra generada
##  [1]  9.948360  9.946225 12.820570  7.195618 11.057105  8.211103  8.126123
##  [8]  7.743977 12.106622  8.840420  8.958294  8.807370  8.988964  9.903350
## [15]  8.824195  9.856798 11.389683 11.006917 10.378072 13.899570 10.279101
## [22] 11.166549  7.996514  9.505961 10.365770 15.329751  9.931408 15.028995
## [29]  7.106841  3.361811  8.579147  7.859542  8.373523 10.296328 15.084664
## [36]  7.196552 15.059271  6.354302  7.448454  7.073693 10.520295  4.045151
## [43] 11.565057  6.562143  8.430889  9.746364 10.109599  7.263787  5.115718
## [50] 12.093796
muestra.gamma=rgamma(40,rate=2, shape=3) # genera una muestra gamma(2,3) de 40 datos
muestra.gamma # devuelve la muestra generada
##  [1] 2.8303459 2.4804876 1.1733628 1.0764862 0.2258607 0.8491683 0.1369941
##  [8] 3.3555643 2.1491391 2.6943299 0.5356545 1.4416502 0.4680046 0.7211067
## [15] 0.4600094 1.2532749 0.8022449 1.3616378 0.7384643 1.0931889 1.4580672
## [22] 1.4579551 0.8705889 1.4999644 2.3970581 0.8079735 1.3974800 1.4320079
## [29] 1.7341655 1.0734878 0.4735892 1.3711478 2.8756068 0.9831509 1.4855759
## [36] 1.4510629 2.9130642 0.7599633 1.2037134 0.7635966
muestra.f=rf(80,df1=5,df2=6) # genera una muestra F de Snedekor(5,6) de 80 datos
muestra.f # devuelve la muestra generada
##  [1]  0.7816374  2.4950283  0.1716568  0.2190316  0.4988687  1.3101435
##  [7]  0.6565118  1.0771670  1.6822010  0.1920139  6.1105602  6.8638902
## [13]  1.1617624  2.1003740  2.0602778  0.2950320  0.2759087  1.3897829
## [19]  2.4187985  0.4134039  0.5422662  2.7060681  5.8710144  0.9432917
## [25]  1.3526495  0.9289754  2.6032814  1.1963459  0.9933244  1.1112781
## [31]  0.5810191  0.5145165  1.1613098  0.5914126  0.2689882  1.7165862
## [37]  2.6315728  0.1257322  0.4364502  1.7380113 18.2909699  0.5267842
## [43]  0.7869181  0.2556803  2.0241606  4.0774168  2.2275640  2.2957841
## [49]  0.8021862 13.9987361  2.5290191  0.5743604  0.4282625  1.1058253
## [55]  6.5111068  1.4877607  1.5672073  0.1241038  0.3096609  1.0945569
## [61]  0.1698885  2.0597161  0.4686950  0.8236358  0.9346092  8.6982744
## [67]  0.3518496  1.0714930  0.6771836  0.8441745  1.1207992  0.1145178
## [73]  0.7508293  0.5540607  1.3773911  1.7045334  0.7007199  0.9543451
## [79]  1.2799168  0.6700914
muestra.exp=rexp(90,2) # genera una muestra exponencial(2) de 90 datos
muestra.exp # devuelve la muestra generada
##  [1] 0.146891257 0.041396276 0.089243189 0.193379929 0.461492539 0.139154651
##  [7] 0.371848039 0.600838534 0.263642893 1.384352672 0.146588288 0.492269787
## [13] 0.462839721 0.963121605 0.547897488 0.239735272 0.142199038 0.123214886
## [19] 0.115045888 1.222349527 0.522918675 0.100731689 0.121986352 0.955774859
## [25] 0.551490530 0.091535015 1.176788542 0.369352909 0.546943967 0.039379181
## [31] 0.061558785 0.085643889 0.074742885 0.396892373 0.265599324 1.010948399
## [37] 0.120154399 0.033734369 0.025818836 1.010403250 0.281340827 0.062566719
## [43] 0.514405075 0.003428954 0.269583077 0.413284824 0.007962151 0.129664049
## [49] 0.085150389 0.021191897 0.207863645 0.857450373 0.341216152 0.798894148
## [55] 0.430740735 1.677423704 0.194771136 0.936760286 0.362510940 0.566323129
## [61] 1.049111219 0.446898828 0.188321537 0.185318798 0.371112982 0.097446594
## [67] 0.044770877 0.574032640 0.036808933 0.214853190 0.026602598 0.297118115
## [73] 0.852686072 0.909643375 0.005212244 0.085993649 1.602962557 0.470145563
## [79] 0.446734530 0.907775566 1.046979311 1.556020416 0.818563580 0.102257351
## [85] 0.580904884 0.279733178 0.008250367 0.153280553 0.410663530 0.529737980
muestra.chi=rchisq(70,df=4) # genera una muestra chi cuadrado con 4 grados de libertad de 70 datos
muestra.chi # devuelve la muestra generada
##  [1]  3.5259103  2.8904494  3.4179077  1.5745689  0.5029640  2.4308665
##  [7]  3.2153110  6.3409944  1.9995501  4.6299788  3.6576959  5.6087074
## [13]  1.4684165  3.1198414  4.9681251  1.6028103  4.9267733  3.7453270
## [19]  2.1521752  5.1412008  2.8693379  3.2127423  5.3215697  1.2952842
## [25]  6.0594948  0.2503803  2.5488674  1.1380526  6.7814683  4.9601378
## [31]  3.5519375  2.0753775  2.1237285  1.2998218  3.7910245  3.9142274
## [37]  4.2486886  3.0048135  1.6949628  3.2462716  4.5358589  5.1001843
## [43]  4.1737611  7.4697364  2.9543772  1.0485618  3.8750156  7.1395889
## [49]  1.7782713  4.7620021  7.9518480  2.3919892  2.1442662  3.3937487
## [55]  6.8857174  0.9055090  3.8099335  2.7275571  4.2068811  3.0387885
## [61]  1.2577280  6.7728146  7.5478957  1.0911562  4.9996565  3.2130827
## [67]  2.6594193  3.8881630  3.8843364 10.0697846
  • EJEMPLO DE OPERACIONES SOBRE DF
# Cargo la base:
PACIENTE<-1:30
EDAD<-c(7, 7, 8, 7, 7, 10,7 ,7, 7 ,9, 9, 11, 7, 9,  9, 11, 12, 7, 11,  6,  8,  8,  7, 10,  7,  8, 10,  7,  9, 10)
SEXO<-c("M", "M", "M", "F", "M", "M" ,"M", "M", "M", "M", "M", "F", "M" ,"M" ,"F" ,"M", "M" ,"M" ,"M" ,"F" ,"F" ,"F", "F","F", "M" ,"M" ,"F", "F" ,"F" ,"M")  
PESO<-c(24.4, 23.6, 47.0, 24.0, 23.9, 41.0, 32.9, 22.4, 28.7, 31.4, 28.9, 51.2, 26.2, 58.5, 23.7, 25.5, 49.7, 39.6,42.5, 21.6, 38.0, 26.6, 20.4, 23.7, 21.4, 45.7, 51.3, 28.0, 26.9, 43.9)
TALLA<-c(1.2, 1.2, 1.4, 1.2, 1.2, 1.4, 1.3, 1.2, 1.3, 1.3, 1.3, 1.6, 1.3, 1.5, 1.3, 1.3, 1.7, 1.3, 1.5, 1.2, 1.3, 1.2, 1.2,1.3, 1.2, 1.4, 1.5, 1.3, 1.3, 1.5)
IMC<-c(16.94444, 16.38889, 23.97959, 16.66667, 16.59722, 20.91837, 19.46746, 15.55556, 16.98225, 18.57988,17.10059, 20.00000, 15.50296, 26.00000, 14.02367, 15.08876, 17.19723, 23.43195, 18.88889, 15.00000,22.48521, 18.47222, 14.16667, 14.02367, 14.86111, 23.31633, 22.80000, 16.56805, 15.91716, 19.51111)
PIMC<-c(7.97, 72.72, 97.08, 83.88, 45.85, 87.33, 96.57, 32.88, 80.77, 92.72, 55.54, 77.77, 70.70, 98.69,  3.25,2.07, 38.08, 98.75, 80.60, 39.97, 96.07, 71.06,  3.44,  2.02, 56.86, 98.99, 90.84, 57.50, 44.77, 84.89)
CC<-c(54, 52, 76, 63, 56, 78, 69, 52, 60, 69, 60, 75, 50, 88, 58, 73, 75, 76, 72, 52, 76, 54, 52, 56, 56, 78, 76, 57, 57, 76)
CatPeso<-c("N",  "N",  "OB", "N",  "N",  "SO", "OB", "N",  "N",  "SO", "N",  "N",  "N",  "OB", "D",  "D",  "N",  "OB","N" , "N",  "OB", "N",  "D",  "D",  "N",  "OB", "SO", "N",  "N",  "N" )
IMCin<-data.frame(PACIENTE,EDAD,SEXO,PESO,TALLA,IMC,PIMC,CC,CatPeso)
#View(IMCin)

head(IMCin) # muestra las seis primeras filas de datos y los nombres de las columnas
##   PACIENTE EDAD SEXO PESO TALLA      IMC  PIMC CC CatPeso
## 1        1    7    M 24.4   1.2 16.94444  7.97 54       N
## 2        2    7    M 23.6   1.2 16.38889 72.72 52       N
## 3        3    8    M 47.0   1.4 23.97959 97.08 76      OB
## 4        4    7    F 24.0   1.2 16.66667 83.88 63       N
## 5        5    7    M 23.9   1.2 16.59722 45.85 56       N
## 6        6   10    M 41.0   1.4 20.91837 87.33 78      SO
tail(IMCin)# muestra las seis últimas filas de datos y los nombres de las columnas
##    PACIENTE EDAD SEXO PESO TALLA      IMC  PIMC CC CatPeso
## 25       25    7    M 21.4   1.2 14.86111 56.86 56       N
## 26       26    8    M 45.7   1.4 23.31633 98.99 78      OB
## 27       27   10    F 51.3   1.5 22.80000 90.84 76      SO
## 28       28    7    F 28.0   1.3 16.56805 57.50 57       N
## 29       29    9    F 26.9   1.3 15.91716 44.77 57       N
## 30       30   10    M 43.9   1.5 19.51111 84.89 76       N
dim(IMCin)#30 9
## [1] 30  9
rbind(head(IMCin),tail(IMCin))#une filas: las seis primeras filas con las seis últimas
##    PACIENTE EDAD SEXO PESO TALLA      IMC  PIMC CC CatPeso
## 1         1    7    M 24.4   1.2 16.94444  7.97 54       N
## 2         2    7    M 23.6   1.2 16.38889 72.72 52       N
## 3         3    8    M 47.0   1.4 23.97959 97.08 76      OB
## 4         4    7    F 24.0   1.2 16.66667 83.88 63       N
## 5         5    7    M 23.9   1.2 16.59722 45.85 56       N
## 6         6   10    M 41.0   1.4 20.91837 87.33 78      SO
## 25       25    7    M 21.4   1.2 14.86111 56.86 56       N
## 26       26    8    M 45.7   1.4 23.31633 98.99 78      OB
## 27       27   10    F 51.3   1.5 22.80000 90.84 76      SO
## 28       28    7    F 28.0   1.3 16.56805 57.50 57       N
## 29       29    9    F 26.9   1.3 15.91716 44.77 57       N
## 30       30   10    M 43.9   1.5 19.51111 84.89 76       N
cbind(IMCin$SEXO[1:5],IMCin$PESO[1:5]) #cuidado!
##      [,1] [,2]  
## [1,] "M"  "24.4"
## [2,] "M"  "23.6"
## [3,] "M"  "47"  
## [4,] "F"  "24"  
## [5,] "M"  "23.9"
data.frame(IMCin$SEXO[1:5],IMCin$PESO[1:5]) #une columnas con sus 5 primeros elementos
##   IMCin.SEXO.1.5. IMCin.PESO.1.5.
## 1               M            24.4
## 2               M            23.6
## 3               M            47.0
## 4               F            24.0
## 5               M            23.9
table(IMCin$SEXO) # devuelve las frecuencias absolutas de las categorías de la variable
## 
##  F  M 
## 11 19
dist.conj=table(IMCin$CatPeso, IMCin$SEXO) # devuelve la distribución conjunta de las variables categoría de peso y sexo

Z= IMCin$TALLA*100 # guarda los datos de la talla en centímetros
mean(Z) # calcula la media
## [1] 133
median(Z) # calcula la mediana 
## [1] 130
quantile(Z, 0.75) # calcula el cualtil 75
## 75% 
## 140
quantile(Z, probs = seq(0, 1, 0.25)) # calcula los cuantiles 0, 25, 50, 75 y 100
##   0%  25%  50%  75% 100% 
##  120  120  130  140  170
is.na(Z) # indica los valores que faltan
##  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE FALSE FALSE FALSE FALSE FALSE
W<-Z 
W[1]<-NA # asigna un valor perdido en la primera componente del vector W
is.na(W)
##  [1]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE FALSE FALSE FALSE FALSE FALSE
mean(W) # devuelve NA
## [1] NA
mean(W,na.rm=T) # no considera los valores no disponibles
## [1] 133.4483
mean(na.omit(W)) # equivalente al anterior
## [1] 133.4483
median(W,na.rm=T) # otra funcion que requiere excluir los valores no disponibles
## [1] 130
sd(W,na.rm=T) # otra funcion que requiere excluir los valores no disponibles
## [1] 13.16811

Ejercicios

Ejercicio 1

x <- c(1,5,7,9,3,5,6,2,4,7,5,6,9,8,6,2,6,1,4)

m <- mean(x)
M <- median(x)

valores_bajo_media <- sum(x < m)
valores_bajo_mediana <- sum(x < M)

Ejercicio 2

# Opción 1
modulo <- function(x) {
  if (x >= 0) {
    return(x)
  } else {
    return(-x)
  }
}

# Opción 2
modu <- function(x) {
  y <- abs(x)
  return(y)
}

Ejercicio 3

cocientes <- numeric(5)
y <- 1111

for (i in 2:6) {
  y <- y / i
  cocientes[i - 1] <- y
}

print(cocientes)
## [1] 555.500000 185.166667  46.291667   9.258333   1.543056

Ejercicio 4

signo <- function(x, y) {
  z <- x * y
  res <- ifelse(z == 0, "Nulo", ifelse(z > 0, "Positivo", "Negativo"))
  return(res)
}

signo(0, -2)
## [1] "Nulo"

Ejercicio 5

sample(1:6, size = 1)
## [1] 5

Ejercicio 6

sample(1:6, size = 4, replace = TRUE)
## [1] 3 2 1 1

Ejercicio 7

sample(c(1, 0), size = 1, prob = c(0.3, 0.7))
## [1] 0

Ejercicio 8

sample(c(1, 0), size = 8, replace = TRUE, prob = c(0.3, 0.7))
## [1] 0 0 0 1 0 1 0 0

Ejercicio 9

muestra <- rnorm(n = 10, mean = 4, sd = 0.5)
muestra
##  [1] 4.997421 3.886136 3.425789 3.608067 4.969224 4.048778 4.042436 4.233697
##  [9] 4.319687 4.251627

Ejercicio 10

# Como la distribución es simétrica, usamos la función qnorm para obtener el valor correspondiente a (1 - 0.15/2) = 0.925
P <- qnorm((1 - 0.15/2), mean = 0, sd = 2)
P
## [1] 2.879063

Ejercicio 11

# Primero encontramos el valor cuya probabilidad acumulada es 60%
x <- qnorm(0.60, mean = 0, sd = 1)

# Evaluamos la función de densidad en ese punto
densidad <- dnorm(x, mean = 0, sd = 1)
densidad
## [1] 0.3863425

1. Análisis Previo de los Datos

Antes de aplicar cualquier tratamiento, examinamos la estructura y características de los datos. Creamos un dataset de ejemplo que contiene tanto valores perdidos como posibles outliers.

# Cargar librerías necesarias
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.5.3
## Warning: package 'readr' was built under R version 4.5.3
## Warning: package 'forcats' was built under R version 4.5.3
## Warning: package 'lubridate' was built under R version 4.5.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.3.0
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── 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(caret)
## Warning: package 'caret' was built under R version 4.5.3
## Cargando paquete requerido: lattice
## 
## Adjuntando el paquete: 'caret'
## 
## The following object is masked from 'package:purrr':
## 
##     lift
library(mice)
## Warning: package 'mice' was built under R version 4.5.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(outliers)  # Para test de Grubbs y Dixon
## Warning: package 'outliers' was built under R version 4.5.2
var1 <- c(rnorm(1000, mean = 30, sd = 10))
var2 <- c(rnorm(1000, mean = 0, sd = 10))
var2 <- var1*3 + var2

p_na <- 0.05  # 5% proba de NA
p_outlier <- 0.005  # 0.5% proba de outlier

introducir_anomalies <- function(x, p_na, p_outlier) {
  for (i in seq_along(x)) {
    rnd <- runif(1)
    if (rnd < p_na) {
      x[i] <- NA
    } else if (rnd < p_na + p_outlier) {
      x[i] <- 10000 
    }
  }
  return(x)
}

var1 <- introducir_anomalies(var1, p_na, p_outlier)
var2 <- introducir_anomalies(var2, p_na, p_outlier)

set.seed(123)
datos <- tibble(
  id = 1:1000,
  variable1 = var1,
  variable2 = var2
)

# Mostrar resumen del conjunto de datos
summary(datos)
##        id           variable1           variable2       
##  Min.   :   1.0   Min.   :   -3.873   Min.   :  -21.29  
##  1st Qu.: 250.8   1st Qu.:   23.316   1st Qu.:   69.12  
##  Median : 500.5   Median :   29.866   Median :   91.07  
##  Mean   : 500.5   Mean   :   71.572   Mean   :  111.85  
##  3rd Qu.: 750.2   3rd Qu.:   37.132   3rd Qu.:  112.30  
##  Max.   :1000.0   Max.   :10000.000   Max.   :10000.00  
##                   NA's   :36          NA's   :44

Se observa que existen valores NA en ambas variables y algunos valores extremos (10000 en ambas variables).

2. Manejo de Valores Perdidos

2.1. Identificación de Valores Perdidos

Para detectar la presencia de valores nulos, utilizamos funciones de R tanto de base como del tidyverse.

# Contar valores perdidos en cada variable
datos %>% summarise(across(everything(), ~sum(is.na(.))))
## # A tibble: 1 × 3
##      id variable1 variable2
##   <int>     <int>     <int>
## 1     0        36        44
  • Ojo!:
    • No identifica patrones o dependencias en la ocurrencia de NA (por ejemplo, si se agrupan por categorías).
ggplot(datos, aes(x = variable1, y = variable2)) +
  geom_point(color = "blue", alpha = 0.6) +  # Scatter plot points
  geom_smooth(method = "lm", color = "red", se = FALSE) +  # Linear regression line
  labs(
    title = "Scatter Plot de Variable1 vs Variable2",
    x = "Variable 1",
    y = "Variable 2"
  ) +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Removed 77 rows containing non-finite outside the scale range
## (`stat_smooth()`).
## Warning: Removed 77 rows containing missing values or values outside the scale range
## (`geom_point()`).

datos_filtered <- datos %>% drop_na()

lower_q1 <- quantile(datos_filtered$variable1, 0.02)
upper_q1 <- quantile(datos_filtered$variable1, 0.98)
lower_q2 <- quantile(datos_filtered$variable2, 0.02)
upper_q2 <- quantile(datos_filtered$variable2, 0.98)

datos_filtered <- datos_filtered %>%
  filter(variable1 >= lower_q1, variable1 <= upper_q1,
         variable2 >= lower_q2, variable2 <= upper_q2)


ggplot(datos_filtered, aes(x = variable1, y = variable2)) +
  geom_point(color = "blue", alpha = 0.6) +  # Scatter plot points
  geom_smooth(method = "lm", color = "red", se = FALSE) +  # Linear regression line
  labs(
    title = "Scatter Plot sacando 2% mas grande y chico",
    x = "Variable 1",
    y = "Variable 2"
  ) +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

2.2. Imputación de Valores Perdidos

Existen diversas técnicas para imputar valores nulos. A continuación se muestran varias, cada una con sus pros y contras.

2.2.1. Eliminación de Observaciones con NA

Se eliminan las filas que contengan algún valor perdido.

datos_omit <- na.omit(datos)
dim(datos_omit)
## [1] 923   3
  • Ventajas:
    • Método sencillo y rápido.
    • Evita introducir supuestos o sesgos derivados de la imputación.
  • Desventajas:
    • Puede reducir considerablemente el tamaño de la muestra si hay muchos NA.
    • Se pierde información potencialmente útil.
    • Se pueden genenrar sesgos si los datos faltantes no son aleatorios.

2.2.2. Imputación Simple con la Media o Mediana

Sustituir los NA por la media (o mediana) de la variable.

# Imputación con la media para variable1
datos <- datos %>% 
  mutate(variable1_impute_mean = ifelse(is.na(variable1), mean(variable1, na.rm = TRUE), variable1))

# Imputación con la mediana para variable2
datos <- datos %>% 
  mutate(variable2_impute_median = ifelse(is.na(variable2), median(variable2, na.rm = TRUE), variable2))
  • Ventajas:
    • Fácil de implementar y entender.
    • Rápido de calcular.
  • Desventajas:
    • Reduce la variabilidad natural de la variable.
    • Puede introducir sesgos, especialmente si la distribución es asimétrica.

2.2.3. Imputación Hot-Deck (Aleatoria)

Se imputan los valores perdidos seleccionando aleatoriamente un valor observado de la misma variable.

# Función para imputación hot-deck
imputacion_hotdeck <- function(x) {
  missing <- is.na(x)
  x[missing] <- sample(x[!missing], sum(missing), replace = TRUE)
  return(x)
}

# Aplicar hot-deck a variable2
datos <- datos %>% mutate(variable2_impute_hotdeck = imputacion_hotdeck(variable2))
  • Ventajas:
    • Preserva la distribución original de la variable.
    • Es simple y rápido de implementar.
  • Desventajas:
    • Introduce aleatoriedad que puede variar entre ejecuciones.
    • No aprovecha información de correlación entre variables.
# Cargar librerías necesarias
library(tidyverse)
library(caret)
library(mice)
library(outliers)  # Para test de Grubbs y Dixon
n <- 1000
# Generar variables iniciales
var1 <- rnorm(n, mean = 30, sd = 10)
var2 <- rnorm(n, mean = 0, sd = 10)
var2 <- var1 * 3 + var2

p_na <- 0.05       # 5% de probabilidad de NA
p_outlier <- 0.005 # 0.5% de probabilidad de outlier

# Función para introducir anomalías (NA y outliers)
introducir_anomalies <- function(x, p_na, p_outlier) {
  for (i in seq_along(x)) {
    rnd <- runif(1)
    if (rnd < p_na) {
      x[i] <- NA
    } else if (rnd < p_na + p_outlier) {
      x[i] <- 400 
    }
  }
  return(x)
}

var1 <- introducir_anomalies(var1, p_na, p_outlier)
var2 <- introducir_anomalies(var2, p_na, p_outlier)

set.seed(123)
datos <- tibble(
  id = 1:n,
  variable1 = var1,
  variable2 = var2
)

# Imputación con la mediana
datos <- datos %>%
  mutate(
    variable1_mediana = if_else(is.na(variable1),
                                median(variable1, na.rm = TRUE),
                                variable1),
    variable2_mediana = if_else(is.na(variable2),
                                median(variable2, na.rm = TRUE),
                                variable2)
  )

# Imputación con la media
datos <- datos %>%
  mutate(
    variable1_media = if_else(is.na(variable1),
                              mean(variable1, na.rm = TRUE),
                              variable1),
    variable2_media = if_else(is.na(variable2),
                              mean(variable2, na.rm = TRUE),
                              variable2)
  )
# Suponiendo que 'datos_imputados' ya está creado y contiene las filas donde había NA
# Convertir a formato largo para cada método de imputación

original <- datos %>%
  select(id, variable1, variable2) %>%
  mutate(metodo = "Original")

mediana <- datos %>%
  select(id, variable1_mediana, variable2_mediana) %>%
  rename(variable1 = variable1_mediana,
         variable2 = variable2_mediana) %>%
  mutate(metodo = "Mediana")

media <- datos %>%
  select(id, variable1_media, variable2_media) %>%
  rename(variable1 = variable1_media,
         variable2 = variable2_media) %>%
  mutate(metodo = "Media")


# Unir todos los datasets en uno solo
datos_long <- bind_rows(original, mediana, media)
datos_long <- datos_long %>% filter(variable1<400, variable2<400 )


# Graficar solo los puntos con sus respectivos colores
ggplot(datos_long, aes(x = variable1, y = variable2, color = metodo)) +
  geom_point(alpha = 1) +
  labs(x = "Variable 1", y = "Variable 2", color = "Metodo de Imputacion") +
  theme_minimal()

3.1. Detección Visual outliers

3.1.1. Boxplot

El boxplot es una herramienta gráfica clásica para identificar posibles outliers.

ggplot(datos, aes(y = variable1)) +
  geom_boxplot(fill = "lightblue") +
  labs(title = "Boxplot de variable1", y = "variable1") +
  theme_minimal()
## Warning: Removed 58 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

3.1.2. Histograma y QQ-Plot

QQ-Plot: ¿Qué es y cómo interpretarlo?

Un QQ-Plot (Quantile-Quantile Plot) es una herramienta gráfica utilizada en estadística para comparar la distribución de una variable con una distribución teórica, generalmente una distribución normal. Es útil para verificar si los datos siguen una distribución específica y detectar posibles desviaciones, como colas pesadas, asimetría o valores atípicos.

Cómo funciona un QQ-Plot

  1. Cuantiles teóricos vs. Cuantiles muestrales
    • Se calculan los cuantiles de los datos observados.
    • Se comparan con los cuantiles teóricos de la distribución de referencia (por ejemplo, normal).
  2. Interpretación del gráfico
    • Si los puntos siguen aproximadamente la línea azul, los datos tienen una distribución cercana a la teórica (normal).
    • Si los puntos se desvían en las colas (extremos), indica presencia de valores extremos o colas pesadas.
    • Si hay curvatura, sugiere asimetría en los datos.

1️⃣ Tomar los datos

Se parte de un conjunto de datos \(X = \{x_1, x_2, \dots, x_n\}\) con \(n\) observaciones.

2️⃣ Ordenar los datos

  • Se ordenan los datos en orden ascendente para obtener los valores \(x_{(1)}, x_{(2)}, \dots, x_{(n)}\), donde \(x_{(i)}\) es el \(i\)-ésimo valor ordenado.
  • Estos valores representan los cuantiles muestrales.

3️⃣ Calcular los cuantiles teóricos

  • Se asume que los datos siguen una distribución normal teórica con media \(\mu\) y desviación estándar \(\sigma\).

  • Se calcula el cuantil teórico esperado para cada observación \(x_{(i)}\). Para esto, se usa la posición de cada dato en la muestra: \[ p_i = \frac{i - 0.5}{n} \] donde \(p_i\) es el percentil estimado para cada dato.

  • Luego, se usa la función inversa de la distribución normal estándar \(\Phi^{-1}(p_i)\) para obtener los valores teóricos: \[ q_i = \Phi^{-1}(p_i) \] donde \(q_i\) representa los cuantiles teóricos de una distribución normal estándar \(N(0,1)\).

4️⃣ Graficar los cuantiles muestrales contra los teóricos

  • En el eje X, se colocan los cuantiles teóricos \(q_i\).
  • En el eje Y, se colocan los cuantiles muestrales \(x_{(i)}\).

5️⃣ Agregar la línea de referencia

  • Se traza una línea recta \(y = ax + b\) ajustada por regresión, donde \(a\) y \(b\) corresponden a la media y la desviación estándar de la muestra.
  • Si los datos siguen una distribución normal, los puntos deberían alinearse con esta línea.

Ejemplo Numérico

Supongamos que tenemos los siguientes datos:

\[ X = \{2.3, 2.9, 3.1, 3.8, 4.2, 4.8, 5.5\} \]

  1. Ordenamos los datos:
    \[ (2.3, 2.9, 3.1, 3.8, 4.2, 4.8, 5.5) \]

  2. Calculamos los cuantiles teóricos (con \(n=7\)) usando:

    \[ p_i = \frac{i - 0.5}{n}, \quad i = 1,2,...,7 \]

    Obtenemos los valores de \(p_i\):

    \[ (0.07, 0.21, 0.36, 0.50, 0.64, 0.79, 0.93) \]

    Luego, aplicamos la función inversa de la normal estándar \(\Phi^{-1}(p_i)\) para obtener:

    \[ (-1.47, -0.81, -0.36, 0.00, 0.36, 0.81, 1.47) \]

  3. Graficamos los puntos:

    • En X, ponemos los cuantiles teóricos.
    • En Y, los valores ordenados de la muestra.
    • Si los datos son normales, los puntos estarán cerca de una línea recta.
obtener_datos_qq_manual <- function(valores, a = 0.5) {
  # Eliminar valores NA
  valores <- na.omit(valores)
  
  # Ordenar los valores observados
  observados <- sort(valores)
  
  # Tamaño de la muestra
  n <- length(observados)
  
  # Calcular manualmente los puntos de probabilidad
  i <- 1:n
  probabilidades <- (i - a) / (n + 1 - 2 * a)
  
  # Cuantiles teóricos de una N(0, 1)
  teoricos <- qnorm(probabilidades)
  
  # Devolver como data frame
  data.frame(teorico = teoricos, observado = observados)
}
# Histograma
ggplot(datos, aes(x = variable2)) +
  geom_histogram(fill = "salmon", color = "black", bins = 20) +
  labs(title = "Histograma de variable2", x = "variable2") +
  theme_minimal()
## Warning: Removed 48 rows containing non-finite outside the scale range
## (`stat_bin()`).

# QQ-Plot
ggplot(datos, aes(sample = variable2)) +
  stat_qq() +
  stat_qq_line(color = "blue") +
  labs(title = "QQ-Plot de variable2") +
  theme_minimal()
## Warning: Removed 48 rows containing non-finite outside the scale range
## (`stat_qq()`).
## Warning: Removed 48 rows containing non-finite outside the scale range
## (`stat_qq_line()`).

3.2. Detección mediante Estandarización (Z-scores)

Calculamos el z-score para identificar observaciones que se alejen más de 3 desviaciones estándar de la media.

datos <- datos %>% mutate(z_variable1 = abs(scale(variable1)))
outliers_z <- datos %>% filter(z_variable1 > 3)
## Warning: Using one column matrices in `filter()` was deprecated in dplyr 1.1.0.
## ℹ Please use one dimensional logical vectors instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
outliers_z
## # A tibble: 8 × 8
##      id variable1 variable2 variable1_mediana variable2_mediana variable1_media
##   <int>     <dbl>     <dbl>             <dbl>             <dbl>           <dbl>
## 1   104       400      78.5               400              78.5             400
## 2   163       400      75.6               400              75.6             400
## 3   302       400      84.1               400              84.1             400
## 4   701       400      82.2               400              82.2             400
## 5   729       400      64.8               400              64.8             400
## 6   794       400     107.                400             107.              400
## 7   841       400      92.0               400              92.0             400
## 8   902       400      87.5               400              87.5             400
## # ℹ 2 more variables: variable2_media <dbl>, z_variable1 <dbl[,1]>
  • Ventajas:
    • Método simple y universal para distribuciones aproximadamente normales.
  • Desventajas:
    • No funciona bien para distribuciones muy asimétricas o con colas pesadas.

3.3. Detección con el Método del IQR

Se calculan el primer (Q1) y tercer cuartil (Q3) y se definen como outliers aquellos puntos que se encuentran fuera de \[Q1 - 1.5·IQR, Q3 + 1.5·IQR\].

# Calcular cuartiles e IQR para variable2
q1 <- quantile(datos$variable2, 0.25, na.rm = TRUE)
q3 <- quantile(datos$variable2, 0.75, na.rm = TRUE)
iqr <- q3 - q1

# Definir límites
limite_inferior <- q1 - 1.5 * iqr
limite_superior <- q3 + 1.5 * iqr

# Filtrar outliers
outliers_iqr <- datos %>% filter(variable2 < limite_inferior | variable2 > limite_superior)
outliers_iqr
## # A tibble: 8 × 8
##      id variable1 variable2 variable1_mediana variable2_mediana variable1_media
##   <int>     <dbl>     <dbl>             <dbl>             <dbl>           <dbl>
## 1    14    -2.27      -9.35            -2.27              -9.35          -2.27 
## 2   119    22.4      400               22.4              400             22.4  
## 3   194    57.1      178.              57.1              178.            57.1  
## 4   216    -3.11     -18.9             -3.11             -18.9           -3.11 
## 5   604    -0.470     -3.85            -0.470             -3.85          -0.470
## 6   707    18.2      400               18.2              400             18.2  
## 7   716    63.0      186.              63.0              186.            63.0  
## 8   944    12.2       -1.58            12.2               -1.58          12.2  
## # ℹ 2 more variables: variable2_media <dbl>, z_variable1 <dbl[,1]>
  • Ventajas:
    • No asume normalidad y es robusto frente a la presencia de outliers.
  • Desventajas:
    • Puede clasificar como outliers valores válidos en distribuciones muy sesgadas.

3.4. Test de Grubbs

Identifica el valor extremo más alejado de la media en una distribución normal (se remueven NA).

Test de Grubbs: Detección de Valores Atípicos en una Distribución Normal

El Test de Grubbs es una prueba estadística utilizada para detectar valores atípicos en un conjunto de datos que sigue una distribución normal. Su objetivo es identificar si el valor extremo más alejado de la media es significativamente diferente del resto de los datos.

1. Formular la Hipótesis

  • Hipótesis Nula (\(H_0\)): No hay valores atípicos en los datos, es decir, todos los valores provienen de la misma distribución normal.
  • Hipótesis Alternativa (\(H_A\)): Existe al menos un valor atípico en los datos.

2. Calcular la Estadística de Grubbs

La estadística del test se calcula como:

\[ G = \frac{\max |X_i - \bar{X}|}{s} \]

Donde: - \(X_i\) es el valor más alejado de la media (\(\bar{X}\)). - \(\bar{X}\) es la media de los datos. - \(s\) es la desviación estándar muestral.

3. Comparar con el Valor Crítico

El valor crítico de \(G\) se obtiene de una tabla de referencia o mediante software estadístico. Si \(G\) es mayor que el valor crítico para un nivel de significancia (\(\alpha\), típicamente 0.05), se rechaza \(H_0\) y se considera el dato como un valor atípico.

4. Interpretación del Resultado

  • Si el valor p (\(p\)-value) es menor que el nivel de significancia (\(\alpha\)), rechazamos \(H_0\) y concluimos que el valor extremo es un atípico.
  • Si el valor p es mayor que \(\alpha\), no se rechaza \(H_0\), lo que indica que no hay evidencia suficiente para considerar un valor como atípico.

5. Posibles Acciones

  • Si se detectan valores atípicos, se pueden eliminar o analizar su causa antes de tomar decisiones sobre los datos.
  • Si no hay valores atípicos detectados, el conjunto de datos se considera limpio respecto a valores extremos.

Consideraciones

  • El test de Grubbs solo detecta un valor atípico por vez. Si hay varios valores atípicos, se debe aplicar iterativamente.
  • Asume que los datos siguen una distribución normal. Si los datos no son normales, se deben usar otros métodos como el test de Dixon o el IQR (Rango Intercuartílico).
grubbs_result <- grubbs.test(na.omit(datos$variable1))
grubbs_result
## 
##  Grubbs test for one outlier
## 
## data:  na.omit(datos$variable1)
## G = 10.36318, U = 0.88575, p-value < 2.2e-16
## alternative hypothesis: highest value 400 is an outlier

Test de Dixon: Detección de Valores Atípicos en Muestras Pequeñas

El Test de Dixon es una prueba estadística diseñada para identificar valores atípicos en conjuntos de datos pequeños o medianos. Es especialmente útil cuando el tamaño de la muestra es menor o igual a 30.

Paso a Paso del Test de Dixon

1. Formular la Hipótesis

  • Hipótesis Nula (\(H_0\)): No hay valores atípicos en los datos.
  • Hipótesis Alternativa (\(H_A\)): Al menos un valor en los datos es un valor atípico.

2. Calcular la Estadística de Dixon

El test de Dixon calcula una estadística basada en la distancia entre el valor más extremo y el resto de la distribución. Existen diferentes fórmulas dependiendo de si se considera el menor o el mayor valor como sospechoso de ser un outlier. La estadística general es:

\[ Q = \frac{x_{\text{extremo}} - x_{\text{más cercano}}}{x_{\text{máximo}} - x_{\text{mínimo}}} \]

Donde: - \(x_{\text{extremo}}\) es el valor sospechoso de ser un outlier (mínimo o máximo del conjunto de datos). - \(x_{\text{más cercano}}\) es el valor más cercano a \(x_{\text{extremo}}\). - \(x_{\text{máximo}}\) y \(x_{\text{mínimo}}\) son los valores máximos y mínimos del conjunto de datos.

El valor de \(Q\) se compara con valores críticos tabulados. Si \(Q\) es mayor que el valor crítico correspondiente al tamaño de la muestra y nivel de significancia (\(\alpha\), típicamente 0.05), se considera que el valor extremo es un outlier significativo.

¿Por qué el Test de Dixon se usa para muestras de 30 o menos?

  1. Precisión en muestras pequeñas:
    • Para tamaños mayores a 30, la probabilidad de obtener valores extremos debido al azar es más alta.
    • Métodos como Grubbs, IQR o tests basados en la desviación estándar son más apropiados para muestras grandes.
  2. Suposición de normalidad en muestras pequeñas:
    • El test de Dixon funciona mejor cuando la muestra sigue una distribución aproximadamente normal. En muestras grandes, la normalidad es más fácil de verificar y otros métodos pueden ser más efectivos.
  3. Valores críticos predefinidos:
    • Los valores críticos del test de Dixon están tabulados solo hasta muestras de tamaño 30.
    • Para muestras grandes, la tabla no es tan confiable y se requieren métodos más robustos.

4. Implementación en R

El código en R implementa el test de Dixon de la siguiente manera:

# Instalar el paquete si no está instalado
install.packages("outliers")
## Warning: package 'outliers' is in use and will not be installed
# Cargar la librería
library(outliers)

# Contar la cantidad de datos no nulos (sin NA)
sample_size <- sum(!is.na(datos$variable1))

# Aplicar la prueba de Dixon solo si la muestra es <= 30
if (sample_size > 30) {
  submuestra <- sample(na.omit(datos$variable1), 30)  # Seleccionar una submuestra de 30 elementos
  dixon_result <- dixon.test(submuestra)  # Aplicar test de Dixon
  print(dixon_result)
} else {
  dixon_result <- dixon.test(na.omit(datos$variable1))  # Aplicar test directamente
  print(dixon_result)
}
## 
##  Dixon test for outliers
## 
## data:  submuestra
## Q = 0.34044, p-value = 0.1769
## alternative hypothesis: lowest value -2.27322829767503 is an outlier

5. Interpretación del Resultado

  • Si el valor p (\(p\)-value) es menor que 0.05 → Se rechaza \(H_0\) y se concluye que hay al menos un valor atípico en los datos.
  • Si el valor p es mayor que 0.05 → No se puede afirmar que haya valores atípicos.

3.6. Tratamiento de Outliers

Una vez identificados los outliers, existen diversas estrategias para su tratamiento.

3.6.1. Eliminación de Outliers

Se eliminan las observaciones detectadas.

datos_sin_outliers <- datos %>% 
  filter(!(variable2 < limite_inferior | variable2 > limite_superior))
dim(datos_sin_outliers)
## [1] 944   8
  • Ventajas:
    • Elimina la influencia de valores extremos en el análisis.
  • Desventajas:
    • Puede llevar a pérdida de información y a sesgar la muestra si los outliers son parte de la variabilidad real.

3.6.2. Winsorización

Consiste en reemplazar los valores extremos por los percentiles de corte (por ejemplo, al 5% y 95%).

winsorizacion <- function(x, lower = 0.05, upper = 0.95) {
  quantiles <- quantile(x, probs = c(lower, upper), na.rm = TRUE)
  x[x < quantiles[1]] <- quantiles[1]
  x[x > quantiles[2]] <- quantiles[2]
  return(x)
}

datos <- datos %>% 
  mutate(variable2_winsorizada = winsorizacion(variable2, 0.05, 0.95))
  • Ventajas:
    • Reduce el efecto de los outliers sin eliminarlos por completo.
    • Conserva el tamaño de la muestra.
  • Desventajas:
    • Modifica la distribución de la variable.
    • Puede ocultar la existencia de valores extremos reales.

Dataset de ejercicio

En el dataset airquality se introducen valores extremos en la variable Temp (simulando outliers) y se mantienen los valores NA presentes en variables como Ozone y Solar.R.

# Cargar las librerías necesarias
library(dplyr)
library(ggplot2)

# Cargar el dataset real
datos <- airquality

# Introducir outliers en 'Temp': se duplican el valor en el 5% de las observaciones
set.seed(123)
indices_extremos <- sample(1:nrow(datos), size = round(0.05 * nrow(datos)))
datos$Temp[indices_extremos] <- datos$Temp[indices_extremos] * 2

# Visualizar un resumen del dataset modificado
summary(datos)
##      Ozone           Solar.R           Wind             Temp       
##  Min.   :  1.00   Min.   :  7.0   Min.   : 1.700   Min.   : 56.00  
##  1st Qu.: 18.00   1st Qu.:115.8   1st Qu.: 7.400   1st Qu.: 73.00  
##  Median : 31.50   Median :205.0   Median : 9.700   Median : 80.00  
##  Mean   : 42.13   Mean   :185.9   Mean   : 9.958   Mean   : 82.01  
##  3rd Qu.: 63.25   3rd Qu.:258.8   3rd Qu.:11.500   3rd Qu.: 86.00  
##  Max.   :168.00   Max.   :334.0   Max.   :20.700   Max.   :184.00  
##  NA's   :37       NA's   :7                                        
##      Month            Day      
##  Min.   :5.000   Min.   : 1.0  
##  1st Qu.:6.000   1st Qu.: 8.0  
##  Median :7.000   Median :16.0  
##  Mean   :6.993   Mean   :15.8  
##  3rd Qu.:8.000   3rd Qu.:23.0  
##  Max.   :9.000   Max.   :31.0  
## 
# Nota: El dataset 'datos' presenta NA en variables como Ozone y Solar.R, lo cual es útil para la práctica.

Ejercicios

Ejercicio 1: Detección de NA

  • Calcular la proporción de valores perdidos en cada variable.
  • Pista: Utilizar summarise y is.na().

Ejercicio 2: Comparación de Imputaciones en Ozone

  1. Imputar los valores NA en la variable Ozone usando la media.
  2. Imputar los valores NA usando una técnica hot-deck (reemplazo aleatorio).
  3. Comparar las distribuciones (mediante resúmenes estadísticos y gráficos) entre la versión original (casos completos) y las versiones imputadas.

Ejercicio 3: Detección de Outliers con IQR en Temp

  1. Detectar outliers en Temp utilizando el método IQR.
  2. Generar un nuevo dataset sin los outliers.
  3. Comparar la media y la desviación estándar antes y después de eliminar los outliers.

Ejercicio 4: Winsorización vs. Eliminación en Temp

  1. Aplicar winsorización (al 5% y 95%) a la variable Temp.
  2. Comparar los resultados de winsorización con la eliminación de outliers (obtenida en el ejercicio 4) mediante resúmenes estadísticos y gráficos de densidad.

Ejercicio 5: Test Estadísticos de Outliers en Ozone

  1. Aplicar el test de Grubbs y el test de Dixon en Ozone para detectar outliers.
  2. Discutir en qué situaciones es recomendable usar uno u otro test.

Ejercicio 6: Uso de Imputación Múltiple con el dataset mtcars

  1. Utilizar el dataset mtcars y modificar la variable mpg introduciendo NA de forma aleatoria (10% de los datos).
  2. Aplicar imputación múltiple utilizando el paquete mice para imputar los valores NA.
  3. Comparar la distribución de mpg antes y después de la imputación (mediante histogramas y resúmenes).

Sección 3: Resoluciones

Ejercicio 1: Detección de NA

# Calcular la proporción de NA en cada variable
proporcion_NA <- datos %>% summarise(across(everything(), ~mean(is.na(.))))
print(proporcion_NA)
##       Ozone    Solar.R Wind Temp Month Day
## 1 0.2418301 0.04575163    0    0     0   0

Ejercicio 2: Comparación de Imputaciones en Ozone

# (a) Imputación con la media en 'Ozone'
datos <- datos %>% 
  mutate(Ozone_impute_mean = ifelse(is.na(Ozone), mean(Ozone, na.rm = TRUE), Ozone))

# (b) Imputación hot-deck para 'Ozone'
imputacion_hotdeck <- function(x) {
  missing <- is.na(x)
  x[missing] <- sample(x[!missing], sum(missing), replace = TRUE)
  return(x)
}

datos <- datos %>% mutate(Ozone_impute_hotdeck = imputacion_hotdeck(Ozone))

# Resumen estadístico y cálculo de la desviación estándar
cat("Imputación con la media:\n")
## Imputación con la media:
print(summary(datos$Ozone_impute_mean))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1.00   21.00   42.13   42.13   46.00  168.00
cat("SD:", sd(datos$Ozone_impute_mean, na.rm = TRUE), "\n\n")
## SD: 28.69337
cat("Imputación hot-deck:\n")
## Imputación hot-deck:
print(summary(datos$Ozone_impute_hotdeck))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1.00   19.00   31.00   41.75   61.00  168.00
cat("SD:", sd(datos$Ozone_impute_hotdeck, na.rm = TRUE), "\n\n")
## SD: 32.24037
# Comparación con la distribución original (solo casos sin NA)
datos_completos <- datos %>% filter(!is.na(Ozone))
cat("Datos originales (sin NA):\n")
## Datos originales (sin NA):
print(summary(datos_completos$Ozone))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1.00   18.00   31.50   42.13   63.25  168.00
cat("SD:", sd(datos_completos$Ozone), "\n")
## SD: 32.98788

Ejercicio 3: Detección de Outliers con IQR en Temp

# Cálculo de cuartiles, IQR y límites para 'Temp'
q1 <- quantile(datos$Temp, 0.25, na.rm = TRUE)
q3 <- quantile(datos$Temp, 0.75, na.rm = TRUE)
iqr <- q3 - q1
limite_inferior <- q1 - 1.5 * iqr
limite_superior <- q3 + 1.5 * iqr

# Detectar outliers
outliers <- datos %>% filter(Temp < limite_inferior | Temp > limite_superior)
cat("Outliers en 'Temp':\n")
## Outliers en 'Temp':
print(outliers)
##   Ozone Solar.R Wind Temp Month Day Ozone_impute_mean Ozone_impute_hotdeck
## 1    14     274 10.9  136     5  14          14.00000                   14
## 2    NA     250  9.2  184     6  12          42.12931                   73
## 3    12     120 11.5  146     6  19          12.00000                   12
## 4    50     275  7.4  172     7  29          50.00000                   50
## 5    64     253  7.4  166     7  30          64.00000                   64
## 6    73     215  8.0  172     8  26          73.00000                   73
## 7    14     191 14.3  150     9  28          14.00000                   14
## 8    20     223 11.5  136     9  30          20.00000                   20
# Crear un dataset sin outliers
datos_sin_outliers <- datos %>% filter(Temp >= limite_inferior & Temp <= limite_superior)

# Comparar media y desviación estándar
media_original <- mean(datos$Temp, na.rm = TRUE)
sd_original <- sd(datos$Temp, na.rm = TRUE)
media_sin <- mean(datos_sin_outliers$Temp, na.rm = TRUE)
sd_sin <- sd(datos_sin_outliers$Temp, na.rm = TRUE)

cat("Media y SD en 'Temp':\n")
## Media y SD en 'Temp':
print(c("Media original" = media_original, "SD original" = sd_original,
        "Media sin outliers" = media_sin, "SD sin outliers" = sd_sin))
##     Media original        SD original Media sin outliers    SD sin outliers 
##          82.006536          20.482501          77.827586           9.513401

Ejercicio 4: Winsorización vs. Eliminación en Temp

# Función para winsorización
winsorizacion <- function(x, lower, upper) {
  qnt <- quantile(x, probs = c(lower, upper), na.rm = TRUE)
  x[x < qnt[1]] <- qnt[1]
  x[x > qnt[2]] <- qnt[2]
  return(x)
}

# Aplicar winsorización en 'Temp'
datos <- datos %>% mutate(Temp_winsorizada = winsorizacion(Temp, 0.05, 0.95))

# Resúmenes estadísticos
cat("Resumen 'Temp' original:\n")
## Resumen 'Temp' original:
print(summary(datos$Temp))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   56.00   73.00   80.00   82.01   86.00  184.00
cat("\nResumen 'Temp' winsorizada:\n")
## 
## Resumen 'Temp' winsorizada:
print(summary(datos$Temp_winsorizada))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   60.20   73.00   80.00   79.78   86.00  112.60
cat("\nResumen 'Temp' sin outliers (eliminación):\n")
## 
## Resumen 'Temp' sin outliers (eliminación):
print(summary(datos_sin_outliers$Temp))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   56.00   72.00   79.00   77.83   84.00   97.00
# Comparación gráfica: gráficos de densidad
ggplot(datos, aes(x = Temp)) +
  geom_density(fill = "gray", alpha = 0.5) +
  labs(title = "Densidad de Temp - Original")

ggplot(datos, aes(x = Temp_winsorizada)) +
  geom_density(fill = "blue", alpha = 0.5) +
  labs(title = "Densidad de Temp - Winsorizada")

ggplot(datos_sin_outliers, aes(x = Temp)) +
  geom_density(fill = "green", alpha = 0.5) +
  labs(title = "Densidad de Temp - Sin Outliers")

Ejercicio 5: Test Estadísticos de Outliers en Ozone

Nota: Para estos tests es necesario el paquete outliers.

library(outliers)

# Test de Grubbs para 'Ozone'
grubbs_result <- grubbs.test(na.omit(datos$Ozone))
cat("Resultado del test de Grubbs en 'Ozone':\n")
## Resultado del test de Grubbs en 'Ozone':
print(grubbs_result)
## 
##  Grubbs test for one outlier
## 
## data:  na.omit(datos$Ozone)
## G = 3.8157, U = 0.8723, p-value = 0.004765
## alternative hypothesis: highest value 168 is an outlier
# Test de Dixon para 'Ozone'
sample_size <- sum(!is.na(datos$Ozone))
if (sample_size > 30) {
  submuestra <- sample(na.omit(datos$Ozone), 30)
  dixon_result <- dixon.test(submuestra)
  cat("Resultado del test de Dixon (muestra de 30) en 'Ozone':\n")
  print(dixon_result)
} else {
  dixon_result <- dixon.test(na.omit(datos$Ozone))
  cat("Resultado del test de Dixon en 'Ozone':\n")
  print(dixon_result)
}
## Resultado del test de Dixon (muestra de 30) en 'Ozone':
## 
##  Dixon test for outliers
## 
## data:  submuestra
## Q = 0.47826, p-value = 0.0116
## alternative hypothesis: highest value 168 is an outlier
# Comentario: El test de Grubbs es adecuado para detectar un único outlier asumiendo normalidad,
# mientras que el test de Dixon es útil en muestras pequeñas o cuando se sospecha de múltiples extremos.

Ejercicio 6: Uso de Imputación Múltiple con el dataset mtcars

library(mice)

# Copiar el dataset mtcars
data("mtcars")
mtcars_mod <- mtcars

# Introducir NA aleatorios en 'mpg' (10% de las observaciones)
set.seed(123)
na_indices <- sample(1:nrow(mtcars_mod), size = round(0.1 * nrow(mtcars_mod)))
mtcars_mod$mpg[na_indices] <- NA

# Resumen de 'mpg' con NA
cat("Resumen de 'mpg' en mtcars_mod (con NA):\n")
## Resumen de 'mpg' en mtcars_mod (con NA):
print(summary(mtcars_mod$mpg))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   10.40   15.80   19.20   20.24   22.80   33.90       3
# Aplicar imputación múltiple con mice para la variable 'mpg'
imputacion_mtcars <- mice(mtcars_mod, m = 5, maxit = 50, method = 'pmm', seed = 500)
## 
##  iter imp variable
##   1   1  mpg
##   1   2  mpg
##   1   3  mpg
##   1   4  mpg
##   1   5  mpg
##   2   1  mpg
##   2   2  mpg
##   2   3  mpg
##   2   4  mpg
##   2   5  mpg
##   3   1  mpg
##   3   2  mpg
##   3   3  mpg
##   3   4  mpg
##   3   5  mpg
##   4   1  mpg
##   4   2  mpg
##   4   3  mpg
##   4   4  mpg
##   4   5  mpg
##   5   1  mpg
##   5   2  mpg
##   5   3  mpg
##   5   4  mpg
##   5   5  mpg
##   6   1  mpg
##   6   2  mpg
##   6   3  mpg
##   6   4  mpg
##   6   5  mpg
##   7   1  mpg
##   7   2  mpg
##   7   3  mpg
##   7   4  mpg
##   7   5  mpg
##   8   1  mpg
##   8   2  mpg
##   8   3  mpg
##   8   4  mpg
##   8   5  mpg
##   9   1  mpg
##   9   2  mpg
##   9   3  mpg
##   9   4  mpg
##   9   5  mpg
##   10   1  mpg
##   10   2  mpg
##   10   3  mpg
##   10   4  mpg
##   10   5  mpg
##   11   1  mpg
##   11   2  mpg
##   11   3  mpg
##   11   4  mpg
##   11   5  mpg
##   12   1  mpg
##   12   2  mpg
##   12   3  mpg
##   12   4  mpg
##   12   5  mpg
##   13   1  mpg
##   13   2  mpg
##   13   3  mpg
##   13   4  mpg
##   13   5  mpg
##   14   1  mpg
##   14   2  mpg
##   14   3  mpg
##   14   4  mpg
##   14   5  mpg
##   15   1  mpg
##   15   2  mpg
##   15   3  mpg
##   15   4  mpg
##   15   5  mpg
##   16   1  mpg
##   16   2  mpg
##   16   3  mpg
##   16   4  mpg
##   16   5  mpg
##   17   1  mpg
##   17   2  mpg
##   17   3  mpg
##   17   4  mpg
##   17   5  mpg
##   18   1  mpg
##   18   2  mpg
##   18   3  mpg
##   18   4  mpg
##   18   5  mpg
##   19   1  mpg
##   19   2  mpg
##   19   3  mpg
##   19   4  mpg
##   19   5  mpg
##   20   1  mpg
##   20   2  mpg
##   20   3  mpg
##   20   4  mpg
##   20   5  mpg
##   21   1  mpg
##   21   2  mpg
##   21   3  mpg
##   21   4  mpg
##   21   5  mpg
##   22   1  mpg
##   22   2  mpg
##   22   3  mpg
##   22   4  mpg
##   22   5  mpg
##   23   1  mpg
##   23   2  mpg
##   23   3  mpg
##   23   4  mpg
##   23   5  mpg
##   24   1  mpg
##   24   2  mpg
##   24   3  mpg
##   24   4  mpg
##   24   5  mpg
##   25   1  mpg
##   25   2  mpg
##   25   3  mpg
##   25   4  mpg
##   25   5  mpg
##   26   1  mpg
##   26   2  mpg
##   26   3  mpg
##   26   4  mpg
##   26   5  mpg
##   27   1  mpg
##   27   2  mpg
##   27   3  mpg
##   27   4  mpg
##   27   5  mpg
##   28   1  mpg
##   28   2  mpg
##   28   3  mpg
##   28   4  mpg
##   28   5  mpg
##   29   1  mpg
##   29   2  mpg
##   29   3  mpg
##   29   4  mpg
##   29   5  mpg
##   30   1  mpg
##   30   2  mpg
##   30   3  mpg
##   30   4  mpg
##   30   5  mpg
##   31   1  mpg
##   31   2  mpg
##   31   3  mpg
##   31   4  mpg
##   31   5  mpg
##   32   1  mpg
##   32   2  mpg
##   32   3  mpg
##   32   4  mpg
##   32   5  mpg
##   33   1  mpg
##   33   2  mpg
##   33   3  mpg
##   33   4  mpg
##   33   5  mpg
##   34   1  mpg
##   34   2  mpg
##   34   3  mpg
##   34   4  mpg
##   34   5  mpg
##   35   1  mpg
##   35   2  mpg
##   35   3  mpg
##   35   4  mpg
##   35   5  mpg
##   36   1  mpg
##   36   2  mpg
##   36   3  mpg
##   36   4  mpg
##   36   5  mpg
##   37   1  mpg
##   37   2  mpg
##   37   3  mpg
##   37   4  mpg
##   37   5  mpg
##   38   1  mpg
##   38   2  mpg
##   38   3  mpg
##   38   4  mpg
##   38   5  mpg
##   39   1  mpg
##   39   2  mpg
##   39   3  mpg
##   39   4  mpg
##   39   5  mpg
##   40   1  mpg
##   40   2  mpg
##   40   3  mpg
##   40   4  mpg
##   40   5  mpg
##   41   1  mpg
##   41   2  mpg
##   41   3  mpg
##   41   4  mpg
##   41   5  mpg
##   42   1  mpg
##   42   2  mpg
##   42   3  mpg
##   42   4  mpg
##   42   5  mpg
##   43   1  mpg
##   43   2  mpg
##   43   3  mpg
##   43   4  mpg
##   43   5  mpg
##   44   1  mpg
##   44   2  mpg
##   44   3  mpg
##   44   4  mpg
##   44   5  mpg
##   45   1  mpg
##   45   2  mpg
##   45   3  mpg
##   45   4  mpg
##   45   5  mpg
##   46   1  mpg
##   46   2  mpg
##   46   3  mpg
##   46   4  mpg
##   46   5  mpg
##   47   1  mpg
##   47   2  mpg
##   47   3  mpg
##   47   4  mpg
##   47   5  mpg
##   48   1  mpg
##   48   2  mpg
##   48   3  mpg
##   48   4  mpg
##   48   5  mpg
##   49   1  mpg
##   49   2  mpg
##   49   3  mpg
##   49   4  mpg
##   49   5  mpg
##   50   1  mpg
##   50   2  mpg
##   50   3  mpg
##   50   4  mpg
##   50   5  mpg
# Completar los datos imputados (se elige la primera imputación)
mtcars_imputed <- complete(imputacion_mtcars, 1)

# Ver resumen de 'mpg' después de la imputación
cat("Resumen de 'mpg' en mtcars_imputed:\n")
## Resumen de 'mpg' en mtcars_imputed:
print(summary(mtcars_imputed$mpg))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   10.40   15.72   19.20   20.13   22.80   33.90
# Histogramas comparativos
par(mfrow = c(1,2))
hist(mtcars$mpg, main = "Histograma de mpg - Original", xlab = "mpg", col = "gray", breaks = 10)
hist(mtcars_imputed$mpg, main = "Histograma de mpg - Imputado", xlab = "mpg", col = "blue", breaks = 10)

par(mfrow = c(1,1))

3. Normalización y Estandarización de Datos

3.1. ¿Por qué normalizar o estandarizar?

Muchos algoritmos de aprendizaje automático (como regresión logística, SVM, KNN y PCA) son sensibles a la escala de las variables. Cuando las variables tienen escalas muy diferentes, aquellas con valores más altos pueden dominar el cálculo de distancias o la función objetivo. Normalizar o estandarizar ayuda a:

  • Asegurar que cada variable contribuya de manera equitativa.
  • Mejorar la convergencia en algoritmos iterativos.
  • Facilitar la interpretación de coeficientes en algunos modelos.

3.2. Normalización (Min-Max Scaling)

La normalización transforma los valores de una variable para que queden en el rango [0, 1] mediante la fórmula:

\[ x_{\text{norm}} = \frac{x - \min(x)}{\max(x) - \min(x)} \]

Implementación Manual

# Crear datos de ejemplo
set.seed(123)
datos <- data.frame(variable = rnorm(100, mean = 50, sd = 10))

# Normalización manual
datos$variable_norm <- (datos$variable - min(datos$variable)) / (max(datos$variable) - min(datos$variable))
summary(datos$variable_norm)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.0000  0.4037  0.5273  0.5337  0.6674  1.0000

Usando el paquete caret

library(caret)
preProc_range <- preProcess(datos, method = c("range"))
datos_norm <- predict(preProc_range, datos)
head(datos_norm)
##    variable variable_norm
## 1 0.3889008     0.3889008
## 2 0.4623575     0.4623575
## 3 0.8601969     0.8601969
## 4 0.5292286     0.5292286
## 5 0.5423008     0.5423008
## 6 0.8949699     0.8949699

La normalización es particularmente útil cuando se desea preservar la forma de la distribución, pero ajustando todos los valores a una escala común.

3.3. Estandarización (Z-score Scaling)

La estandarización reescala la variable para que tenga media 0 y desviación estándar 1, utilizando la fórmula:

\[ z = \frac{x - \mu}{\sigma} \]

Implementación Manual

# Estandarización manual utilizando scale()
datos$variable_std <- scale(datos$variable)
summary(datos$variable_std)
##        V1          
##  Min.   :-2.62876  
##  1st Qu.:-0.64006  
##  Median :-0.03139  
##  Mean   : 0.00000  
##  3rd Qu.: 0.65885  
##  Max.   : 2.29721
sd(datos$variable_std)
## [1] 1

Usando el paquete caret

preProc_std <- preProcess(datos, method = c("center", "scale"))
datos_std_caret <- predict(preProc_std, datos)
head(datos_std_caret)
##      variable variable_norm variable_std
## 1 -0.71304802   -0.71304802  -0.71304802
## 2 -0.35120270   -0.35120270  -0.35120270
## 3  1.60854170    1.60854170   1.60854170
## 4 -0.02179795   -0.02179795  -0.02179795
## 5  0.04259548    0.04259548   0.04259548
## 6  1.77983218    1.77983218   1.77983218

La estandarización es muy útil para algoritmos basados en distancias, ya que asegura que las unidades de medida no influyan en el análisis.

Escalamiento Robusto

El escalamiento robusto se utiliza cuando la presencia de outliers puede distorsionar las medidas tradicionales (media y desviación estándar). Este método utiliza la mediana y el rango intercuartílico (IQR) para centrar y escalar los datos, según la fórmula:

\[ x_{\text{robust}} = \frac{x - \text{Mediana}(x)}{IQR(x)} \]

# Calcular el escalamiento robusto
mediana <- median(datos$variable, na.rm = TRUE)
iqr_val <- IQR(datos$variable, na.rm = TRUE)
datos$variable_robust <- (datos$variable - mediana) / iqr_val
summary(datos$variable_robust)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -1.99964 -0.46860  0.00000  0.02416  0.53140  1.79272

4. Población y Muestra

4.1. Definiciones

  • Población: Es el conjunto total de datos o elementos de interés sobre los cuales se desea hacer inferencias.
  • Muestra: Es un subconjunto representativo extraído de la población que se utiliza para realizar estimaciones o pruebas estadísticas.

4.2. Tipos de Muestreo

Muestreo Probabilístico

  • Aleatorio Simple: Cada elemento de la población tiene la misma probabilidad de ser seleccionado.
  • Sistemático: Se selecciona cada k-ésima observación a partir de un punto inicial aleatorio.
  • Estratificado: La población se divide en estratos (subgrupos) y se selecciona una muestra aleatoria dentro de cada estrato para mantener la representatividad.
  • Por Conglomerados: Se agrupa la población en conglomerados (por ejemplo, áreas geográficas) y se seleccionan algunos de estos conglomerados al azar.

Muestreo No Probabilístico

  • Por Conveniencia: Se elige la muestra con base en la accesibilidad y disponibilidad de los datos.
  • Por Cuotas: Se fija una cuota para cada subgrupo basado en características conocidas.
  • Bola de Nieve: Los sujetos iniciales recomiendan nuevos sujetos para la muestra.

4.3. Selección de Muestra en R

Muestreo Aleatorio Simple

Utilizando la función sample, se extraen elementos al azar:

# Seleccionar 100 valores al azar de 'variable' sin reemplazo
muestra_simple <- sample(datos$variable, size = 100, replace = FALSE)
head(muestra_simple)
## [1] 62.53815 43.72094 46.74068 67.86913 54.35181 63.68602

Muestreo Estratificado usando caret

Si se cuenta con una variable categórica para estratificar, se puede utilizar createDataPartition para asegurar que la distribución de los estratos se mantenga en la partición de entrenamiento y prueba.

# Supongamos que añadimos una variable categórica para estratificar
datos$grupo <- sample(c("A", "B", "C"), size = nrow(datos), replace = TRUE)

library(caret)
set.seed(123)
trainIndex <- createDataPartition(datos$grupo, p = 0.7, list = FALSE)
train <- datos[trainIndex, ]
test <- datos[-trainIndex, ]

# Revisar la distribución de 'grupo' en la población, entrenamiento y prueba
table(datos$grupo)
## 
##  A  B  C 
## 38 32 30
table(train$grupo)
## 
##  A  B  C 
## 27 23 21
table(test$grupo)
## 
##  A  B  C 
## 11  9  9

6. Variantes y Estrategias en Muestreo

6.1. Muestreo Sistemático

El muestreo sistemático consiste en seleccionar cada k-ésima observación de la población, lo cual puede ser eficiente si se conoce la estructura subyacente.

# Calcular el intervalo k y seleccionar cada k-ésimo elemento
k <- 4
muestra_sistematico <- datos[seq(1, nrow(datos), by = k), ]
nrow(muestra_sistematico)
## [1] 25
nrow(datos)
## [1] 100

6.2. Muestreo por Conglomerados

En este método, se dividen los datos en conglomerados (por ejemplo, grupos geográficos o categóricos) y se selecciona aleatoriamente uno o varios conglomerados completos.

# Suponiendo que 'grupo' representa conglomerados en el dataset:
# Asignar aleatoriamente grupos a los datos (si no existe esta variable)
datos$grupo <- sample(c("A", "B", "C", "D"), size = nrow(datos), replace = TRUE)

# Seleccionar aleatoriamente dos conglomerados
conglomerados_seleccionados <- sample(unique(datos$grupo), size = 2)
muestra_conglomerados <- datos[datos$grupo %in% conglomerados_seleccionados, ]
table(muestra_conglomerados$grupo)
## 
##  B  D 
## 30 25

6.3. Técnicas de Remuestreo (Bootstrapping)

El bootstrapping es una técnica de remuestreo utilizada en estadística para estimar la distribución de una estadística muestral sin necesidad de asumir una distribución teórica específica. Consiste en generar múltiples muestras de la misma población mediante remuestreo con reemplazo, permitiendo obtener una distribución empírica de la estadística de interés. Esto es útil para estimar errores estándar, intervalos de confianza y la variabilidad de estimadores.

6.3.1 Tipos de Bootstrapping

Bootstrapping No Paramétrico

Este método no hace suposiciones sobre la distribución de la población. Se basa en seleccionar repetidamente muestras con reemplazo de la muestra original y calcular la estadística de interés en cada una de ellas. Es ampliamente utilizado en situaciones en las que la distribución subyacente es desconocida o difícil de modelar.

Bootstrapping Paramétrico

A diferencia del enfoque no paramétrico, este método asume una distribución específica para los datos y genera muestras sintéticas a partir de parámetros estimados de la población. Se usa cuando se tiene información previa sobre la distribución de los datos y se desea modelar la variabilidad basándose en dicha distribución.

6.3.2 ¿Por qué usar Bootstrapping?

El bootstrapping es útil en diversas situaciones: - Cuando el tamaño de la muestra es pequeño y los métodos asintóticos no son confiables. - Cuando la distribución de la población no es conocida o es difícil de modelar. - Para estimar la varianza de una estadística y construir intervalos de confianza sin necesidad de supuestos fuertes. - Para validar modelos predictivos a través de técnicas como el bootstrap cross-validation.

6.3.3 Ejemplo con un Conjunto de Datos Real

Para ilustrar el uso del bootstrapping, utilizaremos el conjunto de datos mtcars de R. En este ejemplo, estimaremos la media del consumo de combustible (mpg) y su variabilidad usando bootstrapping no paramétrico.

library(boot)
## 
## Adjuntando el paquete: 'boot'
## The following object is masked from 'package:lattice':
## 
##     melanoma
# Cargar los datos
datos <- mtcars
densidad <- density(datos$mpg)
print(mean(datos$mpg))
## [1] 20.09062
# Graficar la densidad
plot(densidad, main="Gráfico de Densidad de MPG", xlab="Millas por galón (mpg)", col="blue", lwd=2)
polygon(densidad, col=rgb(0, 0, 1, 0.2), border="blue") # Relleno con transparencia

# Definir función para calcular la media del consumo de combustible
media_fn <- function(data, indices) {
  return(mean(data[indices], na.rm = TRUE))
}

# Aplicar bootstrapping con 1000 replicaciones
set.seed(123)
boot_result <- boot(data = datos$mpg, statistic = media_fn, R = 1000)
print(boot_result)
## 
## ORDINARY NONPARAMETRIC BOOTSTRAP
## 
## 
## Call:
## boot(data = datos$mpg, statistic = media_fn, R = 1000)
## 
## 
## Bootstrap Statistics :
##     original   bias    std. error
## t1* 20.09062 0.048825    1.048114
plot(boot_result)

El resultado de la función boot() nos proporciona: - Estadística original: La media muestral de mpg. - Sesgo: Diferencia entre la media de los valores bootstrapeados y la estadística original. - Error estándar: Variabilidad estimada de la media.

Para obtener un intervalo de confianza del 95% basado en los percentiles de la distribución empírica obtenida:

boot.ci(boot_result, type = "perc")
## BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
## Based on 1000 bootstrap replicates
## 
## CALL : 
## boot.ci(boot.out = boot_result, type = "perc")
## 
## Intervals : 
## Level     Percentile     
## 95%   (18.12, 22.21 )  
## Calculations and Intervals on Original Scale

Este intervalo de confianza es útil para evaluar la incertidumbre de la estimación de la media de mpg.

6.3.4 Bootstrapping Paramétrico

Para realizar un bootstrapping paramétrico, asumimos que mpg sigue una distribución normal con media y desviación estándar estimadas de la muestra original. Generamos nuevas muestras de mpg basadas en esta distribución y aplicamos bootstrapping:

param_bootstrap <- function(data, R) {
  mu <- mean(data, na.rm = TRUE)
  sigma <- sd(data, na.rm = TRUE)
  
  boot_samples <- replicate(R, mean(rnorm(length(data), mean = mu, sd = sigma)))
  return(boot_samples)
}

# Aplicamos el bootstrapping paramétrico
set.seed(123)
param_results <- param_bootstrap(datos$mpg, R = 1000)

# Estimamos el intervalo de confianza del 95%
quantile(param_results, probs = c(0.025, 0.975))
##     2.5%    97.5% 
## 17.97379 21.98495

En este caso, generamos R=1000 muestras sintéticas de mpg asumiendo una distribución normal. Luego, obtenemos el intervalo de confianza basado en los percentiles de la distribución generada.

7. División en Entrenamiento y Prueba

Una parte esencial del análisis predictivo es dividir el conjunto de datos en dos (o más) subconjuntos:
- Entrenamiento: Donde se ajusta el modelo.
- Validacion: Donde se evalúa el desempeño del modelo en datos no vistos. - Test: Conjunto de datos sobre el cual NO se toman decisiones y sirve para al finalizar reportar metricas

Esta división ayuda a evitar el sobreajuste y proporciona una estimación realista de la capacidad predictiva.

7.1. División usando caret

El paquete caret facilita la creación de particiones de datos manteniendo la distribución de variables importantes (especialmente en el caso de variables categóricas).

datos
##                      mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive      21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
## Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
## Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
## Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
## Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
## Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
## Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
## Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
## Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
## Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
## Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
## Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
## Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
## Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
## Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
## Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
## AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
## Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
## Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
## Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
## Porsche 914-2       26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
## Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
## Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
## Ferrari Dino        19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
## Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
## Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
# Supongamos que 'datos' es nuestro dataset y 'grupo' es una variable de estratificación
set.seed(123)
library(caret)
datos$grupo <- sample(c("A", "B", "C", "D"), size = nrow(datos), replace = TRUE)

trainIndex <- createDataPartition(datos$grupo, p = 0.7, list = FALSE)
train <- datos[trainIndex, ]
test <- datos[-trainIndex, ]

# Revisar la distribución de la variable estratificada en cada subconjunto
table(datos$grupo)
## 
##  A  B  C  D 
##  8 10 10  4
table(train$grupo)
## 
## A B C D 
## 6 7 7 3
table(test$grupo)
## 
## A B C D 
## 2 3 3 1

7.2. Ejemplo con un Modelo Simple

Se puede entrenar un modelo, por ejemplo, una regresión lineal, y evaluarlo en el conjunto de prueba.

# Ajustar un modelo de regresión lineal usando el conjunto de entrenamiento
modelo_lm <- lm(mpg ~ ., data = train)

# Predicción en el conjunto de prueba
prediccionesTest <- predict(modelo_lm, newdata = test)
prediccionesTrain <- predict(modelo_lm, newdata = train)

# Comparar las predicciones con los valores reales
resultados <- data.frame(Real = test$mpg, Predicho = prediccionesTest)
resultadosTrain <- data.frame(Real = train$mpg, Predicho = prediccionesTrain)

head(resultados)
##                   Real Predicho
## Hornet Sportabout 18.7 13.72877
## Merc 450SL        17.3 15.98009
## Merc 450SLC       15.2 16.47472
## Toyota Corolla    33.9 28.24974
## Dodge Challenger  15.5 15.61435
## AMC Javelin       15.2 18.38049
# Calcular error cuadrático medio (MSE) en test
mse <- mean((resultados$Real - resultados$Predicho)^2)
mseTrain <- mean((resultadosTrain$Real - resultadosTrain$Predicho)^2)

print(paste("MSE Test:", round(mse, 2)))
## [1] "MSE Test: 12.13"
print(paste("MSE Train:", round(mseTrain, 2)))
## [1] "MSE Train: 2.72"

8. Validación Cruzada

La validación cruzada es una técnica que permite evaluar el rendimiento de un modelo de forma más robusta al dividir los datos en varios pliegues (folds) y promediar el desempeño en cada uno.

8.1. k-Fold Cross Validation

Una de las formas más comunes es la validación cruzada k-fold, en la que el conjunto de datos se divide en k partes iguales. Se entrena el modelo k veces, cada vez usando k-1 partes para entrenamiento y la parte restante para prueba.

# Definir un control de entrenamiento para validación cruzada k-fold (por ejemplo, k=10)
control_cv <- trainControl(method = "cv", number = 10)

# Entrenar el modelo utilizando validación cruzada
modelo_cv <- train(mpg ~ ., data = train, method = "lm", trControl = control_cv)
print(modelo_cv)
## Linear Regression 
## 
## 23 samples
## 11 predictors
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 20, 21, 21, 21, 20, 21, ... 
## Resampling results:
## 
##   RMSE      Rsquared   MAE     
##   4.619263  0.8849882  4.022908
## 
## Tuning parameter 'intercept' was held constant at a value of TRUE
modelo_cv
## Linear Regression 
## 
## 23 samples
## 11 predictors
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 20, 21, 21, 21, 20, 21, ... 
## Resampling results:
## 
##   RMSE      Rsquared   MAE     
##   4.619263  0.8849882  4.022908
## 
## Tuning parameter 'intercept' was held constant at a value of TRUE

8.2. Leave-One-Out Cross Validation (LOOCV)

El LOOCV es un caso especial de k-fold donde k es igual al número de observaciones. Esto puede ser computacionalmente costoso, pero es útil para conjuntos de datos pequeños.

# Configurar el control para LOOCV
control_loocv <- trainControl(method = "LOOCV")

# Entrenar el modelo utilizando LOOCV
modelo_loocv <- train(mpg ~ ., data = train, method = "lm", trControl = control_loocv)
print(modelo_loocv)
## Linear Regression 
## 
## 23 samples
## 11 predictors
## 
## No pre-processing
## Resampling: Leave-One-Out Cross-Validation 
## Summary of sample sizes: 22, 22, 22, 22, 22, 22, ... 
## Resampling results:
## 
##   RMSE      Rsquared   MAE     
##   4.903128  0.4894812  4.039779
## 
## Tuning parameter 'intercept' was held constant at a value of TRUE

A continuación se presentan nuevos ejercicios utilizando datasets reales de R. Primero se exponen los enunciados de cada ejercicio y, posteriormente, se muestran sus resoluciones correspondientes.

9. Ejercicios

Ejercicio 1: División y Evaluación de un Modelo con mtcars

Utiliza el dataset mtcars para dividirlo en un 70% de entrenamiento y un 30% de prueba. Ajusta un modelo de regresión lineal para predecir el consumo de combustible (mpg) en función de la potencia (hp) y el peso (wt). Evalúa el modelo en el conjunto de prueba calculando el error cuadrático medio (MSE).

Ejercicio 2: Validación Cruzada k-Fold en un Modelo de Regresión

Con el conjunto de entrenamiento obtenido en el Ejercicio 1, realiza una validación cruzada de 10 pliegues para evaluar el rendimiento de un modelo de regresión lineal que prediga mpg a partir de hp y wt. Reporta el RMSE promedio obtenido durante la validación.

Ejercicio 3: Comparación de Técnicas de Preprocesamiento con airquality

Utiliza el dataset airquality y enfócate en la variable Ozone.
1. Elimina los valores faltantes de dicha variable.
2. Aplica dos técnicas de preprocesamiento:
- Normalización (Min-Max Scaling): transforma los datos para que sus valores estén entre 0 y 1.
- Estandarización (Z-score Scaling): centra los datos en 0 y los escala de acuerdo con su desviación estándar.
3. Visualiza, mediante histogramas, las distribuciones de la variable original, la normalizada y la estandarizada. Comenta las diferencias observadas.

9. Resoluciones

Resolución Ejercicio 1

data(mtcars)
set.seed(123)

indices <- sample(1:nrow(mtcars), size = 0.7 * nrow(mtcars))
train_ex1 <- mtcars[indices, ]
test_ex1  <- mtcars[-indices, ]

modelo_ex1 <- lm(mpg ~ hp + wt, data = train_ex1)

predicciones_ex1 <- predict(modelo_ex1, newdata = test_ex1)

mse_ex1 <- mean((test_ex1$mpg - predicciones_ex1)^2)
print(paste("MSE del modelo:", round(mse_ex1, 2)))
## [1] "MSE del modelo: 4.24"

Resolución Ejercicio 2

library(caret)
set.seed(123)

control_ex2 <- trainControl(method = "cv", number = 10)

modelo_ex2 <- train(mpg ~ hp + wt, data = train_ex1, method = "lm", trControl = control_ex2)
## Warning in nominalTrainWorkflow(x = x, y = y, wts = weights, info = trainInfo,
## : There were missing values in resampled performance measures.
print(modelo_ex2)
## Linear Regression 
## 
## 22 samples
##  2 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 19, 20, 20, 20, 20, 20, ... 
## Resampling results:
## 
##   RMSE      Rsquared   MAE     
##   3.037779  0.9912946  2.510219
## 
## Tuning parameter 'intercept' was held constant at a value of TRUE

Resolución Ejercicio 3

library(ggplot2)
data(airquality)

datos_ex3 <- na.omit(airquality[, "Ozone", drop = FALSE])

datos_ex3$Ozone_norm <- (datos_ex3$Ozone - min(datos_ex3$Ozone)) / 
                        (max(datos_ex3$Ozone) - min(datos_ex3$Ozone))

datos_ex3$Ozone_std <- scale(datos_ex3$Ozone)


p1 <- ggplot(datos_ex3, aes(x = Ozone)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "white") +
  labs(title = "Distribucion Original de Ozone")

p2 <- ggplot(datos_ex3, aes(x = Ozone_norm)) +
  geom_histogram(bins = 30, fill = "lightgreen", color = "white") +
  labs(title = "Distribucion Normalizada (Min-Max)")

p3 <- ggplot(datos_ex3, aes(x = Ozone_std)) +
  geom_histogram(bins = 30, fill = "salmon", color = "white") +
  labs(title = "Distribucion Estandarizada (Z-score)")

print(p1)

print(p2)

print(p3)