Importar paquetes y librerias

#install.packages("sf")                    # Análisis de datos espaciales
#install.packages("rnaturalearth")         # Límites geográficos
#install.packages("rnaturalearthdata")     # Datos de geografía
#install.packages("devtools")
#install.packages(c("readr","dplyr","stringr","factoextra","cluster"))
library(sf)
## Linking to GEOS 3.13.0, GDAL 3.8.5, PROJ 9.5.1; sf_use_s2() is TRUE
library(rnaturalearth)
library(rnaturalearthdata)
## 
## Attaching package: 'rnaturalearthdata'
## The following object is masked from 'package:rnaturalearth':
## 
##     countries110
library(devtools)
## Loading required package: usethis
library(readr)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(stringr)
library(factoextra)
## Loading required package: ggplot2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(cluster)
############################
mexico <- ne_states(country = "Mexico", returnclass = "sf")
# names(mexico)  # para ver columnas disponibles (incluye 'name' con el nombre del estado)

Importar base de datos

datosmex <- read_csv("~/Documents/Modulo2/Educacion_06_limpia.csv", show_col_types = FALSE)

Estandarizar

datosmex <- datosmex |>
  rename(
    name            = `Entidad federativa`,
    Media_superior  = `Media superior`
  )
datosmex <- datosmex |>
  filter(name != "Estados Unidos Mexicanos")
head(datosmex)
## # A tibble: 6 × 7
##   name               Total Inicial Preescolar Primaria Secundaria Media_superior
##   <chr>              <dbl>   <dbl>      <dbl>    <dbl>      <dbl>          <dbl>
## 1 Aguascalientes    400455    3099      51878   160480      74675          56636
## 2 Baja California   951597    7435      96630   384953     186104         147224
## 3 Baja California … 210438    2219      26082    83662      40608          33870
## 4 Campeche          248444    3242      34356   100121      45736          36601
## 5 Coahuila de Zara… 856439    7151     129840   329075     157186         117561
## 6 Colima            186651    3126      22792    74563      34808          30173

Armonizar

datosmex <- datosmex |>
  mutate(
    name_harmon = case_when(
      name == "Ciudad de México" ~ "Distrito Federal",  # algunos mapas antiguos usan DF
      TRUE ~ name
    )
  )
# Intento de join con el mapa usando 'name' del shape
mex_join1 <- mexico |>
  select(name, geometry) |>
  left_join(datosmex, by = join_by(name == name_harmon))
# Detectar no empates
no_match <- mex_join1 |>
  filter(is.na(Total)) |>
  pull(name)

if (length(no_match) > 0) {
  message("Estados no empataron en el primer intento: ", paste(no_match, collapse = ", "))
  # Si hay más diferencias de nombre, puedes agregarlas aquí, por ejemplo:
  # datosmex$name_harmon <- recode(datosmex$name_harmon,
  #   "Veracruz de Ignacio de la Llave" = "Veracruz-Llave",
  #   "Michoacán de Ocampo" = "Michoacán de Ocampo",
  #   "Coahuila de Zaragoza" = "Coahuila de Zaragoza"
  # )
  # Y volver a hacer el join si fuera necesario.
}
## Estados no empataron en el primer intento: Coahuila, Veracruz, Michoacán, NA

Escalar la base de datos

# Escalar la base de datos (solo variables numéricas)
############################
# Seleccionamos columnas numéricas para clustering
vars_num <- datosmex |>
  select(Total, Inicial, Preescolar, Primaria, Secundaria, Media_superior)
# Escalado (media 0, var 1)
datos_escalados_mex <- scale(vars_num)

Generar los K means

set.seed(123)
grupos_mex <- 3  # Inicio con valor "cualquiera"; puedes optimizar luego
segmentos_mex <- kmeans(datos_escalados_mex, centers = grupos_mex, nstart = 25)

Añadir asignación de cluster

asignacion_mex <- cbind(
  datosmex |>
    select(name, Total, Inicial, Preescolar, Primaria, Secundaria, Media_superior),
  cluster = segmentos_mex$cluster
)

