21  Dataanalys i R för nybörjare

(där du lär dig att hantera och förstå data)

Världen svämmar över att data. I det här kapitlet får du bekanta dig med R och Quarto och se hur de hjälper dig att arbeta med data. Du lär dig att importera, undersöka och analysera data – och att göra resultaten begripliga för andra.

Att bli ett proffs på data och information tar tid, men i det här kapitlet får du ta dina första steg. Vi ska göra tre saker. Först ska jag lära dig att göra en snygg graf. Därefter lär du dig att välja ut, förändra, gruppera och sammanfatta ditt datamaterial. Sist i kapitlet ska vi titta på hur du importerar och kontrollerar ditt data, löser vanliga problem och samlar ihop kod, text och resultat i ett professionellt Quarto-dokument.

21.1 Att rita en proffsig graf

Visste du att en välgjord graf kan avslöja mönster i komplexa data snabbare och tydligare än tusentals rader i en tabell? Att kunna visualisera sina data är därför guld värt. Här ska du därför få lära dig grunderna i paketet ggplot2 i R.

Vi kommer att använda så kallad explicit kodning, vilket innebär att vi skriver tydligt och pedagogiskt i stället för så kort som möjligt. Det gör koden lättare att läsa och förstå, både för dig själv och för andra. Det här kommer du alldeles snart att behärska:

  • Förstå hur du bygger upp grafer lager för lager.
  • Skriva explicit R-kod med tydliga argumentnamn, till exempel data = ... och mapping = aes(...).
  • Koppla variabler till visuella egenskaper, som position, färg och form, genom estetiska mappningar.
  • Kombinera geometriska lager, till exempel geom_point(), geom_smooth() och geom_bar(), för att skapa olika typer av diagram.
  • Lägg in tydliga rubriker.

För att skapa grafer i R använder vi paketet ggplot2, som ingår i samlingspaketet tidyverse. Grundtanken i ggplot2 är att du skapar din graf genom att kombinera flera lager.

Tänk dig att du ska måla en tavla. Först behöver du en tom duk. I R skapar vi den med funktionen ggplot(). När vi skickar in ett dataset får vi en tom graf som vi sedan kan bygga vidare på.

# Här laddar du nödvändiga paket:
library(tidyverse)
library(palmerpenguins)

# Och skapar den tomma duken:
ggplot(data = penguins)

Som du ser är grafen tom. Allt jag har gjort hittills är ju att berätta för datorn att jag vill använda datasetet penguins, som innehåller massa fakta om pingviner. Datorn har fortfarande ingen aning om exakt vilken information om pingvinerna som ska finnas med i grafen eller hur den informationen ska presenteras.

Nästa steg är därför att bestämma vilka variabler som ska kopplas till grafen. Det kallas estetisk mappning (aesthetic mapping) och anges med funktionen aes() via argumentet mapping.

I det här fallet vill jag studera sambandet mellan längden på en pingvins fena och pingvinens vikt. Kanske är det så att pingviner med långa fenor och väger mer än pingviner med korta fenor? Jag vill därför att fenans längd (flipper_length_mm) ska visas på x-axeln och pingvinens vikt (body_mass_g) på y-axeln.

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
)

rader och kolumner

När du kör koden visas nu axlar, skalor och etiketter. Själva datapunkterna saknas dock fortfarande, eftersom du ännu inte har berättat för datorn exakt hur grafen ska se ut.

Tips

Lägg märke till att jag skriver ut både data = penguins och mapping = aes(...). De gula delarna av koden kan i praktiken ofta skippas, men min skarpa rekommendation är att du ska göra din så tydlig, luftig och pedagogisk som möjligt. Andra människor ska ju kunna titta på din kod och förstå vad den gör och varför.

Nu är det dags att trycka in själva informationen i grafen. För att visa data lägger vi in ett geometriskt objekt, ofta kallat en geom. Geomer anger om data ska visas som exempelvis punkter, linjer, staplar eller boxplots.

I ggplot2 lägger vi till nya lager med plustecknet (+). Funktionen geom_point() skapar ett spridningsdiagram:

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point()

