v23: Introduktion till kursverktyg

Den första veckan kommer innehålla administration och avslutas med en provkörning av kursverktygen i laboration 1.

Mål denna vecka

  • Dina kurskonto på GitHub och RStudio Cloud skall vara kopplade till kursens arbetsytor.
  • Första laborationen inlämnad. (Deadline söndag).

v24: Enkla visualiseringar

Mål denna vecka

  • Att ha grundläggande förståelse för Rs datatyper.
  • Att kunna skapa enkla diagram och förstå de grundläggande komponenterna i en ggplot-visualisering (data, mapping, geom).

Kursmaterial

RStudio Primers:

Övningar

Under rubriken övningar hittar du träningsmaterial som inte ingår i kursens examination. Detta ger bra övning inför inlämningsuppgifterna, med fördelen att du kan fråga och tipsa hur mycket du vill på kursens Discord. Skapa ett separat projekt i RStudio Cloud där du arbetar med övningar och skapa en mapp data i detta projekt där du kan spara de datamaterial vi använder.

Gör inte bara uppgifterna utan experimentera med data och försök hitta på egna frågeställningar att besvara, fråga på Discord om något inte blev som du tänkt.

Ettor på stan

Du kan ladda ner en kopia av data från inlämningsuppgift 1 med

download.file("https://github.com/MT3003-ST21/data/raw/main/booli_ettor_2021-05-28.csv",
              "data/booli_ettor_2021-05-28.csv")

och läsa in den med

booli_ettor <- read_csv("data/booli_ettor_2021-05-28.csv")

Notera att det går bra att läsa direkt från en url, som i

booli_ettor <- read_csv("https://github.com/MT3003-ST21/data/raw/main/booli_ettor_2021-05-28.csv")

men vi föredrar att ladda ner filen en gång, då kan vi vara säkra på att vi arbetar med samma material nästa gång vi öppnar projektet. Använd din egen fantasi för att undersöka materialet med enkla figurer, till exempel kan du undersöka

  • Hur påverkar yta (livingArea) hyran (rent)?
  • SVT rapporterade i våras om rekordmånga anmälningar om lockpriser. Undersök hur förekomsten av lockpriser varierat genom att plotta kvoten mellan slutpris och utgångspris (soldPrice / listPrice) mot försäljningsdatum (soldDate). Tips: Om figuren förstörs av ett extremt värde kan du zooma in genom att lägga till + ylim(c(0, 2)) för att bestämma gränserna på y-axeln.
  • Vilka veckodagar publicerar mäklarna sina annonser? Gör ett stapeldiagram över antalet annonser per veckodag, veckodag får du genom weekdays(published).

