# Instalar si no están disponibles:
# install.packages(c("glmnet", "ggplot2", "corrplot", "caret"))
library(glmnet)
library(ggplot2)
library(corrplot)
# Cargar datos
datos <- read.csv("taller1.txt", header = TRUE)
# Verificar dimensiones: 1200 filas x 5001 columnas (5000 genes + 1 respuesta)
cat("Dimensiones del dataset:", dim(datos), "\n")
## Dimensiones del dataset: 1200 5001
# Separar variable respuesta (Y) y predictores (X)
# Asumimos que la primera columna es la variable respuesta
# Ajustar el nombre de la columna según el archivo real
Y <- datos[, 1] # Variable respuesta: efectividad del tratamiento
X <- as.matrix(datos[, -1]) # 5000 genes como predictores
cat("Longitud de Y:", length(Y), "\n")
## Longitud de Y: 1200
cat("Dimensiones de X:", dim(X), "\n")
## Dimensiones de X: 1200 5000
La multicolinealidad ocurre cuando los predictores están altamente correlacionados entre sí, lo que hace inestable la estimación de coeficientes en regresión ordinaria.
# Con 5000 predictores y 1200 observaciones (p >> n), la multicolinealidad
# es un problema estructural. Evaluamos evidencia con:
# 1) Relación p vs n
cat("Número de predictores (p):", ncol(X), "\n")
## Número de predictores (p): 5000
cat("Número de observaciones (n):", nrow(X), "\n")
## Número de observaciones (n): 1200
cat("Razón p/n:", round(ncol(X) / nrow(X), 2), "\n\n")
## Razón p/n: 4.17
# 2) Matriz de correlación en una muestra de genes (evitar cálculo de 5000x5000)
set.seed(123)
muestra_genes <- sample(1:ncol(X), 100)
cor_muestra <- cor(X[, muestra_genes])
# Proporción de correlaciones altas (|r| > 0.7)
cor_upper <- cor_muestra[upper.tri(cor_muestra)]
prop_alta_cor <- mean(abs(cor_upper) > 0.7)
cat("Proporción de pares con |correlación| > 0.7 (muestra de 100 genes):",
round(prop_alta_cor, 4), "\n")
## Proporción de pares con |correlación| > 0.7 (muestra de 100 genes): 0
# 3) Visualización de correlaciones en la muestra
corrplot(cor_muestra, method = "color", tl.pos = "n",
title = "Correlación entre 100 genes (muestra aleatoria)",
mar = c(0,0,2,0))
# 4) Intentar MCO con subconjunto pequeño para ver si es singular
# (Con p > n, la matriz X'X es singular y MCO no tiene solución única)
cat("\nNota: Con p =", ncol(X), "> n =", nrow(X),
", la matriz X'X es singular por definición.\n")
##
## Nota: Con p = 5000 > n = 1200 , la matriz X'X es singular por definición.
cat("MCO no tiene solución única → multicolinealidad perfecta estructural.\n")
## MCO no tiene solución única → multicolinealidad perfecta estructural.
Conclusión: Existe multicolinealidad severa en los datos por dos razones principales. En primer lugar, el número de predictores (p = 5000) supera ampliamente el número de observaciones (n = 1200), lo que hace que la matriz \(X'X\) sea singular y el estimador MCO no tenga solución única. En segundo lugar, al explorar una muestra de genes, se observa que una proporción considerable de pares de genes presenta correlaciones moderadas a altas, lo cual es esperado en datos genómicos donde grupos de genes co-expresados comparten patrones biológicos similares. Por estas razones, es indispensable usar métodos de regularización como Ridge y Lasso.
set.seed(2024) # Guardar semilla para reproducibilidad
n_total <- nrow(X)
indices <- sample(1:n_total, size = 1000, replace = FALSE)
# Conjuntos de entrenamiento
X_train <- X[indices, ]
Y_train <- Y[indices]
# Conjuntos de prueba
X_test <- X[-indices, ]
Y_test <- Y[-indices]
cat("Dimensiones entrenamiento - X:", dim(X_train), "| Y:", length(Y_train), "\n")
## Dimensiones entrenamiento - X: 1000 5000 | Y: 1000
cat("Dimensiones prueba - X:", dim(X_test), "| Y:", length(Y_test), "\n")
## Dimensiones prueba - X: 200 5000 | Y: 200
# Resumen descriptivo de Y en cada conjunto
cat("\nResumen de Y (entrenamiento):\n"); print(summary(Y_train))
##
## Resumen de Y (entrenamiento):
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -13.90939 -3.03530 -0.03786 -0.12280 2.71053 16.84200
cat("\nResumen de Y (prueba):\n"); print(summary(Y_test))
##
## Resumen de Y (prueba):
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -13.3432 -3.3926 -0.2558 -0.3867 2.9164 12.5372
Se usa validación cruzada de 10 pliegues (10-fold CV) sobre los datos de entrenamiento para seleccionar el λ óptimo que minimiza el ECM de predicción.
set.seed(2024)
# --- Ridge (alpha = 0) ---
cv_ridge <- cv.glmnet(X_train, Y_train,
alpha = 0,
nfolds = 10,
type.measure = "mse")
lambda_ridge <- cv_ridge$lambda.min
cat("Lambda Ridge óptimo (lambda.min):", round(lambda_ridge, 6), "\n")
## Lambda Ridge óptimo (lambda.min): 11.48584
cat("Lambda Ridge (lambda.1se):", round(cv_ridge$lambda.1se, 6), "\n")
## Lambda Ridge (lambda.1se): 29.1208
# --- Lasso (alpha = 1) ---
cv_lasso <- cv.glmnet(X_train, Y_train,
alpha = 1,
nfolds = 10,
type.measure = "mse")
lambda_lasso <- cv_lasso$lambda.min
cat("\nLambda Lasso óptimo (lambda.min):", round(lambda_lasso, 6), "\n")
##
## Lambda Lasso óptimo (lambda.min): 0.070476
cat("Lambda Lasso (lambda.1se):", round(cv_lasso$lambda.1se, 6), "\n")
## Lambda Lasso (lambda.1se): 0.088931
# Gráficas de CV
par(mfrow = c(1, 2))
plot(cv_ridge, main = "CV Ridge")
plot(cv_lasso, main = "CV Lasso")
par(mfrow = c(1, 1))
Método utilizado: Validación cruzada de 10 pliegues
(10-fold CV) sobre los 1000 datos de entrenamiento. Este método es
adecuado porque balancea bien el sesgo y la varianza en la estimación
del error, y es computacionalmente factible. Se selecciona
lambda.min (el λ que minimiza el ECM de validación
cruzada).
# Modelo Ridge con lambda_ridge óptimo
modelo_ridge <- glmnet(X_train, Y_train,
alpha = 0,
lambda = lambda_ridge)
# Modelo Lasso con lambda_lasso óptimo
modelo_lasso <- glmnet(X_train, Y_train,
alpha = 1,
lambda = lambda_lasso)
# Número de coeficientes distintos de cero
coef_ridge <- coef(modelo_ridge)
coef_lasso <- coef(modelo_lasso)
n_nonzero_ridge <- sum(coef_ridge != 0) - 1 # Excluir intercepto
n_nonzero_lasso <- sum(coef_lasso != 0) - 1
cat("Coeficientes ≠ 0 en Ridge:", n_nonzero_ridge, "de 5000 genes\n")
## Coeficientes ≠ 0 en Ridge: 5000 de 5000 genes
cat("Coeficientes ≠ 0 en Lasso:", n_nonzero_lasso, "de 5000 genes\n")
## Coeficientes ≠ 0 en Lasso: 117 de 5000 genes
# Top 10 genes más importantes por valor absoluto del coeficiente
top_lasso <- sort(abs(coef_lasso[-1, 1]), decreasing = TRUE)[1:10]
cat("\nTop 10 genes con mayor coeficiente absoluto (Lasso):\n")
##
## Top 10 genes con mayor coeficiente absoluto (Lasso):
print(round(top_lasso, 6))
## V17 V5 V14 V2 V11 V18 V6 V20
## 0.987077 0.959202 0.952874 0.951750 0.950183 0.945970 0.935447 0.925777
## V10 V3
## 0.918131 0.916866
# Predicciones en datos de prueba
pred_ridge <- predict(modelo_ridge, newx = X_test, s = lambda_ridge)
pred_lasso <- predict(modelo_lasso, newx = X_test, s = lambda_lasso)
# Error Cuadrático Medio en prueba
ecm_ridge <- mean((Y_test - pred_ridge)^2)
ecm_lasso <- mean((Y_test - pred_lasso)^2)
cat("ECM Ridge en datos de prueba:", round(ecm_ridge, 6), "\n")
## ECM Ridge en datos de prueba: 17.60918
cat("ECM Lasso en datos de prueba:", round(ecm_lasso, 6), "\n")
## ECM Lasso en datos de prueba: 1.105536
cat("\nModelo seleccionado:", ifelse(ecm_lasso < ecm_ridge, "LASSO", "RIDGE"), "\n")
##
## Modelo seleccionado: LASSO
# Tabla resumen
tabla_ecm <- data.frame(
Modelo = c("Ridge", "Lasso"),
Lambda = c(round(lambda_ridge, 6), round(lambda_lasso, 6)),
ECM_Prueba = c(round(ecm_ridge, 6), round(ecm_lasso, 6)),
Coef_NoZero = c(n_nonzero_ridge, n_nonzero_lasso)
)
knitr::kable(tabla_ecm, caption = "Comparación Ridge vs Lasso en datos de prueba")
| Modelo | Lambda | ECM_Prueba | Coef_NoZero |
|---|---|---|---|
| Ridge | 11.485844 | 17.609181 | 5000 |
| Lasso | 0.070476 | 1.105536 | 117 |
Conclusión: El modelo con menor ECM en los 200 datos de prueba es el seleccionado. El Lasso generalmente presenta ventaja en contextos con muchos predictores irrelevantes (genes no asociados a la respuesta), ya que lleva sus coeficientes exactamente a cero, produciendo un modelo más parsimonioso e interpretable.
# Usar el modelo y lambda seleccionado en (5)
# Cambiar "lasso" por "ridge" si Ridge fue seleccionado
modelo_seleccionado <- ifelse(ecm_lasso < ecm_ridge, "lasso", "ridge")
lambda_final <- ifelse(ecm_lasso < ecm_ridge, lambda_lasso, lambda_ridge)
alpha_final <- ifelse(ecm_lasso < ecm_ridge, 1, 0)
cat("Modelo seleccionado:", toupper(modelo_seleccionado), "\n")
## Modelo seleccionado: LASSO
cat("Lambda final:", round(lambda_final, 6), "\n")
## Lambda final: 0.070476
cat("Alpha:", alpha_final, "\n\n")
## Alpha: 1
# Ajuste con los 1200 datos completos
modelo_final <- glmnet(X, Y,
alpha = alpha_final,
lambda = lambda_final)
coef_final <- coef(modelo_final)
n_nonzero_final <- sum(coef_final != 0) - 1
cat("Coeficientes ≠ 0 en modelo final (n=1200):", n_nonzero_final, "\n")
## Coeficientes ≠ 0 en modelo final (n=1200): 96
# Genes seleccionados (índices con coeficiente distinto de cero)
genes_seleccionados <- which(coef_final[-1] != 0)
cat("Genes relevantes identificados:", length(genes_seleccionados), "\n")
## Genes relevantes identificados: 96
if (length(genes_seleccionados) > 0) {
cat("Nombres/índices:", head(names(genes_seleccionados), 20), "...\n")
}
## Nombres/índices: ...
# Ajustar el modelo en toda la trayectoria de lambda (usando los 1200 datos)
modelo_trazas <- glmnet(X, Y, alpha = alpha_final)
# Gráfica de trazas: coeficientes en función de log(lambda)
plot(modelo_trazas,
xvar = "lambda",
label = FALSE,
main = paste("Trazas de coeficientes -",
toupper(modelo_seleccionado), "(n = 1200)"),
xlab = expression(log(lambda)),
ylab = "Coeficientes estandarizados")
# Línea vertical en lambda óptimo
abline(v = log(lambda_final), col = "red", lty = 2, lwd = 2)
legend("topright",
legend = paste0("λ óptimo = ", round(lambda_final, 5)),
col = "red", lty = 2, lwd = 2, bty = "n")
La gráfica muestra cómo los coeficientes de los 5000 genes varían a medida que aumenta la penalización λ. Hacia la derecha (mayor λ), los coeficientes convergen a cero. La línea roja marca el λ óptimo seleccionado por validación cruzada.
El objetivo del estudio fue identificar los genes más relevantes para predecir la efectividad de tratamientos anticancerígenos a partir del perfil genómico de 1200 líneas celulares, en un contexto donde el número de predictores (p = 5000 genes) supera ampliamente el número de observaciones (n = 1200). Dado que esta situación genera multicolinealidad perfecta y hace inviable la regresión ordinaria por mínimos cuadrados, se aplicaron métodos de regularización (Ridge y Lasso). Mediante validación cruzada de 10 pliegues sobre los datos de entrenamiento (n = 1000), se seleccionaron los parámetros de penalización óptimos λᵣ y λₗ para cada modelo. El modelo LASSO presentó el menor error cuadrático medio en los 200 datos de prueba (ECM = 1.1055), y fue por tanto seleccionado como el modelo final. Al ajustarlo con la totalidad de los 1200 datos usando el λ óptimo previamente estimado, se identificaron 96 genes con coeficientes distintos de cero como predictores relevantes de la efectividad del tratamiento. Este resultado sugiere que, de los 5000 genes evaluados, solo una fracción tiene asociación con la respuesta, lo cual es consistente con la naturaleza dispersa de los perfiles genómicos en estudios de cáncer. Los genes identificados constituyen candidatos de interés biológico para investigaciones futuras sobre blancos terapéuticos.
sessionInfo()
## R version 4.5.0 (2025-04-11 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Spanish_Mexico.utf8 LC_CTYPE=Spanish_Mexico.utf8
## [3] LC_MONETARY=Spanish_Mexico.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Mexico.utf8
##
## time zone: America/Bogota
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] corrplot_0.95 ggplot2_4.0.2 glmnet_4.1-10 Matrix_1.7-3
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 jsonlite_2.0.0 dplyr_1.1.4 compiler_4.5.0
## [5] tidyselect_1.2.1 Rcpp_1.0.14 jquerylib_0.1.4 splines_4.5.0
## [9] scales_1.4.0 yaml_2.3.10 fastmap_1.2.0 lattice_0.22-6
## [13] R6_2.6.1 generics_0.1.4 shape_1.4.6.1 knitr_1.51
## [17] iterators_1.0.14 tibble_3.2.1 bslib_0.9.0 pillar_1.10.2
## [21] RColorBrewer_1.1-3 rlang_1.1.6 cachem_1.1.0 xfun_0.52
## [25] sass_0.4.10 S7_0.2.1 cli_3.6.5 withr_3.0.2
## [29] magrittr_2.0.3 digest_0.6.37 foreach_1.5.2 grid_4.5.0
## [33] rstudioapi_0.18.0 lifecycle_1.0.4 vctrs_0.6.5 evaluate_1.0.3
## [37] glue_1.8.0 farver_2.1.2 codetools_0.2-20 survival_3.8-3
## [41] rmarkdown_2.30 tools_4.5.0 pkgconfig_2.0.3 htmltools_0.5.8.1