Análisis de Clusters en Comercio entre EE.UU. y México usando DBSCAN
📂 1. Cargar datos
# Datos simulados para Alabamadf <-read_csv(here("data", "State Imports by NAICS Commodities.csv"), skip =2) %>%clean_names() %>%select(-x6)
New names:
• `` -> `...6`
Warning: One or more parsing issues, call `problems()` on your data frame for details,
e.g.:
dat <- vroom(...)
problems(dat)
Rows: 7992 Columns: 6
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (3): State, Commodity, Country
dbl (1): Time
num (1): Customs Value (Gen) ($US)
lgl (1): ...6
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
🧹 2. Limpieza de datos
Responde a lo siguiente:
1. En este chunk borré los valores vacíos en lugar de convertirlos en 0, ¿por qué lo decidí de esta forma? HINT: Tiene que ver con el objetivo de este ejercicio “descubrir las agrupaciones naturales de commodities”.
2. En el siguiente chunk hago un muy elegante filtro usando str_detect, ¿por qué lo hice?
df_clean %>%mutate(x_label =str_c(state, " - ", commodity)) %>%slice_max(order_by = customs_value_gen_us, n =10) %>%ggplot(aes(x =fct_reorder(x_label, customs_value_gen_us), y = customs_value_gen_us)) +geom_col(fill ="steelblue") +coord_flip() +scale_y_continuous(labels = scales::comma) +labs(title ="Top 10 importaciones desde México por estado y producto (2024)",x ="Estado - Producto",y ="Valor en dólares" )
🧠 4. Aplicación de DBSCAN
DBSCAN agrupa por densidad sin necesidad de especificar número de clusters.
# Suponiendo que df_clean contiene tus datos de comercio# Seleccionar múltiples variables relevantes para el agrupamiento (clustering)data_matrix <- df_clean %>%select(log_value, state, commodity) %>%# Convertir variables categóricas a numéricas si es necesariomutate(state_num =as.numeric(factor(state)),commodity_num =as.numeric(factor(commodity)) ) %>%# Seleccionar las columnas numéricas para el agrupamientoselect(log_value, state_num, commodity_num) %>%# Escalar los datos (importante para DBSCAN)scale()# Probar diferentes valores de eps# Primero, busquemos un eps apropiado usando un gráfico de distancia k-vecinoskNNdistplot(data_matrix, k =3)abline(h =0.6, col ="red") # Ajusta este valor según el "codo" del gráfico
# First create the proper data matrix for DBSCAN# This step is crucial and was missingdata_matrix <- df_clean %>%# Create numeric representations of categorical variablesmutate(state_num =as.numeric(factor(state)),commodity_num =as.numeric(factor(commodity)) ) %>%# Select features for clusteringselect(log_value, state_num, commodity_num) %>%# Scale the data for better clusteringscale()# Run DBSCAN with appropriate parametersmodelo_dbscan <-dbscan(data_matrix, eps =0.5, minPts =5)# Add clusters to original datadf_clean <- df_clean %>%mutate(cluster =as.factor(modelo_dbscan$cluster))# Analyze cluster resultstable(df_clean$cluster)
0 1 2 3
44 935 16 5
# For visualization, you need to use numeric values# This plot will work correctlyggplot(df_clean, aes(x = log_value, y = state, color = cluster)) +geom_point(alpha =0.7) +theme_minimal() +labs(title ="DBSCAN Clustering of Mexico-US Trade Data")
# If you want to see commodity patterns clearly, try this visualizationggplot(df_clean, aes(x = log_value, y = commodity, color = cluster)) +geom_point(alpha =0.7) +theme_minimal() +labs(title ="Commodity Clusters in Mexico-US Trade Data")
# This analysis to examine cluster characteristics is goodcluster_summary <- df_clean %>%group_by(cluster) %>%summarize(count =n(),avg_value =mean(exp(log_value)),top_commodities =paste(names(sort(table(commodity), decreasing =TRUE)[1:5]), collapse =", "),top_states =paste(names(sort(table(state), decreasing =TRUE)[1:5]), collapse =", ") )knitr::kable(cluster_summary)
# Add this to see how commodities are distributed across clusterscommodity_distribution <- df_clean %>%group_by(commodity, cluster) %>%summarize(count =n(),avg_value =mean(exp(log_value)) ) %>%arrange(commodity, cluster)
`summarise()` has grouped output by 'commodity'. You can override using the
`.groups` argument.
library(umap)# Aplicar UMAP para reducir dimensionalidadset.seed(123) # Para reproducibilidadumap_result <-umap(data_matrix, n_neighbors =15, min_dist =0.1, n_components =2)# Crear un dataframe con las coordenadas de UMAP y la información de clustersumap_df <-data.frame(UMAP1 = umap_result$layout[,1],UMAP2 = umap_result$layout[,2])# Añadir información de clusters y categoríasumap_df$cluster <- df_clean$clusterumap_df$commodity <- df_clean$commodityumap_df$state <- df_clean$stateumap_df$log_value <- df_clean$log_value# Visualizar los clusters usando UMAPggplot(umap_df, aes(x = UMAP1, y = UMAP2, color = cluster)) +geom_point(alpha =0.7, size =2) +theme_minimal() +labs(title ="UMAP Visualization of DBSCAN Clusters in Mexico-US Trade Data",color ="Cluster") +scale_color_brewer(palette ="Set1")
# Visualización alternativa: colorear por commodity# Limitar a las 10 commodities más frecuentes para claridadtop_commodities <-names(sort(table(df_clean$commodity), decreasing =TRUE)[1:10])umap_df_top <- umap_df %>%filter(commodity %in% top_commodities)ggplot(umap_df_top, aes(x = UMAP1, y = UMAP2, color = commodity)) +geom_point(alpha =0.7, size =2) +theme_minimal() +labs(title ="UMAP Visualization by Commodity (Top 10)",color ="Commodity") +theme(legend.position ="right")
# También puedes visualizar una versión que combine clusters y log_valueggplot(umap_df, aes(x = UMAP1, y = UMAP2, color = cluster, size = log_value)) +geom_point(alpha =0.6) +theme_minimal() +labs(title ="UMAP Visualization by Cluster and Trade Value",color ="Cluster", size ="Log Value") +scale_color_brewer(palette ="Set1")
📌 6. Conclusiones rápidas
DBSCAN detecta agrupaciones sin definir el número de grupos.
Algunos productos quedaron como ruido (cluster 0).
🔥 7. Ejercicio adicional: Encontrar Estados Similares
Supongamos que tenemos datos de varios estados (simularemos aquí para el ejemplo).
Queremos encontrar estados que importan productos similares en valor.
# Crear el dataframe 'df_agg' como antesdf_agg <- df_clean %>%group_by(state, commodity) %>%summarise(total_value =sum(customs_value_gen_us, na.rm =TRUE), .groups ="drop")# Crear el dataframe 'df_wide' para pivotar las columnasdf_wide <- df_agg %>%pivot_wider(names_from = commodity, values_from = total_value, values_fill =0)# Preparamos la matriz para clusteringstate_matrix <- df_wide %>%select(-state) %>%as.matrix()# Asignar los nombres de los estados a las filas de la matrizrownames(state_matrix) <- df_wide$state# Cálculo de distanciasstate_dist <-dist(state_matrix, method ="euclidean")# Clustering jerárquicohc <-hclust(state_dist, method ="ward.D2")# Dendrograma con nombres de los estados como etiquetasplot(hc, main ="Estados agrupados por patrón de importaciones", xlab ="", sub ="", labels =rownames(state_matrix))
# Aplicar transformación logarítmica para manejar valores de comercio sesgadosstate_matrix <- df_wide %>%select(-state) %>%select(where(~sd(.) >0)) %>%as.matrix()# Agregar una constante pequeña antes del logaritmo para manejar cerosstate_matrix_log <-log1p(state_matrix) # log(1+x) maneja ceros de forma segurarownames(state_matrix_log) <- df_wide$state# 2. Escalar los datos para el agrupamiento (clustering)state_matrix_scaled <-scale(state_matrix_log)# 3. Probar diferentes valores de eps para encontrar el agrupamiento óptimo# Optimizar el parámetro eps usando el gráfico de distancia k-vecinoskNNdistplot(state_matrix_scaled, k =3)abline(h =15, col ="red") # Elegir eps en el punto de “codo” del gráfico
# 4. Ejecutar DBSCAN con parámetros optimizadosdbscan_result <-dbscan(state_matrix_scaled, eps =15, minPts =5)# 5. Crear múltiples visualizaciones para obtener mejores perspectivas# A. Visualización con PCA (versión mejorada de la original)df_pca <-data.frame(prcomp(state_matrix_scaled)$x[, 1:2],Cluster =as.factor(dbscan_result$cluster),State = df_wide$state)# Contar los estados por clúster para información del títulocluster_counts <-table(dbscan_result$cluster)title_info <-paste("Agrupación de Estados por Patrón Comercial (DBSCAN)\nClústeres:", length(cluster_counts)-1, "| Puntos de ruido:", cluster_counts["0"])# Gráfico PCA mejorado con estética optimizadapca_plot <-ggplot(df_pca, aes(x = PC1, y = PC2, color = Cluster, label = State)) +geom_point(size =3, alpha =0.7) + ggrepel::geom_text_repel(size =3.5, box.padding =0.35, point.padding =0.5,segment.color ="grey50" ) +theme_minimal() +labs(title = title_info,subtitle ="Análisis de Componentes Principales (PCA)",x ="Componente Principal 1",y ="Componente Principal 2" ) +scale_color_viridis_d(option ="plasma", end =0.9) +theme(legend.position ="right",plot.title =element_text(face ="bold"),plot.subtitle =element_text(face ="italic") )print(pca_plot)
Warning: ggrepel: 39 unlabeled data points (too many overlaps). Consider
increasing max.overlaps
# B. Visualización con UMAP (a menudo mejor para preservar estructuras locales)set.seed(42) # Para reproducibilidadumap_result <-umap(state_matrix_scaled, n_neighbors =5, min_dist =0.1, n_components =2)df_umap <-data.frame(UMAP1 = umap_result$layout[,1],UMAP2 = umap_result$layout[,2],Cluster =as.factor(dbscan_result$cluster),State = df_wide$state)umap_plot <-ggplot(df_umap, aes(x = UMAP1, y = UMAP2, color = Cluster, label = State)) +geom_point(size =3, alpha =0.7) + ggrepel::geom_text_repel(size =3.5, box.padding =0.35, point.padding =0.5,segment.color ="grey50" ) +theme_minimal() +labs(title = title_info,subtitle ="Reducción de Dimensionalidad con UMAP",x ="Dimensión UMAP 1",y ="Dimensión UMAP 2" ) +scale_color_viridis_d(option ="plasma", end =0.9) +theme(legend.position ="right",plot.title =element_text(face ="bold"),plot.subtitle =element_text(face ="italic") ) print(umap_plot)
🧠 Interpretación
Estados cercanos en el dendrograma tienen patrones de importaciones similares.
Esto puede ayudar a diseñar políticas regionales o detectar estrategias comerciales compartidas.
✅ Resumen final
DBSCAN encuentra agrupamientos de productos sin definir el número de clusters.
Podemos usar clustering jerárquico para encontrar estados con patrones similares de importaciones.
El análisis de densidades y patrones de comercio revela oportunidades y riesgos en relaciones internacionales.