Carga de datos y librerias Extraer la variable Conclusion
Variable analizada: Hora de ocurrencia del deslizamiento

La variable HORA representa la hora del dia en la que fue registrado cada deslizamiento. Sus valores son enteros comprendidos entre 0 y 23, por lo que se clasifica como cuantitativa discreta.

Objetivo

Analizar la distribucion de los deslizamientos registrados segun la hora del dia en que ocurrieron, mediante conteos, agrupacion por Sturges, diagramas de barras, ojivas, boxplot e indicadores descriptivos.

1. CARGA DE DATOS Y LIBRERIAS

library(readxl)
library(knitr)

archivo_dataset <- "dataset_deslizamientos_dia_hora.xlsx"

if (!file.exists(archivo_dataset)) {
  stop(
    paste(
      "No se encontro el archivo",
      archivo_dataset,
      "en la carpeta de trabajo."
    )
  )
}

hojas_disponibles <- excel_sheets(
  archivo_dataset
)

if ("Dataset_landslides" %in% hojas_disponibles) {
  hoja_dataset <- "Dataset_landslides"
} else {
  hoja_dataset <- hojas_disponibles[1]
}

datos <- read_excel(
  archivo_dataset,
  sheet = hoja_dataset
)

datos <- as.data.frame(
  datos
)

resumen_dataset <- data.frame(
  Elemento = c(
    "Archivo utilizado",
    "Hoja utilizada",
    "Numero de filas",
    "Numero de columnas",
    "Variable analizada",
    "Tipo de variable"
  ),
  Descripcion = c(
    basename(archivo_dataset),
    hoja_dataset,
    nrow(datos),
    ncol(datos),
    "Hora de ocurrencia del deslizamiento",
    "Cuantitativa discreta"
  )
)

knitr::kable(
  resumen_dataset,
  caption = "Resumen inicial del dataset"
)
Resumen inicial del dataset
Elemento Descripcion
Archivo utilizado dataset_deslizamientos_dia_hora.xlsx
Hoja utilizada Sheet1
Numero de filas 11033
Numero de columnas 32
Variable analizada Hora de ocurrencia del deslizamiento
Tipo de variable Cuantitativa discreta

2. EXTRAER LA VARIABLE

if (!"HORA" %in% names(datos)) {
  stop(
    paste(
      "No se encontro la columna HORA.",
      "Revise que el nombre sea exactamente HORA."
    )
  )
}

HORA_original <- suppressWarnings(
  as.numeric(datos$HORA)
)

total_registros <- length(
  HORA_original
)

datos_faltantes <- sum(
  is.na(HORA_original)
)

datos_fuera_dominio <- sum(
  !is.na(HORA_original) &
    (
      HORA_original < 0 |
      HORA_original > 23 |
      HORA_original %% 1 != 0
    )
)

HORA <- HORA_original[
  !is.na(HORA_original) &
    HORA_original >= 0 &
    HORA_original <= 23 &
    HORA_original %% 1 == 0
]

HORA <- as.integer(
  HORA
)

n <- length(
  HORA
)

if (n == 0) {
  stop(
    "No existen datos validos en la variable HORA."
  )
}

tabla_extraccion <- data.frame(
  Indicador = c(
    "Registros originales",
    "Datos faltantes",
    "Datos fuera del dominio",
    "Datos validos utilizados"
  ),
  Resultado = c(
    total_registros,
    datos_faltantes,
    datos_fuera_dominio,
    n
  )
)

knitr::kable(
  tabla_extraccion,
  caption = "Control de extraccion de la variable HORA"
)
Control de extraccion de la variable HORA
Indicador Resultado
Registros originales 11033
Datos faltantes 0
Datos fuera del dominio 0
Datos validos utilizados 11033
hora_cero_n <- sum(HORA == 0)
hora_cero_pct <- hora_cero_n / n * 100
Nota sobre la hora 0:

La hora 0 pertenece al dominio de la variable y representa las 00:00. En el dataset aparece en 5584 registros (50.61 %). La interpretacion debe hacerse con cautela si la fuente original empleo 0 para una hora no especificada.

3. CONTEO

3.1. Conteo inicial

conteo_inicial <- data.frame(
  Indicador = c(
    "Numero de datos validos",
    "Valor minimo",
    "Valor maximo",
    "Valores diferentes"
  ),
  Resultado = c(
    n,
    min(HORA),
    max(HORA),
    length(unique(HORA))
  )
)

knitr::kable(
  conteo_inicial,
  caption = "Conteo inicial de la variable HORA"
)
Conteo inicial de la variable HORA
Indicador Resultado
Numero de datos validos 11033
Valor minimo 0
Valor maximo 23
Valores diferentes 24

3.2. Justificacion de la agrupacion

La variable HORA posee 24 valores posibles y el dataset contiene una gran cantidad de registros. Una tabla con el conteo de cada valor es util para la revision individual, pero para resumir la distribucion se aplica la regla de Sturges y se construyen intervalos agrupados.

El valor cero se conserva porque pertenece al dominio horario y representa las 00:00. Sin embargo, su interpretacion debe realizarse con cautela si en la fuente original el valor 0 tambien fue utilizado para registrar una hora desconocida:

\[ HORA=\{0,1,2,\ldots,23\} \]

3.3. Aplicacion de Sturges y definicion de clases

La regla de Sturges se expresa como:

\[ k=1+3.322\log_{10}(n) \]

valor_sturges <- 1 +
  3.322 *
  log10(n)

numero_clases <- ceiling(
  valor_sturges
)

valor_minimo <- min(
  HORA
)

valor_maximo <- max(
  HORA
)

limite_inicial <- valor_minimo - 0.5
limite_final <- valor_maximo + 0.5

rango_real <- limite_final -
  limite_inicial

amplitud_clase <- rango_real /
  numero_clases

limites_clase <- seq(
  from = limite_inicial,
  to = limite_final,
  length.out = numero_clases + 1
)

limites_inferiores <- head(
  limites_clase,
  -1
)

limites_superiores <- tail(
  limites_clase,
  -1
)

marcas_clase <- (
  limites_inferiores +
    limites_superiores
) / 2

intervalos_texto <- paste0(
  "[",
  round(limites_inferiores, 2),
  " ; ",
  round(limites_superiores, 2),
  ")"
)

tabla_sturges <- data.frame(
  Elemento = c(
    "Numero de datos",
    "Resultado de Sturges",
    "Numero de clases",
    "Limite inicial",
    "Limite final",
    "Rango real",
    "Amplitud de clase"
  ),
  Resultado = c(
    n,
    valor_sturges,
    numero_clases,
    limite_inicial,
    limite_final,
    rango_real,
    amplitud_clase
  )
)

knitr::kable(
  tabla_sturges,
  digits = 4,
  caption = "Aplicacion de Sturges y definicion de clases"
)
Aplicacion de Sturges y definicion de clases
Elemento Resultado
Numero de datos 11033.00
Resultado de Sturges 14.43
Numero de clases 15.00
Limite inicial -0.50
Limite final 23.50
Rango real 24.00
Amplitud de clase 1.60

Resultado de Sturges

Se obtuvieron 15 clases con una amplitud aproximada de 1.6 horas.

Las barras se presentan separadas para conservar la lectura de una variable discreta.

3.4. Calculo de ni y hi

clase_hora <- cut(
  HORA,
  breaks = limites_clase,
  include.lowest = TRUE,
  right = FALSE,
  dig.lab = 5
)

ni_agrupada <- as.numeric(
  table(clase_hora)
)

hi_agrupada <- ni_agrupada /
  n

Ni_agrupada <- cumsum(
  ni_agrupada
)

porcentaje_acumulado_ascendente <- c(
  0,
  Ni_agrupada /
    n *
    100
)

