Utilizaremos el conjunto de datos iris como ejemplo. Sus datos ya se importaron y están lo suficientemente ordenados para pasar directamente al modelado.
Cargue solo la biblioteca tidymodels. Además de cargar sus paquetes principales de modelado, tidymodels también carga convenientemente algunos paquetes tidyverse, incluidos dplyr y ggplot2. A lo largo de este ejercicio, utilizaremos algunas funciones de esos paquetes, pero no tenemos que cargarlas explícitamente en nuestra sesión R.
library(tidymodels)
## Registered S3 method overwritten by 'xts':
## method from
## as.zoo.xts zoo
## -- Attaching packages ---------------------------------------------------------------------------------- tidymodels 0.0.3 --
## v broom 0.5.2 v purrr 0.3.3
## v dials 0.0.3 v recipes 0.1.7
## v dplyr 0.8.3 v rsample 0.0.5
## v ggplot2 3.2.1 v tibble 2.1.3
## v infer 0.5.1 v yardstick 0.0.4
## v parsnip 0.0.4
## -- Conflicts ------------------------------------------------------------------------------------- tidymodels_conflicts() --
## x purrr::discard() masks scales::discard()
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
## x ggplot2::margin() masks dials::margin()
## x dials::offset() masks stats::offset()
## x recipes::step() masks stats::step()
## x recipes::yj_trans() masks scales::yj_trans()
Este paso se centra en hacer que los datos sean adecuados para el modelado mediante el uso de transformaciones de datos. Todas las transformaciones se pueden lograr con dplyr u otros paquetes de tidyverse. Considere el uso de paquetes de tidymodels cuando el desarrollo del modelo sea más pesado y complejo.
La función initial_split() está especialmente diseñada para separar el conjunto de datos en un conjunto de entrenamiento y uno de prueba. Por defecto, contiene 3/4 de los datos para el entrenamiento y el resto para las pruebas. Eso se puede cambiar pasando el argumento prop. Esta función genera un objeto rplit, no un marco de datos. El resultado impreso muestra el recuento de filas para prueba, para entrenamiento y el total.
iris_split <- initial_split(iris, prop = 0.6)
iris_split
## <90/60/150>
Para acceder a las observaciones reservadas para el entrenamiento, use la función training(). Del mismo modo, use testing() para acceder a los datos de prueba.
iris_split %>%
training() %>%
glimpse()
## Observations: 90
## Variables: 5
## $ Sepal.Length <dbl> 5.1, 4.7, 5.0, 5.4, 5.0, 5.4, 4.8, 5.8, 5.4, 5.1, 4.6,...
## $ Sepal.Width <dbl> 3.5, 3.2, 3.6, 3.9, 3.4, 3.7, 3.0, 4.0, 3.9, 3.7, 3.6,...
## $ Petal.Length <dbl> 1.4, 1.3, 1.4, 1.7, 1.5, 1.5, 1.4, 1.2, 1.3, 1.5, 1.0,...
## $ Petal.Width <dbl> 0.2, 0.2, 0.2, 0.4, 0.2, 0.2, 0.1, 0.2, 0.4, 0.4, 0.2,...
## $ Species <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa...
iris_split %>%
testing() %>%
glimpse()
## Observations: 60
## Variables: 5
## $ Sepal.Length <dbl> 4.9, 4.6, 4.6, 4.4, 4.9, 4.8, 4.3, 5.7, 5.1, 5.7, 5.1,...
## $ Sepal.Width <dbl> 3.0, 3.1, 3.4, 2.9, 3.1, 3.4, 3.0, 4.4, 3.5, 3.8, 3.8,...
## $ Petal.Length <dbl> 1.4, 1.5, 1.4, 1.4, 1.5, 1.6, 1.1, 1.5, 1.4, 1.7, 1.5,...
## $ Petal.Width <dbl> 0.2, 0.2, 0.3, 0.2, 0.1, 0.2, 0.1, 0.4, 0.3, 0.3, 0.3,...
## $ Species <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa...
En tidymodels, el paquete recipes proporciona una interfaz que se especializa en el procesamiento previo de datos. Dentro del paquete, las funciones que inician o ejecutan las transformaciones de datos se nombran después de las acciones de cocción. Eso hace que la interfaz sea más fácil de usar. Por ejemplo:
Cada transformación de datos es un paso. Las funciones corresponden a tipos específicos de pasos, cada uno de los cuales tiene un prefijo step_. Hay varias funciones step_; En este ejemplo, utilizaremos tres de ellas:
Otra característica interesante es que el paso se puede aplicar a una variable específica, grupos de variables o todas las variables. Las funciones all_outocomes() y all_predictors() proporcionan una forma muy conveniente de especificar grupos de variables. Por ejemplo, si queremos que step_corr() solo analice las variables predictoras, usamos step_corr(all_predictors()). Esta capacidad nos ahorra tener que enumerar cada variable. En el siguiente ejemplo, juntaremos recipe(), prep() y las funciones de paso para crear un objeto recipe. La función training() se utiliza para extraer ese conjunto de datos del conjunto de datos de muestra dividida previamente creada.
iris_recipe <- training(iris_split) %>%
recipe(Species ~.) %>%
step_corr(all_predictors()) %>%
step_center(all_predictors(), -all_outcomes()) %>%
step_scale(all_predictors(), -all_outcomes()) %>%
prep()
Si llamamos al objeto iris_recipe, imprimirá detalles sobre la receta. La sección Operations describe lo que se hizo con los datos. Una de las entradas del ejemplo explica que el paso de correlación eliminó la variable Petal.Length.
iris_recipe
## Data Recipe
##
## Inputs:
##
## role #variables
## outcome 1
## predictor 4
##
## 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]
Los datos de prueba ahora se pueden transformar utilizando exactamente los mismos pasos, pesos y categorizaciones utilizados para preprocesar los datos de entrenamiento. Para hacer esto, se usa otra función con un término de cocción: bake(). Observe que la función testing() se usa para extraer el conjunto de datos apropiado.
iris_testing <- iris_recipe %>%
bake(testing(iris_split))
glimpse(iris_testing)
## Observations: 60
## Variables: 4
## $ Sepal.Length <dbl> -1.2013903, -1.5712595, -1.5712595, -1.8178391, -1.201...
## $ Sepal.Width <dbl> -0.17284526, 0.05592053, 0.74221789, -0.40161105, 0.05...
## $ Petal.Width <dbl> -1.3071764, -1.3071764, -1.1743932, -1.3071764, -1.439...
## $ Species <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa...
Realizar la misma operación sobre los datos de entrenamiento es redundante, porque esos datos ya se han preparado. Para cargar los datos de entrenamiento preparados en una variable, usamos juice(). Extraerá los datos del objeto iris_recipe.
iris_training <- juice(iris_recipe)
glimpse(iris_training)
## Observations: 90
## Variables: 4
## $ Sepal.Length <dbl> -0.95481073, -1.44796978, -1.07810049, -0.58494144, -1...
## $ Sepal.Width <dbl> 0.97098368, 0.28468632, 1.19974947, 1.88604684, 0.7422...
## $ Petal.Width <dbl> -1.307176, -1.307176, -1.307176, -1.041610, -1.307176,...
## $ Species <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa...
En R, hay varios paquetes que se ajustan al mismo tipo de modelo. Es común que cada paquete proporcione una interfaz única. En otras palabras, cosas como un argumento para el mismo atributo del modelo se definen de manera diferente para cada paquete. Por ejemplo, los paquetes ranger y randomForest se ajustan a los modelos de Random Forests. En la función ranger(), para definir el número de árboles usamos num.trees. En randomForest, ese argumento se llama ntree. No es fácil cambiar entre paquetes para ejecutar el mismo modelo. En lugar de reemplazar el paquete de modelado, tidymodels reemplaza la interfaz. Mejor dicho, tidymodels proporciona un conjunto único de funciones y argumentos para definir un modelo. Luego ajusta el modelo contra el paquete de modelado solicitado. En el siguiente ejemplo, la función rand_forest() se usa para inicializar un modelo de Random Forest. Para definir el número de árboles, se utiliza el argumento de los árboles. Para usar la versión ranger de Random Forest, se usa la función set_engine(). Finalmente, para ejecutar el modelo, se utiliza la función fit(). Los argumentos esperados son la fórmula y los datos. Observe que el modelo se ejecuta sobre los datos entrenados con juice.
iris_ranger <- rand_forest(trees = 100, mode = "classification") %>%
set_engine("ranger") %>%
fit(Species ~ ., data = iris_training)
La recompensa es que si ahora queremos ejecutar el mismo modelo contra randomForest, simplemente cambiamos el valor en set_engine() a randomForest.
iris_rf <- rand_forest(trees = 100, mode = "classification") %>%
set_engine("randomForest") %>%
fit(Species ~ ., data = iris_training)
También vale la pena mencionar que el modelo no está definido en una sola función grande con muchos argumentos. La definición del modelo se separa en funciones más pequeñas como fit() y set_engine(). Esto permite una interfaz más flexible y más fácil de aprender.
En lugar de un vector, la función predict(), ejecutada contra un modelo parsnip, devuelve una tibble. Por defecto, la variable de predicción se llama .pred_class. En el ejemplo, observe que se utilizan los datos de prueba obtenidos de bake.
predict(iris_ranger, iris_testing)
## # A tibble: 60 x 1
## .pred_class
## <fct>
## 1 setosa
## 2 setosa
## 3 setosa
## 4 setosa
## 5 setosa
## 6 setosa
## 7 setosa
## 8 setosa
## 9 setosa
## 10 setosa
## # ... with 50 more rows
Es muy fácil agregar las predicciones a los datos de prueba utilizando la función bind_cols() de dplyr.
iris_ranger %>%
predict(iris_testing) %>%
bind_cols(iris_testing) %>%
glimpse()
## Observations: 60
## Variables: 5
## $ .pred_class <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa...
## $ Sepal.Length <dbl> -1.2013903, -1.5712595, -1.5712595, -1.8178391, -1.201...
## $ Sepal.Width <dbl> -0.17284526, 0.05592053, 0.74221789, -0.40161105, 0.05...
## $ Petal.Width <dbl> -1.3071764, -1.3071764, -1.1743932, -1.3071764, -1.439...
## $ Species <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa...
Use la función metrics() para medir el rendimiento del modelo. Escogerá automáticamente las métricas apropiadas para un tipo de modelo dado. La función espera una tibble que contenga los resultados reales (observados) y lo que el modelo predijo (estimaciones).
iris_ranger %>%
predict(iris_testing) %>%
bind_cols(iris_testing) %>%
metrics(truth = Species, estimate = .pred_class)
## # A tibble: 2 x 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy multiclass 0.933
## 2 kap multiclass 0.900
Debido a la coherencia de la nueva interfaz, medir las mismas métricas contra el modelo randomForest es tan fácil como reemplazar la variable del modelo en la parte superior del código.
iris_rf %>%
predict(iris_testing) %>%
bind_cols(iris_testing) %>%
metrics(truth = Species, estimate = .pred_class)
## # A tibble: 2 x 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy multiclass 0.933
## 2 kap multiclass 0.900
Es fácil obtener la probabilidad para cada valor pronosticado posible estableciendo el argumento type en prob. Eso devolverá una tibble con tantas variables como posibles valores pronosticados. Su nombre predeterminado será el nombre del valor original, con el prefijo .pred_.
iris_ranger %>%
predict(iris_testing, type = "prob") %>%
glimpse()
## Observations: 60
## Variables: 3
## $ .pred_setosa <dbl> 0.924535714, 0.971412698, 0.997662338, 0.885785714...
## $ .pred_versicolor <dbl> 0.075464286, 0.028587302, 0.002337662, 0.106436508...
## $ .pred_virginica <dbl> 0.000000000, 0.000000000, 0.000000000, 0.007777778...
Nuevamente, use bind_cols() para agregar las predicciones al conjunto de datos de prueba.
iris_probs <- iris_ranger %>%
predict(iris_testing, type = "prob") %>%
bind_cols(iris_testing)
glimpse(iris_probs)
## Observations: 60
## Variables: 7
## $ .pred_setosa <dbl> 0.924535714, 0.971412698, 0.997662338, 0.885785714...
## $ .pred_versicolor <dbl> 0.075464286, 0.028587302, 0.002337662, 0.106436508...
## $ .pred_virginica <dbl> 0.000000000, 0.000000000, 0.000000000, 0.007777778...
## $ Sepal.Length <dbl> -1.2013903, -1.5712595, -1.5712595, -1.8178391, -1...
## $ Sepal.Width <dbl> -0.17284526, 0.05592053, 0.74221789, -0.40161105, ...
## $ Petal.Width <dbl> -1.3071764, -1.3071764, -1.1743932, -1.3071764, -1...
## $ Species <fct> setosa, setosa, setosa, setosa, setosa, setosa, se...
Ahora que todo está en una tibble, es fácil calcular métodos de curva. En este caso estamos usando gain_curve().
iris_probs%>%
gain_curve(Species, .pred_setosa:.pred_virginica) %>%
glimpse()
## Observations: 125
## Variables: 5
## $ .level <chr> "setosa", "setosa", "setosa", "setosa", "setosa", "...
## $ .n <dbl> 0, 2, 5, 7, 9, 10, 12, 13, 14, 16, 17, 18, 19, 20, ...
## $ .n_events <dbl> 0, 2, 5, 7, 9, 10, 12, 13, 14, 16, 17, 18, 19, 20, ...
## $ .percent_tested <dbl> 0.000000, 3.333333, 8.333333, 11.666667, 15.000000,...
## $ .percent_found <dbl> 0, 10, 25, 35, 45, 50, 60, 65, 70, 80, 85, 90, 95, ...
Los métodos de curva incluyen una función autoplot() que crea fácilmente una visualización ggplot2.
iris_probs%>%
gain_curve(Species, .pred_setosa:.pred_virginica) %>%
autoplot()
Este es un ejemplo de roc_curve(). Nuevamente, debido a la consistencia de la interfaz, solo el nombre de la función necesita ser modificado; Incluso los valores del argumento siguen siendo los mismos.
iris_probs%>%
roc_curve(Species, .pred_setosa:.pred_virginica) %>%
autoplot()
Para medir el valor pronosticado único combinado y la probabilidad de cada valor posible, combine los dos modos de predicción (con y sin prob type). En este ejemplo, usar dplyr’s select() hace que la tibble resultante sea más fácil de leer.
predict(iris_ranger, iris_testing, type = "prob") %>%
bind_cols(predict(iris_ranger, iris_testing)) %>%
bind_cols(select(iris_testing, Species)) %>%
glimpse()
## Observations: 60
## Variables: 5
## $ .pred_setosa <dbl> 0.924535714, 0.971412698, 0.997662338, 0.885785714...
## $ .pred_versicolor <dbl> 0.075464286, 0.028587302, 0.002337662, 0.106436508...
## $ .pred_virginica <dbl> 0.000000000, 0.000000000, 0.000000000, 0.007777778...
## $ .pred_class <fct> setosa, setosa, setosa, setosa, setosa, setosa, se...
## $ Species <fct> setosa, setosa, setosa, setosa, setosa, setosa, se...
Canalice la tabla resultante en metrics(). En este caso, especifique .pred_class como la estimación.
predict(iris_ranger, iris_testing, type = "prob") %>%
bind_cols(predict(iris_ranger, iris_testing)) %>%
bind_cols(select(iris_testing, Species)) %>%
metrics(Species, .pred_setosa:.pred_virginica, estimate = .pred_class)
## # A tibble: 4 x 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 accuracy multiclass 0.933
## 2 kap multiclass 0.900
## 3 mn_log_loss multiclass 0.275
## 4 roc_auc hand_till 0.973
Este ejemplo, de extremo a extremo, pretende ser una introducción amable a los tidymodels. El número de funciones y las opciones de tales funciones se mantuvieron al mínimo para los propósitos de esta demostración, pero hay mucho más que se puede hacer con este grupo de paquetes.