0. Librerías

library(knitr)
library(kableExtra)
library(e1071)

1. Leer Datos

variables <- read.csv("C:/Users/WAN/Downloads/GlobalWeatherRepository.csv")

2. Extraer y Depuración de la Variable

UV <- na.omit(variables$uv_index)

# Total de datos limpios
n_total <- length(UV)

3. Frecuencias

3.1 Max y Min

valor_min <- min(UV)
valor_max <- max(UV)
rango <- valor_max - valor_min

3.2. Regla de Sturges

K_sturges <- floor(1 + 3.322 * log10(n_total))
cat("Número de clases:", K_sturges, "\n")
## Número de clases: 18
A_sturges <- rango / K_sturges
cat("Amplitud Sturges:", A_sturges, "\n")
## Amplitud Sturges: 0.9055556

3.3. Intervalos

Li1 <- seq(valor_min, valor_max - A_sturges, by = A_sturges)
Ls1 <- Li1 + A_sturges

3.4. Bucle para las columnas de la tabla

ni1 <- numeric(length(Li1))

for(i in 1:length(Li1)){
  if(i == length(Li1)){
    ni1[i] <- sum(UV >= Li1[i] & UV <= Ls1[i])
  } else {
    ni1[i] <- sum(UV >= Li1[i] & UV < Ls1[i])
  }
}

# Frecuencias adicionales
hi1 <- (ni1 / sum(ni1)) * 100
Ni_asc1 <- cumsum(ni1)
Hi_asc1 <- cumsum(hi1)
Ni_dsc1 <- rev(cumsum(rev(ni1)))
Hi_dsc1 <- rev(cumsum(rev(hi1)))
MC1 <- (Li1 + Ls1)/2

3.5. Amplitud para tabla ajustada (Amplitud = 2)

amplitud <- 2

# Intervalos limpios basados en múltiplos de 2
Li2 <- seq(floor(valor_min/2)*2, ceiling(valor_max/2)*2 - 2, by = 2)
Ls2 <- Li2 + 2

ni2 <- numeric(length(Li2))

for(i in 1:length(Li2)){
  if(i == length(Li2)){
    ni2[i] <- sum(UV >= Li2[i] & UV <= Ls2[i])
  } else {
    ni2[i] <- sum(UV >= Li2[i] & UV < Ls2[i])
  }
}

hi2 <- (ni2 / sum(ni2)) * 100
Ni_asc2 <- cumsum(ni2)
Hi_asc2 <- cumsum(hi2)
Ni_dsc2 <- rev(cumsum(rev(ni2)))
Hi_dsc2 <- rev(cumsum(rev(hi2)))
MC2 <- (Li2 + Ls2)/2

4. Tabla de Distribución de Frecuencias

4.1. Tabla según Sturges

Tabla_Sturges <- data.frame(
  Lim_inf = round(Li1, 2),
  Lim_sup = round(Ls1, 2),
  MC = round(MC1, 2),
  ni = ni1,
  hi = round(hi1, 2),
  Ni_asc = Ni_asc1,
  Hi_asc = round(Hi_asc1, 2),
  Ni_dsc = Ni_dsc1,
  Hi_dsc = round(Hi_dsc1, 2)
)

Tabla_Sturges2 <- Tabla_Sturges
Tabla_Sturges2[] <- lapply(Tabla_Sturges2, as.character)

# Fila de totales
fila_total <- data.frame(
  Lim_inf = "TOTAL", Lim_sup = "", MC = "",
  ni = as.character(sum(ni1)),
  hi = as.character(round(sum(hi1), 2)),
  Ni_asc = "", Hi_asc = "", Ni_dsc = "", Hi_dsc = ""
)

Tabla_Sturges2 <- rbind(Tabla_Sturges2, fila_total)

kable(
  Tabla_Sturges2,
  align = "c",
  caption = "Tabla N°1: Distribución de frecuencias del Índice UV mediante la regla de Sturges"
) |>
  kableExtra::kable_styling(full_width = TRUE, position = "center", bootstrap_options = c("striped", "hover", "condensed", "responsive")) |>
  kableExtra::row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") |>
  kableExtra::row_spec(nrow(Tabla_Sturges2), bold = TRUE, background = "#EAEDED") |>
  footnote(
    general = "Elaborado por Grupo 2. Fuente: Global Weather Repository.",
    general_title = "Nota: ",
    footnote_as_chunk = TRUE,
    title_format = c("italic", "bold")
  )
