Modelización
Frameworks, engines y herramientas de interpretabilidad para modelado estadístico y machine learning en R
Sobre modelización en R
El ecosistema de modelización en R se ha reordenado en la última década en torno a tres familias bien diferenciadas. Conviene tener clara la jerarquía antes de elegir qué aprender o qué imponer en un proyecto:
tidymodelses la propuesta moderna y la opción por defecto para proyectos nuevos. Es un meta-paquete mantenido por el equipo de Posit (Max Kuhn, Hadley Wickham y colaboradores) que cubre todo el pipeline, preprocesado (recipes), especificación del modelo (parsnip), orquestación (workflows), resampling y tuning (rsample,tune), métricas (yardstick) e interpretabilidad (vip). Reemplaza acaret.caretes el framework histórico (Max Kuhn, 2007 en adelante). Sigue funcionando y aparece en código antiguo y en tutoriales, pero está en modo mantenimiento. Su autor ha redirigido el desarrollo haciatidymodels. Para un proyecto nuevo en 2026 no hay razón técnica para empezarlo encaret.mlr3es la alternativa orientada a objetos (basada enR6). Filosofía y API muy distintas atidymodels, más cercana ascikit-learnen estilo. Equipo mantenedor independiente (LMU Munich) y desarrollo activo. Es una elección legítima cuando trabajas con pipelines complejos de OO o necesitas integración profunda conparadoxpara tuning bayesiano avanzado.
Detrás de cualquiera de estos frameworks viven los engines reales: xgboost, ranger, glmnet, randomForestSRC, kernlab, lightgbm. El framework no calcula nada, solo orquesta. Es importante distinguir la decisión de “qué algoritmo entreno” (engine) de “cómo lo orquesto” (framework). Cambiar de engine dentro de tidymodels cuesta una línea (set_engine("ranger") → set_engine("xgboost")). Cambiar de framework cuesta reescribir todo el pipeline.
Tres principios transversales que conviene interiorizar:
- Reproducibilidad y
set.seed. El resampling y los engines aleatorios (random forest, xgboost) exigen semillas explícitas. En backends paralelos (doFuture,doParallel) la propagación de semillas necesita tratamiento específico,furrr::furrr_options(seed = TRUE)otune_grid(control = control_grid(parallel_over = "everything")). - El preprocesado vive dentro del workflow. Centrar, escalar, imputar o codificar variables categóricas se hace dentro de
recipesy se aplica fold a fold en el resampling. Hacerlo fuera (condplyr::mutateantes del split) introduce data leakage. - Train/test split antes de todo. El test set se aparta al principio y no se mira hasta el final. La selección de hiperparámetros, el feature engineering y la comparación de modelos viven dentro del train set, validados con resampling interno (CV, bootstrap).
Esta página cataloga los paquetes que estructuran la mayor parte de los flujos de modelización en R. El orden refleja jerarquía conceptual: primero el framework de referencia (tidymodels), después los engines más relevantes, luego interpretabilidad, y al final las alternativas con peso histórico o nicho OO.
tidymodels
tidymodels es el framework de referencia para modelización en R en 2026. No es un paquete sino una colección coordinada (recipes, parsnip, workflows, rsample, tune, yardstick, dials, vip) que cubre el ciclo completo de un proyecto de modelado supervisado: preprocesamiento, especificación de modelo, resampling, tuning de hiperparámetros, evaluación e interpretabilidad.
Desarrollado por el equipo de Posit con Max Kuhn (autor de caret) al frente. Filosofía coherente con el resto del tidyverse: pipes, verbos especializados, objetos inmutables. Cada subpaquete tiene una responsabilidad acotada, esto es deliberado y facilita razonar sobre el pipeline.
Cuándo usarlo
- Proyectos nuevos de modelado supervisado (clasificación, regresión, survival) en R.
- Cuando necesitas un pipeline reproducible que combine preprocesamiento variable, resampling y tuning de hiperparámetros.
- Cuando el equipo ya trabaja con el tidyverse y la curva de adopción es plana.
- Producción ligera:
workflowsserializables (butcherpara reducir tamaño) que se cargan y predicen sin reentrenar.
Cuándo NO usarlo
- Deep learning / redes neuronales serias. Usa
torch(R),keras3o salta a Python (pytorch,jax).parsniptiene wrappers parakeras/bruleepero no es el flujo natural. - Pipelines orientados a objetos muy complejos con herencia, callbacks y composición programática profunda,
mlr3se siente más natural ahí. - Modelos jerárquicos / bayesianos con estructura específica,
brms,rstanarmostandirecto son mejores herramientas.parsniptienelinear_reg(engine = "stan")pero el control es limitado.
Conceptos clave
recipesdefine el preprocesado como una secuencia de pasos (step_*) que se entrenan (prep) sobre el train set y se aplican (bake) al test. La separaciónprep/bakees lo que evita el data leakage.parsnipunifica la especificación del modelo desacoplándola del engine:rand_forest() |> set_engine("ranger")oset_engine("randomForest")cambia el motor sin tocar el resto del pipeline.workflowsempaqueta receta + modelo en un único objeto. Es lo que se entrena, se resamplea, se tunea y se serializa.rsamplegenera particiones (initial_split,vfold_cv,bootstraps,group_vfold_cvpara datos agrupados,sliding_*para series temporales).tuneorquesta el tuning:tune_grid()(grid search),tune_bayes()(bayesiano sobre un GP) ytune_race_anova()(early stopping de combinaciones inferiores).yardstickson las métricas como verbos tidy (roc_auc,rmse,accuracy,mcc). Funcionan sobre tibbles con columnastruthyestimate.dialsdefine el espacio de hiperparámetros con tipos correctos (mtry(),trees(),tree_depth()). Se finaliza confinalize()cuando hay parámetros que dependen del dataset (p. ej.mtry()necesita conocer el número de predictores).
Patrón mínimo
library(tidymodels)
# 1. Split inicial — el test no se toca hasta el final
set.seed(42)
split <- initial_split(data, prop = 0.8, strata = outcome)
train <- training(split)
test <- testing(split)
# 2. Receta de preprocesado
rec <- recipe(outcome ~ ., data = train) |>
step_impute_median(all_numeric_predictors()) |>
step_dummy(all_nominal_predictors()) |>
step_zv(all_predictors()) |>
step_normalize(all_numeric_predictors())
# 3. Modelo + engine (xgboost como ejemplo)
mod <- boost_tree(
trees = 1000,
tree_depth = tune(),
learn_rate = tune(),
min_n = tune()
) |>
set_engine("xgboost") |>
set_mode("classification")
# 4. Workflow
wf <- workflow() |>
add_recipe(rec) |>
add_model(mod)
# 5. Resampling para tuning
folds <- vfold_cv(train, v = 5, strata = outcome)
# 6. Tuning bayesiano
set.seed(123)
tuned <- tune_bayes(
wf,
resamples = folds,
initial = 10,
iter = 30,
metrics = metric_set(roc_auc, accuracy),
control = control_bayes(no_improve = 10, save_pred = TRUE)
)
# 7. Finalizar y entrenar en todo el train
best <- select_best(tuned, metric = "roc_auc")
final_wf <- finalize_workflow(wf, best)
final_fit <- last_fit(final_wf, split)
# 8. Métricas sobre el test (única vez que se mira)
collect_metrics(final_fit)Trampas habituales
- Preprocesado fuera de la receta = data leakage. El error más común. Cualquier transformación que dependa del target o de estadísticos del dataset (media, mediana, codificación de categóricas) debe ir dentro de
recipes, no en unmutateprevio. set.seeden paralelo. Cuandotune_gridcorre en backend paralelo, una solaset.seed()global no garantiza reproducibilidad entre workers. Pasacontrol = control_grid(parallel_over = "everything")y, parafurrr,furrr_options(seed = TRUE).step_dummyantes destep_zv. Si binarizas y luego algún nivel queda constante (porque solo aparecía en ciertos folds),step_zvlo elimina. Orden importa: dummies → zv → normalize.- Target encoding con leakage.
step_lencode_*(deembed) hace target encoding correctamente dentro de la receta (un valor por fold). Implementarlo manualmente congroup_by(cat) |> mutate(enc = mean(y))antes del split filtra información del test. fit_resamplesvslast_fit.fit_resamplesevalúa con CV (no toca el test).last_fitentrena en todo el train y evalúa en el test, solo una vez al final del proyecto.mtry()requierefinalize(). Hiperparámetros que dependen del dataset (mtry,min_nen algunos engines) deben pasarse porfinalize(param_set, train)antes de generar el grid.
Enlaces
Relacionados en esta página
xgboost,ranger,glmnet, engines naturales del workflow.vipyDALEX, interpretabilidad sobre el objeto entrenado.caretymlr3, alternativas como framework.
xgboost
xgboost es la implementación de referencia de gradient boosting sobre árboles con segunda derivada y regularización L1/L2. Desarrollado por Tianqi Chen et al. (2014), es el engine que ganó la mayoría de competiciones Kaggle entre 2015 y 2019 y sigue siendo baseline de facto para tabular data fuera de deep learning.
En R se usa raramente a pelo: la interfaz idiomática es vía parsnip::boost_tree(engine = "xgboost") dentro de tidymodels. La API nativa (xgb.train, xgb.DMatrix) es funcional pero más rugosa y exige convertir manualmente factores a numérico.
Cuándo usarlo
- Datos tabulares con mezcla de numéricas y categóricas codificadas. Suele ser el primer modelo no lineal a probar tras una baseline lineal.
- Datasets entre 1k y unos pocos millones de filas. Por encima,
lightgbmsuele ser más rápido y con un consumo de memoria menor. - Cuando necesitas un modelo robusto a outliers en las features (los árboles no se inmutan) y a interacciones no lineales.
Cuándo NO usarlo
- Datasets muy pequeños (< 500 filas). El sobreajuste es difícil de controlar y un modelo lineal regularizado (
glmnet) o random forest (ranger) suelen rendir similar con mucha menos varianza. - Datos puramente numéricos lineales. Un GLM con
glmnetes interpretable, rápido y suele ganar. - Series temporales con estructura fuerte. Necesita feature engineering temporal explícito (lags, ventanas). Sin eso, los árboles ignoran la dependencia secuencial.
- Variables de muy alta cardinalidad. Target encoding sin protección filtra información. Usa
step_lencode_mixeddeembedo cambia alightgbmcon su soporte nativo de categóricas.
Conceptos clave
- Booster:
gbtree(default),gblinear,dart. El primero es el habitual.dartañade dropout y suele ayudar en problemas muy ruidosos. - Hiperparámetros que mueven la aguja:
tree_depth(3-8 suele bastar),learn_rate(0.01-0.1 con muchos árboles, 0.1-0.3 con pocos),min_n,mtry/colsample_bytree,sample_size/subsample. nrounds+early_stopping_rounds. Pasar muchos árboles y dejar que el early stopping decida es más eficiente que tunearnroundsdirectamente.- Importance:
gain,weight,cover.gaines la métrica útil por defecto.weightsolo cuenta splits y engaña. - GPU.
tree_method = "hist"(CPU) o"gpu_hist"(GPU CUDA). En tabular grande el speed-up es real.
Patrón mínimo
library(tidymodels)
xgb_spec <- boost_tree(
trees = 1000,
tree_depth = tune(),
min_n = tune(),
learn_rate = tune(),
loss_reduction = tune(),
sample_size = tune(),
mtry = tune()
) |>
set_engine("xgboost", counts = FALSE) |>
set_mode("classification")
# Espacio de hiperparámetros razonable
xgb_grid <- grid_space_filling(
tree_depth(range = c(3L, 8L)),
min_n(range = c(2L, 40L)),
learn_rate(range = c(-3, -1), trans = log10_trans()),
loss_reduction(),
sample_size = sample_prop(c(0.5, 1.0)),
finalize(mtry(), train),
size = 30
)Trampas habituales
- Factores sin codificar.
xgboostexige matriz numérica.parsniplo gestiona automáticamente. Con la API nativa hay quemodel.matrix()a mano y reproducir el mismo orden de columnas en predicción. learn_rateytreesestán acoplados. Bajarlearn_rateexige subirtrees. No los tunees independientemente sin early stopping.- Validación de hiperparámetros con un único split. En datasets medianos, una única validación interna sobreajusta el tuning. Usa 5-fold CV mínimo.
colsample_bytreevsmtry.parsniptraducemtry(número entero de columnas) acolsample_bytree(proporción). Pasarcounts = TRUEal engine cambia la interpretación. Comprueba qué espera tudials::mtry().- Determinismo. Aun con
set.seed,xgboostpuede dar resultados no idénticos en multi-thread. Para reproducibilidad estricta fijanthread = 1o acepta una variabilidad pequeña.
Enlaces
Relacionados en esta página
tidymodels, framework natural sobre el que orquestar xgboost.ranger, alternativa más rápida cuando random forest es suficiente.vip,DALEX, para interpretar el modelo entrenado.
ranger
ranger es la implementación rápida y multi-thread de random forest en R (C++ con paralelismo nativo). Reemplazó a randomForest (Liaw & Wiener) como engine por defecto para random forest cuando importa el tiempo de entrenamiento o trabajas con más de unos pocos miles de árboles.
Autor: Marvin N. Wright (Universidad de Bremen, 2017). Suele ser entre 5x y 50x más rápido que randomForest y soporta variables de alta cardinalidad sin convertirlas a numéricas a la fuerza.
Cuándo usarlo
- Random forest como baseline honesto antes de pasar a gradient boosting. Suele bastar para mucho problema real.
- Datasets con muchas features de bajo coste predictivo individual (p. ej. genómica, NLP con bag-of-words), los árboles aleatorios resisten bien la maldición de la dimensionalidad por sí solos.
- Cuando necesitas estimación de incertidumbre por la vía del quantile regression forest (
quantreg = TRUE).
Cuándo NO usarlo
- Cuando ya sabes que vas a boostear.
xgboostolightgbmcasi siempre baten a random forest en tabular si tunneas. RF es la baseline, no la solución. - Interpretabilidad lineal estricta. Si necesitas coeficientes y p-values, ranger no te los da. Usa
glmneto un GLM. - Datasets muy grandes con memoria limitada. RF guarda todos los árboles. Xgboost/lightgbm son más ligeros en disco.
Conceptos clave
- Sin tuning aterriza decente. Random forest es de los algoritmos más robustos a hiperparámetros razonables.
mtry = sqrt(p)clasificación /p/3regresión es defecto sólido. - Hiperparámetros que sí mueven:
mtry,min.node.size,num.trees(más siempre es mejor o igual. Suele bastar 500-1000). importance = "permutation"es lo correcto para importancia variable."impurity"está sesgado hacia variables con muchos niveles.quantreg = TRUEactiva quantile regression forests (Meinshausen, 2006), te da intervalos de predicción sin asumir distribución del error.probability = TRUEen clasificación entrena un probability forest (Malley et al., 2012) con probabilidades calibradas, no solo voto mayoritario.
Patrón mínimo
library(tidymodels)
rf_spec <- rand_forest(
mtry = tune(),
trees = 1000,
min_n = tune()
) |>
set_engine("ranger", importance = "permutation", num.threads = parallel::detectCores() - 1) |>
set_mode("classification")
rf_grid <- grid_regular(
finalize(mtry(), train),
min_n(range = c(2L, 40L)),
levels = 5
)Trampas habituales
importance = "impurity"por defecto. Sesgo conocido hacia variables de alta cardinalidad. Usa"permutation"o, mejor,"impurity_corrected"(Nembrini et al., 2018) si la permutación es cara.- Factores con muchos niveles. ranger los soporta nativamente con
respect.unordered.factors = "partition", pero el coste crece con2^k. Para >32 niveles, codifica antes (target encoding conembed). - OOB no es CV. El error out-of-bag es útil como diagnóstico interno pero no sustituye a un esquema de resampling externo, especialmente con datos no i.i.d. (grupos, series).
num.threadsglobal. Si paralelizas el tuning por fuera, fijanum.threads = 1dentro de ranger para evitar sobre-suscripción de CPU.
Enlaces
Relacionados en esta página
tidymodels, orquestación natural.xgboost, paso siguiente si RF no es suficiente.randomForestSRC, alternativa con soporte de survival y desbalanceo.
glmnet
glmnet ajusta modelos lineales generalizados con regularización elastic net (combinación convexa de L1/lasso y L2/ridge) por descenso de coordenadas. Es la implementación canónica, Friedman, Hastie y Tibshirani (Stanford), los autores del propio método.
Es la pieza que conviene tener en el cinturón como baseline siempre que haya estructura aproximadamente lineal: regresión lineal, logística, Poisson, Cox (supervivencia), multinomial. Ajusta sobre una rejilla de lambda en un solo paso (regularization path) y elige el óptimo por CV.
Cuándo usarlo
- Baseline lineal regularizada. Antes de probar xgboost, ranger o cualquier no lineal, un
glmnetcon CV te dice cuánto se gana realmente con el modelo complejo. - p ≫ n (más features que observaciones). Lasso hace selección de variables automáticamente. Ridge estabiliza estimaciones colineales.
- Modelos interpretables con coeficientes. Cuando hay que explicar el modelo a un comité regulatorio o clínico.
- Cox regression con regularización (
family = "cox"), uno de los pocos engines en R que lo hace fácil.
Cuándo NO usarlo
- Relaciones fuertemente no lineales o con interacciones complejas sin haberlas codificado explícitamente (splines, polinomios, interacciones). El modelo no las descubre por sí mismo.
- GLM bayesiano o con efectos aleatorios: usa
brms,rstanarm,lme4,glmmTMB. - Variables categóricas con miles de niveles: la matriz dispersa funciona pero el tuning se vuelve sensible.
Conceptos clave
alphacontrola la mezcla L1/L2:alpha = 1lasso puro,alpha = 0ridge puro, intermedio elastic net.lambdala fuerza global.cv.glmnettunealambdapor CV.alphahay que pasarlo por fuera.standardize = TRUEpor defecto. glmnet escala internamente y devuelve los coeficientes en la escala original. No centres ni escales antes a mano salvo que sepas exactamente lo que haces.- Matriz dispersa.
glmnetaceptaMatrix::sparseMatrixy mantiene la dispersión. Esencial cuando trabajas con datos tipo bag-of-words o one-hot de alta cardinalidad. lambda.minvslambda.1se. El primero minimiza el error de CV. El segundo es el modelo más simple dentro de 1 desviación estándar, convención para favorecer parsimonia.relax = TRUEajusta un modelo no regularizado solo sobre las variables seleccionadas por el lasso (técnica de Meinshausen), reduce el sesgo del shrinkage en los coeficientes superviventes.
Patrón mínimo
library(tidymodels)
logit_spec <- logistic_reg(
penalty = tune(),
mixture = tune() # alpha
) |>
set_engine("glmnet") |>
set_mode("classification")
# glmnet exige predictores numéricos; las dummies van en la receta
rec <- recipe(outcome ~ ., data = train) |>
step_novel(all_nominal_predictors()) |>
step_dummy(all_nominal_predictors()) |>
step_zv(all_predictors()) |>
step_normalize(all_numeric_predictors())
# Path en lambda es eficiente: rejilla de penalty + 2-3 valores de mixture
grid <- grid_regular(
penalty(range = c(-5, 0), trans = log10_trans()),
mixture(range = c(0.0, 1.0)),
levels = c(penalty = 30, mixture = 5)
)Trampas habituales
- Centrar/escalar a mano antes de la receta. glmnet ya estandariza internamente. Hacerlo dos veces no rompe nada pero confunde la interpretación de los coeficientes.
alphaylambdaen el mismo grid no es eficiente. glmnet calcula el path completo enlambdacasi gratis para unalphafijo. Tunearmixturecon pocos valores (3-5) ypenaltycon muchos (20-50) aprovecha eso.- Categorías nuevas en test. Si en test aparece un nivel que no estaba en train,
model.matrixfalla.step_novelantes destep_dummylo resuelve. - Coeficientes en escala estandarizada.
coef(glmnet_fit)devuelve coeficientes en la escala original. Si vas a interpretar, asegúrate de no confundirlos con los internos. family = "cox"esperaSurv(time, event)como respuesta, sintaxis distinta a la del resto de familias.
Enlaces
Relacionados en esta página
tidymodels, orquestación.xgboost,ranger, comparativa no lineal.
randomForestSRC
randomForestSRC (RF-SRC) es una implementación de random forest especializada en regresión, clasificación, supervivencia, regresión cuantílica y multivariante. Su valor diferencial frente a ranger está en los modos de survival forest (Ishwaran et al., 2008), competing risks y imbalanced classification con muestreo q-classification.
Autor: Hemant Ishwaran (Universidad de Miami). Es la referencia para random survival forests, ampliamente citado en análisis clínico de tiempo-a-evento.
Cuándo usarlo
- Análisis de supervivencia con random forest (
rfsrcconSurv(time, event) ~ .). Alternativa no paramétrica a Cox cuando la proporcionalidad de riesgos no es razonable. - Riesgos competitivos (
competing.risk = TRUE), dos o más eventos terminales mutuamente excluyentes. - Clasificación con desbalanceo extremo. Soporte de muestreo balanceado por bootstrap dentro del árbol.
- Outcomes multivariantes: varios
ysimultáneos con árboles que splittan sobre la métrica conjunta.
Cuándo NO usarlo
- RF de clasificación o regresión estándar.
rangeres más rápido y suficiente. Reserva RF-SRC para los casos querangerno cubre bien. - Survival simple con proporcionalidad de riesgos razonable: un Cox (
survival::coxphoglmnetconfamily = "cox") es interpretable y suele rendir parecido.
Conceptos clave
rfsrc()es la función principal. Detecta el modo por la respuesta (Surv→ survival. Factor → classification. Numérico → regression).- VIMP (Variable Importance) interno con permutación. Disponible en variantes
permute,random,anti. subsample()para intervalos de confianza sobre la importancia variable, implementa la corrección de Ishwaran & Lu (2019).- OOB error reportado por defecto: útil pero, como en
ranger, no sustituye un esquema de CV externo. - Paralelización via
options(rf.cores = ...)yoptions(mc.cores = ...)para predicciones.
Patrón mínimo
library(randomForestSRC)
library(survival)
# Random survival forest
rsf <- rfsrc(
Surv(time, status) ~ .,
data = veteran,
ntree = 1000,
nodesize = 15,
importance = "permute"
)
# Tabla de importancia
print(rsf$importance)
# Predicción de función de supervivencia para nuevas observaciones
pred <- predict(rsf, newdata = veteran[1:5, ])
pred$survival[1:5, 1:5]Trampas habituales
- Tiempo de entrenamiento. RF-SRC es más lento que ranger por su mayor versatilidad. Para clasificación pura no merece la pena.
- VIMP en survival se interpreta como cambio en concordance index tras permutar, no es lo mismo que VIMP en regresión.
block.sizeybootstrap = "by.user"para esquemas de remuestreo no estándar (grupos, clusters). Por defecto se usa bootstrap clásico, que con datos agrupados infla la confianza.na.action = "na.impute"imputa con árboles internos, cómodo pero opaco. Para análisis serios, imputa antes conmiceomissForesty documenta.
Enlaces
Relacionados en esta página
ranger, alternativa más rápida para RF estándar.glmnet, alternativa lineal para survival (Cox regularizado).
DALEX
DALEX (Descriptive mAchine Learning EXplanations) es un framework agnóstico al modelo para interpretabilidad: variable importance, partial dependence, Break Down / SHAP, Ceteris Paribus y diagnósticos de residuos. Funciona con cualquier modelo que tenga predict(): tidymodels, caret, mlr3, randomForest, xgboost, ranger, modelos lm/glm, keras…
Mantenido por el grupo MI2.AI (Universidad Tecnológica de Varsovia), dirigido por Przemysław Biecek. Pareja natural con su libro Explanatory Model Analysis (gratuito online), que es además la mejor introducción conceptual a interpretabilidad de modelos.
Cuándo usarlo
- Auditar un modelo black-box (gradient boosting, random forest, red neuronal) en producción o pre-deploy.
- Comparar varios modelos en términos de importancia variable y comportamiento sobre features clave.
- Generar explicaciones locales (por observación) para casos clínicos / regulatorios.
Cuándo NO usarlo
- Si solo necesitas importancia variable rápida sobre un modelo tidymodels,
vipes más ligero y se integra directo en el workflow. - SHAP exacto sobre xgboost grande: usa
treeshapoSHAPforxgboost, que aprovechan la estructura de árbol y son órdenes de magnitud más rápidos que las aproximaciones model-agnostic de DALEX (predict_partscontype = "shap").
Conceptos clave
explain()envuelve el modelo en un objetoexplainerconpredict_function, datos de validación, respuesta. Es el constructor central, todo lo demás opera sobre el explainer.model_parts()importance global por permutación.model_profile()partial dependence (efecto marginal medio sobre una variable). Varianteaccumulatedpara ALE (más robusto a correlación entre features).predict_parts()explicación local: Break Down (descomposición secuencial) o SHAP (Shapley values, model-agnostic, costoso).predict_profile()Ceteris Paribus, cómo cambia la predicción de una observación al variar una sola feature.model_diagnostics()residuos, funnel plot, distribución de errores por subgrupos.
Patrón mínimo
library(DALEX)
library(DALEXtra) # bridge directo con tidymodels, mlr3, caret
# Suponiendo `final_fit` un workflow tidymodels entrenado
explainer <- explain_tidymodels(
final_fit,
data = test |> dplyr::select(-outcome),
y = as.integer(test$outcome == "yes"),
label = "xgboost_v1"
)
# Importancia global por permutación
vi <- model_parts(explainer, loss_function = loss_one_minus_auc, B = 25)
plot(vi)
# Partial dependence sobre una feature
pdp <- model_profile(explainer, variables = "age")
plot(pdp)
# Explicación local de una observación
bd <- predict_parts(explainer, new_observation = test[1, ], type = "break_down")
plot(bd)Trampas habituales
- El
predict_functionpor defecto puede no ser el adecuado. Para clasificación binaria, DALEX espera probabilidades de la clase positiva (no clase predicha). Contidymodels,explain_tidymodelslo configura bien. Con engines exóticos, fíjalo explícitamente. - PDP con features correlacionadas engaña. Partial dependence asume independencia entre la feature en estudio y el resto. Cuando hay colinealidad fuerte, ALE (
type = "accumulated") es más fiel. - SHAP model-agnostic es lento. Si tu modelo es xgboost o lightgbm, usa
treeshap, calcula SHAP exacto en tiempo polinómico aprovechando la estructura de árbol. - Compara modelos sobre el mismo
datayy. Crear variosexplainercon datos distintos invalida cualquier comparación entremodel_parts.
Enlaces
Relacionados en esta página
vip, alternativa más ligera para importancia variable.tidymodels, integración directa víaDALEXtra::explain_tidymodels.
vip
vip (Variable Importance Plots) es un paquete ligero y enfocado: calcula y plotea importancia variable para casi cualquier modelo de R. Es la opción por defecto dentro de tidymodels cuando solo necesitas un ranking de features sin la maquinaria completa de DALEX.
Autor: Brandon M. Greenwell. Forma parte del tidymodels universe de facto: la documentación de tidymodels lo usa como herramienta estándar de interpretabilidad.
Cuándo usarlo
- Importancia variable rápida sobre un workflow
tidymodelsentrenado. - Comparar modelos sobre el mismo dataset en términos de qué features pesan más.
- Generar el plot estándar para informes y dashboards,
vip(model)devuelve un objetoggplotdirectamente customizable.
Cuándo NO usarlo
- Si necesitas interpretabilidad local (por observación), partial dependence, ALE, SHAP o diagnósticos completos, DALEX.
- Si trabajas con modelos no estándar fuera del soporte de
vipy no quieres montar elpredict_functiona mano.
Conceptos clave
vi()devuelve la tabla.vip()el plot.- Métodos:
method = "model"(importancia nativa del modelo, p. ej. gain de xgboost),"permute"(permutación model-agnostic),"firm"(basada en partial dependence),"shap"(Shapley values, requierefastshap). - Compatible con tidymodels: pasa el modelo entrenado con
extract_fit_parsnip(workflow). pred_wrapperes el argumento clave cuando usasmethod = "permute"con un modelo cuyopredictno devuelve probabilidades por defecto.
Patrón mínimo
library(vip)
library(tidymodels)
# Importancia nativa del engine (e.g. xgboost gain)
final_fit |>
extract_fit_parsnip() |>
vip(num_features = 20)
# Importancia por permutación (model-agnostic)
fit <- extract_fit_parsnip(final_fit)
vi(
fit$fit,
method = "permute",
train = test,
target = "outcome",
metric = "auc",
pred_wrapper = function(object, newdata) {
predict(object, newdata, type = "prob")[, "yes"]
},
nsim = 25
)Trampas habituales
method = "model"no está disponible para todos los engines. xgboost, ranger, glmnet sí. Modelos exóticos no. Cae a"permute"cuando falte.- Importancia nativa de xgboost (
gain) está sesgada hacia features con muchos splits. Permutación es más honesta cuando el ranking es la decisión final. vip(fit)vsvip(workflow). Pasa elfitextraído (extract_fit_parsnip), no el workflow entero, algunas variantes devipno entienden el wrapper.
Enlaces
Relacionados en esta página
DALEX, alternativa completa de interpretabilidad model-agnostic.tidymodels, integración directa.
caret
caret (Classification And REgression Training) fue durante una década el framework de referencia para modelado supervisado en R. Max Kuhn lo lanzó en 2007 y consolidó una API uniforme sobre 200+ engines, con resampling, preprocesado y tuning integrados.
En 2026 está en modo mantenimiento. El propio Kuhn redirigió su desarrollo hacia tidymodels a partir de 2018. Sigue funcionando, sigue siendo correcto, y sigue siendo legítimo encontrarlo en código heredado. Para proyectos nuevos no hay razón técnica para empezarlos aquí.
Cuándo usarlo
- Mantener o extender código heredado que ya está en
caret. Reescribirlo atidymodelssolo si hay un beneficio claro (no solo modernidad). - Reproducir resultados de papers o tutoriales antiguos que usan
caret. - Casos donde el wrapper específico de
caretpara un engine raro aún no tiene equivalente enparsnip(cada vez menos comunes).
Cuándo NO usarlo
- Proyectos nuevos. Use
tidymodels. La API es más limpia, está activamente desarrollada y tiene mejor integración con el resto del tidyverse. - Pipelines complejos con preprocesado variable por fold:
recipeslo hace mucho más limpio que el preprocesado integrado decaret.
Conceptos clave
train()es la función pivote: combina preprocesado, resampling, tuning y fit final.trainControl()define el esquema de resampling (method = "cv","repeatedcv","boot","LGOCV").expand.grid()para el grid de hiperparámetros, manual, no haydialsequivalente.preProcessacepta una lista de strings (c("center", "scale", "knnImpute")), comparado conrecipeses menos componible.varImp()importancia variable model-agnostic.
Patrón mínimo
library(caret)
ctrl <- trainControl(
method = "repeatedcv",
number = 5,
repeats = 3,
classProbs = TRUE,
summaryFunction = twoClassSummary,
savePredictions = "final"
)
grid <- expand.grid(
mtry = c(2, 4, 8),
splitrule = "gini",
min.node.size = c(1, 5, 10)
)
set.seed(42)
fit <- train(
outcome ~ .,
data = train,
method = "ranger",
metric = "ROC",
trControl = ctrl,
tuneGrid = grid,
preProcess = c("center", "scale")
)
predict(fit, newdata = test, type = "prob")Trampas habituales
set.seedantes detrainno basta en paralelo.caretcondoParallelnecesitaseedsexplícito dentro detrainControl(seeds = ...). Documentado pero fácil de olvidar.preProcessse aplica al dataset entero antes del split de CV en algunas versiones, verifica con la documentación de tu versión específica para evitar leakage sutil.- El nombre del engine cambia entre
carety el paquete subyacente.method = "ranger"no es el mismo grid queparsnip::rand_forest(engine = "ranger"),caretañadesplitruleque parsnip no expone directamente. - Migrar a tidymodels no es trivial. Si el código
caretexistente funciona y está validado, reescribirlo introduce riesgo. Migra cuando haya beneficio concreto, no por estética.
Enlaces
Relacionados en esta página
tidymodels, sucesor moderno y mantenido.mlr3, alternativa contemporánea con estilo OO.
mlr3
mlr3 es la alternativa a tidymodels con filosofía orientada a objetos (basada en R6). Sucesor de mlr (2016), reescrito desde cero por el equipo de la LMU Munich (Bernd Bischl et al.). Estilo de API más cercano a scikit-learn que al tidyverse.
Ecosistema modular: mlr3 (core), mlr3learners (engines), mlr3pipelines (preprocesado componible tipo grafo), mlr3tuning, mlr3mbo (tuning bayesiano basado en paradox), mlr3viz (visualización). Es elección legítima, no inferior, cuando el estilo OO encaja mejor con el equipo o el dominio.
Cuándo usarlo
- Equipos cómodos con OO (Python
scikit-learn, Java) que prefieren llamadastask$train(learner)sobre piping verboso. - Pipelines de preprocesado muy componibles,
mlr3pipelinespermite construir grafos DAG de operaciones con branching condicional, algo más expresivo querecipes. - Tuning bayesiano sofisticado con
mlr3mboyparadox, control fino del acquisition function, surrogates alternativos, multi-objetivo. - AutoML con
mlr3automlo el meta-paquetemlr3verse.
Cuándo NO usarlo
- Si el equipo ya está en tidyverse. La curva de adopción de OO + R6 no es trivial. Tidymodels rinde igual y se integra mejor con el resto del flujo.
- Si solo necesitas un pipeline estándar. El extra de potencia de
mlr3pipelinesno compensa la verbosidad cuando el problema es directo.
Conceptos clave
Task= dataset + target + metadatos.TaskClassif,TaskRegr,TaskSurvson las subclases.Learner= algoritmo.lrn("classif.ranger"),lrn("regr.xgboost"). Cada learner tieneparam_set(hiperparámetros tipados).Resampling= esquema de validación.rsmp("cv", folds = 5),rsmp("holdout"),rsmp("loo").Measure= métrica.msr("classif.auc"),msr("regr.rmse").Pipeline(mlr3pipelines) = grafo dePipeOps, preprocesado, modelo, ensembling, todo serializable como unGraphLearner.paradoxdefine el espacio de hiperparámetros con tipos (ParamInt,ParamDbl,ParamFct) y dependencias.
Patrón mínimo
library(mlr3verse)
library(mlr3learners)
# 1. Task
task <- TaskClassif$new("my_task", backend = train, target = "outcome")
# 2. Learner
learner <- lrn("classif.xgboost",
predict_type = "prob",
nrounds = to_tune(p_int(100, 2000)),
max_depth = to_tune(p_int(3, 8)),
eta = to_tune(p_dbl(1e-3, 0.3, logscale = TRUE))
)
# 3. Resampling + measure
resampling <- rsmp("cv", folds = 5)
measure <- msr("classif.auc")
# 4. Tuning bayesiano
instance <- tune(
tuner = tnr("mbo"),
task = task,
learner = learner,
resampling = resampling,
measure = measure,
term_evals = 40
)
# 5. Mejor configuración
instance$result_learner_param_valsTrampas habituales
R6es por referencia. Modificar unlearnerlo cambia en todos los sitios que lo referencian.learner$clone()antes de modificar es el patrón seguro.set.seedglobal no basta:mlr3gestiona semillas dentro delResamplingy del tuner. Para reproducibilidad estricta, fija el seed del resampling explícitamente.- API distinta a tidymodels. Migrar código entre frameworks rara vez es directo. Planifica la elección al principio del proyecto.
- Documentación dispersa entre subpaquetes.
mlr3book(online) es la referencia central. Navegar las viñetas individuales sin él es frustrante.
Enlaces
Relacionados en esta página
tidymodels, alternativa con estilo tidyverse y la elección por defecto en R hoy.caret, antecesor histórico del modelado supervisado en R.