0. Librerías

library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(gt)
library(e1071)

1. Leer datos

df <- read.csv(
  "waterPollution.csv",
  sep = ",",
  stringsAsFactors = FALSE
)

2. Extracción y depuración de la variable

# -------------------------
# Extraer variable y discretizar por redondeo
# -------------------------
Migraciones <- round(df$netMigration_2011_2018)

# Eliminar valores faltantes
Migraciones <- na.omit(Migraciones)

3. Tabla de distribución de frecuencias

3.1 Tabla de frecuencias

#Se decidió trabajar exclusivamente con los valores de migración neta positivos (a partir de 18,000). Esta delimitación metodológica se fundamenta en el criterio de que, conceptualmente, no resulta técnicamente adecuado denominar 'migración neta negativa'

Migraciones_positivos <- Migraciones[Migraciones >= 18000]

# -------------------------
# Tabla de frecuencias general
# -------------------------
tabla_freq_general <- as.data.frame(
  table(Migraciones_positivos)
)

# Renombrar columnas
colnames(tabla_freq_general) <- c(
  "Migraciones",
  "ni"
)

tabla_freq_general$Migraciones <- as.numeric(as.character(tabla_freq_general$Migraciones))

# Frecuencia relativa (hi)
tabla_freq_general$hi <- round(
  (tabla_freq_general$ni / sum(tabla_freq_general$ni)) * 100,
  2
)

# Frecuencias acumuladas ascendentes
tabla_freq_general$Ni_asc <- cumsum(tabla_freq_general$ni)
tabla_freq_general$Hi_asc <- round(
  (tabla_freq_general$Ni_asc / sum(tabla_freq_general$ni)) * 100, 
  2
)

# Frecuencias acumuladas descendentes
tabla_freq_general$Ni_dsc <- rev(cumsum(rev(tabla_freq_general$ni)))
tabla_freq_general$Hi_dsc <- round(
  (tabla_freq_general$Ni_dsc / sum(tabla_freq_general$ni)) * 100, 
  2
)

# -------------------------
# Agregar fila de totales
# -------------------------
fila_total_gen <- data.frame(
  Migraciones = "TOTAL",
  ni = sum(tabla_freq_general$ni),
  hi = 100,
  Ni_asc = NA,
  Hi_asc = NA,
  Ni_dsc = NA,
  Hi_dsc = NA
)

# Asegurar que la columna Migraciones sea character para aceptar "TOTAL"
tabla_freq_general$Migraciones <- as.character(tabla_freq_general$Migraciones)

tabla_final_gen <- rbind(
  tabla_freq_general,
  fila_total_gen
)

# Reemplazar NAs por espacios vacíos para la vista
tabla_final_gen[is.na(tabla_final_gen)] <- ""

# -------------------------
# Tabla GT General
# -------------------------
tabla_gt_general <- tabla_final_gen %>%
  gt() %>%
  tab_header(
    title = md("**Tabla N°1**"),
    subtitle = md(
      "**Distribución de frecuencias de la migración neta positiva en el estudio de la calidad de agua en Europa (1991-2017)**"
    )
  ) %>%
  cols_label(
    Migraciones = "Migraciones",
    ni = "ni",
    hi = "hi (%)",
    Ni_asc = "Ni ↑",
    Hi_asc = "Hi ↑ (%)",
    Ni_dsc = "Ni ↓",
    Hi_dsc = "Hi ↓ (%)"
  ) %>%
  cols_align(align = "center", columns = everything()) %>%
  tab_style(
    style = list(cell_text(weight = "bold"), cell_borders(sides = "top", color = "black", weight = px(2))),
    locations = cells_body(
      rows = Migraciones == "TOTAL"
    )
  ) %>%
  opt_row_striping()

tabla_gt_general
Tabla N°1
Distribución de frecuencias de la migración neta positiva en el estudio de la calidad de agua en Europa (1991-2017)
Migraciones ni hi (%) Ni ↑ Hi ↑ (%) Ni ↓ Hi ↓ (%)
18927 355 2.25 355 2.25 15751 100
21257 479 3.04 834 5.29 15396 97.75
22769 3 0.02 837 5.31 14917 94.71
22855 82 0.52 919 5.83 14914 94.69
45227 27 0.17 946 6.01 14832 94.17
56745 171 1.09 1117 7.09 14805 93.99
62334 261 1.66 1378 8.75 14634 92.91
74021 91 0.58 1469 9.33 14373 91.25
75808 9661 61.34 11130 70.66 14282 90.67
82158 22 0.14 11152 70.8 4621 29.34
297760 101 0.64 11253 71.44 4599 29.2
325435 3957 25.12 15210 96.57 4498 28.56
582211 541 3.43 15751 100 541 3.43
TOTAL 15751 100.00

