Proyecto estrictamente académico (Estadística y Probabilidades, FIGEMPA – UCE). Datos: EarthChem PetDB (CC BY 4.0), descargados el 13/10/2026. Web del proyecto: https://crisrumiguano-art.github.io/geoquimica-earthchem-ml/

1. Carga de datos

Se usa la base completa (159 760 análisis). Para cada par se toman solo las filas que tienen los dos datos: no se inventa ningún valor que falte.

knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE, fig.align = "center")
if (!file.exists("data/earthchem_limpio.csv.gz")) {
  dir.create("data", showWarnings = FALSE)
  download.file("https://github.com/crisrumiguano-art/geoquimica-earthchem-ml/raw/main/data/earthchem_limpio.csv.gz", "data/earthchem_limpio.csv.gz", mode = "wb")
}
base <- read.csv(gzfile("data/earthchem_limpio.csv.gz", encoding = "UTF-8"), check.names = FALSE)
dim(base)
## [1] 159760     75

2. Asignación de variables

Como en la plantilla del curso, la regresión logística predice una variable binaria (0 o 1). Pregunta: ¿una roca volcánica se formó en un margen convergente (arco volcánico, 1) o no (dorsal o intraplaca, 0)? Variables explicativas: cinco óxidos mayores. Los magmas de arco se forman con agua de la placa que se hunde: tienen más Al2O3 y K2O y menos MgO que los de dorsal.

oxidos <- c("SiO2 (wt%)", "Al2O3 (wt%)", "MgO (wt%)", "Na2O (wt%)", "K2O (wt%)")
volcanica <- grepl("^igneous:volcanic", base[["Sample Type"]])
datos <- na.omit(base[volcanica, c(oxidos, "Tectonic Group")])
datos <- datos[datos[["Tectonic Group"]] != "mixto", ]
colnames(datos) <- c("SiO2", "Al2O3", "MgO", "Na2O", "K2O", "grupo")
datos$arco <- ifelse(datos$grupo == "margen convergente", 1, 0)   # 1 = margen convergente
table(datos$arco)
## 
##     0     1 
## 24915 11239

3. Tabla de valores

knitr::kable(head(datos[, c("SiO2", "Al2O3", "MgO", "Na2O", "K2O", "arco")], 10), caption = "Tabla N° 1: primeras 10 filas")
Tabla N° 1: primeras 10 filas
SiO2 Al2O3 MgO Na2O K2O arco
3 50.84 16.78 7.88 2.22 0.810 1
5 48.78 15.53 8.77 2.85 0.060 0
8 49.20 18.11 5.32 2.76 0.170 1
11 49.28 15.52 7.59 2.55 0.500 0
20 50.64 15.28 7.57 2.87 0.070 0
25 50.81 13.93 6.75 2.43 0.240 0
28 51.41 20.30 4.20 1.98 0.240 1
35 55.88 16.63 5.30 3.12 1.500 1
51 49.13 15.03 9.65 1.88 0.281 0
53 50.00 17.20 4.96 2.67 0.440 1
knitr::kable(aggregate(cbind(SiO2, Al2O3, MgO, Na2O, K2O) ~ arco, data = datos, FUN = mean), digits = 2,
             caption = "Tabla N° 2: media de cada óxido en arco (1) y no arco (0)")
Tabla N° 2: media de cada óxido en arco (1) y no arco (0)
arco SiO2 Al2O3 MgO Na2O K2O
0 50.6 14.51 7.77 3.07 1.11
1 61.0 15.34 3.82 3.48 1.68

4. Entrenamiento y prueba

El 80 % de las filas se usa para ajustar el modelo y el 20 % restante para probarlo con datos que no vio.

set.seed(2026)
entrena <- sample(nrow(datos), round(0.8 * nrow(datos)))
d_ent <- datos[entrena, ]
d_prueba <- datos[-entrena, ]
c(entrenamiento = nrow(d_ent), prueba = nrow(d_prueba))
## entrenamiento        prueba 
##         28923          7231

5. Conjetura del modelo

La regresión logística ajusta una combinación lineal z = b0 + b1·x1 + … + b5·x5 y la convierte en probabilidad con la función sigmoide, p = 1 / (1 + e^(−z)). Es una neurona con activación sigmoide: el puente hacia la Unidad 4.