porcentaje_acumulado_descendente <- c(
  100,
  100 -
    Ni_agrupada /
    n *
    100
)

4. TABLA DE FRECUENCIAS

4.1. Tabla individual

horas_completas <- 0:23

conteo_horas <- table(
  factor(
    HORA,
    levels = horas_completas
  )
)

tabla_individual <- data.frame(
  Hora = horas_completas,
  ni = as.numeric(conteo_horas)
)

tabla_individual$hi <- tabla_individual$ni /
  n

knitr::kable(
  tabla_individual,
  digits = 4,
  col.names = c(
    "Hora del dia",
    "ni",
    "hi"
  ),
  caption = "Tabla Nro. 1. Conteo individual de las horas de ocurrencia"
)
Tabla Nro. 1. Conteo individual de las horas de ocurrencia
Hora del dia ni hi
0 5584 0.5061
1 78 0.0071
2 115 0.0104
3 101 0.0092
4 119 0.0108
5 93 0.0084
6 104 0.0094
7 98 0.0089
8 296 0.0268
9 604 0.0547
10 121 0.0110
11 83 0.0075
12 148 0.0134
13 537 0.0487
14 389 0.0353
15 448 0.0406
16 237 0.0215
17 219 0.0198
18 391 0.0354
19 205 0.0186
20 225 0.0204
21 148 0.0134
22 140 0.0127
23 550 0.0499

4.2. Tabla agrupada

tabla_agrupada <- data.frame(
  Clase = seq_len(numero_clases),
  Intervalo = intervalos_texto,
  ni = ni_agrupada,
  hi = hi_agrupada
)

knitr::kable(
  tabla_agrupada,
  digits = 4,
  col.names = c(
    "Clase",
    "Intervalo",
    "ni",
    "hi"
  ),
  caption = "Tabla Nro. 2. Distribucion agrupada de las horas mediante Sturges"
)
Tabla Nro. 2. Distribucion agrupada de las horas mediante Sturges
Clase Intervalo ni hi
1 [-0.5 ; 1.1) 5662 0.5132
2 [1.1 ; 2.7) 115 0.0104
3 [2.7 ; 4.3) 220 0.0199
4 [4.3 ; 5.9) 93 0.0084
5 [5.9 ; 7.5) 202 0.0183
6 [7.5 ; 9.1) 900 0.0816
7 [9.1 ; 10.7) 121 0.0110
8 [10.7 ; 12.3) 231 0.0209
9 [12.3 ; 13.9) 537 0.0487
10 [13.9 ; 15.5) 837 0.0759
11 [15.5 ; 17.1) 456 0.0413
12 [17.1 ; 18.7) 391 0.0354
13 [18.7 ; 20.3) 430 0.0390
14 [20.3 ; 21.9) 148 0.0134
15 [21.9 ; 23.5) 690 0.0625

5. GRAFICAS

5.1. Diagrama de barras de cantidad por hora

par(
  mar = c(5, 5, 5, 2)
)

posiciones_cantidad <- barplot(
  height = tabla_individual$ni,
  names.arg = tabla_individual$Hora,
  space = 0.20,
  col = "#E9D8C2",
  border = "#444444",
  main = paste0(
    "Grafica Nro. 1: Cantidad de deslizamientos\n",
    "segun la hora del dia"
  ),
  xlab = "Hora de ocurrencia",
  ylab = "Cantidad",
  cex.names = 0.72,
  las = 1,
  ylim = c(
    0,
    max(tabla_individual$ni) * 1.18
  )
)

text(
  x = posiciones_cantidad,
  y = tabla_individual$ni,
  labels = tabla_individual$ni,
  pos = 3,
  cex = 0.58,
  font = 2
)

5.2. Diagrama de barras de porcentaje por hora

porcentaje_individual <- tabla_individual$hi *
  100

par(
  mar = c(5, 5, 5, 2)
)

