Abstract
Este documento le enseñará a aprovechar las herramientas en el Tidyverse para generar, explorar y evaluar modelos de aprendizaje automático.Usando una combinación de los paquetes tidyr y purr, construirá una base sobre cómo trabajar con objetos modelos complejos en tidy. También aprenderá a aprovechar el paquete de broom para explorar los modelos resultantes.Luego se le mostraran las herramientas, es decir, los códigos de prueba - entrenamiento - validación, o que le permitirá evaluar el rendimiento de los modelos de clasificación y regresión, así como proporcionar la información necesaria para optimizar el rendimiento del modelo a través del ajuste de hiperparámetros.
Si estás leyendo este documento, entonces ya debes saber lo fácil que es explorar, manipular y analizar tus datos con herramientas de tidyverse. La buena noticia es que las herramientas de tidyverse también funcionan excepcionalmente bien para construir modelos de aprendizaje automático.
La razón de esto es que las herramientas de tidyverse se centran en
la estructura del marco de datos conocida como tibble. Lo que hace que un
tibble sea especial para el aprendizaje automático es que puede
almacenar de forma nativa objetos arbitrariamente complejos utilizando
una columna especial conocida como columna de lista. Esto es
particularmente útil para almacenar modelos, ya que los resultados de
estos modelos son siempre objetos complejos. Con tibbles puede almacenar
modelos en estas columnas de lista y, como resultado, explorarlos y
evaluarlos con el resto del conjunto de herramientas ordenadas.
Junto con tibble, las funciones de los paquetes tidyr y purrr forman las herramientas fundamentales para trabajar con columnas de lista. Utilizará estas herramientas como parte de un marco denominado Flujo de trabajo de columna de lista.
En esencia, este flujo de trabajo se puede resumir en tres pasos básicos. El primer paso es hacer una columna de lista. El segundo paso consiste en utilizar las herramientas adecuadas para trabajar con la columna de lista. Y el tercer y último paso es simplificar las columnas de la lista en un formato que permita una mayor exploración utilizando las herramientas familiares de tidyverse. Estos tres pasos se basan en la familia de funciones map de purrr y las funciones nest y unnest de tidyr. Para aprender a usar el flujo de trabajo de la columna de lista, trabajará con el conjunto de datos de gapminder.
A diferencia de los cursos anteriores que utilizaron el paquete
gapminder, este curso utilizará una recopilación más granular de datos
de gapminder adaptados del paquete dslabs. Esta versión
contiene observaciones para 77 países durante un período de tiempo de 52
años. Cada observación tiene seis elementos informativos asociados, nos
referiremos a estos elementos como las características de estas
observaciones.
En esta sección y los ejercicios que le siguen, aprenderá cómo usar
las funciones de anidar y anular para manipular los datos de
gapminder.
Aquí hay un extracto de los datos de gapminder coloreados por país.
El proceso de anidamiento compacta la porción de datos de cada país en una entrada correspondiente en el nuevo marco de datos anidados. Esto se logra mediante la función anidar.
Para anidar los datos de gapminder por país, primero debe usar group_by() para agrupar los datos por país y luego use nest() para crear una serie de marcos de datos anidados para cada país. Este proceso crea una nueva columna de lista denominada datos. Cada elemento de esta columna contiene los correspondientes marcos de datos en subconjuntos.
library(tidyverse)
library(gapminder)
anidar <- gapminder %>%
group_by(country) %>%
nest()
anidar
Debido a que la columna de datos en el marco de datos anidados es una
columna de lista, puede acceder a ella directamente. Esto puede ser muy
útil para explorar los datos y crear prototipos de su enfoque.
Por ejemplo, puede ver la cuarta lista, los datos de Africa, especificando la columna de datos y extrayendo la lista con los corchetes dobles.
anidar$data[[4]]
Para el tercer paso del flujo de trabajo de la columna de lista, debe
simplificar las columnas de lista. Si la columna de la lista contiene
marcos de datos, como en este ejemplo, puede simplificarla usando la
función unnest().
En este ejemplo, puede ver cómo los marcos de datos anidados se simplificaron en un marco de datos con columnas regulares. Aquí, la columna para anular se especifica como el primer argumento en la función unnest().
anidar %>%
unnest(data)
En este documento, trabajará con una colección de indicadores
económicos y sociales de 77 países durante un período de 52 años. Estos
datos se almacenan en el marco de datos gapminder
.
En este ejercicio, transformará su marco de datos
gapminder en un marco de datos anidado
mediante mediante el uso de la primera herramienta necesaria para
construir la base de las habilidades ordenadas de
aprendizaje automático: nes()
Nota: Esta es una versión más granular que el conjunto de datos disponible en el paquete gapminder. Esta versión está disponible en el paquete dslabs.
library(dslabs)
# Explorar los datos Gapminder
head(gapminder)
# Preparar el marco de datos anidado gap_anidado
gap_anidado <- gapminder %>%
group_by(country) %>%
nest()
# Explore las primeras seis filas del marco de datos recién
# creado gap_anidado, observe los nuevos datos de columnas
# complejas que contienen tibbles.
head(gap_anidado)
¡Has tenido un gran comienzo! Observe que cada fila en gap_nested contiene un tibble.
Como ha visto en el ejercicio anterior, un marco de datos anidado es simplemente una forma de dar forma a sus datos. Esencialmente tomando las ventanas group_by() y empaquetándolas en la fila correspondientes.
De la misma manera que puede usar la función nest() para dividir sus datos en fragmentos anidados, puede usar la función unnest() para expandir los marcos de datos que están anidados en estos fragmentos.
# Crea el marco de datos no anidado llamado gap_noanidado
gap_noanidado <- gap_anidado %>%
unnest()
# Confirma que tus datos no fueron modificados
identical(gapminder, gap_noanidado)
## [1] FALSE
¡¡Buen trabajo!! Tenga en cuenta que esta transformación solo remodeló sus datos, no los modificó.
En el primer ejercicio, creó con éxito un marco de datos anidado gap_anidado. La data columna contiene tibbles para cada país. En este ejercicio, explorará uno de estos fragmentos anidados.
# Extraer los datos de Argelia
algeria_df <- gap_anidado$data[[1]]
# Calcular el mínimo del vector de población
min(algeria_df$population,na.rm = TRUE)
## [1] 1636054
# Calcular el máximo del vector de población
max(algeria_df$population, na.rm = TRUE)
## [1] 3281453
# Calcular la media del vector poblacional
mean(algeria_df$population,na.rm = TRUE)
## [1] 2708629
Puede ver que trabajar con un solo fragmento en un marco de datos anidado es idéntico a trabajar con marcos de datos normales. En la siguiente sección, aprenderá cómo escalar este enfoque para trabajar en un vector de marcos de datos anidados utilizando la familia de funciones de mapa. # La familia funciones map
En la sección previa y ejercicios, aprendió a usar las funciones
nest() y unnest() para los pasos uno y tres del flujo de trabajo de la
columna de lista.
En esta sección, le presentaré la familia de funciones map(). Estas funciones cumplen los roles de los pasos dos y tres de este flujo de trabajo.
La función map(.x =, .f=) aplica una función deseada
a cada elemento en un vector o una lista y siempre devuelve una lista
como resultado. Esta función requiere dos parámetros, punto x y punto
f.
El punto x es el vector o la lista sobre la que desea iterar,
mientras que el punto f es la función. La función puede ser una función
predefinida o puede ser una función anónima usando la sintaxis de
fórmula.
Por ejemplo, si quisiera usar la función mean(), puede hacer referencia a ella directamente o puede crear una función anónima usando la tilda para indicar que está usando una fórmula y el punto x para indicar el marcador de posición del valor. .f = ~mean(.x)
En el ejercicio anterior, calculó la población media del país de Argelia extrayendo el primer elemento del marco de datos anidado y luego calculando la media de la columna de población.
mean(anidar$data[[1]]$population,na.rm = TRUE)
## [1] 2708629
La estructura de esto es muy similar cuando se usa map(). Utilizará map() para calcular la población media de cada país utilizando el marco de datos anidado correspondiente de ese país.
head(map(.x = anidar$data, .f = ~mean(.x$population,na.rm = TRUE)))
## [[1]]
## [1] 2708629
##
## [[2]]
## [1] 24231378
##
## [[3]]
## [1] 11909433
##
## [[4]]
## [1] 71053.36
##
## [[5]]
## [1] 31638376
##
## [[6]]
## [1] 2925011
Aquí, el parámetro punto x es la columna de datos en el marco de datos anidado. Recuerde que esta columna es una lista de marcos de datos correspondientes a cada país. Dado que estos son marcos de datos, debe usar una función anónima para calcular explícitamente la media de la columna de población de cada marco de datos. Recuerde que el punto x aquí actúa como marcador de posición para cada elemento de la lista. Dado que sabe que esta lista contiene marcos de datos y desea calcular la media de la columna de población de cada marco de datos, puede hacer referencia a este marcador de posición de la misma manera que lo haría para trabajar con un solo elemento. El resultado de esta función es una lista de medias de población para los 185 países.
Recuerde que los tibbles son marcos de datos especiales que nos permiten almacenar columnas de listas arbitrariamente complejas. Debido a esto, puede agregar la lista resultante de medias de población usando la función mutate(). Por supuesto, almacenar una lista de dobles no es muy práctico para la exploración.
pop_df <- anidar %>%
mutate(pop_mean = map(data, ~mean(.x$population,na.rm = TRUE)))
head(pop_df)
entonces necesita simplificar estas columnas usando unnest(). Revisemos estos pasos en el contexto del flujo de trabajo de la columna de lista.
head(pop_df) %>%
unnest(pop_mean)
Primero creamos una columna de lista de marcos de datos para cada país usando nest(). Luego trabajamos con las columnas de la lista calculando la media de población de cada marco de datos usando map(). Finalmente, simplificamos la columna anidada resultante con la función unnest(). En ciertas situaciones, puede combinar los dos últimos pasos usando otra función de la familia map_*().
Si sabe que la salida de la función mapeada es un vector de un tipo especifico, puede usar una función map correspondiente a este tipo para calcular el resultado y devolver explícitamente un vector del tipo esperado.
| Función | Retorno |
|---|---|
| map() | list |
| map_dbl() | double |
| map_lgl() | logical |
| map_chr() | character |
| map_int() | integer |
Por ejemplo, la función mean() devuelve un vector de tipo doble, por lo que puede usar map_double() para devolver un vector de dobles en lugar de una lista de dobles. Esto se puede hacer así y, como resultado, mutate() agrega un vector de tipo doble al marco de datos en lugar de una lista.
También puede usar map() para construir modelos para cada país. Aquí, la función lm() se usa para construir modelos lineales para predecir la población usando la característica de fertilidad. Puede definir el modelo utilizando el parámetro de fórmula y proporcionar los datos para cada modelo utilizando el enfoque de punto x para hacer referencia al marco de datos de cada país al realizar el mapeo.
head(anidar) %>%
mutate(model = map(data,~lm(formula = population~fertility, data = .x)))
En combinación con mutate(), puede usar
map() para agregar los resultados de su cálculo a un
marco de datos. Dado que la función map() siempre
devuelve un vector de listas, debe usar unnest() para
extraer esta información en un vector numérico.
Aquí explorará esta funcionalidad calculando población media de cada país en el conjuno de datos gapminder.
# Calcular la población media de cada país
pop_anidado <- gap_anidado %>%
mutate(mean_pop = map(data, ~mean(.x$population,na.rm = TRUE)))
# Hechemos un vistazo a pop_anidado
head(pop_anidado)
# Extraiga el valor mean_pop usando unnest
pop_mean <- pop_anidado %>%
unnest(mean_pop)
# Eche un vistazo a pop_mean
head(pop_mean)
Cuando sabe que la salida de su función mapeada es un tipo esperado
(aquí es un vector numérico), puede aprovechar la map_*() familia de
funciones para tratar explícitamente de devolver ese tipo de objeto en
lugar de una lista.
Aquí volverá a calcular la población media de cada país, pero en su lugar, utilizará map_dbl()para agregar explícitamente el vector numérico devuelto por mean()a su marco de datos.
# Calcule la población media y almacene el resultado como un doble
pop_mean <- gap_anidado %>%
mutate(mean_pop = map_dbl(data, ~mean(.x$population,na.rm = TRUE)))
# Eche un vistazo a pop_mean
head(pop_mean)
El marco de datos gap_nested disponible en su
espacio de trabajo contiene el conjunto de datos de gapminder anidado
por país.
Utilizaremos estos datos para crear un modelo lineal para cada país a
fin de predecir la esperanza de vida mediante la característica de año
.
Nota: El término característica es sinónimo de los términos variable o predictor . Se refiere a un atributo de sus datos que se puede usar para construir un modelo de aprendizaje automático.
# Construimos un modelo lineal para cada pais
gap_models <- gap_anidado %>%
mutate(model = map(data, ~lm(formula = life_expectancy~year,
data = .x)))
# Extraemos el modelo para Argelia
algeria_model <- gap_models$model[[1]]
# Veamos el resumen del modelo de Argelia
summary(algeria_model)
##
## Call:
## lm(formula = life_expectancy ~ year, data = .x)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.7990 -0.3172 -0.1635 0.5574 1.1488
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -3.974e+02 1.236e+01 -32.14 <2e-16 ***
## year 2.362e-01 6.219e-03 37.99 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.7724 on 55 degrees of freedom
## Multiple R-squared: 0.9633, Adjusted R-squared: 0.9626
## F-statistic: 1443 on 1 and 55 DF, p-value: < 2.2e-16
Ahora que sabe cómo trabajar con columnas de lista de manera
ordenada, puede comenzar a trabajar con las herramientas que necesita
para explorar y evaluar modelos de aprendizaje automático.
Como probablemente pueda imaginar, la mayor parte del trabajo de
aprendizaje automático reside en el paso dos de este flujo de trabajo.
Dado que puede almacenar objetos de
modelos complejos en su marco de datos, también puede trabajar con estos
objetos utilizando las herramientas disponibles en varios paquetes de
R.
En esta sección, nos centraremos en el paquete broom. Un paquete diseñado para convertir resultados de modelos útiles en marcos de datos ordenados.
El núcleo de broom está encapsulado por tres funciones que aspiran a extraer información conceptualmente diferente de cualquier modelo.
Exploremos cada una de estas funciones en mayor detalle revisando los
resultados del modelo lineal que creó para Argelia.
Si observa el summary(argelia_model), puede ver que
hay mucha información útil aquí. Sin embargo, esta información no es
particularmente fácil de extraer directamente del objeto como es
simplemente imprimirla. Pero usando tidy() y
glance() puede extraer facilmente esta información en
marcos de datos.
La función tidy() recopila los resultados estadísticos de un modelo en un marco de datos. Cuando se usa con un modelo lineal, tidy(), devuelve los coeficientes y sus estadísticas correspondientes para ese modelo. Para extraer estas estadísticas, simplemente aplique la función tidy() al objeto modelo como se muestra aquí.
library(broom)
tidy(algeria_model)
La siguiente función de broom, glance(), se usa para devolver un resumen de una fila de un modelo. Para un modelo lineal, este resumen contiene varias estadística sobre el ajuste del modelo, como la \(r^2\). Extraer esta información en un marco de datos es tan simple como llamar a la función en el objeto modelo.
glance(algeria_model)
Finalmente, la función Augment() crea datos de nivel de observación.
augment(algeria_model)
como puede ver este marco contiene los datos originales utilizados para construir el modelo, así como el valor pronósticado para cada observación a medida que se ajusta en el punto de la columna. Además Augment() agrega estadísticas de ajustes específicas del modelo para cada observación. Mediante la construcción de un marco de datos que contiene tanto los valores originales y los predichos por nuestro modelo puede explorar el ajuste del modelo.
Aquí podrá visualizar qué tan bien se ajusta su modelo a los datos trazando los valores pronosticados y reales de la esperanza de vida con respecto al año. En este gráfico, los valores reales son los puntos negros y el ajuste del modelo, o valores pronosticados, se muestra como la línea roja. Al examinar este gráfico, puede aprender que un modelo lineal simple puede no ser el mejor enfoque para este ejemplo y consideraría incluir más funciones o usar un enfoque no lineal para capturar mejor esta relación.
augment(algeria_model) %>%
ggplot(mapping = aes(x = year))+
geom_point(mapping = aes(y = life_expectancy))+
geom_line(mapping = aes(y = .fitted), color = 'red')
En resumen, el uso de estas tres herramientas facilita la extracción de coeficientes del modelo. estadísticas de ajuste y rendimiento a nivel de observación para muchos modelos diferentes de aprendizaje automático. En el capítulo dos, usaremos broom como parte del flujo de trabajo de la columna de lista para hacer esto para los 77 modelos a nivel de país con solo unas pocas líneas de código.
En este ejercicio, utilizará las funciones tidy() y
glance() para extraer información de manera ordenada de
algeria_model.
Para un modelo lineal, tidy() extrae los coeficientes del modelo y glance() devuelve las estadísticas del modelo, como el \(R^2\).
# Extraiga los coeficientes de algeria_model como un marco de datos
tidy(algeria_model)
# Extraiga las estadísticas de algeria_model como un marco de datos
glance(algeria_model)
A partir de los resultados de glance(), aprendió que
al usar las funciones disponibles, el modelo lineal se adapta bien a un
modelo ajustado. \(R^2\) de 0.96. La
función augment() puede ayudarlo a explorar este ajuste
agregando las predicciones a los datos originales.
Aquí aprovechará esto para comparar los valores predichos life_expectancy con los originales en función de la función year.
# Cree el marco de datos aumentado
algeria_fitted <- augment(algeria_model)
# Compare los valores predichos con los valores reales de la
# esperanza de vida
algeria_fitted %>%
ggplot(aes(x = year)) +
geom_point(aes(y = life_expectancy)) +
geom_line(aes(y = .fitted), color = "red")
Ha completado con éxito el Modulo 1. En el próximo capítulo verá cómo puede aprovechar las herramientas que aprendió para construir, evaluar y explorar los modelos que creó para cada país.