{ "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-04T06:42:07+00:00", "source_file": "2-Regression/3-Linear/solution/R/lesson_3-R.ipynb", "language_code": "uk" } }, "cells": [ { "cell_type": "markdown", "source": [], "metadata": { "id": "EgQw8osnsUV-" } }, { "cell_type": "markdown", "source": [ "## Лінійна та поліноміальна регресія для ціноутворення гарбузів - Урок 3\n", "

\n", " \n", "

Інфографіка від Дасані Мадіпаллі
\n", "\n", "\n", "#### Вступ\n", "\n", "До цього моменту ви досліджували, що таке регресія, використовуючи вибіркові дані з набору даних про ціноутворення гарбузів, який ми будемо використовувати протягом цього уроку. Ви також візуалізували ці дані за допомогою `ggplot2`. 💪\n", "\n", "Тепер ви готові заглибитися в регресію для машинного навчання. У цьому уроці ви дізнаєтеся більше про два типи регресії: *основну лінійну регресію* та *поліноміальну регресію*, а також про деякі математичні основи цих методів.\n", "\n", "> Протягом цього курсу ми припускаємо мінімальні знання математики та прагнемо зробити матеріал доступним для студентів з інших галузей, тому звертайте увагу на примітки, 🧮 підказки, діаграми та інші навчальні інструменти, які допоможуть у розумінні.\n", "\n", "#### Підготовка\n", "\n", "Нагадаємо, ви завантажуєте ці дані, щоб ставити запитання.\n", "\n", "- Коли найкращий час для купівлі гарбузів?\n", "\n", "- Яку ціну можна очікувати за ящик мініатюрних гарбузів?\n", "\n", "- Чи варто купувати їх у кошиках на пів-бушеля або в коробках на 1 1/9 бушеля? Давайте продовжимо досліджувати ці дані.\n", "\n", "У попередньому уроці ви створили `tibble` (сучасне переосмислення фрейму даних) і заповнили його частиною оригінального набору даних, стандартизуючи ціни за бушель. Однак, таким чином, ви змогли зібрати лише близько 400 точок даних і лише для осінніх місяців. Можливо, ми зможемо отримати трохи більше деталей про природу даних, якщо очистимо їх краще? Побачимо... 🕵️‍♀️\n", "\n", "Для цього завдання нам знадобляться наступні пакети:\n", "\n", "- `tidyverse`: [tidyverse](https://www.tidyverse.org/) — це [колекція пакетів для R](https://www.tidyverse.org/packages), яка робить науку про дані швидшою, простішою та цікавішою!\n", "\n", "- `tidymodels`: [tidymodels](https://www.tidymodels.org/) — це [фреймворк](https://www.tidymodels.org/packages/) для моделювання та машинного навчання, що складається з колекції пакетів.\n", "\n", "- `janitor`: [пакет janitor](https://github.com/sfirke/janitor) надає прості інструменти для аналізу та очищення \"брудних\" даних.\n", "\n", "- `corrplot`: [пакет corrplot](https://cran.r-project.org/web/packages/corrplot/vignettes/corrplot-intro.html) забезпечує візуальний інструмент для дослідження матриці кореляції, який підтримує автоматичне впорядкування змінних для виявлення прихованих закономірностей між змінними.\n", "\n", "Ви можете встановити їх за допомогою:\n", "\n", "`install.packages(c(\"tidyverse\", \"tidymodels\", \"janitor\", \"corrplot\"))`\n", "\n", "Скрипт нижче перевіряє, чи є у вас необхідні пакети для виконання цього модуля, і встановлює їх у разі відсутності.\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": [ "Ми пізніше завантажимо ці чудові пакети та зробимо їх доступними у нашій поточній R-сесії. (Це лише для ілюстрації, `pacman::p_load()` вже зробив це за вас)\n", "\n", "## 1. Лінія лінійної регресії\n", "\n", "Як ви дізналися в Уроці 1, мета вправи з лінійної регресії полягає в тому, щоб побудувати *лінію* *найкращого* *підходу*, щоб:\n", "\n", "- **Показати взаємозв’язки змінних**. Показати взаємозв’язок між змінними.\n", "\n", "- **Робити прогнози**. Робити точні прогнози щодо того, де нова точка даних може розташуватися відносно цієї лінії.\n", "\n", "Для побудови такого типу лінії ми використовуємо статистичний метод, який називається **регресія найменших квадратів**. Термін `найменші квадрати` означає, що всі точки даних навколо лінії регресії підносяться до квадрату, а потім додаються. Ідеально, щоб кінцева сума була якомога меншою, оскільки ми прагнемо до низької кількості помилок, або `найменших квадратів`. Таким чином, лінія найкращого підходу — це лінія, яка дає нам найнижче значення суми квадратів помилок — звідси назва *регресія найменших квадратів*.\n", "\n", "Ми робимо це, оскільки хочемо змоделювати лінію, яка має найменшу сумарну відстань від усіх наших точок даних. Ми також підносимо терміни до квадрату перед додаванням, оскільки нас цікавить їх величина, а не напрямок.\n", "\n", "> **🧮 Покажіть мені математику**\n", ">\n", "> Ця лінія, яка називається *лінією найкращого підходу*, може бути виражена [рівнянням](https://en.wikipedia.org/wiki/Simple_linear_regression):\n", ">\n", "> Y = a + bX\n", ">\n", "> `X` — це '`пояснювальна змінна` або `предиктор`'. `Y` — це '`залежна змінна` або `результат`'. Нахил лінії — це `b`, а `a` — це точка перетину з віссю Y, яка відповідає значенню `Y`, коли `X = 0`.\n", ">\n", "\n", "> ![](../../../../../../2-Regression/3-Linear/solution/images/slope.png \"нахил = $y/x$\")\n", " Інфографіка від Jen Looper\n", ">\n", "> Спочатку обчисліть нахил `b`.\n", ">\n", "> Іншими словами, і звертаючись до початкового питання про дані гарбузів: \"прогнозуйте ціну гарбуза за бушель залежно від місяця\", `X` буде означати ціну, а `Y` — місяць продажу.\n", ">\n", "> ![](../../../../../../2-Regression/3-Linear/solution/images/calculation.png)\n", " Інфографіка від Jen Looper\n", "> \n", "> Обчисліть значення Y. Якщо ви платите близько \\$4, це має бути квітень!\n", ">\n", "> Математика, яка обчислює лінію, повинна показувати нахил лінії, який також залежить від точки перетину, або де `Y` розташований, коли `X = 0`.\n", ">\n", "> Ви можете ознайомитися з методом обчислення цих значень на веб-сайті [Math is Fun](https://www.mathsisfun.com/data/least-squares-regression.html). Також відвідайте [цей калькулятор найменших квадратів](https://www.mathsisfun.com/data/least-squares-calculator.html), щоб побачити, як значення чисел впливають на лінію.\n", "\n", "Не так вже й страшно, правда? 🤓\n", "\n", "#### Кореляція\n", "\n", "Ще один термін, який потрібно зрозуміти, — це **коефіцієнт кореляції** між заданими змінними X і Y. Використовуючи діаграму розсіювання, ви можете швидко візуалізувати цей коефіцієнт. Графік із точками даних, розташованими в акуратній лінії, має високу кореляцію, але графік із точками даних, розкиданими хаотично між X і Y, має низьку кореляцію.\n", "\n", "Хороша модель лінійної регресії буде тією, яка має високий (ближче до 1, ніж до 0) коефіцієнт кореляції, використовуючи метод регресії найменших квадратів із лінією регресії.\n" ], "metadata": { "id": "cdX5FRpvsoP5" } }, { "cell_type": "markdown", "source": [ "## **2. Танці з даними: створення датафрейму для моделювання**\n", "\n", "

\n", " \n", "

Ілюстрація від @allison_horst
\n", "\n", "\n", "\n" ], "metadata": { "id": "WdUKXk7Bs8-V" } }, { "cell_type": "markdown", "source": [ "Завантажте необхідні бібліотеки та набір даних. Перетворіть дані у датафрейм, що містить підмножину даних:\n", "\n", "- Отримайте лише гарбузи, ціна яких вказана за бушель\n", "\n", "- Перетворіть дату на місяць\n", "\n", "- Розрахуйте ціну як середнє значення між високою та низькою ціною\n", "\n", "- Перетворіть ціну, щоб вона відображала вартість за кількість у бушелях\n", "\n", "> Ми розглянули ці кроки у [попередньому уроці](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": [ "У дусі чистої пригоди давайте дослідимо [`janitor package`](../../../../../../2-Regression/3-Linear/solution/R/github.com/sfirke/janitor), який надає прості функції для аналізу та очищення брудних даних. Наприклад, давайте поглянемо на назви стовпців у наших даних:\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": [ "🤔 Ми можемо зробити краще. Давайте зробимо ці назви стовпців `friendR`, перетворивши їх у формат [snake_case](https://en.wikipedia.org/wiki/Snake_case) за допомогою `janitor::clean_names`. Щоб дізнатися більше про цю функцію: `?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": [ "Багато tidyR 🧹! Тепер танець з даними за допомогою `dplyr`, як у попередньому уроці! 💃\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": [ "Молодець!👌 Тепер у вас є чистий, впорядкований набір даних, на основі якого можна створити нову модель регресії!\n", "\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": [ "Діаграма розсіювання нагадує нам, що у нас є дані лише за місяці з серпня по грудень. Ймовірно, нам потрібно більше даних, щоб мати змогу робити висновки лінійним способом.\n", "\n", "Давайте ще раз подивимося на наші дані для моделювання:\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": [ "А що, якби ми хотіли передбачити `price` гарбуза на основі стовпців `city` або `package`, які є текстовими? Або навіть простіше, як би ми могли знайти кореляцію (яка вимагає, щоб обидва її вхідні значення були числовими) між, наприклад, `package` і `price`? 🤷🤷\n", "\n", "Моделі машинного навчання найкраще працюють із числовими ознаками, а не текстовими значеннями, тому зазвичай потрібно перетворювати категоріальні ознаки на числові представлення.\n", "\n", "Це означає, що нам потрібно знайти спосіб переформатувати наші предиктори, щоб зробити їх зручнішими для використання моделлю, процес, який називається `feature engineering`.\n" ], "metadata": { "id": "7hsHoxsStyjJ" } }, { "cell_type": "markdown", "source": [ "## 3. Підготовка даних для моделювання за допомогою recipes 👩‍🍳👨‍🍳\n", "\n", "Дії, які змінюють формат значень предикторів, щоб зробити їх більш зручними для використання моделлю, називаються `інженерією ознак`.\n", "\n", "Різні моделі мають різні вимоги до попередньої обробки даних. Наприклад, метод найменших квадратів вимагає `кодування категоріальних змінних`, таких як місяць, сорт і назва міста. Це просто передбачає `перетворення` стовпця з `категоріальними значеннями` в один або кілька `числових стовпців`, які замінюють оригінальний.\n", "\n", "Наприклад, припустимо, що ваші дані містять таку категоріальну ознаку:\n", "\n", "| місто |\n", "|:--------:|\n", "| Денвер |\n", "| Найробі |\n", "| Токіо |\n", "\n", "Ви можете застосувати *порядкове кодування*, щоб замінити кожну категорію унікальним цілим числом, ось так:\n", "\n", "| місто |\n", "|:-----:|\n", "| 0 |\n", "| 1 |\n", "| 2 |\n", "\n", "І саме це ми зробимо з нашими даними!\n", "\n", "У цьому розділі ми ознайомимося з іншим чудовим пакетом Tidymodels: [recipes](https://tidymodels.github.io/recipes/) - який створений для того, щоб допомогти вам підготувати дані **перед** тренуванням моделі. У своїй основі рецепт — це об'єкт, який визначає, які кроки слід застосувати до набору даних, щоб підготувати його до моделювання.\n", "\n", "Тепер давайте створимо рецепт, який підготує наші дані для моделювання, замінюючи унікальним цілим числом всі спостереження у стовпцях предикторів:\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": [ "Чудово! 👏 Ми щойно створили наш перший рецепт, який визначає результат (ціна) та відповідні предиктори, а також те, що всі стовпці предикторів мають бути закодовані у набір цілих чисел 🙌! Давайте швидко розберемо це:\n", "\n", "- Виклик `recipe()` із формулою повідомляє рецепту *ролі* змінних, використовуючи дані `new_pumpkins` як еталон. Наприклад, стовпцю `price` було призначено роль `outcome`, тоді як решті стовпців було призначено роль `predictor`.\n", "\n", "- `step_integer(all_predictors(), zero_based = TRUE)` визначає, що всі предиктори мають бути перетворені у набір цілих чисел, починаючи з нуля.\n", "\n", "Ми впевнені, що у вас можуть виникати думки на кшталт: \"Це так круто!! Але що, якщо мені потрібно переконатися, що рецепти роблять саме те, що я очікую? 🤔\"\n", "\n", "Це чудова думка! Розумієте, як тільки ваш рецепт визначено, ви можете оцінити параметри, необхідні для фактичної попередньої обробки даних, а потім витягти оброблені дані. Зазвичай вам не потрібно робити це, коли ви використовуєте Tidymodels (ми побачимо звичайну практику за хвилину -> `workflows`), але це може бути корисним, якщо ви хочете провести перевірку, щоб переконатися, що рецепти працюють так, як ви очікуєте.\n", "\n", "Для цього вам знадобляться ще два дієслова: `prep()` та `bake()`, і, як завжди, наші маленькі друзі з R від [`Allison Horst`](https://github.com/allisonhorst/stats-illustrations) допоможуть вам краще зрозуміти це!\n", "\n", "

\n", " \n", "

Ілюстрація від @allison_horst
\n" ], "metadata": { "id": "KEiO0v7kuC9O" } }, { "cell_type": "markdown", "source": [ "[`prep()`](https://recipes.tidymodels.org/reference/prep.html): оцінює необхідні параметри з навчального набору, які потім можуть бути застосовані до інших наборів даних. Наприклад, для заданого стовпця предиктора, яке спостереження буде призначено цілому числу 0, 1, 2 тощо.\n", "\n", "[`bake()`](https://recipes.tidymodels.org/reference/bake.html): бере підготовлений рецепт і застосовує операції до будь-якого набору даних.\n", "\n", "Отже, давайте підготуємо та застосуємо наші рецепти, щоб дійсно переконатися, що \"під капотом\" стовпці предикторів спочатку будуть закодовані перед тим, як модель буде побудована.\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": [ "Ура! 🥳 Оброблені дані `baked_pumpkins` мають всі свої предиктори закодовані, що підтверджує, що кроки попередньої обробки, визначені як наш рецепт, працюють як очікувалося. Це робить дані складнішими для читання, але набагато зрозумілішими для Tidymodels! Приділіть трохи часу, щоб з'ясувати, яке спостереження було зіставлено з відповідним цілим числом.\n", "\n", "Також варто зазначити, що `baked_pumpkins` — це датафрейм, на якому ми можемо виконувати обчислення.\n", "\n", "Наприклад, давайте спробуємо знайти хорошу кореляцію між двома точками ваших даних, щоб потенційно побудувати хороший предиктивний модель. Для цього ми використаємо функцію `cor()`. Введіть `?cor()`, щоб дізнатися більше про цю функцію.\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": [ "Як виявилося, між Містом і Ціною існує лише слабка кореляція. Однак між Пакетом і його Ціною кореляція трохи сильніша. Це логічно, чи не так? Зазвичай, чим більша коробка з продуктами, тим вища ціна.\n", "\n", "Поки ми цим займаємося, давайте також спробуємо візуалізувати матрицю кореляції для всіх стовпців, використовуючи пакет `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": [ "🤩🤩 Набагато краще.\n", "\n", "Гарне питання, яке тепер можна поставити до цих даних: '`Яку ціну я можу очікувати за певний пакет гарбузів?`' Давайте розберемося!\n", "\n", "> Примітка: Коли ви **`bake()`** підготовлений рецепт **`pumpkins_prep`** з **`new_data = NULL`**, ви отримуєте оброблені (тобто закодовані) навчальні дані. Якщо у вас є інший набір даних, наприклад тестовий набір, і ви хочете побачити, як рецепт його попередньо обробить, просто виконайте **`bake`** для **`pumpkins_prep`** з **`new_data = test_set`**.\n", "\n", "## 4. Побудова моделі лінійної регресії\n", "\n", "

\n", " \n", "

Інфографіка від Дасані Мадіпаллі
\n", "\n", "\n", "\n" ], "metadata": { "id": "YqXjLuWavNxW" } }, { "cell_type": "markdown", "source": [ "Тепер, коли ми створили рецепт і фактично підтвердили, що дані будуть належним чином попередньо оброблені, давайте побудуємо модель регресії, щоб відповісти на запитання: `Яку ціну я можу очікувати для заданого пакунка гарбуза?`\n", "\n", "#### Навчання моделі лінійної регресії за допомогою навчального набору\n", "\n", "Як ви, можливо, вже здогадалися, стовпець *price* є змінною `результату`, тоді як стовпець *package* є змінною `предиктором`.\n", "\n", "Для цього ми спочатку розділимо дані таким чином, щоб 80% потрапило до навчального набору, а 20% — до тестового набору, потім визначимо рецепт, який закодує стовпець предиктора у набір цілих чисел, а потім створимо специфікацію моделі. Ми не будемо готувати та випікати наш рецепт, оскільки вже знаємо, що він попередньо обробить дані належним чином.\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": [ "Чудова робота! Тепер, коли у нас є рецепт і специфікація моделі, потрібно знайти спосіб об'єднати їх в один об'єкт, який спочатку буде виконувати попередню обробку даних (prep+bake за лаштунками), навчати модель на попередньо оброблених даних, а також дозволяти потенційні дії після обробки. Як вам така впевненість!🤩\n", "\n", "У Tidymodels цей зручний об'єкт називається [`workflow`](https://workflows.tidymodels.org/) і зручно зберігає ваші компоненти моделювання! Це те, що ми назвали б *пайплайнами* у *Python*.\n", "\n", "Отже, давайте об'єднаємо все в 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": [ "👌 До того ж, робочий процес можна налаштувати/тренувати майже так само, як і модель.\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": [ "З результатів моделі ми можемо побачити коефіцієнти, отримані під час навчання. Вони представляють коефіцієнти лінії найкращого наближення, яка забезпечує найменшу загальну помилку між фактичними та прогнозованими змінними.\n", "\n", "#### Оцінка продуктивності моделі за допомогою тестового набору\n", "\n", "Час перевірити, як модель справляється 📏! Як це зробити?\n", "\n", "Тепер, коли ми навчили модель, ми можемо використовувати її для створення прогнозів для `test_set` за допомогою `parsnip::predict()`. Потім ми можемо порівняти ці прогнози з фактичними значеннями міток, щоб оцінити, наскільки добре (або не дуже!) працює модель.\n", "\n", "Давайте почнемо з створення прогнозів для тестового набору, а потім об'єднаємо ці стовпці з тестовим набором.\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": [ "Так, ви щойно натренували модель і використали її для прогнозування! 🔮 Чи вона хороша? Давайте оцінемо продуктивність моделі!\n", "\n", "У Tidymodels ми робимо це за допомогою `yardstick::metrics()`! Для лінійної регресії зосередимося на наступних метриках:\n", "\n", "- `Root Mean Square Error (RMSE)`: Квадратний корінь [MSE](https://en.wikipedia.org/wiki/Mean_squared_error). Це дає абсолютну метрику в тих самих одиницях, що й мітка (у цьому випадку ціна гарбуза). Чим менше значення, тим краща модель (у спрощеному сенсі це представляє середню величину, на яку прогнози помиляються!).\n", "\n", "- `Coefficient of Determination (зазвичай відомий як R-squared або R2)`: Відносна метрика, де чим більше значення, тим краще модель відповідає даним. По суті, ця метрика показує, яку частину варіації між прогнозованими та фактичними значеннями міток модель здатна пояснити.\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": [ "Ось і продуктивність моделі. Давайте спробуємо отримати кращу оцінку, візуалізуючи точковий графік пакета та ціни, а потім використаємо зроблені прогнози, щоб накласти лінію найкращого підходу.\n", "\n", "Це означає, що нам потрібно підготувати та обробити тестовий набір, щоб закодувати стовпець пакета, а потім об'єднати це з прогнозами, зробленими нашою моделлю.\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": [ "Чудово! Як бачите, модель лінійної регресії не дуже добре узагальнює взаємозв'язок між пакунком і його відповідною ціною.\n", "\n", "🎃 Вітаємо, ви щойно створили модель, яка може допомогти передбачити ціну кількох сортів гарбузів. Ваш святковий гарбузовий сад буде чудовим. Але, ймовірно, ви можете створити кращу модель!\n", "\n", "## 5. Створіть модель поліноміальної регресії\n", "\n", "

\n", " \n", "

Інфографіка від Дасані Мадіпаллі
\n", "\n", "\n", "\n" ], "metadata": { "id": "HOCqJXLTwtWI" } }, { "cell_type": "markdown", "source": [ "Іноді наші дані можуть не мати лінійної залежності, але ми все одно хочемо передбачити результат. Поліноміальна регресія може допомогти нам робити прогнози для більш складних нелінійних залежностей.\n", "\n", "Візьмемо, наприклад, залежність між упаковкою та ціною в нашому наборі даних про гарбузи. Хоча іноді між змінними існує лінійна залежність — чим більший гарбуз за об'ємом, тим вища ціна — іноді ці залежності не можна зобразити у вигляді площини або прямої лінії.\n", "\n", "> ✅ Ось [декілька прикладів](https://online.stat.psu.edu/stat501/lesson/9/9.8) даних, які можуть використовувати поліноміальну регресію\n", ">\n", "> Погляньте ще раз на залежність між сортом і ціною на попередньому графіку. Чи здається, що цей розсіювальний графік обов’язково слід аналізувати за допомогою прямої лінії? Можливо, ні. У цьому випадку ви можете спробувати поліноміальну регресію.\n", ">\n", "> ✅ Поліноми — це математичні вирази, які можуть складатися з однієї або кількох змінних і коефіцієнтів\n", "\n", "#### Навчання моделі поліноміальної регресії за допомогою навчального набору\n", "\n", "Поліноміальна регресія створює *криву лінію*, щоб краще відповідати нелінійним даним.\n", "\n", "Давайте перевіримо, чи поліноміальна модель буде краще виконувати прогнози. Ми будемо дотримуватися схожої процедури, як і раніше:\n", "\n", "- Створити рецепт, який визначає кроки попередньої обробки, які слід виконати над нашими даними, щоб підготувати їх до моделювання, тобто: кодування предикторів і обчислення поліномів ступеня *n*\n", "\n", "- Побудувати специфікацію моделі\n", "\n", "- Об'єднати рецепт і специфікацію моделі в робочий процес\n", "\n", "- Створити модель шляхом налаштування робочого процесу\n", "\n", "- Оцінити, наскільки добре модель працює на тестових даних\n", "\n", "Давайте почнемо!\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": [ "#### Оцінка продуктивності моделі\n", "\n", "👏👏Ви створили поліноміальну модель, давайте зробимо прогнози на тестовому наборі!\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": [ "Ура, давайте оцінимо, як модель виконала на test_set, використовуючи `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": [ "🤩🤩 Значно краща продуктивність.\n", "\n", "`rmse` зменшився приблизно з 7 до 3, що свідчить про зменшення помилки між фактичною ціною та прогнозованою ціною. Це можна *вільно* інтерпретувати як те, що в середньому неправильні прогнози помиляються приблизно на \\$3. `rsq` зріс приблизно з 0.4 до 0.8.\n", "\n", "Усі ці метрики свідчать про те, що поліноміальна модель працює значно краще, ніж лінійна модель. Чудова робота!\n", "\n", "Давайте спробуємо це візуалізувати!\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": [ "Ви можете побачити криву, яка краще відповідає вашим даним! 🤩\n", "\n", "Ви можете зробити її більш плавною, передавши поліноміальну формулу до `geom_smooth` ось так:\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": [ "Дуже схоже на плавну криву!🤩\n", "\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": [ "Прогнозування за допомогою `поліноміальної моделі` дійсно має сенс, враховуючи діаграми розсіювання для `price` та `package`! І якщо ця модель краща за попередню, дивлячись на ті ж самі дані, вам потрібно врахувати бюджет для цих дорожчих гарбузів!\n", "\n", "🏆 Чудова робота! Ви створили дві регресійні моделі за один урок. У фінальному розділі про регресію ви дізнаєтеся про логістичну регресію для визначення категорій.\n", "\n", "## **🚀Виклик**\n", "\n", "Перевірте кілька різних змінних у цьому ноутбуці, щоб побачити, як кореляція впливає на точність моделі.\n", "\n", "## [**Тест після лекції**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/14/)\n", "\n", "## **Огляд і самостійне навчання**\n", "\n", "У цьому уроці ми вивчили лінійну регресію. Є й інші важливі типи регресії. Прочитайте про методи Stepwise, Ridge, Lasso та Elasticnet. Хороший курс для подальшого вивчення — це [курс зі статистичного навчання Стенфорда](https://online.stanford.edu/courses/sohs-ystatslearning-statistical-learning).\n", "\n", "Якщо ви хочете дізнатися більше про використання дивовижного фреймворку Tidymodels, ознайомтеся з наступними ресурсами:\n", "\n", "- Вебсайт Tidymodels: [Почніть з Tidymodels](https://www.tidymodels.org/start/)\n", "\n", "- Макс Кун і Джулія Сілдж, [*Tidy Modeling with R*](https://www.tmwr.org/)*.*\n", "\n", "###### **ДЯКУЄМО:**\n", "\n", "[Елісон Хорст](https://twitter.com/allison_horst?lang=en) за створення дивовижних ілюстрацій, які роблять R більш доступним і цікавим. Знайдіть більше ілюстрацій у її [галереї](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**Відмова від відповідальності**: \nЦей документ було перекладено за допомогою сервісу автоматичного перекладу [Co-op Translator](https://github.com/Azure/co-op-translator). Хоча ми прагнемо до точності, будь ласка, майте на увазі, що автоматичні переклади можуть містити помилки або неточності. Оригінальний документ на його рідній мові слід вважати авторитетним джерелом. Для критично важливої інформації рекомендується професійний людський переклад. Ми не несемо відповідальності за будь-які непорозуміння або неправильні тлумачення, що виникають у результаті використання цього перекладу.\n" ] } ] }