Las librerías de Tidymodels fueron realizadas con el objetivo de incorporarse al flujo de trabajo del tidyverse. A diferencia de versiones previas, como Caret, son modulares, y cada librería tiene un objetivo acotado.
Dentro del paquete tidymodels se encuentran otros paquetes, cómo:
rsample
- Diferentes tipos de remuestreorecipes
- Organización de las transformaciones para el preprocesamiento de la información de forma tal que sea lo más reproducible posible. En este sentido, se acerca a los pipelines de sk-learnparnip
- Un interfaz común para crear modelos, independientemente de la implementación (paquete)yardstick
- Metricas de performance del modelo.library(tidymodels)
Veamos un ejemplo con iris
con rsample::initial_split()
creamos una división entre train y test
iris_split <- initial_split(iris, prop = 0.6)
iris_split
<90/60/150>
cuando llamamos al objeto iris_split
nos muestra la cantidad de elementos en train/test/total
Si queremos recuprar el dataset de entrenamiento, utilizamos el comando training()
iris_split %>%
training()
Con recipes::recipe()
indicamos que comenzamos un pipeline de preprocesamiento, indicando cual es la variable a predecir, con Species ~.
.
El objetivo de realizar el preprocesamiento de esta manera es que para muchas etapas del preprocesamiento, como puede ser el cálculo de la media para la imputación, sólo podemos utilizar la información del set de entrenamiento para evitar el information leaking. Por ello, si construimos un pipeline donde se calcula todo sobre entrenamiento, después es más sencillo reutilizarlo en test, sin cometer errores de este tipo.
utilizando recipes
entrenamos el preprocesamiento.
recipe()
Empieza un nuevo set de transformaciones para ser aplicadas.
prep()
Es el último paso, que indica que hay que ejecutar toods los pasos anteriores sobre los datos.
Cada tranformación es un step(paso). Las funciones son tipos específicos de pasos. En este caso utilizamos:
step_corr()
Elimina las variables que tienen una correlación alta con otras variablesstep_center()
Centra los datos para que tengan media cerostep_scale()
Normaliza los datos para que tengan desvío estandar de 1.También hay una serie de funciones para seleccionar las variables sobre las que se va a operar:
all_predictors
y all_outcomes
ayudan a espcificar sobre las variables predictivas y predictoras.update_role
y luego seleccionarlas vía has_role
all_numeric
, all_nominal
y has_type
permite elegir las variables según su tipo.iris_recipe <- training(iris_split) %>%
recipe(Species ~.) %>%
step_corr(all_predictors()) %>%
step_center(all_predictors(), -all_outcomes()) %>%
step_scale(all_predictors(), -all_outcomes()) %>%
prep()
iris_recipe
Data Recipe
Inputs:
Training data contained 90 data points and no missing data.
Operations:
Correlation filter removed Petal.Length [trained]
Centering for Sepal.Length, Sepal.Width, Petal.Width [trained]
Scaling for Sepal.Length, Sepal.Width, Petal.Width [trained]
en iris_recipe
tenemos la receta del preprocesamiento, pero aún no se aplicó sobre ningún set de datos.
Dado que la receta fue construida con los datos de entrenamiento, este dataset ya se encuentra implícito en iris_recipe
. Para extraerlo, ya preprocesado, utilizamos la función juice
directamente sobre la receta.
iris_training <- juice(iris_recipe)
iris_training
Con la función bake()
podemos aplicar la receta que preparamos antes para los datos de test. Para ello el primer objeto que pasamos es la receta y como parámetro de bake pasamos el dataset de test (recuperado con la función testing
de rsample)
iris_testing <- iris_recipe %>%
bake(testing(iris_split))
iris_testing
En R hay muchos paquetes que implementan el mismo tipo de modelo. Normalmente cada implementación define los parámetros del modelo de forma distinta. Por ejemplo, para el modelo Random Forest, la librería ranger
define el número de árboles como num.trees, mientras que la librería randomForest
lo nombra ntree.
Al igual que la vieja librería caret, tidymodels remplaza la interfaz, pero no el paquete. Es decir, no es una nueva implementación de los modelos, sino una interfaz que unifica las diferentes sintaxis.
rand_forest
incializa el modelo de Random Forest, definiendo sus parámetros.set_engine
sirve para especificar qué implementación utilizar.fit
iris_ranger <- rand_forest(trees = 100, mode = "classification") %>%
set_engine("ranger") %>%
fit(Species ~ ., data = iris_training)
iris_ranger
parsnip model object
Ranger result
Call:
ranger::ranger(formula = formula, data = data, num.trees = ~100, num.threads = 1, verbose = FALSE, seed = sample.int(10^5, 1), probability = TRUE)
Type: Probability estimation
Number of trees: 100
Sample size: 90
Number of independent variables: 3
Mtry: 1
Target node size: 10
Variable importance mode: none
Splitrule: gini
OOB prediction error (Brier s.): 0.06436353
iris_rf <- rand_forest(trees = 100, mode = "classification") %>%
set_engine("randomForest") %>%
fit(Species ~ ., data = iris_training)
iris_rf
parsnip model object
Call:
randomForest(x = as.data.frame(x), y = y, ntree = ~100)
Type of random forest: classification
Number of trees: 100
No. of variables tried at each split: 1
OOB estimate of error rate: 7.78%
Confusion matrix:
setosa versicolor virginica class.error
setosa 34 0 0 0.0000000
versicolor 0 23 4 0.1481481
virginica 0 3 26 0.1034483
iris_noengine <- rand_forest(trees = 100, mode = "classification") %>%
fit(Species ~ ., data = iris_training)
Engine set to `ranger`
Para predecir los datos de test utilizamos la conocida función predict
. Sin embargo, cuando utilizamos esta función sobre un objeto de la librería parsnip
devuelve un tibble
predict(iris_ranger, iris_testing)
Para agregar las predicciones a los datos de test podemos utilizar la función bind_cols
iris_ranger %>%
predict(iris_testing) %>%
bind_cols(iris_testing)
Con la función yardstick::metrics()
podemos medir la performance del modelo. Elige automáticamente las métricas apropiadas dado el tipo de modelo. Necesitmaos aclarar cuales son los datos predichos,estimate y reales, truth.
iris_ranger %>%
predict(iris_testing) %>%
bind_cols(iris_testing) %>%
metrics(truth = Species, estimate = .pred_class)
iris_rf %>%
predict(iris_testing) %>%
bind_cols(iris_testing) %>%
metrics(truth = Species, estimate = .pred_class)
Si en el predict elegimos type="prob"
iris_ranger %>%
predict(iris_testing, type = "prob")
iris_probs <- iris_ranger %>%
predict(iris_testing, type = "prob") %>%
bind_cols(iris_testing)
iris_probs
Con el tibble iris_probs es fácil calcular métodos de curva, como la ROC, utilizando la función roc_curve
iris_probs%>%
roc_curve(Species, .pred_setosa:.pred_virginica)
finalmente, con la función autoplot
podemos graficar las curvas. Dado que el resultado es un objeto ggplot, podemos hacer otras modificaciones posteriores.
iris_probs%>%
roc_curve(Species, .pred_setosa:.pred_virginica) %>%
autoplot()+
ggthemes::theme_fivethirtyeight()+
labs(title = 'Curvas ROC')
Si queremos un tibble con la clase predicha y la probabilidad de cada clase, podemos utilizar bind_cols
para juntar los resultados de ambos tipos
predict(iris_ranger, iris_testing, type = "prob") %>%
bind_cols(predict(iris_ranger, iris_testing)) %>%
bind_cols(select(iris_testing, Species))
si utilizamos metrics
sobre estos resultados, obtenemos además de accuracy y kap, el log loss y el area debajo de la curva ROC.
predict(iris_ranger, iris_testing, type = "prob") %>%
bind_cols(predict(iris_ranger, iris_testing)) %>%
bind_cols(select(iris_testing, Species)) %>%
metrics(truth=Species, .pred_setosa:.pred_virginica, estimate = .pred_class)
basado en las notas de Edgar Ruiz https://rviews.rstudio.com/2019/06/19/a-gentle-intro-to-tidymodels/↩