posiciones_porcentaje <- barplot(
  height = porcentaje_individual,
  names.arg = tabla_individual$Hora,
  space = 0.20,
  col = "#CFA6B8",
  border = "#444444",
  main = paste0(
    "Grafica Nro. 2: Porcentaje de deslizamientos\n",
    "segun la hora del dia"
  ),
  xlab = "Hora de ocurrencia",
  ylab = "Porcentaje",
  cex.names = 0.72,
  las = 1,
  ylim = c(
    0,
    max(porcentaje_individual) * 1.18
  )
)

text(
  x = posiciones_porcentaje,
  y = porcentaje_individual,
  labels = round(porcentaje_individual, 2),
  pos = 3,
  cex = 0.52,
  font = 2
)

5.3. Diagrama de barras agrupadas mediante Sturges

par(
  mar = c(8, 5, 5, 2)
)

posiciones_agrupadas <- barplot(
  height = ni_agrupada,
  names.arg = intervalos_texto,
  space = 0.25,
  col = "#E9D8C2",
  border = "#333333",
  main = paste0(
    "Grafica Nro. 3: Cantidad agrupada de deslizamientos\n",
    "segun los intervalos obtenidos mediante Sturges"
  ),
  xlab = "Intervalos de la hora de ocurrencia",
  ylab = "Cantidad",
  las = 2,
  cex.names = 0.62,
  ylim = c(
    0,
    max(ni_agrupada) * 1.18
  )
)

text(
  x = posiciones_agrupadas,
  y = ni_agrupada,
  labels = ni_agrupada,
  pos = 3,
  cex = 0.75,
  font = 2
)

5.4. Ojivas ascendente y descendente

puntos_ojiva <- limites_clase

par(
  mar = c(5, 5, 5, 2)
)

plot(
  puntos_ojiva,
  porcentaje_acumulado_ascendente,
  type = "o",
  pch = 16,
  lwd = 2,
  col = "#F39C12",
  main = paste0(
    "Grafica Nro. 4: Distribucion acumulada porcentual\n",
    "mediante ojivas ascendente y descendente"
  ),
  xlab = "Limites de clase de la hora de ocurrencia",
  ylab = "Porcentaje",
  xlim = range(puntos_ojiva),
  ylim = c(0, 100),
  las = 1
)

grid(
  col = "gray85",
  lty = 1
)

lines(
  puntos_ojiva,
  porcentaje_acumulado_ascendente,
  type = "o",
  pch = 16,
  lwd = 2,
  col = "#F39C12"
)

lines(
  puntos_ojiva,
  porcentaje_acumulado_descendente,
  type = "o",
  pch = 16,
  lwd = 2,
  col = "#1F45FC"
)

legend(
  "bottomright",
  legend = c(
    "Ascendente (Menor que)",
    "Descendente (Mayor que)"
  ),
  col = c(
    "#F39C12",
    "#1F45FC"
  ),
  pch = 16,
  lty = 1,
  lwd = 2,
  bg = "white",
  bty = "o"
)

5.5. Diagrama de caja

par(
  mar = c(5, 5, 5, 2)
)

boxplot(
  HORA,
  horizontal = TRUE,
  col = "#A7D3E8",
  border = "#222222",
  outline = TRUE,
  outpch = 1,
  outcol = "red",
  main = paste0(
    "Grafica Nro. 5: Diagrama de caja y bigotes\n",
    "de la hora de ocurrencia"
  ),
  xlab = "Hora de ocurrencia",
  las = 1
)

5.6. Diagrama de barras agrupadas con boxplot superpuesto

frecuencia_maxima <- max(
  ni_agrupada
)

limite_y_superior <- frecuencia_maxima *
  1.25

posicion_boxplot <- frecuencia_maxima *
  0.55

altura_boxplot <- frecuencia_maxima *
  0.18

estadisticas_boxplot <- boxplot.stats(
  HORA
)