3.2 Tabla simplificada de frecuencias

# -------------------------------------
# Tabla de frecuencias con intervalos
# -------------------------------------

# 1. Definir los cortes 
cortes <- c(0, 21000, 62000, 75000, 325000, 450000, 600000)
etiquetas_tabla <- c("[18k - 21k]", "(21k - 62k]", "(62k - 75k]", 
                     "(75k - 325k]", "(325k - 450k]", "(450k - 582k]")

# 2. Categorizar los datos originales de migraciones en los intervalos definidos
Migraciones_intervalos <- cut(Migraciones, breaks = cortes, labels = etiquetas_tabla, include.lowest = TRUE)

# 3. Crear la tabla de frecuencias con los datos agrupados
tabla_freq <- as.data.frame(
  table(Migraciones_intervalos)
)

# Renombrar columnas
colnames(tabla_freq) <- c(
  "Migraciones",
  "ni"
)

# Frecuencia relativa (hi)
tabla_freq$hi <- round(
  (tabla_freq$ni / sum(tabla_freq$ni)) * 100,
  2
)

# Frecuencias acumuladas ascendentes
tabla_freq$Ni_asc <- cumsum(tabla_freq$ni)
tabla_freq$Hi_asc <- round(
  (tabla_freq$Ni_asc / sum(tabla_freq$ni)) * 100, 
  2
)

# Frecuencias acumuladas descendentes
tabla_freq$Ni_dsc <- rev(cumsum(rev(tabla_freq$ni)))
tabla_freq$Hi_dsc <- round(
  (tabla_freq$Ni_dsc / sum(tabla_freq$ni)) * 100, 
  2
)

# -------------------------
# Agregar fila de totales
# -------------------------
fila_total <- data.frame(
  Migraciones = "TOTAL",
  ni = sum(tabla_freq$ni),
  hi = 100,
  Ni_asc = NA,
  Hi_asc = NA,
  Ni_dsc = NA,
  Hi_dsc = NA
)

tabla_final <- rbind(
  tabla_freq,
  fila_total
)

# Reemplazar NAs por espacios vacíos para la vista
tabla_final[is.na(tabla_final)] <- ""

# -------------------------
# Tabla GT
# -------------------------
tabla_gt <- tabla_final %>%
  gt() %>%
  tab_header(
    title = md("**Tabla N°2**"),
    subtitle = md(
      "**Distribución de frecuencias simplificada de la migración neta en el estudio de la calidad de agua en Europa (1991-2017)**"
    )
  ) %>%
  cols_label(
    Migraciones = "Migración Neta",
    ni = "ni",
    hi = "hi (%)",
    Ni_asc = "Ni ↑",
    Hi_asc = "Hi ↑ (%)",
    Ni_dsc = "Ni ↓",
    Hi_dsc = "Hi ↓ (%)"
  ) %>%
  cols_align(align = "center", columns = everything()) %>%
  tab_style(
    style = list(cell_text(weight = "bold"), cell_borders(sides = "top", color = "black", weight = px(2))),
    locations = cells_body(
      rows = Migraciones == "TOTAL"
    )
  ) %>%
  opt_row_striping()

tabla_gt
Tabla N°2
Distribución de frecuencias simplificada de la migración neta en el estudio de la calidad de agua en Europa (1991-2017)
Migración Neta ni hi (%) Ni ↑ Hi ↑ (%) Ni ↓ Hi ↓ (%)
[18k - 21k] 552 3.46 552 3.46 15948 100
(21k - 62k] 762 4.78 1314 8.24 15396 96.54
(62k - 75k] 352 2.21 1666 10.45 14634 91.76
(75k - 325k] 9784 61.35 11450 71.8 14282 89.55
(325k - 450k] 3957 24.81 15407 96.61 4498 28.2
(450k - 582k] 541 3.39 15948 100 541 3.39
TOTAL 15948 100.00

