Den första veckan kommer innehålla administration och avslutas med en provkörning av kursverktygen i laboration 1.
ggplot-visualisering (data, mapping, geom).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.
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
livingArea) hyran (rent)?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.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.
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 Varugrupp på y-axeln och PrisPerLiter på x-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 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")arrange och filter.mutategroup_by och summariseAnvä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?
income är räknat i per capita, du behöver alltså bestämma kontinenternas totala inkomst och dela på den totala populationen.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")).SkattPerLiter som innehåller skattesatsen. Plotta sedan PrisPerLiter - SkattPerLiter mot Alkoholhalt (för varugruppen öl) med trendlinje.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).
ggplot.Inga speciella övningar denna vecka, experimetera med visualiseringar av data från tidigare veckor.
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å.
.csvcsv 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, 4motsvarar 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     4Detta 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; 4Eftersom 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     4Nä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   3med
readr::parse_number(c("3,14", "3 000"), locale = readr::locale(decimal_mark = ",", 
                                                               grouping_mark = " "))## [1]    3.14 3000.00Ibland 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.
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").
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.
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!
RStudio primers:
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.På 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
cell_phones_total till “tidy”-format med pivot_longer.*_join (om du gör allt i en pipe-sekvens kan right_join vara lämpligt).1.1k och 12 600 000 som 12.6M. Detta kan t.ex. lösas med case_when som nedancase_when(
  str_detect(antal_mobiler, "k") ~ 1000 * (str_remove(antal_mobiler, "k") %>% as.numeric()),
  str_detect(antal_mobiler, "M") ~ ...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      9innehå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            55Koropletkartor 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.
stringr, forcats och lubridate.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.
På 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 rowshä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")))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 rowsde 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")anti_join för att ta bort alla stoppord från listan på de vanligaste orden.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")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.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")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.
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.På 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 rowsEn 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 rowsmap_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.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)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 rowshär kan du ge kolumnen variable värdet str_sub(file, 1, -5).
map_df med argument list.files("data/gapminder_raw/") och read_gapminder.httr, dbplyr och rvest.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\) ä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"På 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 rowsIbland 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 rowsmen 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 rowsPunkten 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 rowsdä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.
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 rowsHä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 rowsSom 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.90Vi 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
DBI::dbConnect.DBI::dbListTables.tbl.tidyverse-syntax.collect.DBI::dbDisconnect.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.
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().
På 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).
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.