bigote_inferior <- estadisticas_boxplot$stats[1]
primer_cuartil <- estadisticas_boxplot$stats[2]
mediana_boxplot <- estadisticas_boxplot$stats[3]
tercer_cuartil <- estadisticas_boxplot$stats[4]
bigote_superior <- estadisticas_boxplot$stats[5]
atipicos_boxplot <- estadisticas_boxplot$out

ancho_gap <- amplitud_clase *
  0.08

par(
  mar = c(5, 5, 5, 2)
)

plot(
  NA,
  xlim = c(limite_inicial, limite_final),
  ylim = c(0, limite_y_superior),
  main = paste0(
    "Grafica Nro. 6: Cantidad agrupada de deslizamientos\n",
    "con boxplot superpuesto"
  ),
  xlab = "Hora de ocurrencia",
  ylab = "Cantidad",
  xaxt = "n",
  las = 1
)

axis(
  side = 1,
  at = marcas_clase,
  labels = round(marcas_clase, 1),
  cex.axis = 0.75
)

for (i in seq_len(numero_clases)) {
  rect(
    xleft = limites_inferiores[i] + ancho_gap,
    ybottom = 0,
    xright = limites_superiores[i] - ancho_gap,
    ytop = ni_agrupada[i],
    col = "#E9D8C2",
    border = "#333333"
  )
}

text(
  x = marcas_clase,
  y = ni_agrupada,
  labels = ni_agrupada,
  pos = 3,
  cex = 0.78,
  font = 2
)

segments(
  x0 = bigote_inferior,
  y0 = posicion_boxplot,
  x1 = bigote_superior,
  y1 = posicion_boxplot,
  lty = 2,
  lwd = 1.5,
  col = "#333333"
)

segments(
  x0 = bigote_inferior,
  y0 = posicion_boxplot - altura_boxplot * 0.18,
  x1 = bigote_inferior,
  y1 = posicion_boxplot + altura_boxplot * 0.18,
  lwd = 1.5
)

segments(
  x0 = bigote_superior,
  y0 = posicion_boxplot - altura_boxplot * 0.18,
  x1 = bigote_superior,
  y1 = posicion_boxplot + altura_boxplot * 0.18,
  lwd = 1.5
)

rect(
  xleft = primer_cuartil,
  ybottom = posicion_boxplot - altura_boxplot / 2,
  xright = tercer_cuartil,
  ytop = posicion_boxplot + altura_boxplot / 2,
  col = "#A7D3E8",
  border = "#222222",
  lwd = 1.3
)

segments(
  x0 = mediana_boxplot,
  y0 = posicion_boxplot - altura_boxplot / 2,
  x1 = mediana_boxplot,
  y1 = posicion_boxplot + altura_boxplot / 2,
  lwd = 3,
  col = "#111111"
)

if (length(atipicos_boxplot) > 0) {
  points(
    x = atipicos_boxplot,
    y = rep(
      posicion_boxplot,
      length(atipicos_boxplot)
    ),
    pch = 1,
    col = "red",
    cex = 1.1,
    lwd = 1.3
  )
}

6. INDICADORES

6.1. Calculo de indicadores

media_hora <- mean(
  HORA
)

mediana_hora <- median(
  HORA
)

frecuencias_moda <- table(
  HORA
)

frecuencia_maxima_moda <- max(
  frecuencias_moda
)

modas_hora <- as.numeric(
  names(
    frecuencias_moda[
      frecuencias_moda ==
        frecuencia_maxima_moda
    ]
  )
)

texto_moda <- paste(
  modas_hora,
  collapse = ", "
)

rango_texto <- paste0(
  min(HORA),
  " - ",
  max(HORA)
)

varianza_hora <- var(
  HORA
)

desviacion_hora <- sd(
  HORA
)

coeficiente_variacion <- (
  desviacion_hora /
    media_hora
) * 100

momento_2 <- mean(
  (HORA - media_hora)^2
)

momento_3 <- mean(
  (HORA - media_hora)^3
)

momento_4 <- mean(
  (HORA - media_hora)^4
)

asimetria_hora <- momento_3 /
  momento_2^(3 / 2)

curtosis_hora <- (
  momento_4 /
    momento_2^2
) - 3

valores_atipicos <- boxplot.stats(
  HORA
)$out

cantidad_atipicos <- length(
  valores_atipicos
)

texto_valores_atipicos <- if (
  cantidad_atipicos == 0
) {
  "No existen"
} else {
  paste0(
    cantidad_atipicos,
    " valores entre ",
    min(valores_atipicos),
    " y ",
    max(valores_atipicos)
  )
}

interpretacion_cv <- ifelse(
  coeficiente_variacion <= 30,
  "homogeneos",
  "heterogeneos"
)

interpretacion_asimetria <- ifelse(
  asimetria_hora > 0.10,
  "asimetria positiva hacia la derecha",
  ifelse(
    asimetria_hora < -0.10,
    "asimetria negativa hacia la izquierda",
    "distribucion aproximadamente simetrica"
  )
)

interpretacion_curtosis <- ifelse(
  curtosis_hora > 0.10,
  "leptocurtica",
  ifelse(
    curtosis_hora < -0.10,
    "platicurtica",
    "mesocurtica"
  )
)

clase_modal <- which.max(
  ni_agrupada
)

centro_intervalo_modal <- marcas_clase[
  clase_modal
]

tercio_1 <- limite_inicial +
  rango_real / 3

tercio_2 <- limite_inicial +
  2 * rango_real / 3

parte_variable <- ifelse(
  centro_intervalo_modal <= tercio_1,
  "parte baja",
  ifelse(
    centro_intervalo_modal <= tercio_2,
    "parte media",
    "parte alta"
  )
)

intervalo_modal_texto <- intervalos_texto[
  clase_modal
]

6.2. Tabla resumen de indicadores

tabla_indicadores <- data.frame(
  Medida = c(
    "Media",
    "Mediana",
    "Moda",
    "Rango",
    "Varianza",
    "Desviacion estandar",
    "Coeficiente de variacion (%)",
    "Asimetria",
    "Curtosis",
    "Valores atipicos"
  ),
  Valor = c(
    round(media_hora, 2),
    round(mediana_hora, 2),
    texto_moda,
    rango_texto,
    round(varianza_hora, 2),
    round(desviacion_hora, 2),
    round(coeficiente_variacion, 2),
    round(asimetria_hora, 2),
    round(curtosis_hora, 2),
    texto_valores_atipicos
  )
)

knitr::kable(
  tabla_indicadores,
  col.names = c(
    "Medida",
    "Valor"
  ),
  caption = "Tabla Nro. 3. Indicadores estadisticos de la variable HORA"
)
Tabla Nro. 3. Indicadores estadisticos de la variable HORA
Medida Valor
Media 6.84
Mediana 0
Moda 0
Rango 0 - 23
Varianza 64.84
Desviacion estandar 8.05
Coeficiente de variacion (%) 117.68
Asimetria 0.66
Curtosis -1.09
Valores atipicos No existen

7. CONCLUSION

Conclusion de la variable HORA

La variable hora de ocurrencia fluctua entre 0 y 23 horas, y sus valores giran alrededor de un promedio de 6.84 horas, con una desviacion estandar de 8.05 horas. De acuerdo con el coeficiente de variacion, los datos son heterogeneos. La mayor presencia de registros se encuentra en el intervalo [-0.5 ; 1.1), ubicado en la parte baja de la variable. La distribucion presenta asimetria positiva hacia la derecha y una forma platicurtica, sin presencia de valores atipicos. Este comportamiento no se considera beneficioso ni perjudicial por si mismo, pero permite identificar la concentracion temporal de los registros a lo largo del dia. Debido a que la hora 0 representa 50.61% de los datos, debe confirmarse si todos esos registros corresponden realmente a las 00:00 o si algunos representan una hora no especificada.