Asignar etiquetas de grupo

asignacion_mex <- asignacion_mex |>
  mutate(
    cluster_label = case_when(
      cluster == 1 ~ "Grupo 1",
      cluster == 2 ~ "Grupo 2",
      cluster == 3 ~ "Grupo 3",
      TRUE ~ paste0("Grupo ", cluster)
    )
  )

head(asignacion_mex)
##                   name  Total Inicial Preescolar Primaria Secundaria
## 1       Aguascalientes 400455    3099      51878   160480      74675
## 2      Baja California 951597    7435      96630   384953     186104
## 3  Baja California Sur 210438    2219      26082    83662      40608
## 4             Campeche 248444    3242      34356   100121      45736
## 5 Coahuila de Zaragoza 856439    7151     129840   329075     157186
## 6               Colima 186651    3126      22792    74563      34808
##   Media_superior cluster cluster_label
## 1          56636       1       Grupo 1
## 2         147224       1       Grupo 1
## 3          33870       1       Grupo 1
## 4          36601       1       Grupo 1
## 5         117561       1       Grupo 1
## 6          30173       1       Grupo 1

Gráfica de los cluster

fviz_cluster(segmentos_mex, data = datos_escalados_mex) +
  ggplot2::ggtitle(paste("Clusters k-means con k =", grupos_mex))