rader och kolumner

Nu börjar vår målning se bättre ut. Spridningsdiagrammet gör att vi se sambandet mellan variablerna: Pingviner med längre fenor tenderar att väga mer. Sambandet vara ungefär linjärt, det vill säga varje extra centimeter verkar hänga ihop med en given ökning på vågen. Kan vi göra grafen ännu finare?

En av styrkorna med lagerprincipen är att vi kan lägga flera geomer ovanpå varandra. Förutom punkterna kan vi till exempel lägga till en trendlinje med geom_smooth():

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point() +
  geom_smooth(method = "lm")

Argumentet method = "lm" anger att trendlinjen ska anpassas med en linjär modell (linear model).

Viktigt

När du bygger en graf ska plustecknet stå i slutet av raden som fortsätter med nästa lager. Om ett uttryck saknar + där det behövs kan R nämligen tolka raden som slutet på grafen.

Vårt spridningsdiagram visar nu sambandet mellan två numeriska variabler. Men vad händer om vi vill visa ytterligare en variabel, till exempel vilken art (species) pingvinen tillhör? Ett smart sätt är att koppla fler variabler till visuella egenskaper som färg (color) och form (shape). Titta på följande kod. Förstår du vad jag gör här?

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point(
    mapping = aes(color = species, shape = species)
  ) +
  geom_smooth(method = "lm")

Nu blev grafen ännu lite mer informativ. Som läsare ser vi nästan direkt att Gentoo-pingviner är större och tyngre än de andra två pingvinarterna. Notera också att jag kopplade arten till både färg och form, eftersom det gör grafen lättare att läsa för färgblinda och för de läsare som skrivit ut din rapport i svartvitt. Tänk alltid på din läsare.

Men fortfarande är grafen inte fulländad. De engelska namnen längs axlarna är provocerande usla och fula. Låt oss därför lägga till ytterligare ett lager med rubriker. Med labs() kan du lägga till en titel, en undertitel, axelrubriker och en bildtext. Paketet ggthemes innehåller också färgpaletter, bland annat scale_color_colorblind(), som är utformad för att fungera bättre för färgblinda.

library(ggthemes)

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point(
    mapping = aes(color = species, shape = species)
  ) +
  geom_smooth(method = "lm") +
  labs(
    title = "Förhållandet mellan fenans längd och kroppsvikt",
    subtitle = "Mätningar för Adelie-, Chinstrap- och Gentoo-pingviner",
    x = "Fenans längd (mm)",
    y = "Kroppsvikt (g)",
    color = "Pingvinart:",
    shape = "Pingvinart:",
    caption = "Data från Palmerpenguins"
  ) +
  scale_color_colorblind()

Andra slags grafer

Ett spridningsdiagram är ofta användbart för att undersöka samband mellan två variabler, men olika frågor kräver olika slags grafer. Om du vill visa hur en aktiekurs har utvecklats under 2026 passar ett linjediagram, eftersom det visar förändringen över tid. Vill du jämföra arbetslösheten mellan olika län kan ett stapeldiagram göra skillnaderna tydliga. Ett histogram kan i stället visa hur hushållens inkomster fördelar sig, medan ett låddiagram kan hjälpa dig att jämföra inkomstfördelningen mellan flera grupper. Som tur är bygger du dessa grafer enligt exakt samma lager-på-lager-metod. Om du klarar att ta fram ett spridningsdiagram kommer du alltså att klara alla andra slags grafer.

Vissa geomer visar inte de råa värdena direkt, utan beräknar sammanfattande statistik. Ett exempel är geom_bar(), som automatiskt räknar antalet observationer i varje kategori med hjälp av en statistisk transformation.

ggplot(
  data = penguins,
  mapping = aes(x = species)
) +
  geom_bar()

Om du vill färglägga staplarna efter en ytterligare kategori kan du använda fill, som fyller staplarnas yta. color används i stället för att färga staplarnas konturer.