Tabla N°1: Distribución de frecuencias del Índice UV mediante la regla de Sturges
Lim_inf Lim_sup MC ni hi Ni_asc Hi_asc Ni_dsc Hi_dsc
0 0.91 0.45 55288 39.02 55288 39.02 141703 100
0.91 1.81 1.36 16129 11.38 71417 50.4 86415 60.98
1.81 2.72 2.26 8207 5.79 79624 56.19 70286 49.6
2.72 3.62 3.17 7094 5.01 86718 61.2 62079 43.81
3.62 4.53 4.08 7382 5.21 94100 66.41 54985 38.8
4.53 5.43 4.98 7416 5.23 101516 71.64 47603 33.59
5.43 6.34 5.89 9836 6.94 111352 78.58 40187 28.36
6.34 7.24 6.79 8698 6.14 120050 84.72 30351 21.42
7.24 8.15 7.7 6779 4.78 126829 89.5 21653 15.28
8.15 9.06 8.6 4403 3.11 131232 92.61 14874 10.5
9.06 9.96 9.51 2483 1.75 133715 94.36 10471 7.39
9.96 10.87 10.41 3045 2.15 136760 96.51 7988 5.64
10.87 11.77 11.32 1913 1.35 138673 97.86 4943 3.49
11.77 12.68 12.22 1309 0.92 139982 98.79 3030 2.14
12.68 13.58 13.13 794 0.56 140776 99.35 1721 1.21
13.58 14.49 14.04 551 0.39 141327 99.73 927 0.65
14.49 15.39 14.94 299 0.21 141626 99.95 376 0.27
15.39 16.3 15.85 77 0.05 141703 100 77 0.05
TOTAL 141703 100
Nota: Elaborado por Grupo 2. Fuente: Global Weather Repository.

4.2. Tabla ajustada (Intervalos de 2 en 2)

TDF_UV <- data.frame(
  Lim_inf = round(Li2, 2),
  Lim_sup = round(Ls2, 2),
  MC = round(MC2, 2),
  ni = ni2,
  hi = round(hi2, 2),
  Ni_asc = Ni_asc2,
  Hi_asc = round(Hi_asc2, 2),
  Ni_dsc = Ni_dsc2,
  Hi_dsc = round(Hi_dsc2, 2)
)

TDF_UV[] <- lapply(TDF_UV, as.character)

TDF_Total <- data.frame(
  Lim_inf = "TOTAL", Lim_sup = "", MC = "",
  ni = as.character(sum(ni2)),
  hi = as.character(round(sum(hi2), 2)),
  Ni_asc = "", Hi_asc = "", Ni_dsc = "", Hi_dsc = "",
  stringsAsFactors = FALSE
)

TDF_UV <- rbind(TDF_UV, TDF_Total)
colnames(TDF_UV) <- c("Lim. Inf.", "Lim. Sup.", "MC", "ni", "hi (%)", "Ni Asc", "Hi Asc (%)", "Ni Dsc", "Hi Dsc (%)")

kable(
  TDF_UV,
  align = "c",
  caption = "Tabla N°2: Distribución de frecuencias del Índice UV en intervalos de 2 unidades"
) |>
  kable_styling(full_width = TRUE, position = "center", bootstrap_options = c("striped", "hover", "condensed", "responsive")) |>
  row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") |>
  row_spec(nrow(TDF_UV), bold = TRUE, background = "#EAEDED") |>
  footnote(general = "Elaborado por Grupo 2. Fuente: Global Weather Repository.", general_title = "Nota: ", footnote_as_chunk = TRUE, title_format = c("italic", "bold"))
Tabla N°2: Distribución de frecuencias del Índice UV en intervalos de 2 unidades
Lim. Inf. Lim. Sup. MC ni hi (%) Ni Asc Hi Asc (%) Ni Dsc Hi Dsc (%)
0 2 1 72442 51.12 72442 51.12 141703 100
2 4 3 16133 11.39 88575 62.51 69261 48.88
4 6 5 15137 10.68 103712 73.19 53128 37.49
6 8 7 18611 13.13 122323 86.32 37991 26.81
8 10 9 11392 8.04 133715 94.36 19380 13.68
10 12 11 5301 3.74 139016 98.1 7988 5.64
12 14 13 2043 1.44 141059 99.55 2687 1.9
14 16 15 635 0.45 141694 99.99 644 0.45
16 18 17 9 0.01 141703 100 9 0.01
TOTAL 141703 100
Nota: Elaborado por Grupo 2. Fuente: Global Weather Repository.

5. Gráficos de Distribución de Frecuencias

5.1. Histograma Original (ni)

hist(
  UV,
  breaks = c(Li2, max(Ls2)),
  main = "Gráfico N°1: Distribución de frecuencias absolutas del Índice UV\nRegistros meteorológicos mundiales, período 2024–2026",
  xlab = "Índice UV",
  ylab = "Frecuencia absoluta (ni)",
  col = "lightgreen",
  border = "black",
  xaxt = "n"
)
axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.2. Histograma con relación al todo (ni)

