0. Carga de librerias

library(readr)
library(dplyr)
library(knitr)
library(kableExtra)
library(ggplot2)
library(e1071)
library(tidyverse)
library(maps)

1. Leer datos

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

2. Depuración y selección de la variable

# Eliminamos registros con valores faltantes

variables <- na.omit(variables)


# Base de datos mundial de ciudades

data("world.cities")

ref_poblacion <-
  world.cities %>%
  select(name, pop) %>%
  rename(
    location_name = name,
    poblacion = pop
  )

# Eliminamos ciudades repetidas

ref_poblacion <-
  ref_poblacion %>%
  group_by(location_name) %>%
  summarise(
    poblacion = max(poblacion),
    .groups="drop"
  )

# Corrección de nombres de capitales

variables <-
  variables %>%
  mutate(

    location_name = case_when(

      location_name=="Bogot" ~ "Bogota",

      location_name=="Porto-Novo" ~ "Porto Novo",

      location_name=="Andorra La Vella" ~ "Andorra",

      location_name=="Bras" ~ "Brasilia",

      location_name=="N'djamena" ~ "N'Djamena",

      location_name=="Ivory" ~ "Yamoussoukro",

      location_name=="Havana" ~ "La Habana",

      location_name=="Addis Ababa" ~ "Addis Abeba",

      location_name=="New Delhi" ~ "Delhi",

      location_name=="Kuwait City" ~ "Kuwait",

      location_name=="Panama City" ~ "Panama",

      location_name=="Kyiv" ~ "Kiev",

      location_name=="Hanoi" ~ "Ha Noi",

      location_name=="Ar Riyadh" ~ "Riyadh",

      location_name=="Beijing Shi" ~ "Beijing",

      TRUE ~ location_name

    )

  )

# Unión de ambas bases

dataset_final <-
  variables %>%
  left_join(
    ref_poblacion,
    by="location_name"
  )

# Verificación de datos faltantes

total_na <- sum(is.na(dataset_final$poblacion))

cat("Registros sin población:", total_na)
## Registros sin población: 18602
# Eliminación de registros sin población

dataset_limpio <-
  dataset_final %>%
  filter(!is.na(poblacion))

# Eliminación de registros inconsistentes


No_valido <- c(

"Laos",

"Moldova",

"Grenada",

"Ivory",

"National",

"-Kingdom",

"Ban Lom",

"Carreria",

"Kiyabo",

"Garrapata",

"Sartorio"

)

dataset_limpio <-

  dataset_limpio %>%

  filter(

    !location_name %in% No_valido

  )

# Variable de estudio

variable <- dataset_limpio$poblacion

N <- length(variable)

cat("Número de observaciones:",N)
## Número de observaciones: 123101
summary(variable)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##      555   202488   744159  1730159  1862930 11595183

3. Frecuencias

3.1. Frecuencias para tabla según Sturges

# 1. Determinación de rango e intervalos mediante Sturges
rango <- max(variable) - min(variable)
k <- round(1 + 3.322 * log10(N))  # Número de intervalos
amplitud <- ceiling(rango / k)     # Amplitud de cada clase

# Cortar la variable en los intervalos definidos
cortes <- seq(min(variable), min(variable) + k * amplitud, by = amplitud)
intervalos <- cut(variable, breaks = cortes, right = FALSE, include.lowest = TRUE)

# 2. Construcción de las Frecuencias Básicas
TDF_pob <- data.frame(table(intervalos))

ni <- TDF_pob$Freq
N_total <- sum(ni)

# Frecuencia relativa porcentual (hi%) con ajuste de cierre al 100%
hi <- round((ni / N_total) * 100, 2)
hi[length(hi)] <- 100 - sum(hi[-length(hi)])

# Frecuencias acumuladas
Ni_asc  <- cumsum(ni)
Ni_desc <- rev(cumsum(rev(ni)))

Hi_asc  <- round(cumsum(hi), 2)
Hi_asc[length(Hi_asc)] <- 100  