4 Gráficas

4.1 Diagrama de barras de cantidad

# ================================================
# Diagrama de barras de cantidad que genera RStudio
# ================================================
par(mar = c(7, 5, 4, 2) + 0.1) 

barplot(
  tabla_freq$ni,
  main = "Gráfica N°1: Distribución de la migración neta \nen el estudio de la calidad de agua en Europa (1991-2017)",
  xlab = "",
  ylab = "Cantidad",
  col = "skyblue",
  border = "black",
  ylim = c(0, max(tabla_freq$ni) * 1.2),
  names.arg = tabla_freq$Migraciones, 
  las = 2,          
  cex.names = 0.9,
  cex.main = 0.85
)

title(xlab = "Migración neta", line = 5, font.lab = 2)
box()

4.2 Diagrama de barras de cantidad general

# ===========================================================================
# Diagrama de barras de cantidad con relación a la totalidad de los datos
# ===========================================================================
par(mar = c(7, 5, 4, 2) + 0.1) 

barplot(
  tabla_freq$ni,
  main = "Gráfica N°2: Distribución general de la migración neta \nen el estudio de la calidad de agua en Europa (1991-2017)",
  xlab = "",
  ylab = "Cantidad",
  col = "lightgreen",
  border = "black",
  ylim = c(0, 20000),
  names.arg = tabla_freq$Migraciones,
  las = 2,          
  cex.names = 0.9,
  cex.main = 0.85
)

title(xlab = "Migración neta", line = 5, font.lab = 2)
box()

4.3 Diagrama de barras porcentual

# ===============================================
# Diagrama de barras porcentual que genera RStudio
# ===============================================
par(mar = c(7, 5, 4, 2) + 0.1) 

barplot(
  tabla_freq$hi,
  main = "Gráfica N°3: Distribución porcentual de la migración neta \nen el estudio de la calidad de agua en Europa (1991-2017)",
  xlab = "",
  ylab = "Porcentaje (%)",
  col = "skyblue",
  border = "black",
  ylim = c(0, max(tabla_freq$hi) * 1.2),
  names.arg = tabla_freq$Migraciones,
  las = 2,           
  cex.names = 0.9,
  cex.main = 0.85
)

# Título del eje X y recuadro
title(xlab = "Migración neta", line = 5, font.lab = 2)
box()

4.4 Diagrama de barras porcentual general

# ===========================================================
# Diagrama de barras porcentual con relación a la totalidad 
# ===========================================================
par(mar = c(7, 5, 4, 2) + 0.1) 

barplot(
  tabla_freq$hi,
  main = "Gráfica N°4: Distribución porcentual general de la migración \nneta en el estudio de la calidad de agua en Europa (1991-2017)",
  xlab = "",
  ylab = "Porcentaje (%)",
  col = "lightgreen",
  border = "black",
  ylim = c(0, 100),
  names.arg = tabla_freq$Migraciones,
  las = 2,           
  cex.names = 0.9,
  cex.main = 0.85
)

# Título del eje X y recuadro
title(xlab = "Migración neta", line = 5, font.lab = 2)
box()

4.5 Diagrama de Caja

# =========================
# DIAGRAMA DE CAJA
# =========================
options(scipen = 999)
Migraciones_filtradas <- Migraciones[Migraciones >= 18000]

# 2. Definir los intervalos de la tabla resumida 
intervalos_migracion <- cut(
  Migraciones_filtradas,
  breaks = c(18000, 21000, 62000, 75000, 325000, 450000, 582211),
  include.lowest = TRUE,
  right = FALSE, 
  labels = c("[18k - 21k]", "(21k - 62k]", "(62k - 75k]", "(75k - 325k]", "(325k - 450k]", "(450k - 582k]")
)

# 3. Configurar los márgenes 
par(mar = c(10, 4, 4, 2))

# 4. Generar el boxplot
boxplot(
  Migraciones_filtradas,
  horizontal = TRUE,
  xaxt = "n",
  xlab = "",
  col = "orange",
  main = "Gráfica N°5: Distribución de la migración neta\nen el estudio de la calidad de agua en Europa (1991-2017)"
)