modelo <- glm(arco ~ SiO2 + Al2O3 + MgO + Na2O + K2O, data = d_ent, family = "binomial")
summary(modelo)$coefficients
##                 Estimate  Std. Error    z value      Pr(>|z|)
## (Intercept) -22.06584719 0.328615601 -67.147899  0.000000e+00
## SiO2          0.28664457 0.004304305  66.594857  0.000000e+00
## Al2O3         0.47805944 0.009547605  50.071139  0.000000e+00
## MgO           0.01177993 0.005053370   2.331104  1.974787e-02
## Na2O         -0.31888615 0.021002093 -15.183542  4.545584e-52
## K2O          -0.60435840 0.021364908 -28.287433 4.933698e-176
round(exp(coef(modelo)), 3)                            # razón de momios: cuánto cambian las chances por cada 1 % del óxido
## (Intercept)        SiO2       Al2O3         MgO        Na2O         K2O 
##       0.000       1.332       1.613       1.012       0.727       0.546
z <- predict(modelo, d_prueba)                         # combinación lineal
p <- 1 / (1 + exp(-z))                                 # probabilidad de ser arco
plot(z, d_prueba$arco + runif(length(z), -0.04, 0.04), pch = 16, cex = 0.4, col = rgb(0.22, 0.53, 0.9, 0.25), las = 1,
     xlim = quantile(z, c(0.005, 0.995)), main = "Gráfica N° 1: Curva sigmoide de la regresión logística",
     xlab = "z = b0 + b1·SiO2 + … + b5·K2O", ylab = "Arco (1) o no (0)")
curve(1 / (1 + exp(-x)), add = TRUE, col = "#d95926", lwd = 3)
abline(h = 0.5, lty = 3, col = "gray50")

6. Test de aprobación

Se clasifica como arco si p ≥ 0,5. La matriz de confusión cuenta aciertos y errores en los datos de prueba.

estimado <- ifelse(p >= 0.5, 1, 0)
confusion <- table(real = d_prueba$arco, estimado = estimado)
confusion
##     estimado
## real    0    1
##    0 4729  249
##    1  865 1388
exactitud <- sum(diag(confusion)) / sum(confusion) * 100
sensibilidad <- confusion["1", "1"] / sum(confusion["1", ]) * 100     # arcos reconocidos
especificidad <- confusion["0", "0"] / sum(confusion["0", ]) * 100    # no arcos reconocidos
knitr::kable(data.frame(exactitud = round(exactitud, 1), sensibilidad = round(sensibilidad, 1),
                        especificidad = round(especificidad, 1)), caption = "Tabla N° 3: test de aprobación (%)")
Tabla N° 3: test de aprobación (%)
exactitud sensibilidad especificidad
84.6 61.6 95

7. Restricciones

Vale para rocas volcánicas con los cinco óxidos dentro del rango medido. Las clases no tienen el mismo tamaño (hay menos arcos), por eso se informan sensibilidad y especificidad además de la exactitud.

8. Estimación de pronósticos

¿Qué probabilidad de ser de arco tiene una andesita con 59 % SiO2, 17 % Al2O3, 3 % MgO, 3,5 % Na2O y 1,5 % K2O? ¿Y un basalto de dorsal con 50 %, 15 %, 8 %, 2,5 % y 0,15 %?

nuevas <- data.frame(SiO2 = c(59, 50), Al2O3 = c(17, 15), MgO = c(3, 8), Na2O = c(3.5, 2.5), K2O = c(1.5, 0.15))
p_nuevas <- predict(modelo, nuevas, type = "response")
round(p_nuevas * 100, 1)                               # en %
##    1    2 
## 72.8 20.5

9. Conclusión

La regresión logística reconoce si una roca volcánica es de margen convergente en el 84.6 % de los casos de prueba (sensibilidad 61.6 %, especificidad 95 %). La andesita del ejemplo sale con 73 % de probabilidad de ser de arco y el basalto de dorsal con 20 %. La red neuronal de la Unidad 4 resuelve el mismo tipo de problema con tres clases y más exactitud, porque combina los óxidos de forma no lineal.