hist(
  UV,
  breaks = c(Li2, max(Ls2)),
  ylim = c(0, n_total),
  main = "Gráfico N°2: Histograma del Índice UV en Relación al Todo\n(Escala completa de la muestra)",
  xlab = "Índice UV",
  ylab = "Frecuencia absoluta (ni)",
  col = "lightgreen",
  border = "black",
  xaxt = "n"
)
axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.3. Histograma original (hi)

bp <- barplot(
  hi2,
  names.arg = paste(Li2, Ls2, sep = "-"),
  ylim = c(0, max(hi2) * 1.1),
  main = "Gráfico N°3: Distribución de frecuencias relativas del Índice UV",
  xlab = "Intervalos de Índice UV",
  ylab = "Frecuencia Relativa (%)",
  col = "skyblue",
  border = "black",
  las = 2,
  cex.names = 0.8,
  space = 0
)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.4. Histograma con relación a todo (hi)

barplot(
  hi2,
  names.arg = paste(Li2, Ls2, sep = "-"),
  ylim = c(0, 100),
  main = "Gráfico N°4: Histograma Global de Frecuencia Relativa\n(Escala completa de 0 a 100%)",
  xlab = "Intervalos de Índice UV",
  ylab = "Frecuencia Relativa (%)",
  col = "lightgreen",
  border = "black",
  las = 2,
  cex.names = 0.8,
  space = 0
)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.5. Polígono de frecuencias (hi)

bp <- barplot(
  hi2,
  names.arg = paste(Li2, Ls2, sep = "-"),
  col = "lightgreen",
  border = "black",
  ylim = c(0, max(hi2) * 1.2),
  main = "Gráfico N°5: Frecuencia Relativa (%) y Polígono del Índice UV",
  xlab = "Intervalos de Índice UV",
  ylab = "Frecuencia relativa (%)",
  las = 2,
  cex.names = 0.8,
  space = 0
)

lines(
  bp,
  hi2,
  type = "o",
  col = "red",
  lwd = 2,
  pch = 16  # <-- Corregido aquí el error tipográfico
)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.6. Ojiva ascendente y descendente (ni)

eje_x_asc <- c(Li2, max(Ls2))
ni_asc_graf <- c(0, Ni_asc2)

eje_x_dsc <- c(Li2, max(Ls2))
ni_dsc_graf <- c(Ni_dsc2, 0)

plot(
  eje_x_asc,
  ni_asc_graf,
  type = "o",
  pch = 16,
  lwd = 2,
  col = "blue",
  ylim = c(0, n_total),
  main = "Gráfico N°6: Ojivas de Frecuencia Absoluta Acumulada del Índice UV",
  xlab = "Índice UV",
  ylab = "Frecuencia Acumulada (Ni)",
  xaxt = "n"
)

lines(
  eje_x_dsc,
  ni_dsc_graf,
  type = "o",
  pch = 17,
  lwd = 2,
  col = "red"
)

axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)

legend(
  "right",
  legend = c("Ojiva Ascendente (<= Ls)", "Ojiva Descendente (>= Li)"),
  col = c("blue", "red"),
  pch = c(16, 17),
  lwd = 2,
  bty = "n",
  cex = 0.9
)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.7. Ojiva ascendente y descendente (hi)

hi_asc_graf <- c(0, Hi_asc2)
hi_dsc_graf <- c(Hi_dsc2, 0)

plot(
  eje_x_asc,
  hi_asc_graf,
  type = "o",
  pch = 16,
  lwd = 2,
  col = "blue",
  ylim = c(0, 100),
  main = "Gráfico N°7: Ojivas de Frecuencia Relativa Acumulada del Índice UV",
  xlab = "Índice UV",
  ylab = "Frecuencia Acumulada (%)",
  xaxt = "n"
)

lines(
  eje_x_dsc,
  hi_dsc_graf,
  type = "o",
  pch = 17,
  lwd = 2,
  col = "red"
)

axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)

legend(
  "right",
  legend = c("Ascendente (<= Ls)", "Descendente (>= Li)"),
  col = c("blue", "red"),
  pch = c(16, 17),
  lwd = 2,
  bty = "n",
  cex = 0.9
)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.8. Boxplot

Q1 <- quantile(UV, 0.25)
Q3 <- quantile(UV, 0.75)
RIC <- Q3 - Q1

Lim_inf_Tukey <- max(min(Li2), Q1 - 1.5 * RIC)
Lim_sup_Tukey <- min(max(Ls2), Q3 + 1.5 * RIC)

boxplot(UV,
        horizontal = TRUE,
        outline = FALSE,
        ylim = c(min(Li2), max(Ls2)), 
        main = "Gráfico N°8: Diagrama de Caja y Bigotes del Índice UV con Límites de Tukey",
        xlab = "Índice UV",
        col = "lightblue",
        xaxt = "n")

axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)

atipicos <- UV[UV < (Q1 - 1.5 * RIC) | UV > (Q3 + 1.5 * RIC)]
if(length(atipicos) > 0) {
  points(atipicos, rep(1, length(atipicos)), col = "red", pch = 19, cex = 0.8)
}

abline(v = Lim_inf_Tukey, col = "blue", lwd = 2, lty = 2)
abline(v = Lim_sup_Tukey, col = "blue", lwd = 2, lty = 2)

legend("topright",
       legend = c(paste("Atípicos (N =", length(atipicos), ")"), "Límites de Tukey"),
       col = c("red", "blue"), pch = c(19, NA), lty = c(NA, 2), lwd = c(NA, 2), bty = "n", cex = 0.9)
grid(nx = NULL, ny = NA, lty = 2, col = "gray")

6. Indicadores Estadísticos

6.1. Tendencia central

media <- mean(UV)

mediana <- median(UV)

# Moda aproximada usando la clase modal

tabla_moda <- TDF_UV[
  TDF_UV$`Lim. Inf.` != "TOTAL",
]

ni_num <- as.numeric(tabla_moda$ni)

max_ni <- max(ni_num)

moda <- as.numeric(
  tabla_moda$MC[
    ni_num == max_ni
  ]
)

6.2. Dispersión

varianza <- var(UV)

desv_est <- sd(UV)

cv <- (desv_est / media) * 100

6.3. Forma

asimetria <- skewness(UV)

curtosis <- kurtosis(UV)

6.4. Valores atípicos

Q1 <- quantile(UV, 0.25)

Q3 <- quantile(UV, 0.75)

RIC <- Q3 - Q1

lim_inf <- Q1 - 1.5 * RIC

lim_sup <- Q3 + 1.5 * RIC

atipicos <- UV[
  UV < lim_inf |
    UV > lim_sup
]

n_atipicos <- length(atipicos)

intervalo_atipicos <- paste0(
  "[",
  round(lim_inf,2),
  "; ",
  round(lim_sup,2),
  "]"
)

# Rango

rango_texto <- paste0(
  "[",
  round(min(UV),2),
  "; ",
  round(max(UV),2),
  "]"
)

6.5. Tabla de indicadores

tabla_indicadores <- data.frame(
  
  Variable = "UV",
  
  Rango = rango_texto,
  
  Media = round(media,2),
  
  Mediana = round(mediana,2),
  
  Moda = round(moda,2),
  
  Varianza = round(varianza,2),
  
  Desv_Est = round(desv_est,2),
  
  CV = round(cv,2),
  
  Asimetria = round(asimetria,2),
  
  Curtosis = round(curtosis,2),
  
  Limite_Atipicos = intervalo_atipicos,
  
  N_Atipicos = n_atipicos
)

# Mostrar tabla

kable(
  tabla_indicadores,
  align = "c",
  caption = "Tabla N°3. Indicadores estadísticos descriptivos de la variable 
Índice UV en registros meteorológicos mundiales, período 2024–2026 en 
  registros meteorológicos mundiales, período 2024–2026"
) |>
  kable_styling(
    full_width = FALSE,
    position = "center",
    bootstrap_options = c(
      "striped",
      "hover",
      "condensed",
      "responsive"
    )
  ) |>
  row_spec(
    0,
    bold = TRUE,
    color = "white",
    background = "#2C3E50"
  )
Tabla N°3. Indicadores estadísticos descriptivos de la variable Índice UV en registros meteorológicos mundiales, período 2024–2026 en registros meteorológicos mundiales, período 2024–2026
Variable Rango Media Mediana Moda Varianza Desv_Est CV Asimetria Curtosis Limite_Atipicos N_Atipicos
UV [0; 16.3] 3.27 1.8 1 12.51 3.54 108.1 0.93 -0.11 [-8.75; 14.85] 222

7. Conclusiones

El análisis del índice UV revela un promedio de 3.27 unidades y una marcada asimetría positiva de 0.93 (junto con una curtosis de -0.11), lo que demuestra que la mayor parte del tiempo los valores se concentran en niveles bajos y moderados. Las mediciones presentan una alta heterogeneidad debido a una desviación estándar de 3.54 y un coeficiente de variación de 108.1%, fluctuando en un rango desde 0 hasta 16.3 unidades e identificando 222 valores atípicos. Esta distribución general es muy beneficiosa para las personas y el ambiente porque un promedio de 3.27 entra en la categoría de riesgo bajo a moderado, permitiendo realizar actividades al aire libre de forma segura y estimulando la producción natural de vitamina D en la piel sin alterar el equilibrio de las plantas. Sin embargo, la presencia de registros atípicos que escalan hasta 16.3 es altamente perjudicial, ya que la radiación en niveles “extremos” provoca quemaduras graves en la piel, incrementando drásticamente el riesgo de cáncer cutáneo y cataratas oculares si no se utiliza protección solar adecuada.