ggplot(
  data = penguins,
  mapping = aes(x = island, fill = species)
) +
  geom_bar(position = "dodge", color = "yellow") +
  labs(
    title = "Antalet pingviner på tre öar",
    subtitle = "Mätningar för Adelie-, Chinstrap- och Gentoo-pingviner",
    x = "Ön",
    y = "Antalet pingviner",
    fill = "Pingvinart:",
    caption = "Data från Palmerpenguins"
  )

Tack vare det här stapeldiagrammet har du lärt dig en hel del om pingvinerna i datamaterialet. Du vet till exempel att det är på ön Biscoe som det finns flest pingviner (drygt 160 stycken). Du ser också att Gentoo-pingvinen endast finns på ön Biscoe och att Chinstrap-pingvinen bara finns på ön Dream. Argumentet position = "dodge" placerar staplarna bredvid varandra. Om jag i stället hade skrivit position = "stack" skulle de ha staplas ovanpå varandra. Testa så ser du!

Sammanfattning: Grafer

Nu vet du lite mer om hur du visualiserar ditt data med ggplot2 och hur en graf byggs upp lager för lager. Kom ihåg att tydlig och explicit kodning gör analysen lättare att läsa, förstå och återskapa.

Viktiga lärdomar:

  • Lager-på-lager-principen: En graf byggs upp stegvis genom att kombinera ggplot(), estetiska mappningar (aes()), geometriska lager (geom_*()) och etiketter (labs()) med +.
  • Explicit kod: Argumentnamn som data = ... och mapping = aes(...) gör koden tydligare, särskilt när du lär dig nya funktioner.
  • Färg och tillgänglighet: Genom att kombinera visuella egenskaper som färg och form, och använda färgblindhetsanpassade paletter, kan du göra grafer lättare att tolka för fler.

21.2 Att hantera data

Som nybörjare är det lätt att tro att empirisk analys handlar om att memorera ändlösa rader av obegriplig kod. Ingenting kunde vara mer felaktigt! Att analysera data handlar i stället om att ställa begåvade frågor till sina data och sedan besvara dessa frågor steg för steg.

Vi ska nu arbeta en stund med datatransformation. Det handlar om att välja ut, sortera, förändra och sammanfatta data. Vi kommer att använda paketet dplyr och jobba med datasetet flights som exempel.

Inom en timme kommer du kunna följande saker:

  • beskriva hur tabulär data är uppbyggd av rader, kolumner och värden
  • använda filter() och arrange() för att välja ut och sortera rader
  • använda select(), mutate() och relocate() för att välja, skapa och ordna kolumner
  • gruppera data med group_by() och sammanfatta grupper med summarize()
  • hantera saknade värden med is.na() och na.rm = TRUE
  • skriva tydlig kod i en kedja med den fantastiska “pipen” (|>)

Kolumner och rader

Nästan alltid jobbar vi med så kallad tabulär data, vilket bara är ett annat ord för att materialet är ordnat i rader och kolumner. Du kan komma ihåg att en tabell består av tre saker:

  • Observationer (rader): enskilda enheter eller händelser som har mätts, till exempel ett flyg eller en mätning vid en viss tidpunkt.
  • Variabler (kolumner): egenskaper eller mått som registreras, till exempel flygförsening i minuter eller temperatur.
  • Värden (celler): det registrerade värdet för en viss variabel i en viss observation.

rader och kolumner

Observationer, variabler och värden.

Använd “pipen” (|>) och skriv kod som går att läsa!

Ett hett tips när du transformerar data är att du använder den så kallade pipe-operatorn |>. Du kan tänka dig att |> betyder “och sedan”. Det gör det möjligt att skriva transformationer som en tydlig följd av steg, i stället för att nästla funktioner i varandra eller skapa många tillfälliga objekt. Du kommer snart att förstå vad jag menar.

Det kan vara lockande att försöka skriva så kort kod som möjligt. Men kortast är inte alltid tydligast. När du lär dig är det ofta bättre att skriva ut argumentnamn och dela upp koden i lättlästa steg.

