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
.mutate
group_by
och summarise
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?
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å.
.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
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
.
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 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
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.
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 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")))
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")
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 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
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.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 rows
hä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 rows
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.
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 tidyverse
s 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
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.