LS0tCnRpdGxlOiAiRWR1YWNpw7NuIGVuIE3DqXhpY28iCmF1dGhvcjogIkR5YW5uIEVzdGVmYW5pYSBIZXJuYW5kZXogQXlhbGEiCmRhdGU6ICIyMDI1LTA4LTIxIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogICAgY29kZV9kb3dubG9hZDogVFJVRQogICAgdGhlbWU6IHlldGkKLS0tCiFbXShodHRwczovL2dhY2V0YWVjb25vbWljYS5maW5hbmNlLmJsb2cvd3AtY29udGVudC91cGxvYWRzLzIwMjIvMDYvZWR1Y2FjaW9uLW1leGljby5qcGcpCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+SW1wb3J0YXIgcGFxdWV0ZXMgeSBsaWJyZXJpYXMgPC9zcGFuPgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoInNmIikgICAgICAgICAgICAgICAgICAgICMgQW7DoWxpc2lzIGRlIGRhdG9zIGVzcGFjaWFsZXMKI2luc3RhbGwucGFja2FnZXMoInJuYXR1cmFsZWFydGgiKSAgICAgICAgICMgTMOtbWl0ZXMgZ2VvZ3LDoWZpY29zCiNpbnN0YWxsLnBhY2thZ2VzKCJybmF0dXJhbGVhcnRoZGF0YSIpICAgICAjIERhdG9zIGRlIGdlb2dyYWbDrWEKI2luc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKI2luc3RhbGwucGFja2FnZXMoYygicmVhZHIiLCJkcGx5ciIsInN0cmluZ3IiLCJmYWN0b2V4dHJhIiwiY2x1c3RlciIpKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KHJuYXR1cmFsZWFydGgpCmxpYnJhcnkocm5hdHVyYWxlYXJ0aGRhdGEpCmxpYnJhcnkoZGV2dG9vbHMpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KGNsdXN0ZXIpCmBgYAoKYGBge3J9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKbWV4aWNvIDwtIG5lX3N0YXRlcyhjb3VudHJ5ID0gIk1leGljbyIsIHJldHVybmNsYXNzID0gInNmIikKIyBuYW1lcyhtZXhpY28pICAjIHBhcmEgdmVyIGNvbHVtbmFzIGRpc3BvbmlibGVzIChpbmNsdXllICduYW1lJyBjb24gZWwgbm9tYnJlIGRlbCBlc3RhZG8pCgpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWU7Ij5JbXBvcnRhciBiYXNlIGRlIGRhdG9zIDwvc3Bhbj4KYGBge3J9CmRhdG9zbWV4IDwtIHJlYWRfY3N2KCJ+L0RvY3VtZW50cy9Nb2R1bG8yL0VkdWNhY2lvbl8wNl9saW1waWEuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+RXN0YW5kYXJpemFyIDwvc3Bhbj4KYGBge3J9CmRhdG9zbWV4IDwtIGRhdG9zbWV4IHw+CiAgcmVuYW1lKAogICAgbmFtZSAgICAgICAgICAgID0gYEVudGlkYWQgZmVkZXJhdGl2YWAsCiAgICBNZWRpYV9zdXBlcmlvciAgPSBgTWVkaWEgc3VwZXJpb3JgCiAgKQpgYGAKCmBgYHtyfQpkYXRvc21leCA8LSBkYXRvc21leCB8PgogIGZpbHRlcihuYW1lICE9ICJFc3RhZG9zIFVuaWRvcyBNZXhpY2Fub3MiKQpgYGAKCgpgYGB7cn0KaGVhZChkYXRvc21leCkKYGBgCiMgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWU7Ij5Bcm1vbml6YXIgPC9zcGFuPgpgYGB7cn0KZGF0b3NtZXggPC0gZGF0b3NtZXggfD4KICBtdXRhdGUoCiAgICBuYW1lX2hhcm1vbiA9IGNhc2Vfd2hlbigKICAgICAgbmFtZSA9PSAiQ2l1ZGFkIGRlIE3DqXhpY28iIH4gIkRpc3RyaXRvIEZlZGVyYWwiLCAgIyBhbGd1bm9zIG1hcGFzIGFudGlndW9zIHVzYW4gREYKICAgICAgVFJVRSB+IG5hbWUKICAgICkKICApCmBgYAoKCmBgYHtyfQojIEludGVudG8gZGUgam9pbiBjb24gZWwgbWFwYSB1c2FuZG8gJ25hbWUnIGRlbCBzaGFwZQptZXhfam9pbjEgPC0gbWV4aWNvIHw+CiAgc2VsZWN0KG5hbWUsIGdlb21ldHJ5KSB8PgogIGxlZnRfam9pbihkYXRvc21leCwgYnkgPSBqb2luX2J5KG5hbWUgPT0gbmFtZV9oYXJtb24pKQpgYGAKCmBgYHtyfQojIERldGVjdGFyIG5vIGVtcGF0ZXMKbm9fbWF0Y2ggPC0gbWV4X2pvaW4xIHw+CiAgZmlsdGVyKGlzLm5hKFRvdGFsKSkgfD4KICBwdWxsKG5hbWUpCgppZiAobGVuZ3RoKG5vX21hdGNoKSA+IDApIHsKICBtZXNzYWdlKCJFc3RhZG9zIG5vIGVtcGF0YXJvbiBlbiBlbCBwcmltZXIgaW50ZW50bzogIiwgcGFzdGUobm9fbWF0Y2gsIGNvbGxhcHNlID0gIiwgIikpCiAgIyBTaSBoYXkgbcOhcyBkaWZlcmVuY2lhcyBkZSBub21icmUsIHB1ZWRlcyBhZ3JlZ2FybGFzIGFxdcOtLCBwb3IgZWplbXBsbzoKICAjIGRhdG9zbWV4JG5hbWVfaGFybW9uIDwtIHJlY29kZShkYXRvc21leCRuYW1lX2hhcm1vbiwKICAjICAgIlZlcmFjcnV6IGRlIElnbmFjaW8gZGUgbGEgTGxhdmUiID0gIlZlcmFjcnV6LUxsYXZlIiwKICAjICAgIk1pY2hvYWPDoW4gZGUgT2NhbXBvIiA9ICJNaWNob2Fjw6FuIGRlIE9jYW1wbyIsCiAgIyAgICJDb2FodWlsYSBkZSBaYXJhZ296YSIgPSAiQ29haHVpbGEgZGUgWmFyYWdvemEiCiAgIyApCiAgIyBZIHZvbHZlciBhIGhhY2VyIGVsIGpvaW4gc2kgZnVlcmEgbmVjZXNhcmlvLgp9CmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZTsiPkVzY2FsYXIgbGEgYmFzZSBkZSBkYXRvcyA8L3NwYW4+CmBgYHtyfQojIEVzY2FsYXIgbGEgYmFzZSBkZSBkYXRvcyAoc29sbyB2YXJpYWJsZXMgbnVtw6lyaWNhcykKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNlbGVjY2lvbmFtb3MgY29sdW1uYXMgbnVtw6lyaWNhcyBwYXJhIGNsdXN0ZXJpbmcKdmFyc19udW0gPC0gZGF0b3NtZXggfD4KICBzZWxlY3QoVG90YWwsIEluaWNpYWwsIFByZWVzY29sYXIsIFByaW1hcmlhLCBTZWN1bmRhcmlhLCBNZWRpYV9zdXBlcmlvcikKCmBgYAoKCmBgYHtyfQojIEVzY2FsYWRvIChtZWRpYSAwLCB2YXIgMSkKZGF0b3NfZXNjYWxhZG9zX21leCA8LSBzY2FsZSh2YXJzX251bSkKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+R2VuZXJhciBsb3MgSyBtZWFucyA8L3NwYW4+CmBgYHtyfQoKc2V0LnNlZWQoMTIzKQpncnVwb3NfbWV4IDwtIDMgICMgSW5pY2lvIGNvbiB2YWxvciAiY3VhbHF1aWVyYSI7IHB1ZWRlcyBvcHRpbWl6YXIgbHVlZ28Kc2VnbWVudG9zX21leCA8LSBrbWVhbnMoZGF0b3NfZXNjYWxhZG9zX21leCwgY2VudGVycyA9IGdydXBvc19tZXgsIG5zdGFydCA9IDI1KQpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWU7Ij5Bw7FhZGlyIGFzaWduYWNpw7NuIGRlIGNsdXN0ZXIgPC9zcGFuPgpgYGB7cn0KYXNpZ25hY2lvbl9tZXggPC0gY2JpbmQoCiAgZGF0b3NtZXggfD4KICAgIHNlbGVjdChuYW1lLCBUb3RhbCwgSW5pY2lhbCwgUHJlZXNjb2xhciwgUHJpbWFyaWEsIFNlY3VuZGFyaWEsIE1lZGlhX3N1cGVyaW9yKSwKICBjbHVzdGVyID0gc2VnbWVudG9zX21leCRjbHVzdGVyCikKYGBgCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+QXNpZ25hciBldGlxdWV0YXMgZGUgZ3J1cG8gPC9zcGFuPgpgYGB7cn0KYXNpZ25hY2lvbl9tZXggPC0gYXNpZ25hY2lvbl9tZXggfD4KICBtdXRhdGUoCiAgICBjbHVzdGVyX2xhYmVsID0gY2FzZV93aGVuKAogICAgICBjbHVzdGVyID09IDEgfiAiR3J1cG8gMSIsCiAgICAgIGNsdXN0ZXIgPT0gMiB+ICJHcnVwbyAyIiwKICAgICAgY2x1c3RlciA9PSAzIH4gIkdydXBvIDMiLAogICAgICBUUlVFIH4gcGFzdGUwKCJHcnVwbyAiLCBjbHVzdGVyKQogICAgKQogICkKCmhlYWQoYXNpZ25hY2lvbl9tZXgpCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZTsiPkdyw6FmaWNhIGRlIGxvcyBjbHVzdGVyIDwvc3Bhbj4KYGBge3J9CmZ2aXpfY2x1c3RlcihzZWdtZW50b3NfbWV4LCBkYXRhID0gZGF0b3NfZXNjYWxhZG9zX21leCkgKwogIGdncGxvdDI6OmdndGl0bGUocGFzdGUoIkNsdXN0ZXJzIGstbWVhbnMgY29uIGsgPSIsIGdydXBvc19tZXgpKQpgYGAKCg==