Tydlig kod hjälper dig att:

  1. Fokusera på ett steg i taget. Du behöver inte hålla hela transformationen i huvudet på en gång.
  2. Förklara vad koden gör. Koden blir lättare att förstå för både dig själv och andra.
  3. Hitta fel. Om något går snett kan du undersöka ett steg i taget.
Tips

Tydlig kod är inte ett tecken på oerfarenhet. Det är ett sätt att göra analysen lättare att läsa, kontrollera och bygga vidare på.

Att lära sig koda handlar alltså inte om att aldrig göra fel. Det handlar om att lära sig kommunicera med datorn steg för steg. Skriv kod som människor kan läsa och låt datorn göra beräkningarna.

Hantera rader: filtrera och sortera

När ett datamaterial innehåller många observationer vill du ofta fokusera på en viss del av den eller ordna raderna så att det blir lättare att upptäcka mönster.

Filtrera rader med filter()

Funktionen filter() väljer ut rader som uppfyller ett eller flera logiska villkor. Rader där villkoret är TRUE behålls.

Vanliga jämförelseoperatorer är:

Operator Betydelse
> och < större än och mindre än
>= och <= större än eller lika med, mindre än eller lika med
== lika med
!= inte lika med

Observera att == används för att testa om två värden är lika. Ett enda = används vanligen för att tilldela ett värde eller ange ett funktionsargument.

När du jämför flyttal kan avrundningsfel göra att en jämförelse med == inte fungerar som väntat. Till exempel kan sqrt(2)^2 == 2 ge FALSE. Använd då near() för att jämföra värden som förväntas vara ungefär lika.

Du kan kombinera villkor med logiska operatorer:

Operator Betydelse
& OCH: båda villkoren måste vara sanna
| ELLER: minst ett villkor måste vara sant
! ICKE: vänder på ett logiskt villkor
%in% testar om ett värde finns i en angiven mängd

Exempelvis betyder !is.na(x) att värdet i x inte är saknat.

Varning

Skriv inte month == 11 | 12 när du menar ”månad 11 eller 12”. R tolkar inte uttrycket som en upprepning av jämförelsen. Skriv i stället month == 11 | month == 12, eller enklare: month %in% c(11, 12).

Med %in% kontrollerar du om värdet i en variabel finns bland värdena i en vektor. Uttrycket month %in% c(11, 12) väljer alltså rader där month är 11 eller 12.

Hantera saknade värden (NA)

I R representeras saknade värden med NA. Om ett värde saknas kan R ofta inte avgöra resultatet av en beräkning eller jämförelse:

NA > 5
NA == NA

Båda uttrycken ger NA. Det andra uttrycket ger inte TRUE, eftersom två saknade värden inte nödvändigtvis representerar samma okända värde.

Därför fungerar inte följande kod för att hitta saknade avgångstider:

flights |>
  filter(dep_time == NA)

Använd i stället funktionen is.na():

# Hitta flyg där avgångstiden saknas
flights |>
  filter(is.na(dep_time))

filter() behåller rader där villkoret är TRUE och tar bort rader där villkoret är FALSE eller NA.

Sortera rader med arrange()

Funktionen arrange() ändrar ordningen på raderna utifrån värdena i en eller flera kolumner. Som standard sorteras värden i stigande ordning. Använd desc() för att sortera i fallande ordning.

# Stigande ordning
flights |>
  arrange(dep_delay)

# Fallande ordning
flights |>
  arrange(desc(dep_delay))

Som standard hamnar NA-värden sist när du sorterar med arrange(). Om du vill placera saknade värden först kan du sortera på desc(is.na(variabel)). Funktionen is.na() ger TRUE för saknade värden och FALSE för övriga. När sorteringen vänds med desc() hamnar de saknade värdena först.

Exempel: filtrera och sortera flyg

# Filtrera flyg som avgick i november eller december
# och som hade mer än 60 minuters avgångsförsening

flights |>
  filter(
    month %in% c(11, 12),
    dep_delay > 60
  )

Flera villkor som anges i filter() kombineras med OCH: båda villkoren måste vara uppfyllda.