# 5. Asignar los puntos en el eje X 
axis(
  side = 1,
  at = seq(min(Migraciones_filtradas), max(Migraciones_filtradas), length.out = length(levels(intervalos_migracion))),
  labels = levels(intervalos_migracion),
  las = 2,
  cex.axis = 0.8
)

# 6. Título del eje X con el margen ajustado
title(
  xlab = "Migración neta",
  line = 7
)

# 7. Agregar la media
points(
  mean(Migraciones_filtradas),
  1,
  pch = 19,
  col = "red"
)

# 8. Leyenda
legend(
  "topright",
  legend = "Media",
  pch = 19,
  col = "red"
)

4.6 Ojivas ascendentes y descendentes (Ni)

# ======================================
# OJIVAS ASCENDENTES Y DESCENDENTES (Ni)
# ======================================

# Configuración de márgenes 
par(mar = c(7, 5, 4, 2) + 0.1)

# Posiciones en el eje X basadas en la tabla agrupada
x_pos <- 1:(nrow(tabla_freq) - 1) 

# Ojiva descendente 
plot(
  x_pos,
  tabla_freq$Ni_dsc[1:(nrow(tabla_freq)-1)],
  main = "Gráfica N°6: Ojiva ascendente y descendente de la migración\nneta en el estudio de la calidad de agua en Europa (1991-2017)",
  xlab = "",
  ylab = "Frecuencia acumulada",
  type = "b",
  pch = 19,
  col = "orange",
  cex = 1.5,
  lwd = 2,
  xaxt = "n",
  ylim = c(0, max(tabla_freq$Ni_dsc[1:(nrow(tabla_freq)-1)], na.rm = TRUE) * 1.1)
)

# Ojiva ascendente (puntos y línea conectados)
points(
  x_pos,
  tabla_freq$Ni_asc[1:(nrow(tabla_freq)-1)],
  pch = 19,
  col = "green",
  cex = 1.5
)

lines(
  x_pos,
  tabla_freq$Ni_asc[1:(nrow(tabla_freq)-1)],
  col = "green",
  lwd = 2
)

# Etiquetas del eje X c
axis(
  side = 1,
  at = x_pos,
  labels = tabla_freq$Migraciones[1:(nrow(tabla_freq)-1)],
  las = 2,
  cex.axis = 0.9
)

# Leyenda 
legend(
  "topright",
  legend = c("Descendente", "Ascendente"),
  col = c("orange", "green"),
  pch = 19,
  pt.cex = 1.5,
  lty = 1,
  lwd = 2,
  bty = "n"
)

# Título del eje X y recuadro
title(xlab = "Migración neta", line = 5, font.lab = 2)
box()

4.7 Ojivas ascendentes y descendentes (Hi)

# ======================================
# OJIVAS ASCENDENTES Y DESCENDENTES (Hi)
# ======================================

# Configuración de márgenes 
par(mar = c(7, 5, 4, 2) + 0.1)

# Posiciones en el eje X basadas en la tabla agrupada 
x_pos <- 1:(nrow(tabla_freq) - 1) 

# Ojiva descendente porcentual 
plot(
  x_pos,
  tabla_freq$Hi_dsc[1:(nrow(tabla_freq)-1)],
  main = "Gráfica N°7: Ojiva ascendente y descendente de la migración\nneta en el estudio de la calidad de agua en Europa (1991-2017)",
  xlab = "",
  ylab = "Porcentaje acumulado (%)",
  type = "b",
  pch = 19,
  col = "red",
  cex = 1.5,
  lwd = 2,
  xaxt = "n",
  ylim = c(0, 100)
)

# Ojiva ascendente porcentual 
points(
  x_pos,
  tabla_freq$Hi_asc[1:(nrow(tabla_freq)-1)],
  pch = 19,
  col = "blue",
  cex = 1.5
)

lines(
  x_pos,
  tabla_freq$Hi_asc[1:(nrow(tabla_freq)-1)],
  col = "blue",
  lwd = 2
)

# Etiquetas del eje X 
axis(
  side = 1,
  at = x_pos,
  labels = tabla_freq$Migraciones[1:(nrow(tabla_freq)-1)],
  las = 2,
  cex.axis = 0.9
)

# Leyenda 
legend(
  "topright",
  legend = c("Descendente", "Ascendente"),
  col = c("red", "blue"),
  pch = 19,
  pt.cex = 1.5,
  lty = 1,
  lwd = 2,
  bty = "n"
)

