install.packages(c("tidyverse", "GGally", "cluster", "ggrepel"))
## Installing packages into '/cloud/lib/x86_64-pc-linux-gnu-library/4.5'
## (as 'lib' is unspecified)
install.packages("factoextra")
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.5'
## (as 'lib' is unspecified)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.1 ✔ stringr 1.5.2
## ✔ ggplot2 4.0.0 ✔ tibble 3.3.0
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.1.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(GGally)
library(cluster)
library(ggrepel)
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
# 1) lectura de datos
dat <- read.csv("datos_taller_02.csv",
sep = ";",
stringsAsFactors = FALSE) #hace que las columnas de texto se queden como texto y no se conviertan en factor.
# Vairables numéricas
dat_num <- dat %>% select(where(is.numeric))#De todo el data frame dat, selecciona solo las columnas que son numéricas.Lo guarda en dat_num.
summary(dat_num)
## Bunker_fuel_consumption_TBPD Bunker_residual_fuel_oil_consumption_TBPD
## Min. : 0.00 Min. : 0.00
## 1st Qu.: 2.70 1st Qu.: 0.00
## Median : 8.40 Median : 1.70
## Mean : 46.01 Mean : 24.63
## 3rd Qu.: 32.00 3rd Qu.: 15.50
## Max. :924.00 Max. :750.00
## Crude_oil_including_lease_condensate_exports_TBPD
## Min. : 0.0
## 1st Qu.: 0.0
## Median : 8.5
## Mean : 361.6
## 3rd Qu.: 220.5
## Max. :7120.0
## Crude_oil_including_lease_condensate_imports_TBPD
## Min. : 0.0
## 1st Qu.: 0.0
## Median : 36.0
## Mean : 367.3
## 3rd Qu.: 212.0
## Max. :7344.0
## Crude_oil_including_lease_condensate_production_TBPD
## Min. : 0.0
## 1st Qu.: 0.2
## Median : 38.0
## Mean : 702.1
## 3rd Qu.: 531.0
## Max. :10107.0
## Crude_oil_including_lease_condensate_reserves_BB
## Min. : 0.00
## 1st Qu.: 0.00
## Median : 0.20
## Mean : 14.82
## 3rd Qu.: 3.35
## Max. :298.00
## Distillate_fuel_oil_consumption_TBPD Distillate_fuel_oil_production_TBPD
## Min. : 4.7 Min. : 0.0
## 1st Qu.: 31.0 1st Qu.: 12.5
## Median : 68.0 Median : 57.0
## Mean : 233.6 Mean : 239.9
## 3rd Qu.: 214.0 3rd Qu.: 188.5
## Max. :4037.0 Max. :4916.0
## Jet_fuel_consumption_TBPD Jet_fuel_production_TBPD Kerosene_consumption_TBPD
## Min. : 0.10 Min. : 0.00 Min. : 0.000
## 1st Qu.: 2.80 1st Qu.: 1.70 1st Qu.: 0.000
## Median : 11.00 Median : 8.10 Median : 0.500
## Mean : 48.65 Mean : 52.71 Mean : 8.225
## 3rd Qu.: 31.00 3rd Qu.: 40.00 3rd Qu.: 2.300
## Max. :1470.00 Max. :1541.00 Max. :298.000
## Kerosene_production_TBPD Liquefied_petroleum_gases_.LPG._consumption_TBPD
## Min. : 0.00 Min. : 0.20
## 1st Qu.: 0.00 1st Qu.: 3.80
## Median : 0.30 Median : 14.00
## Mean : 11.57 Mean : 73.94
## 3rd Qu.: 2.40 3rd Qu.: 54.50
## Max. :291.00 Max. :1348.00
## Liquefied_petroleum_gases_.LPG._production_TBPD
## Min. : 0.00
## 1st Qu.: 0.60
## Median : 5.80
## Mean : 35.26
## 3rd Qu.: 18.50
## Max. :777.00
## Motor_gasoline_consumption_TBPD Motor_gasoline_production_TBPD
## Min. : 0.8 Min. : 0.0
## 1st Qu.: 13.0 1st Qu.: 5.6
## Median : 33.0 Median : 37.0
## Mean : 214.1 Mean : 211.5
## 3rd Qu.: 108.5 3rd Qu.: 106.5
## Max. :8921.0 Max. :9571.0
## Natural_gas_plant_liquids_production_TBPD Other_liquids_production_TBPD
## Min. : 0.00 Min. : 0.00
## 1st Qu.: 0.00 1st Qu.: 0.00
## Median : 0.20 Median : 0.70
## Mean : 85.83 Mean : 31.01
## 3rd Qu.: 20.50 3rd Qu.: 8.00
## Max. :3015.00 Max. :1273.00
## Other_refined_products_consumption_TBPD Other_refined_products_production_TBPD
## Min. : 0.00 Min. : 0.00
## 1st Qu.: 5.25 1st Qu.: 4.95
## Median : 22.00 Median : 20.00
## Mean : 170.96 Mean : 148.13
## 3rd Qu.: 94.00 3rd Qu.: 110.50
## Max. :3156.00 Max. :2867.00
## Petroleum_and_other_liquids_CO2_emissions_MMTCD
## Min. : 2.30
## 1st Qu.: 9.65
## Median : 26.00
## Mean : 101.04
## 3rd Qu.: 94.50
## Max. :2263.00
## Petroleum_and_other_liquids_consumption_TBPD
## Min. : 15.0
## 1st Qu.: 71.5
## Median : 202.0
## Mean : 811.4
## 3rd Qu.: 750.0
## Max. :19100.0
## Petroleum_and_other_liquids_production_TBPD
## Min. : -0.30
## 1st Qu.: 11.50
## Median : 64.67
## Mean : 838.75
## 3rd Qu.: 569.00
## Max. :14158.00
## Refined_petroleum_products_consumption_TBPD
## Min. : 15.0
## 1st Qu.: 71.5
## Median : 202.0
## Mean : 811.4
## 3rd Qu.: 750.0
## Max. :19100.0
## Refined_petroleum_products_production_TBPD Refinery_processing_gain_TBPD
## Min. : 0.0 Min. : -12.00
## 1st Qu.: 39.5 1st Qu.: 0.00
## Median : 172.0 Median : 2.70
## Mean : 777.3 Mean : 19.78
## 3rd Qu.: 555.0 3rd Qu.: 8.45
## Max. :19653.0 Max. :1081.00
## Residual_fuel_oil_consumption_TBPD Residual_fuel_oil_production_TBPD
## Min. : 0.00 Min. : 0.00
## 1st Qu.: 4.20 1st Qu.: 6.10
## Median : 17.00 Median : 29.00
## Mean : 61.86 Mean : 78.22
## 3rd Qu.: 53.50 3rd Qu.: 77.00
## Max. :788.00 Max. :1577.00
## Biomass_and_waste_electricity_installed_capacity_MK
## Min. : 0.0000
## 1st Qu.: 0.0000
## Median : 0.0000
## Mean : 0.8102
## 3rd Qu.: 0.6500
## Max. :16.0000
## Biomass_and_waste_electricity_net_generation_BKWH
## Min. : 0.000
## 1st Qu.: 0.000
## Median : 0.200
## Mean : 3.854
## 3rd Qu.: 2.200
## Max. :77.000
## Electricity_distribution_losses_BKWH Electricity_exports_BKWH
## Min. : 0.10 Min. : 0.000
## 1st Qu.: 1.65 1st Qu.: 0.000
## Median : 3.80 Median : 0.600
## Mean : 17.00 Mean : 5.158
## 3rd Qu.: 11.00 3rd Qu.: 5.550
## Max. :310.00 Max. :75.000
## Electricity_imports_BKWH Electricity_installed_capacity_MK
## Min. : 0.000 Min. : 0.40
## 1st Qu.: 0.000 1st Qu.: 3.50
## Median : 0.700 Median : 11.00
## Mean : 5.671 Mean : 51.63
## 3rd Qu.: 7.550 3rd Qu.: 33.00
## Max. :67.000 Max. :1380.00
## Electricity_net_consumption_BKWH Electricity_net_generation_BKWH
## Min. : 0.80 Min. : 0.9
## 1st Qu.: 9.95 1st Qu.: 12.5
## Median : 37.00 Median : 43.0
## Mean : 181.82 Mean : 198.3
## 3rd Qu.: 122.50 3rd Qu.: 136.0
## Max. :5079.00 Max. :5400.0
## Electricity_net_imports_BKWH Fossil_fuels_electricity_installed_capacity_MK
## Min. :-67.0000 Min. : 0.10
## 1st Qu.: -0.7500 1st Qu.: 1.65
## Median : 0.0000 Median : 7.20
## Mean : 0.4961 Mean : 33.41
## 3rd Qu.: 0.8500 3rd Qu.: 24.00
## Max. : 53.0000 Max. :924.00
## Fossil_fuels_electricity_net_generation_BKWH
## Min. : 0.00
## 1st Qu.: 4.25
## Median : 21.00
## Mean : 132.58
## 3rd Qu.: 80.00
## Max. :3985.00
## Geothermal_electricity_installed_capacity_MK
## Min. :0.00000
## 1st Qu.:0.00000
## Median :0.00000
## Mean :0.09369
## 3rd Qu.:0.00000
## Max. :2.50000
## Geothermal_electricity_net_generation_BKWH
## Min. : 0.0000
## 1st Qu.: 0.0000
## Median : 0.0000
## Mean : 0.6108
## 3rd Qu.: 0.0000
## Max. :16.0000
## Hydroelectric_pumped_storage_electricity_installed_capacity_MK
## Min. : 0.000
## 1st Qu.: 0.000
## Median : 0.000
## Mean : 1.316
## 3rd Qu.: 0.700
## Max. :27.000
## Hydroelectric_pumped_storage_electricity_net_generation_BKWH
## Min. :-6.2000
## 1st Qu.: 0.0000
## Median : 0.0000
## Mean :-0.2532
## 3rd Qu.: 0.0000
## Max. : 0.0000
## Hydroelectricity_installed_capacity_MK Hydroelectricity_net_generation_BKWH
## Min. : 0.000 Min. : 0.00
## 1st Qu.: 0.200 1st Qu.: 0.50
## Median : 1.600 Median : 4.70
## Mean : 8.747 Mean : 32.57
## 3rd Qu.: 4.550 3rd Qu.: 14.50
## Max. :283.000 Max. :1041.00
## Non.hydro_renewable_electricity_installed_capacity_MK
## Min. : 0.000
## 1st Qu.: 0.000
## Median : 0.500
## Mean : 5.189
## 3rd Qu.: 2.400
## Max. :132.000
## Non.hydro_renewable_electricity_net_generation_BKWH
## Min. : 0.00
## 1st Qu.: 0.00
## Median : 1.00
## Mean : 12.21
## 3rd Qu.: 7.55
## Max. :304.00
## Nuclear_electricity_installed_capacity_MK
## Min. : 0.000
## 1st Qu.: 0.000
## Median : 0.000
## Mean : 2.943
## 3rd Qu.: 0.600
## Max. :99.000
## Nuclear_electricity_net_generation_BKWH
## Min. : 0.00
## 1st Qu.: 0.00
## Median : 0.00
## Mean : 21.22
## 3rd Qu.: 3.80
## Max. :797.00
## Renewable_electricity_installed_capacity_MK
## Min. : 0.00
## 1st Qu.: 0.60
## Median : 2.30
## Mean : 13.94
## 3rd Qu.: 7.90
## Max. :415.00
## Renewable_electricity_net_generation_BKWH
## Min. : 0.00
## 1st Qu.: 1.50
## Median : 8.30
## Mean : 44.77
## 3rd Qu.: 26.50
## Max. :1287.00
## Solar_electricity_installed_capacity_MK Solar_electricity_net_generation_BKWH
## Min. : 0.000 Min. : 0.000
## 1st Qu.: 0.000 1st Qu.: 0.000
## Median : 0.000 Median : 0.000
## Mean : 1.339 Mean : 1.579
## 3rd Qu.: 0.300 3rd Qu.: 0.450
## Max. :28.000 Max. :29.000
## Tide_and_wave_electricity_installed_capacity_MK
## Min. :0.00000
## 1st Qu.:0.00000
## Median :0.00000
## Mean :0.00961
## 3rd Qu.:0.00000
## Max. :0.40000
## Tide_and_wave_electricity_net_generation_BKWH
## Min. :0.00000
## 1st Qu.:0.00000
## Median :0.00000
## Mean :0.03934
## 3rd Qu.:0.00000
## Max. :1.40000
## Wind_electricity_installed_capacity_MK Wind_electricity_net_generation_BKWH
## Min. : 0.000 Min. : 0.000
## 1st Qu.: 0.000 1st Qu.: 0.000
## Median : 0.000 Median : 0.100
## Mean : 2.902 Mean : 6.085
## 3rd Qu.: 0.700 3rd Qu.: 1.450
## Max. :97.000 Max. :182.000
dat_long <- dat_num %>%
pivot_longer(everything(),
names_to = "variable",
values_to = "valor")
#Tomamos el data frame numérico y lo pasa de muchas columnas a dos columnas.
#pivot_longer(everything()) convierte todas las columnas en filas.
#names_to = "variable": el nombre de la columna original pasa a una columna llamada "variable".
#values_to = "valor": los valores pasan a una columna llamada "valor".
ggplot(dat_long, aes(x = variable, y = valor)) +
geom_boxplot(outlier.colour = "red", outlier.alpha = 0.4) +
coord_flip() +
theme_minimal() +
labs(title = "Boxplots de variables energéticas",
x = NULL, y = "valor")
#Creamos un gráfico con ggplot, usando la variable en el eje x y los valores en el eje y.
#geom_boxplot(...) dibuja un boxplot por cada variable; pinta los outliers en rojo
#coord_flip() gira el gráfico para que las variables queden en el eje vertical (más legible).
#theme_minimal() aplica un tema limpio.
#labs(...) pone título y nombres de ejes.
# Eleccion de variables
vars_bivariado <- dat %>%
select(
Bunker_fuel_consumption_TBPD,
Hydroelectricity_net_generation_BKWH,
Nuclear_electricity_net_generation_BKWH,
Renewable_electricity_net_generation_BKWH,
Solar_electricity_net_generation_BKWH,
Tide_and_wave_electricity_net_generation_BKWH,
Wind_electricity_net_generation_BKWH,
) %>%
drop_na()
GGally::ggpairs(
vars_bivariado,
title = "Relaciones bivariadas entre variables de energía"
)
library(dplyr)
library(cluster)
library(ggplot2)
library(ggrepel)
# 1. Definimos Variables a usar
#Se crea un vector de nombres de columnas que te interesan para el clustering.
candidatas <- c(
"Bunker_fuel_consumption_TBPD",
"Hydroelectricity_net_generation_BKWH",
"Nuclear_electricity_net_generation_BKWH",
"Renewable_electricity_net_generation_BKWH",
"Solar_electricity_net_generation_BKWH",
"Tide_and_wave_electricity_net_generation_BKWH",
"Wind_electricity_net_generation_BKWH"
)
vars_existentes <- intersect(candidatas, names(dat))
#Comprueba cuáles de esas columnas realmente existen en dat.
#intersect() se queda con los nombres que están en los dos lados.
#Esto evita errores si alguna columna no está en el CSV.
# 3. construir el dataset numérico para clustering
dat_num_k <- dat %>%
select(all_of(vars_existentes)) %>%
drop_na()
# 4. escalar
dat_scaled <- scale(dat_num_k)
#Estandariza las variables (media 0, desviación 1).Esto es importante porque las variables están en unidades muy distintas y el clustering se basa en distancias.
# 5. elegir k por silueta
#Creamos un vector vacío sil_width para guardar los anchos de silueta.
#Haces un bucle para probar distintos números de clusters k desde 2 hasta 8.
#En cada iteración:
#pam(dat_scaled, k = k) ajusta un modelo PAM (k-medoides) con ese k.
#pam_fit$silinfo$avg.width es el promedio de la silueta para ese k (qué tan bien separados quedaron los clusters).
#Por ultimo lo guardamos en la posición k del vector sil_width.
sil_width <- c()
for (k in 2:8) {
pam_fit <- pam(dat_scaled, k = k)
sil_width[k] <- pam_fit$silinfo$avg.width
}
plot(2:8, sil_width[2:8],
type = "b", pch = 19,
xlab = "k",
ylab = "Ancho medio de silueta",
main = "Selección de k (PAM)")
best_k <- which.max(sil_width)
#Grafica los valores de silueta para k = 2,…,8.
#type = "b" dibuja puntos y líneas.
#Asi se deja ver cuál k es mejor (el que tenga silueta más alta).
# 6. modelo final
set.seed(123)
pam_final <- pam(dat_scaled, k = 3)
# 7. Graficamos los Clusters con herramienta Fviz_cluster
fviz_cluster(pam_final, geom = "point", ellipse.type = "norm")
## Too few points to calculate an ellipse
# 8. PCA para graficar con nombres de país
pca <- prcomp(dat_scaled)
pca_df <- data.frame(
PC1 = pca$x[,1],
PC2 = pca$x[,2],
cluster = factor(pam_final$clustering)
)
#Filtramos solo las filas que tienen datos completos en las variables de clustering para que coincidan con las filas que usamos en dat_num_k.
#pull(Country) extrae la columna Country como un vector.Esto nos da los nombres de los países en el mismo orden que los datos usados en el clustering.
paises_complete <- dat %>%
filter(complete.cases(select(., all_of(vars_existentes)))) %>%
pull(Country)
#Añadimos la columna con los nombres de los países al data frame de PCA, para poder etiquetar los nombres de los paises.
pca_df$Country <- paises_complete
#Graficamos con ggplot.
ggplot(pca_df, aes(PC1, PC2, color = cluster, label = Country)) +
geom_point(size = 3, alpha = 0.7) +
ggrepel::geom_text_repel(max.overlaps = 20, size = 3) +
theme_minimal() +
labs(title = "Clusters de países (PCA + PAM)",
x = "Componente principal 1",
y = "Componente principal 2")
## Warning: ggrepel: 96 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps
install.packages("plotly")
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.5'
## (as 'lib' is unspecified)
library(plotly)
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
# Usamos las tres primeras componentes principales
pca_3d <- data.frame(
PC1 = pca$x[,1],
PC2 = pca$x[,2],
PC3 = pca$x[,3],
cluster = factor(pam_final$clustering),
Country = paises_complete
)
# Gráfico 3D
plot_ly(
data = pca_3d,
x = ~PC1, y = ~PC2, z = ~PC3,
color = ~cluster,
colors = c("red", "green", "blue"),
text = ~Country, # muestra nombre del país al pasar el mouse
hoverinfo = "text",
type = "scatter3d",
mode = "markers"
) %>%
layout(
title = "Clusters de países (PCA 3D + PAM)",
scene = list(
xaxis = list(title = "Componente 1"),
yaxis = list(title = "Componente 2"),
zaxis = list(title = "Componente 3")
)
)
Conclusiones:
-El gráfico muestra la proyección PCA-Kmedoides del clustering PAM, es decir, países reales representativos del comportamiento de cada grupo. Se observan tres clusters bien diferenciados: uno rojo, que agrupa economías intermedias con matrices energéticas mixtas (Japón, Rusia, Brasil, España); uno azul, con potencias europeas diversificadas y alta generación nuclear o hidroeléctrica (Francia, Alemania, Italia, Corea del Sur); y uno verde, formado por gigantes energéticos con volúmenes excepcionales de generación (China y Estados Unidos). La distancia entre grupos refleja la magnitud de sus diferencias estructurales, y el medoide de cada cluster sería el país más central y representativo de su perfil energético.
-Es más claro visualizar este conjunto de datos y cluster en un entorno 3D para efectos de análisis.
-En este ejercicio, el método k-medoides (PAM) ofrece varias bondades frente a k-means: al usar como centro un objeto real (medoide) en lugar del promedio, es mucho más robusto ante valores extremos y escalas muy diferentes entre variables energéticas, evitando que países con valores desproporcionados —como China o Estados Unidos— distorsionen los centroides. Además, k-medoides puede utilizar cualquier medida de distancia, no solo la euclidiana, lo que le da mayor flexibilidad para datos heterogéneos o con unidades distintas. En este contexto, produce clusters más estables y realistas, donde los grupos están representados por países existentes, facilitando la interpretación práctica del perfil energético de cada grupo. En cambio, k-means tiende a formar clusters alrededor de promedios abstractos y es más sensible a la escala y a los outliers, lo que podría haber generado agrupaciones menos coherentes entre países con magnitudes energéticas muy dispares.