4 min read

Ladda hem data från SCB med R del 2 - SCB:s api med json som format

I mitt förra inlägg beskrev jag hur man använder det utmärkta pxweb-paketet för att ladda ner data från SCB:s öppna databas. Men ibland kan det vara smidigare att jobba direkt mot SCB:s API. R har via paketen httr och jsonlite utmärkta funktioner för att jobba med frågor till databaser på nätet.

Men varför krångla med att använda SCB:s API när pxweb redan finns? Det finns några fall då det är smidigare att jobba direkt mot SCB:s API.

  • Man kan hoppa över dimensioner via API:et. Om man är ute efter totalbefolkningen i alla Sveriges kommuner är det onödigt att samtidigt behöva ladda ner civilstånd och kön, vilket man måste göra om man använder pxweb.

  • Det går enkelt att fråga efter metadata om en tabell. Man kan till exempel ställa en fråga om vilket som är senaste år i en av SCB:s tabeller och kolla om man redan har laddat ned data för det året och sparat det lokalt. Om så är fallet behöver man inte uppdatera den lokala tabellen.

Ska man anropa SCB:s API behöver man två uppgifter: URL:en till databasen samt en json-formaterad fråga. Enklast sättet att få tag i uppgifterna är att gå in i SCB:s databas och bygga en fråga i SCB:s användargränssnitt. Nedan har jag frågat efter befolkningen i Malmö och Lund år 2017. Jag har hoppat över alla andra dimensioner.

Välj därefter att visa resultatet som tabell i webbläsaren. Under tabellen finns det en länk med texten “API för denna tabell”. När du trycker på länken visas en två textboxar: Den första innehåller URL:en till tabellen och den andra visar json-frågan som returnerar tabellen jag frågat efter.

Vi börjar med att spara URL:en till databasen i variabeln apiurl:

apiurl <- "http://api.scb.se/OV0104/v1/doris/sv/ssd/START/BE/BE0101/BE0101A/BefolkningNy"

Json-frågan ovan ska vi använda för att hämta hem data från SCB. En minimal justering av frågan behöver dock göras först: Ändra “format”: “px” till “format”: “json” under “response”. Därefter sparar vi frågan i variabeln “bodytxt” (glöm inte att sätta frågan inom enkla citattecken):

bodytxt <- '{
  "query": [
    {
      "code": "Region",
      "selection": {
        "filter": "vs:RegionKommun07",
        "values": [
          "1280",
          "1281"
        ]
      }
    },
    {
      "code": "ContentsCode",
      "selection": {
        "filter": "item",
        "values": [
          "BE0101N1"
        ]
      }
    },
    {
      "code": "Tid",
      "selection": {
        "filter": "item",
        "values": [
          "2017"
        ]
      }
    }
  ],
  "response": {
    "format": "json"
  }
}'

Nu är det dags att ladda hem tabellen från SCB genom att anropa SCB:s api med hjälp av httr:

library(jsonlite)
library(httr)
library(tidyverse)

bodytxt <- jsonlite::fromJSON(bodytxt, 
                                   simplifyVector = FALSE,
                                   simplifyDataFrame = FALSE)
  
req <- POST(apiurl,
            body = bodytxt, encode = "json", verbose())
stop_for_status(req)
# content(req)
lanswer <- content(req, simplifyDataFrame = TRUE)

Funktionen nedan gör om listan som returnerats från SCB till en någorlunda vettigt formaterad dataframe. Notera dock att vi saknar namn på kommunerna. Dessvärre returnerar anropet till SCB bara kommunkoderna om man väljer json som returformat.Behöver man kommunnamnen så får man skapa en tabell med kommunkoder och kommunnamn som man kan joina med kommunkoderna från SCB.

fparse_jsontab_scb <- function(ldatajson) {
  library(tidyverse)
  dftab <- bind_cols(do.call(rbind.data.frame, ldatajson$data$key), 
                      do.call(rbind.data.frame, ldatajson$data$values))
  names(dftab) <- ldatajson$columns$text
  print(dftab)
  dftab <- mutate_if(dftab, is.factor, as.character)
  for (i in 1:ncol(dftab)) {
    typ <- ldatajson$columns[[i, "type"]]
    if (typ == "c") {
      dftab[i] <- as.numeric(dftab[[i]]) }
    else if (typ == "t" &
             !is.na(as.numeric(dftab[[1,i]])) &
             !(ldatajson$columns$text[i] %in% c("år", "year"))) { dftab[i] <- as.numeric(dftab[[i]]) } 
    else if (typ == "t" &
                !is.na(as.numeric(dftab[[1,i]])) &
             ldatajson$columns$text[i] %in% c("år", "year")) {
      dftab[i] <- as.integer(dftab[[i]])
    }
  }
  return(dftab)
} 

Nu provar vi att formatera den lista som returnerades från SCB med funktionen ovan, Resultatet blir en dataframe med korrekt angivna variabeltyper.

dftest <- fparse_jsontab_scb(lanswer)
##   region   år Folkmängd
## 1   1280 2017    333633
## 2   1281 2017    121274

Det går också att få fram metainformation om en tabell genom att anropa SCB:s API med bara URL:en som argument. Nedan visas en funktion som visar senaste år som det finns statistik för i en specifik tabell som anges i den URL som skickas med som argument. Vilket av metavariablerna som innehåller årtalet kan variera och måste preciseras i funktionsargumentet index1.

fscb_senaste_ar <- function(url, index1 = 2) {
  library(httr)
  library(tidyverse)
  library(jsonlite)
  library(magrittr)
  
  ltabmeta <- content(GET(url))
  max_ar <- ltabmeta$variables[[index1]]$valueTexts %>%
  unlist(.) %>%
  as.integer(.) %>%
  max(.)
  
  return(max_ar)
}
print(fscb_senaste_ar(apiurl, 6))
## [1] 2019