# Título del eje X y recuadro
title(xlab = " Migración neta", line = 5, font.lab = 2)
box()

5. Indicadores Estadísticos

5.1 Indicadores de Tendencia Central

# =========================
# MEDIDAS DE TENDENCIA CENTRAL
# =========================

# Media
media <- round(mean(Migraciones_positivos), 2)

# Moda
tabla_moda <- table(Migraciones_positivos)
max_frecuencia <- max(tabla_moda)
moda <- names(tabla_moda)[tabla_moda == max_frecuencia]

# Mediana
mediana <- median(Migraciones_positivos)

5.2 Dispersión

# =========================
# MEDIDAS DE DISPERSIÓN
# =========================

# Rango
rango <- max(Migraciones_positivos) - min(Migraciones_positivos)

# Varianza
varianza <- var(Migraciones_positivos)

# Desviación estándar
desviacion <- sd(Migraciones_positivos)

# Coeficiente de variación
cv <- round((desviacion / media) * 100, 2)

5.3 Asimetría

# =========================
# MEDIDAS DE FORMA
# =========================
n <- length(Migraciones_positivos)

# Asimetría utilizando la fórmula de Fisher (e1071)
asimetria <- skewness(Migraciones_positivos, type = 2)

# Curtosis utilizando la fórmula de Fisher (e1071)
curtosis <- kurtosis(Migraciones_positivos, type = 2)

# =========================
# VALORES ATÍPICOS
# =========================
Q1 <- quantile(Migraciones_positivos, 0.25)
Q3 <- quantile(Migraciones_positivos, 0.75)
RIQ <- Q3 - Q1

LI <- Q1 - 1.5 * RIQ
LS <- Q3 + 1.5 * RIQ

atipicos <- Migraciones_positivos[
  Migraciones_positivos < LI |
  Migraciones_positivos > LS
]

mensaje_atipicos <- length(atipicos)

5.4 Tabla de indicadores

# =========================
# TABLA RESUMEN DE INDICADORES
# =========================
tabla_indicadores <- data.frame(
  Variable = "Migración neta",
  Rango = paste0("[", min(Migraciones_positivos), " ; ", max(Migraciones_positivos), "]"),
  X = media,
  Me = mediana,
  Mo = paste(moda, collapse = ", "),
  V = round(varianza, 2),
  Sd = round(desviacion, 2),
  Cv = cv,
  As = round(asimetria, 2),
  K = round(curtosis, 2),
  Valores_Atipicos = mensaje_atipicos,
  stringsAsFactors = FALSE
)

fila <- which(
  tabla_indicadores$Variable == "Migración neta"
)

tabla_indicadores_gt <- tabla_indicadores %>%
  gt() %>%
  tab_header(
    title = md("**Tabla N°3**"),
    subtitle = md(
      "**Indicadores estadísticos de la migración neta en el estudio de la calidad de agua en Europa (1991-2017)**"
    )
  ) %>%
  tab_source_note(
    source_note = md("Autor: Grupo 3")
  ) %>%
  tab_options(
    table.border.top.color = "black",
    table.border.bottom.color = "black",
    column_labels.border.bottom.width = px(2),
    row.striping.include_table_body = TRUE
  ) %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_body(
      rows = fila
    )
  )

tabla_indicadores_gt
Tabla N°3
Indicadores estadísticos de la migración neta en el estudio de la calidad de agua en Europa (1991-2017)
Variable Rango X Me Mo V Sd Cv As K Valores_Atipicos
Migración neta [18927 ; 582211] 153625.7 75808 75808 18911334732 137518.5 89.52 1.36 0.98 0
Autor: Grupo 3

6. Conclusión

La variable Migración neta fluctúa entre 18927 y 582211, y sus valores giran en torno a una mediana de 75808, con una desviación estándar de 137518.5. Dado que el coeficiente de variación es de 1.36%, se trata de un conjunto de valores homogéneo. Los datos presentan una asimetría positiva, lo que indica que los valores se acumulan de manera pronunciada en la parte baja de la variable, sin la presencia de valores atípicos, lo cual resulta perjudicial para la gestión ambiental en zonas puntuales debido a una presión antrópica localizada sobre los cuerpos hídricos.