Faktorvariabler: Ovanstående övning illustrerar ett vanligt problem. Veckodagarna blir sorterade i alfabetisk ordning medan vi antagligen vill ha dem i kronologisk. Problemet kommer av att en variabel av typen character inte innehåller någon information om kategoriernas ordning, det gör däremot en variabel av typen factor. För att ändra ordning behöver vi alltså göra om weekdays(published) till en faktorvariabel. Om vi istället för weekdays(published) använder factor(weekdays(published), levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")) så ordnas staplarna i den ordning vi angett.

Systembolagets katalog

Systembolaget brukade ha hela sin katalog öppen för nedladdning, så är inte längre fallet men en kopia från 2019 finns sparad i systembolaget_2019-10-30.csv. Ladda ner den med

download.file("https://github.com/MT3003-ST21/data/raw/main/systembolaget_2019-10-30.csv",
              "data/systembolaget_2019-10-30.csv")

och läs in med

bolaget <- read_csv("data/systembolaget_2019-10-30.csv")

skapa dig en överblick genom att använda t.ex. glimpse eller View. Ett enkelt sätt att få en överblick över en viss kategorisk variabel (om kategorierna inte är för många) är ett stapeldiagram med geom_bar, prova till exempel med variablerna Varugrupp eller Ursprungsland. Man kan koppla den kategoriska variabeln antingen till y eller x-axeln (med aes(x = Varugrupp) respektive aes(y = Varugrupp)). Vilket passar bäst?

Vilken varugrupp har högst literpris? Gör ett punktdiagram med Varugruppy-axeln och PrisPerLiterx-axeln. Eftersom fördelningen för PrisPerLiter kan det öka läsbarheten att använda en logaritmisk skala på x-axeln genom att lägga till + scale_x_log10() till anropet. Som med veckodagarna ovan är det inte optimalt att ordna kategorierna alfabetiskt, prova att använda y = fct_reorder(Varugrupp, PrisPerLiter) istället för y = Varugrupp. Funktionen fct_reorder gör om Varugrupp till faktorvariabel och ordnar kategorierna efter PrisPerLiter (medianen i varje grupp).

Med hjälp av filter kan vi begränsa oss till vissa kategorier (mer om detta nästa vecka). Vad genererar till exempel följande kod?

bolaget_subdata <- filter(bolaget, 
                      Ursprunglandnamn %in% c("Sverige", "Storbritannien", "Frankrike", "Spanien"), 
                      Varugrupp %in% c("Öl", "Rött vin", "Whisky", "Aperitif och dessert"))
ggplot(data = bolaget_subdata,
       mapping = aes(y = Ursprunglandnamn)) + 
  geom_bar() + 
  facet_wrap(~Varugrupp, scales = "free_x")

Gapminder data

Gapminder foundation verkar för att förändra människors syn på världen med data. Titta gärna på något av Hans Roslings föredrag eller använd deras interaktiva verktyg och försök återskapa figurerna med ggplot. En begränsad del av deras data har sammanställts i gapminder_2021-06-14.csv (se dokumentation). Ladda ner med

download.file("https://github.com/MT3003-ST21/data/raw/main/gapminder_2021-06-14.csv",
              "data/gapminder_2021-06-14.csv")

och läs in med

gapminder <- read_csv("data/gapminder_2021-06-14.csv")

v25: Sammanfatta en tabell

Mål denna vecka

  • Att kunna sortera och filtrera med arrange och filter.
  • Skapa nya kolumner med mutate
  • Sammanställningar med group_by och summarise

Övningar

Ettor på stan

  • Använd arrange (eventuellt i kombination med select för att begränsa utskriften) för att verifiera att bostaden med äldst constructionYear ligger i vad som troligen är Stockholms äldsta bevarande bostadshus. För flera rader antar constructionYear värdet NA, värde saknas. Hur hanteras detta av arrange?

  • Använd count och arrange för att hitta den gatuadress där det sålts flest ettor. Detta är Kvinnornas hus som huvudsakligen består av ettor med kokvrå.

  • Inför variabeln priceRise som prisökningen i procent (baserat på soldPrice och listPrice) och bestäm medelvärdet av priceRise per mäklare (source.name) för alla mäklare med minst 100 försäljningar och sammanfatta med ett histogram. OBS: Hur hanterar funktionen mean saknade värden? En försäljning i materialet sticker ut, kan du hitta och filtrera bort den?

Gapminder data

  • Gör en tabell över medelinkomsten per capita för varje kontinent 2020. Tänk på att även income är räknat i per capita, du behöver alltså bestämma kontinenternas totala inkomst och dela på den totala populationen.

Systembolagets katalog

  • Illustrera hur medelpriset per liter på Röda viner beror på årgång (du kan begränsa dig till årgångar med minst 10 produkter).
  • Illustrera hur medelpriset per liter på Röda viner beror på ursprungsland med ett stapeldiagram, ordna staplarna efter medelpris (du kan begränsa dig till länder med minst 10 produkter).
  • Kolumnen Alkoholhalt är kodad som textvariabel eftersom den innehåller ett procenttecken, gör om den till numerisk genom att först ta bort procenttecknet med str_remove och sedan konvertera med as.numeric. Illustrera sedan hur pris per liter beror på alkoholhalt för varugruppen öl i ett punktdiagram med en rät trendlinje (geom_smooth(method = "lm")).
  • Skattesatsen för öl är 2.02 kronor per liter och volymprocent, bestäm en ny variabel SkattPerLiter som innehåller skattesatsen. Plotta sedan PrisPerLiter - SkattPerLiter mot Alkoholhalt (för varugruppen öl) med trendlinje.

Badväder

Hos Havs och Vattenmyndigheten kan du hitta statistik över vattenprover tagna vid badplatser runt om i landet, ett utdrag i Excel-format kan laddas ned med

download.file("https://github.com/MT3003-ST21/data/raw/main/Provresultat.xlsx",
              "data/Provresultat.xlsx")

och läsas in med

badvatten <- readxl::read_excel("data/Provresultat.xlsx") %>% 
  fill(Län, Kommun)

Vad gör funktionen fill i detta fall? Undersök materialet närmare avseende t.ex. skillnader mellan län, EU-bad/Övriga och Badplatstyp. En svårighet är att man för E.coli och enterokocker ibland använder ett mätvärde och ibland en övre gräns (se prefix-kolumnerna).


v26: Utforska data med visualiseringar

Mål denna vecka

  • Skriva en enkel rapport i Rmarkdown.
  • Utforska data med ggplot.
  • Andra laborationen inlämnad (deadline söndag).

Kursmaterial

RStudio Primers:

Övningar

Inga speciella övningar denna vecka, experimetera med visualiseringar av data från tidigare veckor.

v27: Läsa data från fil

Mål denna vecka

  • Att kunna importera tabeller från textfiler.

Kursmaterial

Kapitel 11 från ett svenskt perspektiv

Paketet readr följer amerikansk standard om du inte ber det göra något annat, här listar vi några skillnader mellan svensk och amerikansk standard som kan vara bra att ha koll på.

Filformatet .csv

csv står för comma separated values och är ett av de vanligaste formaten för att spara tabeller i en textfil. I en csv-fil motsvarar varje rad en rad i tabellen (översta raden består ofta av kolumnrubriker) och kommatecken används för att separera kolumner. Filen med innehåll

x, y
1, 2
3, 4

motsvarar alltså tabellen

readr::read_csv(
"x, y
1, 2
3, 4")
## # A tibble: 2 x 2
##       x     y
##   <dbl> <dbl>
## 1     1     2
## 2     3     4

Detta fungerar bra i engelskspråkiga länder där man använder punkt som decimaltecken. När man som i svensk standard använder kommatecken som decimaltecken används istället ofta semikolon som kolumnavgränsare. Så är till exempel fallet i Excel; om du har Excel med svenska nationella inställningar och väljer att spara en tabell i formatet CSV (kommaavgränsad) *.csv så sparas den i själva verket som en fil där kolumner avgränsas med semikolon. Försöker du sedan läsa in den med read_csv placeras alla värden i samma kolumn

readr::read_csv(
"x; y
1; 2
3; 4")
## # A tibble: 2 x 1
##   `x; y`
##   <chr> 
## 1 1; 2  
## 2 3; 4

Eftersom detta är vanligt förekommande finns en specialfunktion read_csv2 som utgår ifrån att kolumner skiljs med semikolon och komma används som decimaltecken

readr::read_csv2(
"x; y
1; 2
3; 4")
## # A tibble: 2 x 2
##       x     y
##   <dbl> <dbl>
## 1     1     2
## 2     3     4
Decimaltecken igen

När siffror kombineras med punkt, kommatecken och mellanslag gissar paketet readr vad dessa betyder utifrån amerikansk standard. Här kan det lätt bli fel om vi slarvar med att ange lokala inställningar (locale). Jämför till exempel

readr::parse_number(c("3,14", "3 000"))
## [1] 314   3

med

readr::parse_number(c("3,14", "3 000"), locale = readr::locale(decimal_mark = ",", 
                                                               grouping_mark = " "))
## [1]    3.14 3000.00

Ibland får man lösa konverteringen själv. I data som matats in manuellt är det inte ovanligt att både punkt och kommatecken förekommer som decimaltecken om vartannat i samma fil. Då är det säkrare att läsa in alla kolumner som text (chr) och använda till exempel str_replace(x, ",", ".") följt av as.numeric.

Spagetti med köttfärssås

Antagligen har du någon gång stött på en text där svenska tecken bytts ut mot obegripliga symboler. Problemet beror på att det utvecklats många olika konventioner för teckenkodning som kan vara specifika för operativsystem och applikationer. De viktigaste att känna till är UTF-8, som är standard på webben och kan koda alla möjliga språk, och ISO 8859-1 eller latin1, som är en enklare teckenkodning anpassad för västeropeiska språk. Paketet readr förutsätter alltid UTF-8, medan motsvarande funktioner i Rs base-paket (t.ex. read.csv som motsvarar readr::read_csv) anpassar sig efter lokala systeminställningar.

Ser ditt resutat ut som i rubriken har du antagligen försökt läsa in “köttfärssås” som latin1 när den i själva verket var kodad i UTF-8, detta är mindre vanligt med just readr-paketet eftersom det läser in som UTF-8 om du inte anger något annat. Om du däremot försöker läsa in en fil kodad i latin1 med readr blir det k\xf6ttf\xe4rss\xe5s om du inte anger locale = locale(encoding = "latin1").

Övningar

SCB-data

I SCBs statistikdatabas kan man skapa en länk (url) till tabeller man tagit fram. Filen på https://www.statistikdatabasen.scb.se/sq/110431 innehåller medelålder per län i formatet Kommaavgränsad med rubrik. Undersök den med read_lines("https://www.statistikdatabasen.scb.se/sq/110431") och försök sedan läsa in den med read_csv.

Mer badvatten

Badvattenfilen som användes i tidigare övningar var exporterad som Excel-fil från Hav och Vattenmyndigheten, om vi istället väljer att exportera som csv får vi en fil som kan hämtas med

download.file("https://github.com/MT3003-ST21/data/raw/main/Provresultat_2021-06-28.csv",
              "data/Provresultat_2021-06-28.csv")

Försök läsa in den med read_csv, vad händer med vattentemperaturen? Fixa!


v28: Omforma och sammanfläta tabeller

Mål denna vecka

  • Att kunna omforma tabeller till ett “tidy”-format.
  • Att kunna skapa nya tabeller genom att sammanfoga flera tabeller med gemensamma kolumner.

Kursmaterial

RStudio primers:

  • Tidy your data. OBS: I delen Reshape Data används funktionerna gather och spread, dessa har ersatts med pivot_longer och pivot_wider som är enklare att använda (de gamla fungerar dock fortfarande). Försök lösa övningen med pivot_longer och pivot_wider.

Övningar

Gapminder data

gapminders dataarkiv kan man ladda ner deras sammanställda datamaterial, en fil för varje variabel. Tabellen som använts i tidigare övningar är sammanställd av sådana filer. Filen med antal mobiltelefoner per land och år har vi hämtat så att den kan laddas ned med

download.file("https://github.com/MT3003-ST21/data/raw/main/cell_phones_total_2021-06-27.csv",
              "data/cell_phones_total_2021-06-27.csv")

och sedan läsas in med

cell_phones_total <- read_csv("data/cell_phones_total_2021-06-27.csv",
                              col_types = cols(.default = "c"))

här ser col_types = cols(.default = "c" ) till att alla kolumner läses in som text (chr) vilket kommer visa sig vara praktiskt. Du kan också prova med någon anna variabel du hämtad ned direkt från gapminder.

Uppgiften är nu att lägga till variabeln till gapminder-tabellen från tidigare övningar genom att

  • Gör om cell_phones_total till “tidy”-format med pivot_longer.
  • Lägg till den nya variabeln som en kolumn i gapminder tabellen med lämplig *_join (om du gör allt i en pipe-sekvens kan right_join vara lämpligt).
  • Anledningen till att vi läste in alla kolumner som text är att gapminder valt att koda numeriska värden i olika enheter. Talet 1100 kodas t.ex. som 1.1k och 12 600 000 som 12.6M. Detta kan t.ex. lösas med case_when som nedan
case_when(
  str_detect(antal_mobiler, "k") ~ 1000 * (str_remove(antal_mobiler, "k") %>% as.numeric()),
  str_detect(antal_mobiler, "M") ~ ...

Jämställdhet

Tabellen

read_csv("https://www.statistikdatabasen.scb.se/sq/110673", 
         skip = 1, locale = locale(encoding = "latin1"))
## # A tibble: 2 x 8
##   kön     `1995` `1999` `2004` `2009` `2011` `2014` `2019`
##   <chr>    <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
## 1 kvinnor      9      9     11     10      9     11     11
## 2 män         13     13      8      8     11      9      9

innehåller antal svenska EU-parlamentariker efter kön och år. Använd pivot_longer, pivot_wider, mutate och select i en följd för att generera

## # A tibble: 7 x 2
##   year  percent_female
##   <chr>          <dbl>
## 1 1995            40.9
## 2 1999            40.9
## 3 2004            57.9
## 4 2009            55.6
## 5 2011            45  
## 6 2014            55  
## 7 2019            55

Koroplet

Koropletkartor brukar användas när man vill visa hur en variabel varierar geografiskt, ofta med en indelning i områden som t.ex. län eller kommuner. Du kan ladda ner och packa upp en karta över sveriges län i som en så kallad shapefil (hämtad från SCB) med

download.file("https://github.com/MT3003-ST21/data/raw/main/LanSweref99TM.zip",
              "LanSweref99TM.zip")
unzip("LanSweref99TM.zip", exdir = "LanSweref99TM")

Paketet sf innehåller funktioner för att arbeta med geografiska data och kan bland annat läsa in en shapefil i tabellformat med st_read som sedan kan plottas med geom_sf

library(sf)
karta_lan <- st_read("LanSweref99TM/Lan_Sweref99TM_region.shp", quiet = TRUE)
ggplot(karta_lan, aes(fill = LnNamn)) + geom_sf()

Objektet karta_lan kan användas som en vanlig tabell/tibble. Det betyder att vi kan lägga till länsvisa variabler med *_join om vi vill färglägga efter något mer intressant än LnNamn. Prova tabellen med medelålder från tidigare övningar, en nyckel får du t.ex. genom att skapa en variabel LnKod = str_sub(region, 1, 2) i tabellen över medelålder.

Ett alternativ till ggplot för just kartframställning är paketet tmap som kan vara enklare att använda men mindre flexibelt.


v29: Arbeta med text, faktorer och datum.

Mål denna vecka

  • Att kunna använda verktyg i paketen stringr, forcats och lubridate.

Kursmaterial

  • R4DS: Kapitel 14-16 (14.3 om reguljära uttryck kan läsas kursivt, ni förväntas ha en grundläggande förståelse för konceptet men inte detaljerna).

Övningar

Kapitel 14-16 innehåller specifika verktyg för att arbeta med text, faktorer och datum. Vi har redan stött på flera av dessa, t.ex. förekom stringr::str_detect, forcats::fct_relevel och lubridate::yday i Laboration 2. Funktionerna i stringr och forcats börjar alltid med str_ respective fct_ vilket gör det enkelt att leta efter dem i RStudios Console-fönster då man får upp en lista genom att inleda raden med dessa tecken (skriv lubridate:: för motsvarande lista för detta paket). Specifika övningar ges i kursboken, här ger vi ett exempel på hur man kan arbeta med textdata.

En textanalys

Projekt Gutenberg finns en stor samling gratis e-böcker. Vi kan ladda ner Strindbergs Hemsöborna med

download.file("https://www.gutenberg.org/cache/epub/30078/pg30078.txt",
              "data/hemsoborna.txt")

läsa ett valt stycke med

read_lines("data/hemsoborna.txt", skip = 194, n_max = 21) %>% 
  str_c(collapse = " ")
## [1] "kaffet och sockret, flickor, och seglena är inne i bo'n? Så, kom då opp,\r så ska ni väl ha er något att äta.\r \r Och sällskapet tågade upp för backen, Carlsson tyst, nyfiken, i väntan\r att få reda på huru hans liv skulle ställa sig på den nya platsen.\r \r Det brann eld i spiseln inne i stugan och det vita slagbordet hade en ren\r duk på; och på duken stod en brännvinsflaska hopsnörd mittpå som ett\r timglas, och runt kring henne Gustavsbergskoppar med rosor och\r förgätmigej; en nybakad bulle och skörtorkade skorpor, en smörtallrik,\r sockerskål och gräddkanna fullbordade uppdukningen, som Carlsson fann\r rikemansaktig och som han ej väntat sig så långt bortom all ära och\r redlighet. Men stugan själv såg inte heller dålig ut, när han mönstrade\r henne i det flammande skenet från spiseln, som korsade sig mot\r talgljusets i mässingsstaken och lyste i mahognychiffonjéns något suddiga\r polityr, speglade sig i väggklockans lackerade fodral och mässingspendel,\r gnistrade i silverinläggningarne på de långa fågelbössornas damascherade\r pipor och ritade upp de förgyllda bokstäverna på ryggarne av postillor,\r psalmböcker, almanackor och bondepraktikor.\r \r -- Stig fram, Carlsson, bjöd gumman, och Carlsson, som var en nyare tids\r"

och spara hela boken i en tibble med

hemsoborna <- tibble(row = read_lines("data/hemsoborna.txt", 
                                       skip = 90, n_max = 10178)) %>% 
  filter(row != "") %>% 
  mutate(row_no = 1:n())
hemsoborna
## # A tibble: 5,407 x 2
##    row                                                                    row_no
##    <chr>                                                                   <int>
##  1 "\r"                                                                        1
##  2 "FÖRSTA KAPITLET.\r"                                                        2
##  3 "\r"                                                                        3
##  4 "Carlsson går in i tjänsten och befinnes\r"                                 4
##  5 "vara en spelfågel.\r"                                                      5
##  6 "\r"                                                                        6
##  7 "\r"                                                                        7
##  8 "Han kom som ett yrväder en aprilafton och hade ett höganäskrus i en\~      8
##  9 "svångrem om halsen. Clara och Lotten voro inne med sköt-ekan att häm~      9
## 10 "honom på Dalarö brygga; men det dröjde evigheter, innan de kommo i b~     10
## # ... with 5,397 more rows

här har vi klippt bort text som inte hör till boken med skip och n_max, tagit bort tomma rader och lagt till radnummer. För att analysera texten vidare delar vi upp den ord för ord, konverterar bokstäver till gemener och lägger in en räknare för kapitel

hemsoborna_words <- hemsoborna %>% 
  mutate(word = str_extract_all(row, boundary("word"))) %>% 
  group_by(row_no) %>% 
  summarise(word = unlist(word)) %>% 
  ungroup() %>% 
  mutate(word = str_to_lower(word),
         chapter = cumsum(str_detect(word, "kapitlet")))
  • Vad gör egentligen chapter = cumsum(str_detect(word, "kapitlet"))?

Vi kan nu få de vanligaste orden med

hemsoborna_words %>% count(word, sort = TRUE)
## # A tibble: 9,146 x 2
##    word      n
##    <chr> <int>
##  1 och    2456
##  2 i      1052
##  3 han     940
##  4 att     875
##  5 på      849
##  6 som     811
##  7 det     796
##  8 en      756
##  9 med     571
## 10 sig     509
## # ... with 9,136 more rows

de flesta av dessa är så kallade stoppord, ord som är viktiga för att binda ihop texten men annars betydelsefattiga. För att ta bort dem hämtar vi en lista på svenska stoppord

stopwords <- read_table("https://raw.githubusercontent.com/stopwords-iso/stopwords-sv/master/stopwords-sv.txt",
           col_names = "word")
  • Använd anti_join för att ta bort alla stoppord från listan på de vanligaste orden.
  • Illustrera resultatet med funktionen wordcloud2::wordcloud2.

Stämningsanalys (semtiment analysis) är en populär metod för att beskriva känslostämningar i texter (se till exempel Jane Austen och Donald Trump). I allmänhet bygger det på att man anväder ett lexikon som kvantifierar stämningen i enskilda ord, till exempel associerar ett posotivt numeriskt värde till “kärlek” och ett negativt till “svartsjuka”. Ett svenskt sådant lexikon kan laddas ned från språkbanken som

sentiment_lex <- read_csv("https://svn.spraakdata.gu.se/sb-arkiv/pub/lmf/sentimentlex/sentimentlex.csv")
  • Koppla ihop hemsoborna_words och sentiment_lex med en inner_join och illustrera hur det går utför för Carlsson genom att bestämma medelvärdet av känslostämningen (variabeln strength) för varje kapitel.

v30: Funktioner och iteration

Mål denna vecka

  • Att kunna skriva enkla funktioner för att göra koden mer lättläst och enklare att underhålla.
  • Att kunna arbeta med Rs list-format.
  • Att kunna förenkla upprepade uppgifter med iteration.

Kursmaterial

Övningar

Booli figurer

Koden bakom en figur skapad med ggplot kan bli många rader om man vill finjustera detaljer. Skall man upprepa en figur för olika parametervärden är det därför praktiskt att skriva en funktion. Använd tabellen över alla försäljningar i innerstaden som laddas ned med

download.file("https://github.com/MT3003-ST21/data/raw/main/booli_innerstad_2021-07-02.csv",
              "data/booli_innerstad_2021-07-02.csv")
booli_data <- read_csv("data/booli_innerstad_2021-07-02.csv")
  • Konstruera en funktion
plot_price_ts <- function(data, title = "Pris per kvadratmeter i Stockholms innerstad"){
  ...
}

som skapar en figur motsvarande den i Laboration 1 baserat på givet data.

  • Vill vi nu plotta t.ex. tvåor kan vi använda funktionen med anropet booli_data %>% filter(rooms == 2) %>% plot_price_ts(). Lägg till ett argument rooms till funktionen så att plot_price_ts(booli_data, rooms = 2) ger samma resultat.

Olympiska medaljer

https://sv.wikipedia.org/wiki/Medaljf%C3%B6rdelning_vid_olympiska_sommarspelen_2020 hittar du aktuell medaljfördelning för OS i Tokyo. Tabellen kan hämtas med

library(rvest)
url <- "https://sv.wikipedia.org/wiki/Medaljf%C3%B6rdelning_vid_olympiska_sommarspelen_2020"
read_html(url) %>% 
  html_element(".wikitable") %>% 
  html_table()
## # A tibble: 81 x 7
##    Placering Land            Guld Silver Brons `Totalt antalme~ `Placering efte~
##    <chr>     <chr>          <int>  <int> <int>            <int>            <int>
##  1 1         Kina              29     17    16               62                2
##  2 2         USA               22     25    17               64                1
##  3 3         Japan             17      6    10               33                5
##  4 4         Australien        14      4    15               33                5
##  5 5         ROC               12     21    17               50                3
##  6 6         Storbritannien    11     12    12               35                4
##  7 7         Frankrike          6     10     7               23                8
##  8 8         Tyskland           6      6    11               23                8
##  9 9         Sydkorea           6      4     9               19               10
## 10 10        Nederländerna      5      7     6               18               11
## # ... with 71 more rows

En motsvarande tabell för t.ex. OS i Rio kan hämtas genom att byta 2020 mot 2016.

Konstruera en funktion

get_medals <- function(year){
  ...
}

som returnerar medaljtabellen för OS-år year som nedan (glöm inte ta bort Total-raden)

get_medals(2016)
## # A tibble: 264 x 4
##     year country        class  number
##    <dbl> <chr>          <chr>   <int>
##  1  2016 USA            Guld       46
##  2  2016 USA            Silver     37
##  3  2016 USA            Brons      38
##  4  2016 Storbritannien Guld       27
##  5  2016 Storbritannien Silver     23
##  6  2016 Storbritannien Brons      17
##  7  2016 Kina           Guld       26
##  8  2016 Kina           Silver     18
##  9  2016 Kina           Brons      26
## 10  2016 Ryssland       Guld       19
## # ... with 254 more rows
  • Använd map_df med seq(1948, 2020, by = 4) och get_medals som argument för att skapa en tabell över medaljfördelningen för alla sommar OS sedan 1948.

Gapminder data

I en tidigare övning omformade vi en csv-fil från gapminder till ett mer användbart format. Nedanstående kod hämtar fler filer och placerar dem i data/gapminder_raw

dir.create("data/gapminder_raw")
filenames <- c("cell_phones_total.csv", "child_mortality_0_5_year_olds_dying_per_1000_born.csv", 
               "children_per_woman_total_fertility.csv", "co2_emissions_tonnes_per_person.csv", 
               "income_per_person_gdppercapita_ppp_inflation_adjusted.csv", 
               "life_expectancy_years.csv", "population_total.csv")

urls <- str_c("https://github.com/MT3003-ST21/data/raw/main/gapminder_raw/", filenames)
destfiles <- str_c("data/gapminder_raw/", filenames)
walk2(urls, destfiles, download.file)
  • Konstruera en funktion
read_gapminder <- function(file, path = "data/gapminder_raw/"){
  ...
}

som genererar följande

read_gapminder("life_expectancy_years.csv")
## # A tibble: 56,889 x 4
##    country     year  value variable             
##    <chr>       <chr> <dbl> <chr>                
##  1 Afghanistan 1800   28.2 life_expectancy_years
##  2 Afghanistan 1801   28.2 life_expectancy_years
##  3 Afghanistan 1802   28.2 life_expectancy_years
##  4 Afghanistan 1803   28.2 life_expectancy_years
##  5 Afghanistan 1804   28.2 life_expectancy_years
##  6 Afghanistan 1805   28.2 life_expectancy_years
##  7 Afghanistan 1806   28.1 life_expectancy_years
##  8 Afghanistan 1807   28.1 life_expectancy_years
##  9 Afghanistan 1808   28.1 life_expectancy_years
## 10 Afghanistan 1809   28.1 life_expectancy_years
## # ... with 56,879 more rows

här kan du ge kolumnen variable värdet str_sub(file, 1, -5).

  • Skapa en tabell (samma fyra kolumner) med alla variabler genom att använda map_df med argument list.files("data/gapminder_raw/") och read_gapminder.

v31: APIer, databaser och webbskrap

Mål denna vecka

  • Använda grundläggande funktioner i paket som httr, dbplyr och rvest.

Kursmaterial

Kommunicera med ett web-API

Ett vanligt sätt att kommunicera med en databas är via ett så kallat REST API, där anrop sker genom en webbadress (url). För populära API finns i allmänhet färdiga paket som förenklar kommunikationen, som t.ex. rtweet, spotifyr och rgbif. Saknas sådant kan man använta paketet httr för att själv konstruera anrop, vi kommer använda funktionerna GET och content från detta paket.

A-\(\pi\)

A-\(\pi\) är ett enkelt API där du kan hämta en sekvens av \(\pi\):s decimaler. Du behöver ange en startposition (start) och antal siffror (numberOfDigits), första 10 decimalerna ges av anropet http://api.pi.delivery/v1/pi?start=1&numberOfDigits=10 som vi gör från R med

library(httr)
response <- GET("http://api.pi.delivery/v1/pi?start=1&numberOfDigits=10")
content(response)
## $content
## [1] "1415926535"
Nobel API

https://nobelprize.readme.io/ finns ett API där du kan hämta data om nobelpriser och pristagare. Välj en kategori, t.ex. prize och prova göra ett anrop under “Try it out” längst ned på sidan. Klicka “Try it!” och notera den URL som genereras i fönstret under. Väljer vi format csv och category literature blir denna http://api.nobelprize.org/v1/prize.csv?category=literature. För att hämta resultatet till R

response <- GET("http://api.nobelprize.org/v1/prize.csv?category=literature")
content(response)
## # A tibble: 117 x 8
##     year category   overallMotivation    id firstname surname  motivation  share
##    <dbl> <chr>      <lgl>             <dbl> <chr>     <chr>    <chr>       <dbl>
##  1  2020 literature NA                  993 Louise    Glück    "\"for her~     1
##  2  2019 literature NA                  980 Peter     Handke   "\"for an ~     1
##  3  2018 literature NA                  979 Olga      Tokarcz~ "\"for a n~     1
##  4  2017 literature NA                  947 Kazuo     Ishiguro "\"who in ~     1
##  5  2016 literature NA                  937 Bob       Dylan    "\"for hav~     1
##  6  2015 literature NA                  924 Svetlana  Alexiev~ "\"for her~     1
##  7  2014 literature NA                  912 Patrick   Modiano  "\"for the~     1
##  8  2013 literature NA                  892 Alice     Munro    "\"master ~     1
##  9  2012 literature NA                  880 Mo        Yan      "\"who wit~     1
## 10  2011 literature NA                  868 Tomas     Transtr~ "\"because~     1
## # ... with 107 more rows

Skrapa webbsidor

Ibland saknas ett öppet API och vi blir tvungna att skrapa tabeller direkt från en webbsidas html-kod. Ett exempel gavs i föregående veckas uppgifter där vi hämtade tabeller över OS-medaljer från Wikipedia. För att lyckas med detta behövs grundläggande förståelse för hur html-koden är uppbyggd med taggar och element. Ett element består av en starttagg med eventuella attribut, innehåll och en sluttagg kodat som <tagg attributnamn="värde">Innehåll</tagg>. Informationen vi är ute efter är oftast Innehåll (men ibland värde).

Paketet rvest innehåller funktioner för att läsa webbsidor och extrahera information, vi illustrerar med ett minimalt exempel där vi vill få ut innehållet i taggen b ur <a> 1 </a> <b> 2 </b>. Först läser vi in html-koden

page <- read_html("<a> 1 </a> <b> 2 </b>")
page
## {html_document}
## <html>
## [1] <body>\n<a> 1 </a> <b> 2 </b>\n</body>

sedan extraherar vi taggen b med en så kallad CSS-väljare

elements <- html_elements(page, css = "b")
elements
## {xml_nodeset (1)}
## [1] <b> 2 </b>

slutligen drar vi ut innehållet som text

html_text(elements)
## [1] " 2 "

Svårigheten ligger i allmänhet att hitta en CSS-väljare som plockar ut precis vad vi vill ha. I övningen med OS-medaljer vill vi extrahera en specifik tabell från en Wikipedia-sida. En tabell i standardformat skrivs i html med start- och sluttagg <table> ... </table>, vi kan därför prova med css = "table"

url <- "https://sv.wikipedia.org/wiki/Medaljf%C3%B6rdelning_vid_olympiska_sommarspelen_2020"
page <- read_html(url)
elements <- html_elements(page, css = "table")
elements
## {xml_nodeset (5)}
## [1] <table class="wikitable sortable"><tbody>\n<tr>\n<th>Placering\n</th>\n<t ...
## [2] <table class="navbox" style="border-spacing:0; ;"><tbody><tr><td style="p ...
## [3] <table class="collapsible autocollapse" style="width:100%;border-spacing: ...
## [4] <table class="navbox" style="border-spacing:0; ;"><tbody><tr><td style="p ...
## [5] <table class="collapsible autocollapse" style="width:100%;border-spacing: ...

Sidan innehåller fem tabeller där vi vill ha den första. Vi kan extrahera denna med

html_table(elements[[1]])
## # A tibble: 81 x 7
##    Placering Land            Guld Silver Brons `Totalt antalme~ `Placering efte~
##    <chr>     <chr>          <int>  <int> <int>            <int>            <int>
##  1 1         Kina              29     17    16               62                2
##  2 2         USA               22     25    17               64                1
##  3 3         Japan             17      6    10               33                5
##  4 4         Australien        14      4    15               33                5
##  5 5         ROC               12     21    17               50                3
##  6 6         Storbritannien    11     12    12               35                4
##  7 7         Frankrike          6     10     7               23                8
##  8 8         Tyskland           6      6    11               23                8
##  9 9         Sydkorea           6      4     9               19               10
## 10 10        Nederländerna      5      7     6               18               11
## # ... with 71 more rows

men väljer istället att utnyttja class-attributet wikitable eftersom medaljtabellen inte ligger först på alla OS-sidor

elements <- html_elements(page, css = ".wikitable")
html_table(elements[[1]])
## # A tibble: 81 x 7
##    Placering Land            Guld Silver Brons `Totalt antalme~ `Placering efte~
##    <chr>     <chr>          <int>  <int> <int>            <int>            <int>
##  1 1         Kina              29     17    16               62                2
##  2 2         USA               22     25    17               64                1
##  3 3         Japan             17      6    10               33                5
##  4 4         Australien        14      4    15               33                5
##  5 5         ROC               12     21    17               50                3
##  6 6         Storbritannien    11     12    12               35                4
##  7 7         Frankrike          6     10     7               23                8
##  8 8         Tyskland           6      6    11               23                8
##  9 9         Sydkorea           6      4     9               19               10
## 10 10        Nederländerna      5      7     6               18               11
## # ... with 71 more rows

Punkten i .wikitable anger att det just är class-attributet vi skall välja efter (se CSS-väljare). Ett verktyg som är användbart för att identifiera dessa är SelectorGadget, som till exempel kan installeras som ett tillägg i Chrome.

På linande sätt kan vi hämta aktuell tabell för Allsvenskan med

url <- "https://www.allsvenskan.se/tabell"
page <- read_html(url)
elements <- html_elements(page, css = ".col-md-6")
html_table(elements[[1]])
## # A tibble: 33 x 10
##    `#`   ``    LAG   ``             M     V     O     F     `+/-` P    
##    <chr> <lgl> <chr> <chr>          <chr> <chr> <chr> <chr> <chr> <chr>
##  1 1     NA    ""    Djurgårdens IF 13    9     3     1     24-8  30   
##  2 2     NA    ""    Malmö FF       13    9     2     2     31-16 29   
##  3 3     NA    ""    IF Elfsborg    13    7     2     4     19-13 23   
##  4 4     NA    ""    Hammarby IF    13    6     4     3     25-17 22   
##  5 5     NA    ""    AIK            12    6     3     3     16-12 21   
##  6 6     NA    ""    IFK Norrköping 12    5     2     5     15-12 17   
##  7 7     NA    ""    Kalmar FF      13    4     5     4     12-14 17   
##  8 8     NA    ""    BK Häcken      13    4     4     5     18-18 16   
##  9 9     NA    ""    IFK Göteborg   12    3     6     3     14-14 15   
## 10 10    NA    ""    Halmstads BK   12    3     6     3     10-10 15   
## # ... with 23 more rows

där SelectorGadget har använts för att hitta väljaren .col-md-6. Notera att rubrikerna behöver justering; tabellen är ju inte avsedd för skrapning och resultatet behöver därför ofta justeras. Vid webbskrapning är det viktigt att respektera att syftet i allmänhet inte är att dela data och därför inte belasta servrar i onödan. Upphovsrätten till data kan även vara skyddat genom katalogskyddet.

Kommunicera med en SQL-databas

SQL är det idag dominerande språket för att ställa frågor till relationsdatabaser. Syntax är lik den vi lärt oss från tidyverses dplyr-paket och med den kunskapen är det inte svårt att lära sig ställa enkla frågor i SQL. Här kommer vi dock använda paketet dbplyr som sköter översättningen till databasens språk.

Innan vi kan ställa frågor till en databas behöver vi skapa en anslutning med DBI::dbConnect. Vi ansluter här till en öppen exempeldatabas imdb_small som innehåller ett mindre utrag ur IMDB (se Relational Dataset Repository för en lista på deras öppna databaser)

con <- DBI::dbConnect(
  RMariaDB::MariaDB(),
  host = "relational.fit.cvut.cz",
  port = 3306,
  username = "guest",
  password = "relational",
  db = "imdb_small"
)

Raden RMariaDB::MariaDB() anger hur vi skall ansluta, vilket beror på vilken mjukvara som driver databasen. I det här fallet är det en MySQL-databas som stöds av R-paketet RMariaDB (se DBI för alternativ).

Vi kan nu lista databasens tabeller med

DBI::dbListTables(con)
## [1] "actors"           "directors"        "directors_genres" "movies"          
## [5] "movies_directors" "movies_genres"    "roles"

och skapa en virtuell kopia av tabellen movies med

movies <- tbl(con, "movies")
movies
## # Source:   table<movies> [?? x 4]
## # Database: mysql [guest@relational.fit.cvut.cz:NA/imdb_small]
##        id name             year  rank
##     <int> <chr>           <int> <dbl>
##  1  10920 Aliens           1986  8.20
##  2  17173 Animal House     1978  7.5 
##  3  18979 Apollo 13        1995  7.5 
##  4  30959 Batman Begins    2005 NA   
##  5  46169 Braveheart       1995  8.30
##  6 109093 Fargo            1996  8.20
##  7 111813 Few Good Men, A  1992  7.5 
##  8 112290 Fight Club       1999  8.5 
##  9 116907 Footloose        1984  5.80
## 10 124110 Garden State     2004  8.30
## # ... with more rows

Här noterar vi att antalet rader i tabellen är listat som ??, vilket beror på att dbplyr ser till att bara hämta så mycket som är nödvändigt för att visa tabellens inledande rader. Vill vi hämta hela tabellen som en vanlig tibble använder vi collect

collect(movies)
## # A tibble: 36 x 4
##        id name             year  rank
##     <int> <chr>           <int> <dbl>
##  1  10920 Aliens           1986  8.20
##  2  17173 Animal House     1978  7.5 
##  3  18979 Apollo 13        1995  7.5 
##  4  30959 Batman Begins    2005 NA   
##  5  46169 Braveheart       1995  8.30
##  6 109093 Fargo            1996  8.20
##  7 111813 Few Good Men, A  1992  7.5 
##  8 112290 Fight Club       1999  8.5 
##  9 116907 Footloose        1984  5.80
## 10 124110 Garden State     2004  8.30
## # ... with 26 more rows

Som ett exempel skapar vi en tabell över alla Sci-Fi-filmer i databasen med

movies_genres <- tbl(con, "movies_genres")
movies_genres %>% 
  filter(genre == "Sci-Fi") %>% 
  left_join(movies, by = c("movie_id" = "id")) %>% 
  collect()
## # A tibble: 6 x 5
##   movie_id genre  name         year  rank
##      <int> <chr>  <chr>       <int> <dbl>
## 1    10920 Sci-Fi Aliens       1986  8.20
## 2   147603 Sci-Fi Hollow Man   2000  5.30
## 3   207992 Sci-Fi Matrix, The  1999  8.5 
## 4   254943 Sci-Fi Pi           1998  7.5 
## 5   313459 Sci-Fi Star Wars    1977  8.80
## 6   350424 Sci-Fi Vanilla Sky  2001  6.90

Vi kan även se den SQL-fråga som genererade resultatet med show_query

movies_genres %>% 
  filter(genre == "Sci-Fi") %>% 
  left_join(movies, by = c("movie_id" = "id")) %>% 
  show_query()
## <SQL>
## SELECT `movie_id`, `genre`, `name`, `year`, `rank`
## FROM (SELECT *
## FROM `movies_genres`
## WHERE (`genre` = 'Sci-Fi')) `LHS`
## LEFT JOIN `movies` AS `RHS`
## ON (`LHS`.`movie_id` = `RHS`.`id`)

Slutligen avslutar vi med att stänga ned anslutningen för att inte belasta servern i onödan

DBI::dbDisconnect(con)

Sammanfattningsvis

  • Anslut till databasen med DBI::dbConnect.
  • Lista tabeller med DBI::dbListTables.
  • Skapa virtuella tabeller med tbl.
  • Skriv din kod med virtuella tabelller och vanlig tidyverse-syntax.
  • Avsluta med collect.
  • Stäng anslutningen med DBI::dbDisconnect.

Övningar

Nobel API
  • Hämta alla pristagare (Laureate) och bestäm andelen män i varje priskategori.
SR API

Sveriges radio har ett öppet API där man bland annat kan hämta låtlistor från en given kanal och datum. Svaret ges dock inte i vanligt csv-format utan som XML eller JSON, vilket är vanligt då dessa format är mer flexibla. Ofta är JSON enklare att arbeta med i R.

  • Konstruera en funktion
get_songs <- function(channel, date){
  ...
}

som hämtar låtlistan för ett givet kanalnummer och datum. Begär svaret i JSON genom att avsluta anropet med &format=json. Du kan konvertera det svar (response) du får av GET med content(response, "text") %>% jsonlite::fromJSON().

Skrapa Dramaten

Dramatens arkiv Rollboken kan man söka efter uppgifter om deras produktioner. Eftersom de tabeller som genereras inte är i standardformat blir det enklast att skrapa varje variabel för sig. Namn på uppsättningarna från 2019 får vi t.ex. med

url <- "https://old.dramaten.se/medverkande/rollboken/?category=date&query=2019"
page <- read_html(url)
elements <- html_elements(page, css = ".play-name")
html_text(elements)[-1]
##  [1] "Förlovningen"                               
##  [2] "Hålla andan"                                
##  [3] "Den sista kabarén"                          
##  [4] "Orfeus stiger ner"                          
##  [5] "Lauras läppar"                              
##  [6] "Hamlet"                                     
##  [7] "Andante"                                    
##  [8] "Hugh och Nancys många världar"              
##  [9] "Guds olydiga revben"                        
## [10] "Vilddjur"                                   
## [11] "Linje Lusta"                                
## [12] "Bortbytingen"                               
## [13] "Performance Lecture: Micael Dahlen/Om lycka"
## [14] "Lilla måsen"                                
## [15] "Dövheten"                                   
## [16] "Ifigenia i Aulis"                           
## [17] "Häxjakten"                                  
## [18] "Evakuering"                                 
## [19] "Fedra/Hippolytos"                           
## [20] "Vi som fick leva om våra liv"               
## [21] "I väntan på Godot"                          
## [22] "Lilla döden hälsar på"                      
## [23] "Performance lecture: Om sanning"

(här tar [-1] bort första elementet, rubriken).

  • Identifiera CSS-väljare och skrapa övriga kolumner i tabellen.
Relational Dataset Repository

Anslut till valfri databas på Relational Dataset Repository och prova ställa frågor som ovan. En mer fulltändig version av IMDB finns till exempel i db = "imdb_ijs". Glöm inte stänga anslutningen när du är färdig.


v32: Mer om visualisering

Kursmaterial

Övningar

  • Gå igenom dina figurer från laborationer, kan de förbättras?