Hi_desc <- round(rev(cumsum(rev(hi))), 2)
Hi_desc[1] <- 100

3.2. Frecuencias para tabla simplificada

# Desactivamos la notación científica para evitar los "e+07"
options(scipen = 999)

# 1. Definimos exactamente 10 intervalos (k = 10)
k <- 10  
rango <- max(variable) - min(variable)
amplitud <- ceiling(rango / k)

# Puntos de corte para los intervalos
cortes <- seq(min(variable), min(variable) + k * amplitud, by = amplitud)

# Creamos etiquetas personalizadas y legibles en millones (M) o miles (K)
etiquetas_limpias <- sapply(1:k, function(i) {
  inf <- cortes[i]
  sup <- cortes[i+1]
  
  # Formateador rápido para simplificar el texto en el eje X
  fmt <- function(num) {
    if (num >= 1000000) {
      return(paste0(round(num / 1000000, 1), "M"))
    } else if (num >= 1000) {
      return(paste0(round(num / 1000, 0), "K"))
    } else {
      return(as.character(num))
    }
  }
  paste0("[", fmt(inf), " a ", fmt(sup), ")")
})

# Agrupamos la variable usando nuestras etiquetas personalizadas
intervalos <- cut(variable, breaks = cortes, right = FALSE, 
                  labels = etiquetas_limpias, include.lowest = TRUE)

# 2. Construcción de la tabla de frecuencias básica
TDF_pob <- data.frame(table(intervalos))
ni <- TDF_pob$Freq
N_total <- sum(ni)

# Frecuencias relativas (%) con ajuste de cierre al 100%
hi <- round((ni / N_total) * 100, 2)
hi[length(hi)] <- 100 - sum(hi[-length(hi)])

# Frecuencias acumuladas
Ni_asc  <- cumsum(ni)
Ni_desc <- rev(cumsum(rev(ni)))
Hi_asc  <- round(cumsum(hi), 2)
Hi_asc[length(Hi_asc)] <- 100  
Hi_desc <- round(rev(cumsum(rev(hi))), 2)
Hi_desc[1] <- 100

4. Tabla de frecuencia

4.1. Tabla según Sturges

# Construcción de la estructura
Cuerpo_Tabla <- data.frame(
  Intervalo    = as.character(TDF_pob$intervalos),
  ni           = ni,
  "hi(%)"      = hi,
  "Ni asc"     = as.character(Ni_asc),
  "Ni desc"    = as.character(Ni_desc),
  "Hi asc(%)"  = as.character(Hi_asc),
  "Hi desc(%)" = as.character(Hi_desc),
  check.names  = FALSE
)

# Fila inferior de totales
Fila_Total <- data.frame(
  Intervalo    = "TOTAL",
  ni           = N_total,
  "hi(%)"      = 100,
  "Ni asc"     = "",
  "Ni desc"    = "",
  "Hi asc(%)"  = "",
  "Hi desc(%)" = "",
  check.names  = FALSE
)

Tabla_Final <- rbind(Cuerpo_Tabla, Fila_Total)

# Presentación con formato kableExtra
kable(Tabla_Final, align = "c",
      caption = "Tabla N°1: Distribución de frecuencias de la población de las capitales del mundo 
      período 2024 hasta el 2026") |> 
  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(Tabla_Final), bold = TRUE, background = "#EAEDED") |> 
  footnote(general = "Fuente: GlobalWeatherRepository y base de datos de mapas de R.",
           general_title = "Grupo 2: ",
           footnote_as_chunk = TRUE,
           title_format = c("italic", "bold"))