# Placera flyg med saknad avgångstid överst.
# Sortera därefter efter störst avgångsförsening.

flights |>
  arrange(
    desc(is.na(dep_time)),
    desc(dep_delay)
  )

Nu kan du välja vilka rader som ska ingå i analysen och hur de ska ordnas. Nästa steg är att forma kolumnerna.

Hantera kolumner: välj, skapa och ordna

Medan filter() och arrange() arbetar med rader använder vi bland annat följande funktioner för att arbeta med kolumner:

  • select() väljer ut eller tar bort kolumner.
  • mutate() skapar nya kolumner eller ändrar befintliga.
  • relocate() flyttar kolumner till en annan position.

Skapa nya kolumner med mutate()

Ibland finns inte den variabel du behöver. Då kan du skapa den utifrån befintliga kolumner med mutate().

Ett exempel är en tid som lagras som ett heltal. Värdet 515 kan representera klockan 05:15. Med heltalsdivision och restberäkning kan vi dela upp värdet i timmar och minuter:

  • %/% gör heltalsdivision: 515 %/% 100 ger 5.
  • %% beräknar resten: 515 %% 100 ger 15.

När du vill skapa en kategori utifrån ett villkor kan du använda if_else(). Funktionen har tre huvudargument:

  • condition: villkoret som ska testas
  • true: värdet som används om villkoret är sant
  • false: värdet som används om villkoret är falskt

Argumentet missing anger vad som ska hända om villkoret är saknat (NA). Det gör hanteringen av saknade värden tydlig och avsiktlig.

Exempel: välj, skapa och flytta kolumner

flights |>
  # Välj de variabler som behövs i analysen
  select(
    year,
    month,
    day,
    sched_dep_time,
    dep_delay,
    arr_delay
  ) |>
  # Skapa nya kolumner steg för steg
  mutate(
    # Dela upp schemalagd avgångstid i timmar och minuter
    sched_hour = sched_dep_time %/% 100,
    sched_min = sched_dep_time %% 100,

    # Klassificera flyget och ange vad som gäller för saknade värden
    status = if_else(
      arr_delay > 0,
      true = "försenad",
      false = "i tid",
      missing = "inställt"
    )
  ) |>
  # Flytta de nya kolumnerna längst till vänster
  relocate(status, sched_hour, sched_min)

I den här kedjan hände tre saker:

  1. select() begränsar tabellen till de variabler vi behöver.
  2. mutate() skapar kolumnerna sched_hour, sched_min och status.
  3. relocate() flyttar de nya kolumnerna till början av tabellen.

När du kan välja rader och skapa kolumner är du redo att gå från enskilda observationer till gruppvisa sammanfattningar.

Gruppera och sammanfatta data

Ofta vill vi jämföra grupper, till exempel flygplatser eller månader. Då kan vi kombinera group_by() och summarize():

  1. group_by() delar in data i grupper. Själva tabellen ser vanligtvis likadan ut, men efterföljande beräkningar utförs separat för varje grupp.
  2. summarize() beräknar sammanfattande mått och komprimerar varje grupp till en rad.

Välj sammanfattningsmått med eftertanke

Olika statistiska mått passar olika typer av data. Vid symmetriska fördelningar kan medelvärde och standardavvikelse vara användbara. Vid skeva fördelningar, som ofta förekommer i data om förseningar och inkomster, kan median och kvartilavstånd ge en mer representativ bild.

Funktion Vad den mäter När användbar
mean(x) aritmetiskt medelvärde ofta för symmetriska fördelningar
median(x) det mittersta värdet i en ordnad uppsättning robustare än medelvärdet vid skeva fördelningar och extrema värden
sd(x) standardavvikelse, ett mått på spridning kring medelvärdet ofta för symmetriska fördelningar
IQR(x) avståndet mellan den 25:e och 75:e percentilen beskriver spridningen i mitten av data och påverkas mindre av extrema värden
n() antal observationer i gruppen ger viktig information om gruppens storlek
n_distinct(x) antal unika värden i x till exempel antal unika flygbolag per flygplats

