{ "nbformat": 4, "nbformat_minor": 2, "metadata": { "colab": { "name": "lesson_2-R.ipynb", "provenance": [], "collapsed_sections": [], "toc_visible": true }, "kernelspec": { "name": "ir", "display_name": "R" }, "language_info": { "name": "R" }, "coopTranslator": { "original_hash": "f3c335f9940cfd76528b3ef918b9b342", "translation_date": "2025-09-04T07:15:50+00:00", "source_file": "2-Regression/2-Data/solution/R/lesson_2-R.ipynb", "language_code": "da" } }, "cells": [ { "cell_type": "markdown", "source": [ "# Byg en regressionsmodel: forbered og visualiser data\n", "\n", "## **Lineær regression for græskar - Lektion 2**\n", "#### Introduktion\n", "\n", "Nu hvor du har de nødvendige værktøjer til at begynde at bygge maskinlæringsmodeller med Tidymodels og Tidyverse, er du klar til at begynde at stille spørgsmål til dine data. Når du arbejder med data og anvender ML-løsninger, er det meget vigtigt at forstå, hvordan man stiller de rigtige spørgsmål for korrekt at udnytte potentialet i dit datasæt.\n", "\n", "I denne lektion vil du lære:\n", "\n", "- Hvordan du forbereder dine data til modelbygning.\n", "\n", "- Hvordan du bruger `ggplot2` til datavisualisering.\n", "\n", "Det spørgsmål, du ønsker besvaret, vil afgøre, hvilken type ML-algoritmer du skal anvende. Og kvaliteten af det svar, du får tilbage, vil i høj grad afhænge af karakteren af dine data.\n", "\n", "Lad os se dette ved at arbejde igennem en praktisk øvelse.\n", "\n", "\n", "
\n",
" \n",
"
\n",
"\n",
"> En genopfriskning: Pipe-operatoren (`%>%`) udfører operationer i logisk rækkefølge ved at sende et objekt videre ind i en funktion eller et kald. Du kan tænke på pipe-operatoren som at sige \"og så\" i din kode.\n"
],
"metadata": {
"id": "REWcIv9yX29v"
}
},
{
"cell_type": "markdown",
"source": [
"## 2. Kontroller for manglende data\n",
"\n",
"En af de mest almindelige udfordringer, som dataforskere skal håndtere, er ufuldstændige eller manglende data. R repræsenterer manglende eller ukendte værdier med en speciel sentinelværdi: `NA` (Not Available).\n",
"\n",
"Så hvordan kan vi finde ud af, om dataframen indeholder manglende værdier?\n",
"
\n",
"- En direkte metode ville være at bruge base R-funktionen `anyNA`, som returnerer de logiske objekter `TRUE` eller `FALSE`.\n"
],
"metadata": {
"id": "Zxfb3AM5YbUe"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"pumpkins %>% \n",
" anyNA()"
],
"outputs": [],
"metadata": {
"id": "G--DQutAYltj"
}
},
{
"cell_type": "markdown",
"source": [
"Fantastisk, det ser ud til, at der mangler nogle data! Det er et godt sted at starte.\n",
"\n",
"- En anden måde ville være at bruge funktionen `is.na()`, som angiver, hvilke individuelle kolonneelementer der mangler, med en logisk `TRUE`.\n"
],
"metadata": {
"id": "mU-7-SB6YokF"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"pumpkins %>% \n",
" is.na() %>% \n",
" head(n = 7)"
],
"outputs": [],
"metadata": {
"id": "W-DxDOR4YxSW"
}
},
{
"cell_type": "markdown",
"source": [
"Okay, fik jobbet gjort, men med en stor dataramme som denne ville det være ineffektivt og praktisk talt umuligt at gennemgå alle rækker og kolonner individuelt😴.\n",
"\n",
"- En mere intuitiv måde ville være at beregne summen af de manglende værdier for hver kolonne:\n"
],
"metadata": {
"id": "xUWxipKYY0o7"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"pumpkins %>% \n",
" is.na() %>% \n",
" colSums()"
],
"outputs": [],
"metadata": {
"id": "ZRBWV6P9ZArL"
}
},
{
"cell_type": "markdown",
"source": [
"Meget bedre! Der mangler data, men måske vil det ikke have betydning for opgaven. Lad os se, hvad yderligere analyse bringer frem.\n",
"\n",
"> Sammen med de fantastiske sæt af pakker og funktioner har R en meget god dokumentation. For eksempel kan du bruge `help(colSums)` eller `?colSums` for at finde ud af mere om funktionen.\n"
],
"metadata": {
"id": "9gv-crB6ZD1Y"
}
},
{
"cell_type": "markdown",
"source": [
"## 3. Dplyr: En grammatik for datamanipulation\n",
"\n",
"
\n",
" \n",
"
\n"
],
"metadata": {
"id": "i5o33MQBZWWw"
}
},
{
"cell_type": "markdown",
"source": [
"#### dplyr::select()\n",
"\n",
"`select()` er en funktion i pakken `dplyr`, som hjælper dig med at vælge kolonner, du vil beholde eller udelade.\n",
"\n",
"For at gøre din data frame lettere at arbejde med, kan du fjerne flere af dens kolonner ved hjælp af `select()` og kun beholde de kolonner, du har brug for.\n",
"\n",
"For eksempel vil vores analyse i denne øvelse involvere kolonnerne `Package`, `Low Price`, `High Price` og `Date`. Lad os vælge disse kolonner.\n"
],
"metadata": {
"id": "x3VGMAGBZiUr"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"# Select desired columns\n",
"pumpkins <- pumpkins %>% \n",
" select(Package, `Low Price`, `High Price`, Date)\n",
"\n",
"\n",
"# Print data set\n",
"pumpkins %>% \n",
" slice_head(n = 5)"
],
"outputs": [],
"metadata": {
"id": "F_FgxQnVZnM0"
}
},
{
"cell_type": "markdown",
"source": [
"#### dplyr::mutate()\n",
"\n",
"`mutate()` er en funktion i pakken `dplyr`, som hjælper dig med at oprette eller ændre kolonner, samtidig med at de eksisterende kolonner bevares.\n",
"\n",
"Den generelle struktur for `mutate` er:\n",
"\n",
"`data %>% mutate(new_column_name = what_it_contains)`\n",
"\n",
"Lad os prøve `mutate` af ved at bruge `Date`-kolonnen og udføre følgende operationer:\n",
"\n",
"1. Konverter datoerne (som i øjeblikket er af typen karakter) til et månedsformat (disse er amerikanske datoer, så formatet er `MM/DD/YYYY`).\n",
"\n",
"2. Uddrag måneden fra datoerne til en ny kolonne.\n",
"\n",
"I R gør pakken [lubridate](https://lubridate.tidyverse.org/) det nemmere at arbejde med dato-tidsdata. Så lad os bruge `dplyr::mutate()`, `lubridate::mdy()`, `lubridate::month()` og se, hvordan vi kan opnå ovenstående mål. Vi kan fjerne `Date`-kolonnen, da vi ikke får brug for den igen i de efterfølgende operationer.\n"
],
"metadata": {
"id": "2KKo0Ed9Z1VB"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"# Load lubridate\n",
"library(lubridate)\n",
"\n",
"pumpkins <- pumpkins %>% \n",
" # Convert the Date column to a date object\n",
" mutate(Date = mdy(Date)) %>% \n",
" # Extract month from Date\n",
" mutate(Month = month(Date)) %>% \n",
" # Drop Date column\n",
" select(-Date)\n",
"\n",
"# View the first few rows\n",
"pumpkins %>% \n",
" slice_head(n = 7)"
],
"outputs": [],
"metadata": {
"id": "5joszIVSZ6xe"
}
},
{
"cell_type": "markdown",
"source": [
"Woohoo! 🤩\n",
"\n",
"Lad os nu oprette en ny kolonne `Price`, som repræsenterer gennemsnitsprisen på et græskar. For at udfylde den nye Price-kolonne tager vi gennemsnittet af kolonnerne `Low Price` og `High Price`. \n",
"
\n"
],
"metadata": {
"id": "nIgLjNMCZ-6Y"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"# Create a new column Price\n",
"pumpkins <- pumpkins %>% \n",
" mutate(Price = (`Low Price` + `High Price`)/2)\n",
"\n",
"# View the first few rows of the data\n",
"pumpkins %>% \n",
" slice_head(n = 5)"
],
"outputs": [],
"metadata": {
"id": "Zo0BsqqtaJw2"
}
},
{
"cell_type": "markdown",
"source": [
"Yeees!💪\n",
"\n",
"\"Men vent lige!\", vil du sige efter at have skimmet hele datasættet med `View(pumpkins)`, \"Der er noget mærkeligt her!\"🤔\n",
"\n",
"Hvis du kigger på kolonnen `Package`, bliver græskar solgt i mange forskellige konfigurationer. Nogle sælges i mål af `1 1/9 bushel`, nogle i `1/2 bushel`, nogle per græskar, nogle per pund, og nogle i store kasser med varierende bredder.\n",
"\n",
"Lad os verificere dette:\n"
],
"metadata": {
"id": "p77WZr-9aQAR"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"# Verify the distinct observations in Package column\n",
"pumpkins %>% \n",
" distinct(Package)"
],
"outputs": [],
"metadata": {
"id": "XISGfh0IaUy6"
}
},
{
"cell_type": "markdown",
"source": [
"Fantastisk!👏\n",
"\n",
"Græskar ser ud til at være meget svære at veje konsekvent, så lad os filtrere dem ved kun at vælge græskar med strengen *bushel* i `Package`-kolonnen og lægge dette i en ny data frame `new_pumpkins`. \n",
"
\n"
],
"metadata": {
"id": "7sMjiVujaZxY"
}
},
{
"cell_type": "markdown",
"source": [
"#### dplyr::filter() og stringr::str_detect()\n",
"\n",
"[`dplyr::filter()`](https://dplyr.tidyverse.org/reference/filter.html): opretter et datasæt, der kun indeholder **rækker**, som opfylder dine betingelser, i dette tilfælde græskar med teksten *bushel* i kolonnen `Package`.\n",
"\n",
"[stringr::str_detect()](https://stringr.tidyverse.org/reference/str_detect.html): registrerer tilstedeværelsen eller fraværet af et mønster i en tekst.\n",
"\n",
"Pakken [`stringr`](https://github.com/tidyverse/stringr) tilbyder simple funktioner til almindelige tekstoperationer.\n"
],
"metadata": {
"id": "L8Qfcs92ageF"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"# Retain only pumpkins with \"bushel\"\n",
"new_pumpkins <- pumpkins %>% \n",
" filter(str_detect(Package, \"bushel\"))\n",
"\n",
"# Get the dimensions of the new data\n",
"dim(new_pumpkins)\n",
"\n",
"# View a few rows of the new data\n",
"new_pumpkins %>% \n",
" slice_head(n = 5)"
],
"outputs": [],
"metadata": {
"id": "hy_SGYREampd"
}
},
{
"cell_type": "markdown",
"source": [
"Du kan se, at vi har indsnævret det til omkring 415 rækker med data, der indeholder græskar i store mængder.🤩 \n",
"
\n"
],
"metadata": {
"id": "VrDwF031avlR"
}
},
{
"cell_type": "markdown",
"source": [
"#### dplyr::case_when()\n",
"\n",
"**Men vent! Der er én ting mere, du skal gøre**\n",
"\n",
"Lagde du mærke til, at mængden af bushels varierer fra række til række? Du skal normalisere prissætningen, så du viser prisen per bushel og ikke per 1 1/9 eller 1/2 bushel. Det er tid til at lave lidt matematik for at standardisere det.\n",
"\n",
"Vi bruger funktionen [`case_when()`](https://dplyr.tidyverse.org/reference/case_when.html) til at *mutere* kolonnen Price afhængigt af nogle betingelser. `case_when` giver dig mulighed for at vektorisere flere `if_else()`-udsagn.\n"
],
"metadata": {
"id": "mLpw2jH4a0tx"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"# Convert the price if the Package contains fractional bushel values\n",
"new_pumpkins <- new_pumpkins %>% \n",
" mutate(Price = case_when(\n",
" str_detect(Package, \"1 1/9\") ~ Price/(1 + 1/9),\n",
" str_detect(Package, \"1/2\") ~ Price/(1/2),\n",
" TRUE ~ Price))\n",
"\n",
"# View the first few rows of the data\n",
"new_pumpkins %>% \n",
" slice_head(n = 30)"
],
"outputs": [],
"metadata": {
"id": "P68kLVQmbM6I"
}
},
{
"cell_type": "markdown",
"source": [
"Nu kan vi analysere prisen pr. enhed baseret på deres bushel-mål. Hele denne undersøgelse af bushels af græskar viser dog, hvor `vigtigt` det er at `forstå naturen af dine data`!\n",
"\n",
"> ✅ Ifølge [The Spruce Eats](https://www.thespruceeats.com/how-much-is-a-bushel-1389308) afhænger en bushels vægt af typen af afgrøde, da det er en volumenmåling. \"En bushel tomater, for eksempel, skal veje 56 pund... Blade og grønt fylder mere med mindre vægt, så en bushel spinat vejer kun 20 pund.\" Det er alt sammen ret kompliceret! Lad os undlade at lave en bushel-til-pund-konvertering og i stedet prissætte efter bushel. Hele denne undersøgelse af bushels af græskar viser dog, hvor vigtigt det er at forstå naturen af dine data!\n",
">\n",
"> ✅ Lagde du mærke til, at græskar solgt pr. halv-bushel er meget dyre? Kan du finde ud af hvorfor? Hint: små græskar er langt dyrere end store, sandsynligvis fordi der er så mange flere af dem pr. bushel, givet den ubrugte plads, som ét stort, hult tærtegræskar optager.\n"
],
"metadata": {
"id": "pS2GNPagbSdb"
}
},
{
"cell_type": "markdown",
"source": [
"Vigtigt, lad os også flytte kolonnen \"Month\" til den første position, altså **før** kolonnen \"Package\".\n",
"\n",
"`dplyr::relocate()` bruges til at ændre kolonnepositioner.\n"
],
"metadata": {
"id": "qql1SowfbdnP"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"# Create a new data frame new_pumpkins\n",
"new_pumpkins <- new_pumpkins %>% \n",
" relocate(Month, .before = Package)\n",
"\n",
"new_pumpkins %>% \n",
" slice_head(n = 7)"
],
"outputs": [],
"metadata": {
"id": "JJ1x6kw8bixF"
}
},
{
"cell_type": "markdown",
"source": [
"Godt klaret!👌 Du har nu et rent og ryddeligt datasæt, som du kan bruge til at bygge din nye regressionsmodel! \n",
"
\n"
],
"metadata": {
"id": "y8TJ0Za_bn5Y"
}
},
{
"cell_type": "markdown",
"source": [
"## 4. Datavisualisering med ggplot2\n",
"\n",
"
\n",
" \n",
"
\n"
],
"metadata": {
"id": "Ml7SDCLQcPvE"
}
},
{
"cell_type": "markdown",
"source": [
"### **Hvordan gør vi det nyttigt?**\n",
"\n",
"For at få grafer til at vise nyttige data, skal du normalt gruppere dataene på en eller anden måde. For eksempel i vores tilfælde vil det at finde gennemsnitsprisen på græskar for hver måned give mere indsigt i de underliggende mønstre i vores data. Dette leder os til endnu en **dplyr** gennemgang:\n",
"\n",
"#### `dplyr::group_by() %>% summarize()`\n",
"\n",
"Grupperet aggregering i R kan nemt beregnes ved hjælp af\n",
"\n",
"`dplyr::group_by() %>% summarize()`\n",
"\n",
"- `dplyr::group_by()` ændrer analyseenheden fra det komplette datasæt til individuelle grupper, såsom pr. måned.\n",
"\n",
"- `dplyr::summarize()` opretter en ny data frame med én kolonne for hver grupperingsvariabel og én kolonne for hver af de opsummeringsstatistikker, du har angivet.\n",
"\n",
"For eksempel kan vi bruge `dplyr::group_by() %>% summarize()` til at gruppere græskarrene i grupper baseret på **Month**-kolonnen og derefter finde **gennemsnitsprisen** for hver måned.\n"
],
"metadata": {
"id": "jMakvJZIcVkh"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"# Find the average price of pumpkins per month\r\n",
"new_pumpkins %>%\r\n",
" group_by(Month) %>% \r\n",
" summarise(mean_price = mean(Price))"
],
"outputs": [],
"metadata": {
"id": "6kVSUa2Bcilf"
}
},
{
"cell_type": "markdown",
"source": [
"Kort og godt!✨\n",
"\n",
"Kategoriske funktioner som måneder repræsenteres bedst ved hjælp af et søjlediagram 📊. De lag, der bruges til søjlediagrammer, er `geom_bar()` og `geom_col()`. Se `?geom_bar` for at lære mere.\n",
"\n",
"Lad os lave et!\n"
],
"metadata": {
"id": "Kds48GUBcj3W"
}
},
{
"cell_type": "code",
"execution_count": null,
"source": [
"# Find the average price of pumpkins per month then plot a bar chart\r\n",
"new_pumpkins %>%\r\n",
" group_by(Month) %>% \r\n",
" summarise(mean_price = mean(Price)) %>% \r\n",
" ggplot(aes(x = Month, y = mean_price)) +\r\n",
" geom_col(fill = \"midnightblue\", alpha = 0.7) +\r\n",
" ylab(\"Pumpkin Price\")"
],
"outputs": [],
"metadata": {
"id": "VNbU1S3BcrxO"
}
},
{
"cell_type": "markdown",
"source": [
"🤩🤩Dette er en mere nyttig datavisualisering! Det ser ud til at indikere, at den højeste pris for græskar forekommer i september og oktober. Stemmer det overens med dine forventninger? Hvorfor eller hvorfor ikke?\n",
"\n",
"Tillykke med at have afsluttet den anden lektion 👏! Du forberedte dine data til modelbygning og opdagede derefter flere indsigter ved hjælp af visualiseringer!\n"
],
"metadata": {
"id": "zDm0VOzzcuzR"
}
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Ansvarsfraskrivelse**: \nDette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på at sikre nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.\n"
]
}
]
}