Tabla N°1: Distribución de frecuencias de la población de las capitales del mundo período 2024 hasta el 2026
Intervalo ni hi(%) Ni asc Ni desc Hi asc(%) Hi desc(%)
[555 a 1.2M) 75997 61.74 75997 123101 61.74 100
[1.2M a 2.3M) 22819 18.54 98816 47104 80.28 38.26
[2.3M a 3.5M) 8007 6.50 106823 24285 86.78 19.72
[3.5M a 4.6M) 2473 2.01 109296 16278 88.79 13.22
[4.6M a 5.8M) 2181 1.77 111477 13805 90.56 11.21
[5.8M a 7M) 727 0.59 112204 11624 91.15 9.44
[7M a 8.1M) 5081 4.13 117285 10897 95.28 8.85
[8.1M a 9.3M) 2181 1.77 119466 5816 97.05 4.72
[9.3M a 10.4M) 728 0.59 120194 3635 97.64 2.95
[10.4M a 11.6M) 2907 2.36 123101 2907 100 2.36
TOTAL 123101 100.00
Grupo 2: Fuente: GlobalWeatherRepository y base de datos de mapas de R.

4.2. Tabla simplificada

Cuerpo_Tabla <- data.frame(
  Intervalo    = as.character(TDF_pob$intervalos),
  ni           = ni,
  "hi(%)"      = hi,
  "Ni asc"     = as.character(Ni_asc),
  "Ni desc"    = as.character(Ni_desc),
  "Hi asc(%)"  = as.character(Hi_asc),
  "Hi desc(%)" = as.character(Hi_desc),
  check.names  = FALSE
)

# Fila de Totales
Fila_Total <- data.frame(
  Intervalo    = "TOTAL",
  ni           = N_total,
  "hi(%)"      = 100,
  "Ni asc"     = "",
  "Ni desc"    = "",
  "Hi asc(%)"  = "",
  "Hi desc(%)" = "",
  check.names  = FALSE
)
Tabla_Final <- rbind(Cuerpo_Tabla, Fila_Total)

# Renderizado de la Tabla 
kable(Tabla_Final, align = "c",
      caption = "Tabla N°1: Distribución de frecuencias de la población en 10 intervalos limpios 
      Período 2024 hasta 2026") |> 
  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(Tabla_Final), bold = TRUE, background = "#EAEDED") |> 
  footnote(general = "Fuente: GlobalWeatherRepository y librería maps de R.",
           general_title = "Grupo 2: ",
           footnote_as_chunk = TRUE,
           title_format = c("italic", "bold"))
Tabla N°1: Distribución de frecuencias de la población en 10 intervalos limpios Período 2024 hasta 2026
Intervalo ni hi(%) Ni asc Ni desc Hi asc(%) Hi desc(%)
[555 a 1.2M) 75997 61.74 75997 123101 61.74 100
[1.2M a 2.3M) 22819 18.54 98816 47104 80.28 38.26
[2.3M a 3.5M) 8007 6.50 106823 24285 86.78 19.72
[3.5M a 4.6M) 2473 2.01 109296 16278 88.79 13.22
[4.6M a 5.8M) 2181 1.77 111477 13805 90.56 11.21
[5.8M a 7M) 727 0.59 112204 11624 91.15 9.44
[7M a 8.1M) 5081 4.13 117285 10897 95.28 8.85
[8.1M a 9.3M) 2181 1.77 119466 5816 97.05 4.72
[9.3M a 10.4M) 728 0.59 120194 3635 97.64 2.95
[10.4M a 11.6M) 2907 2.36 123101 2907 100 2.36
TOTAL 123101 100.00
Grupo 2: Fuente: GlobalWeatherRepository y librería maps de R.

5. Gráficas

5.1. Diagrama de barras

df_grafico <- Tabla_Final[Tabla_Final$Intervalo != "TOTAL", ]
df_grafico$ni <- as.numeric(df_grafico$ni)

# Aseguramos el orden correcto de los intervalos en el gráfico
df_grafico$Intervalo <- factor(df_grafico$Intervalo, levels = etiquetas_limpias)