Hantera saknade värden i sammanfattningar

Om en variabel innehåller minst ett NA kan funktioner som mean() och sum() ge resultatet NA. Lägg till na.rm = TRUE när du vill beräkna måttet utan de saknade värdena:

mean(arr_delay, na.rm = TRUE)

När du vill räkna saknade värden kan du använda is.na() tillsammans med sum() eller mean():

  • sum(is.na(x)) räknar antalet saknade värden.
  • mean(is.na(x)) beräknar andelen saknade värden, uttryckt som ett tal mellan 0 och 1.
Viktigt

Redovisa gruppens storlek tillsammans med sammanfattande mått. Ett medelvärde som bygger på få observationer kan vara osäkert. n() ger läsaren viktig kontext för att tolka resultatet.

Exempel: sammanfatta flyg per destination

# Sammanfatta flyg och förseningar per destination

flights |>
  group_by(dest) |>
  summarize(
    antal_flyg = n(),
    antal_bolag = n_distinct(carrier),
    antal_inställda = sum(is.na(dep_time)),
    andel_inställda = mean(is.na(dep_time)),
    medel_fordrojning = mean(arr_delay, na.rm = TRUE),
    median_fordrojning = median(arr_delay, na.rm = TRUE)
  ) |>
  # Behåll destinationer med minst 100 flyg
  filter(antal_flyg >= 100) |>
  # Visa destinationerna med störst medianförsening först
  arrange(desc(median_fordrojning))

Här beräknar vi först mått för varje destination. Därefter behåller vi destinationer med minst 100 flyg och sorterar dem efter medianförsening.

Tips

Tänk på skillnaden mellan antal och andel: sum(is.na(dep_time)) ger antalet saknade avgångstider, medan mean(is.na(dep_time)) ger andelen saknade avgångstider.

Sammanfattning: Att hantera data

Du har nu fått en grund för att välja och transformera data med dplyr. Genom att kombinera några centrala funktioner kan du gå från rådata till tabeller som är lättare att undersöka och tolka. Här är en lista med användbara kommandon:

  • filter() väljer rader utifrån logiska villkor.
  • arrange() sorterar rader, och desc() vänder sorteringsordningen.
  • select() väljer ut eller tar bort kolumner.
  • mutate() skapar eller ändrar kolumner.
  • relocate() flyttar kolumner.
  • group_by() delar in data i grupper.
  • summarize() beräknar sammanfattande mått för grupper.

Kom också ihåg att:

  • använda is.na() för att hitta saknade värden
  • använda na.rm = TRUE när sammanfattningar ska beräknas utan saknade värden
  • redovisa gruppens storlek tillsammans med sammanfattande mått
  • skriva kod steg för steg så att den går att läsa och felsöka

Nu har du kommit igång. I Avsnitt 21.1 fick du träna på att rita grafer och i Avsnitt 21.2 jobbade du med data. Kom ihåg att det tar tid att bli en duktig empirisk analytiker. Bryt inte ihop om din kod inte fungerar. Felmeddelanden och oväntade resultat är en naturlig del av arbetet och kan hjälpa dig att förstå vad koden gör. När du har den här verktygslådan på plats kan du börja ställa egna frågor till data.

21.3 Att importera, felsöka och jobba med Quarto

I det här delkapitlet ska vi gå vidare till det praktiska dataarbetet. Vi ska lära oss tre saker:

  • Import av data: Hur du läser in externa filer och kontrollerar att data och datatyper har tolkats som förväntat.
  • Felsökning: Hur du använder felmeddelanden och metodisk testning för att hitta problem i koden.
  • Struktur med Quarto: Hur du samlar kod, text och resultat i ett enda dokument som kan köras om och uppdateras automatiskt.

Importera och kontrollera data

Rådata är sällan helt ren, perfekt strukturerad eller redo att analyseras direkt. Du kommer därför att stöta på felmeddelanden, saknade värden, oväntade tecken och kolumner med datatyper som behöver justeras. Det betyder inte att du gör fel. Att undersöka och åtgärda sådana problem är en naturlig del av dataanalys.

