{ "nbformat": 4, "nbformat_minor": 2, "metadata": { "colab": { "name": "lesson_3-R.ipynb", "provenance": [], "collapsed_sections": [], "toc_visible": true }, "kernelspec": { "name": "ir", "display_name": "R" }, "language_info": { "name": "R" }, "coopTranslator": { "original_hash": "5015d65d61ba75a223bfc56c273aa174", "translation_date": "2025-09-04T01:11:48+00:00", "source_file": "2-Regression/3-Linear/solution/R/lesson_3-R.ipynb", "language_code": "es" } }, "cells": [ { "cell_type": "markdown", "source": [], "metadata": { "id": "EgQw8osnsUV-" } }, { "cell_type": "markdown", "source": [ "## Regresión Lineal y Polinómica para Precios de Calabazas - Lección 3\n", "

\n", " \n", "

Infografía por Dasani Madipalli
\n", "\n", "\n", "#### Introducción\n", "\n", "Hasta ahora has explorado qué es la regresión utilizando datos de muestra obtenidos del conjunto de datos sobre precios de calabazas que usaremos a lo largo de esta lección. También lo has visualizado usando `ggplot2`. 💪\n", "\n", "Ahora estás listo para profundizar en la regresión para ML. En esta lección, aprenderás más sobre dos tipos de regresión: *regresión lineal básica* y *regresión polinómica*, junto con algunas matemáticas que sustentan estas técnicas.\n", "\n", "> A lo largo de este currículo, asumimos un conocimiento mínimo de matemáticas y buscamos hacerlo accesible para estudiantes provenientes de otros campos, así que presta atención a notas, 🧮 llamados, diagramas y otras herramientas de aprendizaje para facilitar la comprensión.\n", "\n", "#### Preparación\n", "\n", "Como recordatorio, estás cargando estos datos para hacer preguntas sobre ellos.\n", "\n", "- ¿Cuándo es el mejor momento para comprar calabazas?\n", "\n", "- ¿Qué precio puedo esperar por una caja de calabazas miniatura?\n", "\n", "- ¿Debería comprarlas en cestas de medio bushel o en cajas de 1 1/9 bushel? Sigamos investigando estos datos.\n", "\n", "En la lección anterior, creaste un `tibble` (una reinterpretación moderna del marco de datos) y lo llenaste con parte del conjunto de datos original, estandarizando los precios por bushel. Sin embargo, al hacer eso, solo pudiste recopilar alrededor de 400 puntos de datos y solo para los meses de otoño. ¿Quizás podamos obtener un poco más de detalle sobre la naturaleza de los datos limpiándolos más? Veremos... 🕵️‍♀️\n", "\n", "Para esta tarea, necesitaremos los siguientes paquetes:\n", "\n", "- `tidyverse`: El [tidyverse](https://www.tidyverse.org/) es una [colección de paquetes de R](https://www.tidyverse.org/packages) diseñada para hacer la ciencia de datos más rápida, fácil y divertida.\n", "\n", "- `tidymodels`: El marco [tidymodels](https://www.tidymodels.org/) es una [colección de paquetes](https://www.tidymodels.org/packages/) para modelado y aprendizaje automático.\n", "\n", "- `janitor`: El paquete [janitor](https://github.com/sfirke/janitor) proporciona herramientas simples para examinar y limpiar datos sucios.\n", "\n", "- `corrplot`: El paquete [corrplot](https://cran.r-project.org/web/packages/corrplot/vignettes/corrplot-intro.html) ofrece una herramienta visual exploratoria sobre matrices de correlación que admite el reordenamiento automático de variables para ayudar a detectar patrones ocultos entre variables.\n", "\n", "Puedes instalarlos con:\n", "\n", "`install.packages(c(\"tidyverse\", \"tidymodels\", \"janitor\", \"corrplot\"))`\n", "\n", "El siguiente script verifica si tienes los paquetes necesarios para completar este módulo y los instala por ti en caso de que falten.\n" ], "metadata": { "id": "WqQPS1OAsg3H" } }, { "cell_type": "code", "execution_count": null, "source": [ "suppressWarnings(if (!require(\"pacman\")) install.packages(\"pacman\"))\n", "\n", "pacman::p_load(tidyverse, tidymodels, janitor, corrplot)" ], "outputs": [], "metadata": { "id": "tA4C2WN3skCf", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "c06cd805-5534-4edc-f72b-d0d1dab96ac0" } }, { "cell_type": "markdown", "source": [ "Más adelante cargaremos estos paquetes increíbles y los haremos disponibles en nuestra sesión actual de R. (Esto es solo para ilustración, `pacman::p_load()` ya hizo eso por ti)\n", "\n", "## 1. Una línea de regresión lineal\n", "\n", "Como aprendiste en la Lección 1, el objetivo de un ejercicio de regresión lineal es poder trazar una *línea* *de* *mejor ajuste* para:\n", "\n", "- **Mostrar relaciones entre variables**. Mostrar la relación entre las variables.\n", "\n", "- **Hacer predicciones**. Hacer predicciones precisas sobre dónde caería un nuevo punto de datos en relación con esa línea.\n", "\n", "Para dibujar este tipo de línea, usamos una técnica estadística llamada **Regresión por Mínimos Cuadrados**. El término `mínimos cuadrados` significa que todos los puntos de datos que rodean la línea de regresión se elevan al cuadrado y luego se suman. Idealmente, esa suma final es lo más pequeña posible, porque queremos un número bajo de errores, o `mínimos cuadrados`. Por lo tanto, la línea de mejor ajuste es la línea que nos da el valor más bajo para la suma de los errores al cuadrado, de ahí el nombre *regresión por mínimos cuadrados*.\n", "\n", "Hacemos esto porque queremos modelar una línea que tenga la menor distancia acumulativa de todos nuestros puntos de datos. También elevamos los términos al cuadrado antes de sumarlos, ya que nos preocupa su magnitud más que su dirección.\n", "\n", "> **🧮 Muéstrame las matemáticas**\n", ">\n", "> Esta línea, llamada la *línea de mejor ajuste*, puede expresarse mediante [una ecuación](https://es.wikipedia.org/wiki/Regresi%C3%B3n_lineal_simple):\n", ">\n", "> Y = a + bX\n", ">\n", "> `X` es la '`variable explicativa` o `predictor`'. `Y` es la '`variable dependiente` o `resultado`'. La pendiente de la línea es `b` y `a` es la intersección con el eje Y, que se refiere al valor de `Y` cuando `X = 0`.\n", ">\n", "\n", "> ![](../../../../../../2-Regression/3-Linear/solution/images/slope.png \"pendiente = $y/x$\")\n", " Infografía por Jen Looper\n", ">\n", "> Primero, calcula la pendiente `b`.\n", ">\n", "> En otras palabras, y refiriéndonos a la pregunta original de los datos de las calabazas: \"predecir el precio de una calabaza por bushel según el mes\", `X` se referiría al precio y `Y` se referiría al mes de venta.\n", ">\n", "> ![](../../../../../../translated_images/calculation.989aa7822020d9d0ba9fc781f1ab5192f3421be86ebb88026528aef33c37b0d8.es.png)\n", " Infografía por Jen Looper\n", "> \n", "> Calcula el valor de Y. Si estás pagando alrededor de \\$4, ¡debe ser abril!\n", ">\n", "> Las matemáticas que calculan la línea deben demostrar la pendiente de la línea, que también depende de la intersección, o dónde se sitúa `Y` cuando `X = 0`.\n", ">\n", "> Puedes observar el método de cálculo para estos valores en el sitio web [Math is Fun](https://www.mathsisfun.com/data/least-squares-regression.html). También visita [este calculador de mínimos cuadrados](https://www.mathsisfun.com/data/least-squares-calculator.html) para ver cómo los valores de los números impactan la línea.\n", "\n", "No es tan aterrador, ¿verdad? 🤓\n", "\n", "#### Correlación\n", "\n", "Otro término que debes entender es el **Coeficiente de Correlación** entre las variables X y Y dadas. Usando un diagrama de dispersión, puedes visualizar rápidamente este coeficiente. Un gráfico con puntos de datos dispersos en una línea ordenada tiene alta correlación, pero un gráfico con puntos de datos dispersos por todas partes entre X y Y tiene baja correlación.\n", "\n", "Un buen modelo de regresión lineal será aquel que tenga un Coeficiente de Correlación alto (más cercano a 1 que a 0) utilizando el método de Regresión por Mínimos Cuadrados con una línea de regresión.\n" ], "metadata": { "id": "cdX5FRpvsoP5" } }, { "cell_type": "markdown", "source": [ "## **2. Un baile con los datos: creando un marco de datos que será utilizado para modelar**\n", "\n", "

\n", " \n", "

Obra de arte por @allison_horst
\n", "\n", "\n", "\n" ], "metadata": { "id": "WdUKXk7Bs8-V" } }, { "cell_type": "markdown", "source": [ "Carga las bibliotecas necesarias y el conjunto de datos. Convierte los datos a un marco de datos que contenga un subconjunto de los datos:\n", "\n", "- Obtén únicamente calabazas con precios por fanega.\n", "\n", "- Convierte la fecha a un mes.\n", "\n", "- Calcula el precio como un promedio entre los precios altos y bajos.\n", "\n", "- Convierte el precio para reflejar la cantidad por fanega.\n", "\n", "> Cubrimos estos pasos en la [lección anterior](https://github.com/microsoft/ML-For-Beginners/blob/main/2-Regression/2-Data/solution/lesson_2-R.ipynb).\n" ], "metadata": { "id": "fMCtu2G2s-p8" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Load the core Tidyverse packages\n", "library(tidyverse)\n", "library(lubridate)\n", "\n", "# Import the pumpkins data\n", "pumpkins <- read_csv(file = \"https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/2-Regression/data/US-pumpkins.csv\")\n", "\n", "\n", "# Get a glimpse and dimensions of the data\n", "glimpse(pumpkins)\n", "\n", "\n", "# Print the first 50 rows of the data set\n", "pumpkins %>% \n", " slice_head(n = 5)" ], "outputs": [], "metadata": { "id": "ryMVZEEPtERn" } }, { "cell_type": "markdown", "source": [ "En el espíritu de pura aventura, exploremos el [`paquete janitor`](../../../../../../2-Regression/3-Linear/solution/R/github.com/sfirke/janitor) que proporciona funciones simples para examinar y limpiar datos desordenados. Por ejemplo, echemos un vistazo a los nombres de las columnas de nuestros datos:\n" ], "metadata": { "id": "xcNxM70EtJjb" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Return column names\n", "pumpkins %>% \n", " names()" ], "outputs": [], "metadata": { "id": "5XtpaIigtPfW" } }, { "cell_type": "markdown", "source": [ "🤔 Podemos hacerlo mejor. Convirtamos estos nombres de columna en `friendR` utilizando la convención [snake_case](https://es.wikipedia.org/wiki/Snake_case) con `janitor::clean_names`. Para obtener más información sobre esta función: `?clean_names`\n" ], "metadata": { "id": "IbIqrMINtSHe" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Clean names to the snake_case convention\n", "pumpkins <- pumpkins %>% \n", " clean_names(case = \"snake\")\n", "\n", "# Return column names\n", "pumpkins %>% \n", " names()" ], "outputs": [], "metadata": { "id": "a2uYvclYtWvX" } }, { "cell_type": "markdown", "source": [ "¡Mucho tidyR 🧹! Ahora, ¡un baile con los datos usando `dplyr` como en la lección anterior! 💃\n" ], "metadata": { "id": "HfhnuzDDtaDd" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Select desired columns\n", "pumpkins <- pumpkins %>% \n", " select(variety, city_name, package, low_price, high_price, date)\n", "\n", "\n", "\n", "# Extract the month from the dates to a new column\n", "pumpkins <- pumpkins %>%\n", " mutate(date = mdy(date),\n", " month = month(date)) %>% \n", " select(-date)\n", "\n", "\n", "\n", "# Create a new column for average Price\n", "pumpkins <- pumpkins %>% \n", " mutate(price = (low_price + high_price)/2)\n", "\n", "\n", "# Retain only pumpkins with the string \"bushel\"\n", "new_pumpkins <- pumpkins %>% \n", " filter(str_detect(string = package, pattern = \"bushel\"))\n", "\n", "\n", "# Normalize the pricing so that you show the pricing per bushel, not per 1 1/9 or 1/2 bushel\n", "new_pumpkins <- new_pumpkins %>% \n", " mutate(price = case_when(\n", " str_detect(package, \"1 1/9\") ~ price/(1.1),\n", " str_detect(package, \"1/2\") ~ price*2,\n", " TRUE ~ price))\n", "\n", "# Relocate column positions\n", "new_pumpkins <- new_pumpkins %>% \n", " relocate(month, .before = variety)\n", "\n", "\n", "# Display the first 5 rows\n", "new_pumpkins %>% \n", " slice_head(n = 5)" ], "outputs": [], "metadata": { "id": "X0wU3gQvtd9f" } }, { "cell_type": "markdown", "source": [ "¡Buen trabajo!👌 Ahora tienes un conjunto de datos limpio y ordenado sobre el cual puedes construir tu nuevo modelo de regresión.\n", "\n", "¿Te apetece un diagrama de dispersión?\n" ], "metadata": { "id": "UpaIwaxqth82" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Set theme\n", "theme_set(theme_light())\n", "\n", "# Make a scatter plot of month and price\n", "new_pumpkins %>% \n", " ggplot(mapping = aes(x = month, y = price)) +\n", " geom_point(size = 1.6)\n" ], "outputs": [], "metadata": { "id": "DXgU-j37tl5K" } }, { "cell_type": "markdown", "source": [ "Un diagrama de dispersión nos recuerda que solo tenemos datos mensuales desde agosto hasta diciembre. Probablemente necesitemos más datos para poder sacar conclusiones de manera lineal.\n", "\n", "Echemos un vistazo nuevamente a nuestros datos de modelado:\n" ], "metadata": { "id": "Ve64wVbwtobI" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Display first 5 rows\n", "new_pumpkins %>% \n", " slice_head(n = 5)" ], "outputs": [], "metadata": { "id": "HFQX2ng1tuSJ" } }, { "cell_type": "markdown", "source": [ "¿Qué pasaría si quisiéramos predecir el `precio` de una calabaza basándonos en las columnas `ciudad` o `paquete`, que son de tipo carácter? O incluso más simple, ¿cómo podríamos encontrar la correlación (que requiere que ambos de sus valores sean numéricos) entre, por ejemplo, `paquete` y `precio`? 🤷🤷\n", "\n", "Los modelos de aprendizaje automático funcionan mejor con características numéricas en lugar de valores de texto, por lo que generalmente necesitas convertir las características categóricas en representaciones numéricas.\n", "\n", "Esto significa que debemos encontrar una manera de reformatear nuestros predictores para que sean más fáciles de usar por un modelo de manera efectiva, un proceso conocido como `ingeniería de características`.\n" ], "metadata": { "id": "7hsHoxsStyjJ" } }, { "cell_type": "markdown", "source": [ "## 3. Preprocesamiento de datos para modelado con recipes 👩‍🍳👨‍🍳\n", "\n", "Las actividades que reformatean los valores de los predictores para que sean más fáciles de usar por un modelo se han denominado `ingeniería de características`.\n", "\n", "Diferentes modelos tienen diferentes requisitos de preprocesamiento. Por ejemplo, los mínimos cuadrados requieren `codificar variables categóricas` como mes, variedad y city_name. Esto simplemente implica `traducir` una columna con `valores categóricos` en una o más `columnas numéricas` que reemplazan a la original.\n", "\n", "Por ejemplo, supongamos que tus datos incluyen la siguiente característica categórica:\n", "\n", "| ciudad |\n", "|:---------:|\n", "| Denver |\n", "| Nairobi |\n", "| Tokio |\n", "\n", "Puedes aplicar *codificación ordinal* para sustituir un valor entero único para cada categoría, así:\n", "\n", "| ciudad |\n", "|:------:|\n", "| 0 |\n", "| 1 |\n", "| 2 |\n", "\n", "¡Y eso es lo que haremos con nuestros datos!\n", "\n", "En esta sección, exploraremos otro increíble paquete de Tidymodels: [recipes](https://tidymodels.github.io/recipes/) - que está diseñado para ayudarte a preprocesar tus datos **antes** de entrenar tu modelo. En esencia, una receta es un objeto que define qué pasos deben aplicarse a un conjunto de datos para que esté listo para el modelado.\n", "\n", "Ahora, vamos a crear una receta que prepare nuestros datos para el modelado sustituyendo un entero único para todas las observaciones en las columnas de predictores:\n" ], "metadata": { "id": "AD5kQbcvt3Xl" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Specify a recipe\n", "pumpkins_recipe <- recipe(price ~ ., data = new_pumpkins) %>% \n", " step_integer(all_predictors(), zero_based = TRUE)\n", "\n", "\n", "# Print out the recipe\n", "pumpkins_recipe" ], "outputs": [], "metadata": { "id": "BNaFKXfRt9TU" } }, { "cell_type": "markdown", "source": [ "¡Increíble! 👏 ¡Acabamos de crear nuestra primera receta que especifica un resultado (precio) y sus correspondientes predictores, y que todas las columnas de predictores deben codificarse en un conjunto de enteros 🙌! Vamos a desglosarlo rápidamente:\n", "\n", "- La llamada a `recipe()` con una fórmula le indica a la receta los *roles* de las variables utilizando los datos de `new_pumpkins` como referencia. Por ejemplo, la columna `price` ha sido asignada como un rol de `outcome`, mientras que el resto de las columnas han sido asignadas como rol de `predictor`.\n", "\n", "- `step_integer(all_predictors(), zero_based = TRUE)` especifica que todos los predictores deben convertirse en un conjunto de enteros, comenzando la numeración desde 0.\n", "\n", "Estamos seguros de que podrías estar pensando algo como: \"¡Esto es genial! Pero, ¿qué pasa si necesito confirmar que las recetas están haciendo exactamente lo que espero que hagan? 🤔\"\n", "\n", "¡Esa es una excelente pregunta! Verás, una vez que defines tu receta, puedes estimar los parámetros necesarios para preprocesar realmente los datos y luego extraer los datos procesados. Normalmente no necesitas hacer esto cuando usas Tidymodels (veremos la convención normal en un momento -> `workflows`), pero puede ser útil cuando quieres hacer algún tipo de verificación para confirmar que las recetas están funcionando como esperas.\n", "\n", "Para eso, necesitarás dos verbos más: `prep()` y `bake()`, y como siempre, nuestros pequeños amigos de R creados por [`Allison Horst`](https://github.com/allisonhorst/stats-illustrations) te ayudarán a entender esto mejor.\n", "\n", "

\n", " \n", "

Ilustración por @allison_horst
\n", "\n", "\n", "\n" ], "metadata": { "id": "KEiO0v7kuC9O" } }, { "cell_type": "markdown", "source": [ "[`prep()`](https://recipes.tidymodels.org/reference/prep.html): estima los parámetros necesarios a partir de un conjunto de entrenamiento que luego pueden aplicarse a otros conjuntos de datos. Por ejemplo, para una columna predictora dada, qué observación será asignada como entero 0, 1, 2, etc.\n", "\n", "[`bake()`](https://recipes.tidymodels.org/reference/bake.html): toma una receta preparada y aplica las operaciones a cualquier conjunto de datos.\n", "\n", "Dicho esto, preparemos y apliquemos nuestras recetas para confirmar realmente que, en el fondo, las columnas predictoras serán primero codificadas antes de ajustar un modelo.\n" ], "metadata": { "id": "Q1xtzebuuTCP" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Prep the recipe\n", "pumpkins_prep <- prep(pumpkins_recipe)\n", "\n", "# Bake the recipe to extract a preprocessed new_pumpkins data\n", "baked_pumpkins <- bake(pumpkins_prep, new_data = NULL)\n", "\n", "# Print out the baked data set\n", "baked_pumpkins %>% \n", " slice_head(n = 10)" ], "outputs": [], "metadata": { "id": "FGBbJbP_uUUn" } }, { "cell_type": "markdown", "source": [ "¡Woo-hoo! 🥳 Los datos procesados `baked_pumpkins` tienen todos sus predictores codificados, confirmando que, efectivamente, los pasos de preprocesamiento definidos como nuestra receta funcionarán como se espera. Esto hace que sea más difícil de leer para ti, pero mucho más comprensible para Tidymodels. Tómate un momento para descubrir qué observación ha sido asignada a un entero correspondiente.\n", "\n", "También vale la pena mencionar que `baked_pumpkins` es un marco de datos sobre el cual podemos realizar cálculos.\n", "\n", "Por ejemplo, intentemos encontrar una buena correlación entre dos puntos de tus datos para potencialmente construir un buen modelo predictivo. Usaremos la función `cor()` para hacerlo. Escribe `?cor()` para obtener más información sobre la función.\n" ], "metadata": { "id": "1dvP0LBUueAW" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Find the correlation between the city_name and the price\n", "cor(baked_pumpkins$city_name, baked_pumpkins$price)\n", "\n", "# Find the correlation between the package and the price\n", "cor(baked_pumpkins$package, baked_pumpkins$price)\n" ], "outputs": [], "metadata": { "id": "3bQzXCjFuiSV" } }, { "cell_type": "markdown", "source": [ "Resulta que solo hay una correlación débil entre la Ciudad y el Precio. Sin embargo, hay una mejor correlación entre el Paquete y su Precio. Tiene sentido, ¿verdad? Normalmente, cuanto más grande es la caja de productos, mayor es el precio.\n", "\n", "Ya que estamos, intentemos también visualizar una matriz de correlación de todas las columnas utilizando el paquete `corrplot`.\n" ], "metadata": { "id": "BToPWbgjuoZw" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Load the corrplot package\n", "library(corrplot)\n", "\n", "# Obtain correlation matrix\n", "corr_mat <- cor(baked_pumpkins %>% \n", " # Drop columns that are not really informative\n", " select(-c(low_price, high_price)))\n", "\n", "# Make a correlation plot between the variables\n", "corrplot(corr_mat, method = \"shade\", shade.col = NA, tl.col = \"black\", tl.srt = 45, addCoef.col = \"black\", cl.pos = \"n\", order = \"original\")" ], "outputs": [], "metadata": { "id": "ZwAL3ksmutVR" } }, { "cell_type": "markdown", "source": [ "🤩🤩 Mucho mejor.\n", "\n", "Una buena pregunta para hacer ahora con estos datos sería: '`¿Qué precio puedo esperar de un paquete de calabazas dado?`' ¡Vamos a ello!\n", "\n", "> Nota: Cuando **`bake()`** la receta preparada **`pumpkins_prep`** con **`new_data = NULL`**, extraes los datos de entrenamiento procesados (es decir, codificados). Si tuvieras otro conjunto de datos, por ejemplo, un conjunto de prueba, y quisieras ver cómo una receta lo preprocesaría, simplemente hornearías **`pumpkins_prep`** con **`new_data = test_set`**\n", "\n", "## 4. Construir un modelo de regresión lineal\n", "\n", "

\n", " \n", "

Infografía por Dasani Madipalli
\n", "\n", "\n", "\n" ], "metadata": { "id": "YqXjLuWavNxW" } }, { "cell_type": "markdown", "source": [ "Ahora que hemos creado una receta y confirmado que los datos se preprocesarán adecuadamente, construyamos un modelo de regresión para responder a la pregunta: `¿Qué precio puedo esperar de un paquete de calabaza dado?`\n", "\n", "#### Entrenar un modelo de regresión lineal utilizando el conjunto de entrenamiento\n", "\n", "Como probablemente ya te habrás dado cuenta, la columna *price* es la variable `resultado`, mientras que la columna *package* es la variable `predictora`.\n", "\n", "Para hacer esto, primero dividiremos los datos de manera que el 80% se destine al conjunto de entrenamiento y el 20% al conjunto de prueba. Luego definiremos una receta que codifique la columna predictora en un conjunto de enteros, y después construiremos una especificación del modelo. No prepararemos ni aplicaremos nuestra receta, ya que sabemos que preprocesará los datos como se espera.\n" ], "metadata": { "id": "Pq0bSzCevW-h" } }, { "cell_type": "code", "execution_count": null, "source": [ "set.seed(2056)\n", "# Split the data into training and test sets\n", "pumpkins_split <- new_pumpkins %>% \n", " initial_split(prop = 0.8)\n", "\n", "\n", "# Extract training and test data\n", "pumpkins_train <- training(pumpkins_split)\n", "pumpkins_test <- testing(pumpkins_split)\n", "\n", "\n", "\n", "# Create a recipe for preprocessing the data\n", "lm_pumpkins_recipe <- recipe(price ~ package, data = pumpkins_train) %>% \n", " step_integer(all_predictors(), zero_based = TRUE)\n", "\n", "\n", "\n", "# Create a linear model specification\n", "lm_spec <- linear_reg() %>% \n", " set_engine(\"lm\") %>% \n", " set_mode(\"regression\")" ], "outputs": [], "metadata": { "id": "CyoEh_wuvcLv" } }, { "cell_type": "markdown", "source": [ "¡Buen trabajo! Ahora que tenemos una receta y una especificación del modelo, necesitamos encontrar una forma de combinarlas en un objeto que primero preprocese los datos (prep+bake detrás de escena), ajuste el modelo con los datos preprocesados y también permita posibles actividades de postprocesamiento. ¡Qué tranquilidad, ¿verdad?! 🤩\n", "\n", "En Tidymodels, este práctico objeto se llama [`workflow`](https://workflows.tidymodels.org/) y contiene convenientemente tus componentes de modelado. Esto es lo que llamaríamos *pipelines* en *Python*.\n", "\n", "¡Así que vamos a agrupar todo en un workflow! 📦\n" ], "metadata": { "id": "G3zF_3DqviFJ" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Hold modelling components in a workflow\n", "lm_wf <- workflow() %>% \n", " add_recipe(lm_pumpkins_recipe) %>% \n", " add_model(lm_spec)\n", "\n", "# Print out the workflow\n", "lm_wf" ], "outputs": [], "metadata": { "id": "T3olroU3v-WX" } }, { "cell_type": "markdown", "source": [ "👌 Además, un flujo de trabajo puede ajustarse/entrenarse de manera muy similar a como se entrena un modelo.\n" ], "metadata": { "id": "zd1A5tgOwEPX" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Train the model\n", "lm_wf_fit <- lm_wf %>% \n", " fit(data = pumpkins_train)\n", "\n", "# Print the model coefficients learned \n", "lm_wf_fit" ], "outputs": [], "metadata": { "id": "NhJagFumwFHf" } }, { "cell_type": "markdown", "source": [ "A partir de la salida del modelo, podemos observar los coeficientes aprendidos durante el entrenamiento. Representan los coeficientes de la línea de mejor ajuste que nos da el menor error general entre la variable real y la predicha.\n", "\n", "#### Evaluar el rendimiento del modelo usando el conjunto de prueba\n", "\n", "¡Es hora de ver cómo se desempeñó el modelo 📏! ¿Cómo hacemos esto?\n", "\n", "Ahora que hemos entrenado el modelo, podemos usarlo para hacer predicciones para el conjunto_de_prueba utilizando `parsnip::predict()`. Luego podemos comparar estas predicciones con los valores reales de las etiquetas para evaluar qué tan bien (¡o no!) está funcionando el modelo.\n", "\n", "Comencemos haciendo predicciones para el conjunto de prueba y luego unamos las columnas al conjunto de prueba.\n" ], "metadata": { "id": "_4QkGtBTwItF" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Make predictions for the test set\n", "predictions <- lm_wf_fit %>% \n", " predict(new_data = pumpkins_test)\n", "\n", "\n", "# Bind predictions to the test set\n", "lm_results <- pumpkins_test %>% \n", " select(c(package, price)) %>% \n", " bind_cols(predictions)\n", "\n", "\n", "# Print the first ten rows of the tibble\n", "lm_results %>% \n", " slice_head(n = 10)" ], "outputs": [], "metadata": { "id": "UFZzTG0gwTs9" } }, { "cell_type": "markdown", "source": [ "¡Sí, acabas de entrenar un modelo y usarlo para hacer predicciones!🔮 ¿Es bueno? Vamos a evaluar el rendimiento del modelo.\n", "\n", "En Tidymodels, hacemos esto utilizando `yardstick::metrics()`! Para la regresión lineal, enfoquémonos en las siguientes métricas:\n", "\n", "- `Root Mean Square Error (RMSE)`: La raíz cuadrada del [MSE](https://en.wikipedia.org/wiki/Mean_squared_error). Esto proporciona una métrica absoluta en la misma unidad que la etiqueta (en este caso, el precio de una calabaza). Cuanto más pequeño sea el valor, mejor será el modelo (en términos simples, representa el precio promedio por el cual las predicciones están equivocadas).\n", "\n", "- `Coefficient of Determination (usualmente conocido como R-squared o R2)`: Una métrica relativa en la que cuanto mayor sea el valor, mejor será el ajuste del modelo. En esencia, esta métrica representa cuánto de la variación entre los valores predichos y reales de la etiqueta el modelo es capaz de explicar.\n" ], "metadata": { "id": "0A5MjzM7wW9M" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Evaluate performance of linear regression\n", "metrics(data = lm_results,\n", " truth = price,\n", " estimate = .pred)" ], "outputs": [], "metadata": { "id": "reJ0UIhQwcEH" } }, { "cell_type": "markdown", "source": [ "Ahí va el rendimiento del modelo. Veamos si podemos obtener una mejor indicación visualizando un gráfico de dispersión del paquete y el precio, y luego usar las predicciones realizadas para superponer una línea de mejor ajuste.\n", "\n", "Esto significa que tendremos que preparar y procesar el conjunto de prueba para codificar la columna de paquete y luego vincular esto a las predicciones realizadas por nuestro modelo.\n" ], "metadata": { "id": "fdgjzjkBwfWt" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Encode package column\n", "package_encode <- lm_pumpkins_recipe %>% \n", " prep() %>% \n", " bake(new_data = pumpkins_test) %>% \n", " select(package)\n", "\n", "\n", "# Bind encoded package column to the results\n", "lm_results <- lm_results %>% \n", " bind_cols(package_encode %>% \n", " rename(package_integer = package)) %>% \n", " relocate(package_integer, .after = package)\n", "\n", "\n", "# Print new results data frame\n", "lm_results %>% \n", " slice_head(n = 5)\n", "\n", "\n", "# Make a scatter plot\n", "lm_results %>% \n", " ggplot(mapping = aes(x = package_integer, y = price)) +\n", " geom_point(size = 1.6) +\n", " # Overlay a line of best fit\n", " geom_line(aes(y = .pred), color = \"orange\", size = 1.2) +\n", " xlab(\"package\")\n", " \n" ], "outputs": [], "metadata": { "id": "R0nw719lwkHE" } }, { "cell_type": "markdown", "source": [ "¡Genial! Como puedes ver, el modelo de regresión lineal no generaliza muy bien la relación entre un paquete y su precio correspondiente.\n", "\n", "🎃 ¡Felicidades! Acabas de crear un modelo que puede ayudar a predecir el precio de algunas variedades de calabazas. Tu huerto de calabazas para las fiestas será hermoso. ¡Pero probablemente puedas crear un modelo mejor!\n", "\n", "## 5. Construir un modelo de regresión polinómica\n", "\n", "

\n", " \n", "

Infografía por Dasani Madipalli
\n", "\n", "\n", "\n" ], "metadata": { "id": "HOCqJXLTwtWI" } }, { "cell_type": "markdown", "source": [ "A veces nuestros datos pueden no tener una relación lineal, pero aún queremos predecir un resultado. La regresión polinómica puede ayudarnos a hacer predicciones para relaciones no lineales más complejas.\n", "\n", "Tomemos, por ejemplo, la relación entre el paquete y el precio en nuestro conjunto de datos de calabazas. Aunque a veces hay una relación lineal entre las variables - cuanto mayor es la calabaza en volumen, mayor es el precio - en ocasiones estas relaciones no pueden representarse como un plano o una línea recta.\n", "\n", "> ✅ Aquí tienes [algunos ejemplos adicionales](https://online.stat.psu.edu/stat501/lesson/9/9.8) de datos que podrían usar regresión polinómica\n", ">\n", "> Observa nuevamente la relación entre la variedad y el precio en el gráfico anterior. ¿Parece que este diagrama de dispersión debería analizarse necesariamente con una línea recta? Quizás no. En este caso, puedes intentar usar regresión polinómica.\n", ">\n", "> ✅ Los polinomios son expresiones matemáticas que pueden consistir en una o más variables y coeficientes\n", "\n", "#### Entrenar un modelo de regresión polinómica usando el conjunto de entrenamiento\n", "\n", "La regresión polinómica crea una *línea curva* para ajustar mejor los datos no lineales.\n", "\n", "Veamos si un modelo polinómico puede desempeñarse mejor en la predicción. Seguiremos un procedimiento algo similar al que hicimos antes:\n", "\n", "- Crear una receta que especifique los pasos de preprocesamiento que deben realizarse en nuestros datos para prepararlos para el modelado, es decir: codificar predictores y calcular polinomios de grado *n*\n", "\n", "- Construir una especificación del modelo\n", "\n", "- Combinar la receta y la especificación del modelo en un flujo de trabajo\n", "\n", "- Crear un modelo ajustando el flujo de trabajo\n", "\n", "- Evaluar qué tan bien se desempeña el modelo con los datos de prueba\n", "\n", "¡Vamos a ello!\n" ], "metadata": { "id": "VcEIpRV9wzYr" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Specify a recipe\r\n", "poly_pumpkins_recipe <-\r\n", " recipe(price ~ package, data = pumpkins_train) %>%\r\n", " step_integer(all_predictors(), zero_based = TRUE) %>% \r\n", " step_poly(all_predictors(), degree = 4)\r\n", "\r\n", "\r\n", "# Create a model specification\r\n", "poly_spec <- linear_reg() %>% \r\n", " set_engine(\"lm\") %>% \r\n", " set_mode(\"regression\")\r\n", "\r\n", "\r\n", "# Bundle recipe and model spec into a workflow\r\n", "poly_wf <- workflow() %>% \r\n", " add_recipe(poly_pumpkins_recipe) %>% \r\n", " add_model(poly_spec)\r\n", "\r\n", "\r\n", "# Create a model\r\n", "poly_wf_fit <- poly_wf %>% \r\n", " fit(data = pumpkins_train)\r\n", "\r\n", "\r\n", "# Print learned model coefficients\r\n", "poly_wf_fit\r\n", "\r\n", " " ], "outputs": [], "metadata": { "id": "63n_YyRXw3CC" } }, { "cell_type": "markdown", "source": [ "#### Evaluar el rendimiento del modelo\n", "\n", "👏👏 ¡Has creado un modelo polinómico! ¡Hagamos predicciones en el conjunto de prueba!\n" ], "metadata": { "id": "-LHZtztSxDP0" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Make price predictions on test data\r\n", "poly_results <- poly_wf_fit %>% predict(new_data = pumpkins_test) %>% \r\n", " bind_cols(pumpkins_test %>% select(c(package, price))) %>% \r\n", " relocate(.pred, .after = last_col())\r\n", "\r\n", "\r\n", "# Print the results\r\n", "poly_results %>% \r\n", " slice_head(n = 10)" ], "outputs": [], "metadata": { "id": "YUFpQ_dKxJGx" } }, { "cell_type": "markdown", "source": [ "Woo-hoo, evaluemos cómo se desempeñó el modelo en el test_set usando `yardstick::metrics()`.\n" ], "metadata": { "id": "qxdyj86bxNGZ" } }, { "cell_type": "code", "execution_count": null, "source": [ "metrics(data = poly_results, truth = price, estimate = .pred)" ], "outputs": [], "metadata": { "id": "8AW5ltkBxXDm" } }, { "cell_type": "markdown", "source": [ "🤩🤩 Mucho mejor rendimiento.\n", "\n", "El `rmse` disminuyó de aproximadamente 7 a aproximadamente 3, lo que indica una reducción del error entre el precio real y el precio predicho. Puedes *interpretar libremente* esto como que, en promedio, las predicciones incorrectas tienen un error de alrededor de \\$3. El `rsq` aumentó de aproximadamente 0.4 a 0.8.\n", "\n", "Todos estos indicadores muestran que el modelo polinómico funciona mucho mejor que el modelo lineal. ¡Buen trabajo!\n", "\n", "¡Veamos si podemos visualizar esto!\n" ], "metadata": { "id": "6gLHNZDwxYaS" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Bind encoded package column to the results\r\n", "poly_results <- poly_results %>% \r\n", " bind_cols(package_encode %>% \r\n", " rename(package_integer = package)) %>% \r\n", " relocate(package_integer, .after = package)\r\n", "\r\n", "\r\n", "# Print new results data frame\r\n", "poly_results %>% \r\n", " slice_head(n = 5)\r\n", "\r\n", "\r\n", "# Make a scatter plot\r\n", "poly_results %>% \r\n", " ggplot(mapping = aes(x = package_integer, y = price)) +\r\n", " geom_point(size = 1.6) +\r\n", " # Overlay a line of best fit\r\n", " geom_line(aes(y = .pred), color = \"midnightblue\", size = 1.2) +\r\n", " xlab(\"package\")\r\n" ], "outputs": [], "metadata": { "id": "A83U16frxdF1" } }, { "cell_type": "markdown", "source": [ "¡Puedes ver una línea curva que se ajusta mejor a tus datos! 🤩\n", "\n", "¡Puedes hacer esto aún más suave pasando una fórmula polinómica a `geom_smooth` así:\n" ], "metadata": { "id": "4U-7aHOVxlGU" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Make a scatter plot\r\n", "poly_results %>% \r\n", " ggplot(mapping = aes(x = package_integer, y = price)) +\r\n", " geom_point(size = 1.6) +\r\n", " # Overlay a line of best fit\r\n", " geom_smooth(method = lm, formula = y ~ poly(x, degree = 4), color = \"midnightblue\", size = 1.2, se = FALSE) +\r\n", " xlab(\"package\")" ], "outputs": [], "metadata": { "id": "5vzNT0Uexm-w" } }, { "cell_type": "markdown", "source": [ "Muy parecido a una curva suave. 🤩\n", "\n", "Aquí tienes cómo hacer una nueva predicción:\n" ], "metadata": { "id": "v9u-wwyLxq4G" } }, { "cell_type": "code", "execution_count": null, "source": [ "# Make a hypothetical data frame\r\n", "hypo_tibble <- tibble(package = \"bushel baskets\")\r\n", "\r\n", "# Make predictions using linear model\r\n", "lm_pred <- lm_wf_fit %>% predict(new_data = hypo_tibble)\r\n", "\r\n", "# Make predictions using polynomial model\r\n", "poly_pred <- poly_wf_fit %>% predict(new_data = hypo_tibble)\r\n", "\r\n", "# Return predictions in a list\r\n", "list(\"linear model prediction\" = lm_pred, \r\n", " \"polynomial model prediction\" = poly_pred)\r\n" ], "outputs": [], "metadata": { "id": "jRPSyfQGxuQv" } }, { "cell_type": "markdown", "source": [ "El modelo `polynomial` tiene sentido, dado los gráficos de dispersión de `price` y `package`. ¡Y, si este modelo es mejor que el anterior al observar los mismos datos, necesitas presupuestar para estas calabazas más caras!\n", "\n", "🏆 ¡Bien hecho! Creaste dos modelos de regresión en una sola lección. En la sección final sobre regresión, aprenderás sobre la regresión logística para determinar categorías.\n", "\n", "## **🚀Desafío**\n", "\n", "Prueba varias variables diferentes en este notebook para ver cómo la correlación corresponde a la precisión del modelo.\n", "\n", "## [**Cuestionario posterior a la lección**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/14/)\n", "\n", "## **Revisión y Autoestudio**\n", "\n", "En esta lección aprendimos sobre la Regresión Lineal. Existen otros tipos importantes de Regresión. Lee sobre las técnicas Stepwise, Ridge, Lasso y Elasticnet. Un buen curso para aprender más es el [curso de Stanford sobre Aprendizaje Estadístico](https://online.stanford.edu/courses/sohs-ystatslearning-statistical-learning).\n", "\n", "Si quieres aprender más sobre cómo usar el increíble marco de trabajo Tidymodels, consulta los siguientes recursos:\n", "\n", "- Sitio web de Tidymodels: [Comienza con Tidymodels](https://www.tidymodels.org/start/)\n", "\n", "- Max Kuhn y Julia Silge, [*Tidy Modeling with R*](https://www.tmwr.org/)*.*\n", "\n", "###### **AGRADECIMIENTOS A:**\n", "\n", "[Allison Horst](https://twitter.com/allison_horst?lang=es) por crear las increíbles ilustraciones que hacen que R sea más accesible y atractivo. Encuentra más ilustraciones en su [galería](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM).\n" ], "metadata": { "id": "8zOLOWqMxzk5" } }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n---\n\n**Descargo de responsabilidad**: \nEste documento ha sido traducido utilizando el servicio de traducción automática [Co-op Translator](https://github.com/Azure/co-op-translator). Si bien nos esforzamos por lograr precisión, tenga en cuenta que las traducciones automáticas pueden contener errores o imprecisiones. El documento original en su idioma nativo debe considerarse como la fuente autorizada. Para información crítica, se recomienda una traducción profesional realizada por humanos. No nos hacemos responsables de malentendidos o interpretaciones erróneas que puedan surgir del uso de esta traducción.\n" ] } ] }