ggplot(data = df_grafico, aes(x = Intervalo, y = ni)) +
  geom_bar(stat = "identity", fill = "skyblue", color = "navy", width = 0.7) +
  geom_text(aes(label = ni), vjust = -0.5, size = 3) +
  labs(title = "Gráfica N°1: Frecuencia absoluta de población en capitales del mundo 
       desde el año 2024 al 2026",
       x = "Intervalos de Población (Habitantes)", y = "Número de ciudades (ni)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 35, hjust = 1, size = 9)) # Inclinación ideal

5.2. Diagrama de barras porcentual

df_grafico$`hi(%)` <- as.numeric(df_grafico$`hi(%)`)

ggplot(data = df_grafico, aes(x = Intervalo, y = `hi(%)`)) +
  geom_bar(stat = "identity", fill = "orange", color = "indianred", width = 0.7) +
  geom_text(aes(label = paste0(`hi(%)`, "%")), vjust = -0.5, size = 3) +
  labs(title = "Gráfica N°2: Frecuencia relativa de población en capitales del mundo 2024 -2026", 
       x = "Intervalos de Población (Habitantes)", y = "Porcentaje (%)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 35, hjust = 1, size = 9))

5.3. Diagrama de barras general

options(scipen = 999)
ggplot(data = df_grafico, aes(x = Intervalo, y = ni)) +
  geom_bar(stat = "identity", fill = "skyblue", color = "navy", width = 0.7) +
  labs(title = "Gráfica N°3: Frecuencia absoluta en relación al total muestral 
       desde el período 2024 hasta 2026", 
       x = "Población", y = "Frecuencia Absoluta") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  scale_y_continuous(limits = c(0, N_total))

5.4. Diagrama de barras porcentual general

ggplot(data = df_grafico, aes(x = Intervalo, y = `hi(%)`)) +
  geom_bar(stat = "identity", fill = "orange", color = "indianred", width = 0.7) +
  labs(title = "Gráfica N°4: Frecuencia relativa con respecto al 100% de los datos 
       2024 hasta 2026", 
       x = "Población", y = "Porcentaje %") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  ylim(0, 100)

5.5. Diagrama de caja y bigotes

par(mar = c(5, 5, 4, 3))
boxplot(
  variable, 
  horizontal = TRUE,
  main = "Gráfica N°5: Caja y Bigotes para la Población de las Capitales del 
  período 2024 hasta el 2026 ",
  xlab = "Población (Habitantes)",
  col = "darkred",     
  las = 1  
)

5.6. Ojivas

5.6.1.- Ojivas frecuenacias absolutas

par(mfrow = c(1, 1), mar = c(6, 7, 5, 3))

num_clases <- length(ni)

plot(
  1:num_clases, Ni_asc, 
  type = "b",                  
  col = "skyblue",               
  bg = "skyblue",                
  pch = 21,                    
  lwd = 2,                     
  main = "Gráfica N°6: Ojivas acumuladas (Absolutas)\nde la Población desde 2024 hasta 2026 ",
  xlab = "Intervalos (Índice)", 
  ylab = "Cantidad Acumulada de Ciudades",
  ylim = c(0, N_total),              
  xaxt = "n",                  
  las = 1 
)
points(1:num_clases, Ni_desc, type = "b", col = "navy", bg = "navy", pch = 21, lwd = 2)
axis(1, at = 1:num_clases, labels = 1:num_clases)

legend(
  "right", 
  legend = c("Ojiva descendente", "Ojiva ascendente"), 
  col = c("navy", "skyblue"), 
  pch = c(21, 21),                
  bg = "white"
)

5.6.2. Ojivas Frecuencias relativas

plot(
  1:num_clases, Hi_asc, 
  type = "b", 
  col = "indianred", 
  bg = "indianred", 
  pch = 21, 
  lwd = 2,
  main = "Gráfica N°7: Ojivas acumuladas (Relativas %)\nde la Población desde 2024 hasta 2026",
  xlab = "Intervalos (Índice)",                
  ylab = "Porcentaje Acumulado (%)",                  
  ylim = c(0, 100),            
  xaxt = "n", 
  las = 1
)
points(1:num_clases, Hi_desc, type = "b", col = "orange", bg = "orange", pch = 21, lwd = 2)
axis(1, at = 1:num_clases, labels = 1:num_clases)

legend(
  "right", 
  legend = c("Ojiva descendente %", "Ojiva ascendente %"), 
  col = c("orange", "indianred"), 
  pch = c(21, 21), 
  bg = "white"
)

6. Indicadores de frecuencia

# Indicadores de posición y tendencia central
media_pob    <- round(mean(variable), 2)
mediana_pob  <- round(median(variable), 2)

# Determinar intervalo modal (Moda)
pos_max_frec <- which.max(ni)
moda_pob     <- TDF_pob$intervalos[pos_max_frec]

# Indicadores de dispersión
varianza_pob <- round(var(variable), 2)
sd_pob       <- round(sd(variable), 2)
cv_pob       <- round((sd_pob / media_pob) * 100, 2)

# Indicadores de forma
asimetria_pob <- round(skewness(variable, type = 2), 2)
curtosis_pob  <- round(kurtosis(variable), 2)

# Identificación de Outliers (Valores atípicos usando criterio de IQR)
q1 <- quantile(variable, 0.25)
q3 <- quantile(variable, 0.75)
iqr <- q3 - q1
outliers_limite_sup <- q3 + 1.5 * iqr
num_outliers <- sum(variable > outliers_limite_sup)
presencia_atipicos <- ifelse(num_outliers > 0, paste("Presencia de", num_outliers, "valores atípicos"), "Ausencia de atípicos")

# Tabla resumida de indicadores
tabla_indicadores <- data.frame(
  Variable         = "Población (Hab)",
  Rango            = paste("[", min(variable), ";", max(variable), "]"),
  X                = media_pob,     
  Me               = mediana_pob,
  Mo               = as.character(moda_pob),
  V                = varianza_pob,
  Sd               = sd_pob,
  Cv               = cv_pob,
  As               = asimetria_pob,
  K                = curtosis_pob,
  Valores_Atipicos = presencia_atipicos
)

kable(tabla_indicadores, align = "c",
      caption = "Tabla N°2: Indicadores de tendencia central, dispersión y forma para la variable Población 
      desde el año 2024 al 2026") |> 
  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(tabla_indicadores), bold = TRUE, background = "#EAEDED") |> 
  footnote(general = "Cálculos basados en GlobalWeatherRepository y r-maps.",
           general_title = "Grupo 2: ",
           footnote_as_chunk = TRUE,
           title_format = c("italic", "bold"))
Tabla N°2: Indicadores de tendencia central, dispersión y forma para la variable Población desde el año 2024 al 2026
Variable Rango X Me Mo V Sd Cv As K Valores_Atipicos
Población (Hab) [ 555 ; 11595183 ] 1730159 744159 [555 a 1.2M) 6454187600040 2540509 146.84 2.24 4.33 Presencia de 14093 valores atípicos
Grupo 2: Cálculos basados en GlobalWeatherRepository y r-maps.

7. Conclusiones

El comportamiento de la Población sigue la siguiente manera: fluctúa entre 555 y 11M de habitantes y sus valores giran en torno a 744,159 (mediana, debido a que hay presencia de 14,093 valores atípicos que distorsionan y sobreestiman la media aritmética), con una desviación estándar de 2,540,509 habitantes siendo un conjunto de datos muy heterogéneo (con un coeficiente de variación del 146.84%). El conjunto de valores se concentra de forma leptocúrtica (con una curtosis de 4.33, lo que indica un pico alto y colas pesadas) en la parte baja debido a una marcada asimetría positiva (con un valor de asimetría de 2.24). Por todo lo anterior, el comportamiento de la variable es favorable y beneficioso para comprender la jerarquía demográfica urbana real, pero perjudicial si se intenta analizar el comportamiento global usando únicamente el promedio simple.