Läsa in data med readr

Paketet readr, som ingår i tidyverse, används bland annat för att läsa in textbaserade datafiler som CSV-filer. Funktionen read_csv() läser in en CSV-fil och returnerar normalt en tibble.

En tibble är en modern variant av R:s data frame. Den skrivs ut på ett överskådligt sätt i konsolen och visar bland annat kolumnernas datatyper.

Ladda tidyverse innan du använder read_csv():

Ange sedan sökvägen till filen. Relativa sökvägar gör ofta projektet lättare att flytta mellan datorer:

Sökvägen "data/mina_observationer.csv" utgår från projektets arbetskatalog. Mappen data behöver alltså finnas där, och filen behöver ligga i den mappen.

Vanliga problem vid import

Importproblem märks inte alltid direkt. Ibland körs koden utan fel, men data tolkas på ett sätt som skapar problem senare i analysen.

Problem Möjlig konsekvens Vad du kan göra
Felaktig sökväg R hittar inte filen och importen avbryts. Kontrollera filnamn och mappstruktur. Använd en relativ sökväg, till exempel "data/min_fil.csv".
Felaktig datatyp Tal kan läsas in som text, vilket gör beräkningar svåra eller omöjliga. Kontrollera datatyperna med glimpse(). Om det behövs kan du använda funktioner som parse_number().
Stavfel i kolumnnamn R hittar inte den kolumn du hänvisar till. Kontrollera stavning och kolumnnamn med names() eller glimpse().
Oväntade saknade värden Beräkningar kan ge NA eller färre observationer än väntat. Undersök vilka värden som saknas och välj hur de ska hanteras i analysen.

En kolumn med värden som "$1,250" eller "45%" kan läsas in som text, eftersom den innehåller andra tecken än siffror. Funktionen parse_number() kan plocka ut talet ur en sådan text. Kontrollera ändå att tolkningen passar dina data: exempelvis kan "45%" då bli 45, inte 0.45.

Här skapas en ny kolumn, pris_num, utifrån kolumnen pris. Om du vill ersätta den ursprungliga kolumnen kan du använda samma namn på båda sidor om likhetstecknet i mutate():

Kontrollera importen med glimpse() och spec()

Gör det till en vana att kontrollera data direkt efter importen. Då kan du upptäcka problem innan du börjar analysera.

  • glimpse() visar en kompakt översikt över objektets dimensioner, kolumnnamn, datatyper och några värden.
  • spec() visar hur readr tolkade kolumnerna vid importen.

Vanliga datatyper i utskriften från glimpse() är:

  • <dbl>: numeriskt decimaltal.
  • <int>: heltal.
  • <chr>: text.
  • <fct>: faktor, ofta använd för kategorier med fördefinierade nivåer.

Om en kolumn som borde innehålla tal visas som <chr>, undersök värdena innan du försöker omvandla den. Det kan finnas exempelvis valutasymboler, mellanslag eller andra oväntade tecken.

Felsökning

Ett felmeddelande är inte ett bevis på att du misslyckats. Det är en ledtråd om vad R inte kunde göra. Läs meddelandet noggrant och försök identifiera vilken funktion, kolumn eller rad det handlar om.

Även erfarna programmerare skriver kod som inte fungerar direkt. Att testa, upptäcka fel och rätta till dem är en normal del av arbetet. Här kommer några klassiska fel:

Paranteser eller citat som inte är avslutade

Varje öppnad parentes och varje öppnat citationstecken måste också stängas:

# Fel: en avslutande parentes saknas
mean(c(1, 2, 3)

# Rätt
mean(c(1, 2, 3))
Konsolen visar +

Om konsolen visar + i stället för den vanliga prompten >, väntar R på att du ska avsluta ett ofullständigt uttryck. Det kan till exempel bero på en parentes eller ett citationstecken som inte har stängts.

# Ofullständigt uttryck: citationstecknet är inte stängt
text <- "Välkommen till kursen

Tryck på Esc för att avbryta det ofullständiga uttrycket och börja om. Gå sedan tillbaka till koden och hitta det som saknas.

Felplacerat + i ggplot2

När du bygger en graf med ggplot2 ska + stå i slutet av raden där det föregående ledet avslutas.

# Rätt
ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point()

Metod för att hitta fel: Rad för rad med |>

Om en lång pipeline med |> ger ett fel, försök inte gissa genom att läsa hela koden om och om igen. Dela i stället upp arbetet:

  1. Kör pipelinen från början.
  2. Lägg till ett steg i taget tills felet uppstår.
  3. Inspektera resultatet från det sista steget som fungerade.
  4. Kontrollera särskilt stavningen på kolumnnamn och om kolumnernas datatyper verkar rimliga.

Här är ett exempel med datasetet penguins från paketet palmerpenguins:

Börja med att filtrera data och kontrollera resultatet:

Rows: 152
Columns: 8
$ species           <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adel…
$ island            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgerse…
$ bill_length_mm    <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, …
$ bill_depth_mm     <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, …
$ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186…
$ body_mass_g       <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, …
$ sex               <fct> male, female, female, NA, female, male, female, male…
$ year              <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007…

Lägg sedan till nästa steg i pipelinen. Om du exempelvis råkar skriva body_mass i stället för body_mass_g kan R inte hitta kolumnen:

# Fel: kolumnen heter body_mass_g, inte body_mass
adelie |>
  summarize(
    medel_vikt = mean(body_mass, na.rm = TRUE)
  )

Rätta kolumnnamnet och kör igen:

medel_vikt
3700.662

Argumentet na.rm = TRUE gör att mean() beräknar medelvärdet utan att saknade värden (NA) stoppar beräkningen.

Tips

När du felsöker en pipeline kan du spara ett mellanresultat i ett eget objekt, som adelie i exemplet. Det gör det enklare att kontrollera vad som händer i varje steg.

Skriv kod som är lätt att läsa

Kompakt kod kan verka snabb att skriva, men den kan vara svårare att läsa och felsöka. Radbrytningar, indrag och tydliga argumentnamn gör det enklare att se hur koden är uppbyggd.

Båda exemplen nedan skapar ett spridningsdiagram, men den andra versionen är lättare att överblicka:

# Kompakt kod
ggplot(penguins, aes(flipper_length_mm, body_mass_g)) +
  geom_point(aes(color = species))

# Tydligare kod
ggplot(
  data = penguins,
  mapping = aes(
    x = flipper_length_mm,
    y = body_mass_g
  )
) +
  geom_point(
    mapping = aes(color = species)
  )

Det här sättet att koda gör att det är lättare att förstå vad man gör. Kanske behöver din kompis läsa koden? Eller kanske du själv kommer tillbaka till koden om ett år. Du kommer att tacka dig själv för att du har skrivit en kod som är lätt att följa och förstå

Quarto: Data, kod och text på samma ställe!

Reproducerbarhet innebär att analysen kan köras igen och att det går att följa hur resultaten skapades. Det är värdefullt både för andra som läser arbetet och för ditt framtida jag.

Med Quarto kan du samla förklarande text, R-kod och resultat i ett och samma dokument. När dokumentet renderas körs kodblocken, och resultaten infogas i den färdiga rapporten.

Ett Quarto-dokument innehåller vanligtvis:

  1. YAML-huvud: Dokumentets metadata och inställningar, till exempel titel och utdataformat. Det placeras högst upp och avgränsas med ---. Du kan till exempel välja om du vill få dokumentet i Word-format, som PDF, som html. Du kan även få dokumentet som en e-bok (som den här boken) eller som slides (se mina föreläsningsanteckningar).
  2. Markdown-text: Rubriker, stycken, länkar, punktlistor och annan formaterad text.
  3. Kodblock: Avgränsade block där R-kod kan köras. Inställningar för ett kodblock kan anges med kommentarer som börjar med #|.

Här är ett minimalt exempel på ett Quarto-dokument. Kommer hit.