Icoon grafiek

Tijdreeksen OData v4

Wanneer dezelfde variabele op verschillende momenten gemeten wordt ontstaat een tijdreeks. De meeste cijfers van het CBS worden elke maand, elk kwartaal of elk jaar geactualiseerd. In deze handleiding wordt aan de hand van cijfers over toeristenbelasting uitgelegd hoe een tijdreeks verwerkt en gevisualiseerd kan worden. De informatie uit de Snelstartgids wordt bekend verondersteld.

De codevoorbeelden kunnen eenvoudig naar het klembord worden gekopieerd door op de knop in het codeblok te klikken. De voorbeelden op deze pagina zijn ook gebundeld te vinden op GitHub.

Keuzemenu programmeertaal:

Tabel 84120NED bevat gegevens over de belastingopbrengsten en de ontvangsten van wettelijke sociale premies van de overheid. De gegevens zijn uitgesplitst naar soort belasting of wettelijke sociale premie. De code van toeristenbelasting is op te zoeken in de metadata.

library(tidyverse)
library(jsonlite)

get_odata <- function(targetUrl) {
  data <- data.frame()
  
  while(!is.null(targetUrl)){
    response <- fromJSON(url(targetUrl))
    data <- bind_rows(data,response$value)
    targetUrl <- response[["@odata.nextLink"]]
  }
  return(data)
}

# Bekijk welke metadata beschikbaar is
tableUrl <- "https://odata4.cbs.nl/CBS/84120NED"
get_odata(tableUrl) %>% select(name,kind)

# Zoek de code van toeristenbelasting op
belastingCodes <- get_odata(paste0(tableUrl,"/BelastingenEnWettelijkePremiesCodes"))
belastingCodes %>% filter(str_detect(Title, "Toerist")) %>% select(Identifier,Title,Description)

De Key die hoort bij toeristenbelasting is A045081. Om alleen de juiste informatie op te halen voegen we het filter "BelastingenEnWettelijkePremies eq 'A045081'"" toe aan de url.

# Haal de data op
targetUrl <- paste0(tableUrl,"/Observations?$filter=BelastingenEnWettelijkePremies eq 'A045081'")
data <- get_odata(targetUrl)

# Verwijder overbodige kolommen
data <- data %>% select(Perioden, Value)

De data die met de API wordt binnengehaald bevat een kolom Perioden die de verslagperiode in het formaat yyyyXXww aangegeeft, waarbij yyyy het jaar is, XX aangeeft of het jaarcijfers (JJ), kwartaalcijfers (KW) of maandcijfers (MM) zijn en ww het volgnummer is. Om de data verder te verwerken is het nodig om de perioden te converteren naar een standaardformaat en dit kan met de functie cbs_add_date_column uit het package cbsodataR. Deze functie voegt een kolom Perioden_freq toe met het type van de periode (maand/kwartaal/jaar) en een kolom met de startdatum van de periode. Bij jaarcijfers wordt de letter Y in de kolom Perioden_freq ingevuld. Vervolgens kan er eenvoudig op jaarcijfers gefilterd worden met filter(Perioden_freq == "Y").

# Definieer een functie voor datumconversie
# Dit is een aangepaste versie van de functie cbs_add_date_column uit cbsodataR
cbs_add_date_column <- function(x, date_type = c("Date", "numeric"), period_name="Perioden",...){
  if (!period_name %in% colnames(x)){
    warning(paste("No time dimension found called",period_name))
    return(x)
  }
  
  period <- x[[period_name]]
  PATTERN <- "(\\d{4})(\\w{2})(\\d{2})"
  
  year   <- as.integer(sub(PATTERN, "\\1", period))
  number <- as.integer(sub(PATTERN, "\\3", period))
  type   <- factor(sub(PATTERN, "\\2", period))
  
  # detectie van frequentie van cijfers
  is_year <- type %in% c("JJ")
  is_quarter <- type %in% c("KW")
  is_month <- type %in% c("MM")
  
  # datumherkenning
  date_type <- match.arg(date_type)
  
  if (date_type == "Date"){
    period <- as.POSIXct(character())
    period[is_year] <- ISOdate(year, 1, 1, tz="")[is_year]
    period[is_quarter] <- ISOdate(year, 1 + (number - 1) * 3, 1, tz="")[is_quarter]
    period[is_month] <- ISOdate(year, number, 1, tz="")[is_month]
    period <- as.Date(period)
  } else if (date_type == "numeric"){
    period <- numeric()
    period[is_year] <- year[is_year] + 0.5
    period[is_quarter] <- (year + (3*(number - 1) + 2) / 12)[is_quarter]
    period[is_month] <- (year + (number - 0.5) / 12)[is_month]
    if (all(is_year)){
      period <- as.integer(period)
    }
  }
  
  type1 <- factor(levels=c("Y","Q", "M"))
  type1[is_year] <- "Y"
  type1[is_quarter] <- "Q"
  type1[is_month] <- "M"
  type1 <- droplevels(type1)
  
  # put the column just behind the period column
  i <- which(names(x) == period_name)
  x <- x[c(1:i, i, i:ncol(x))]
  idx <- c(i+1, i+2)
  x[idx] <- list(period, type1)
  names(x)[idx] <- paste0(period_name, paste0("_", c(date_type,"freq")))
  x
}

# Datumconversie en selectie van jaarcijfers
data <- data %>% cbs_add_date_column() 
jaarcijfers <- data %>% filter(Perioden_freq == "Y")

Tenslotte kan er een grafiek van de tijdreeks worden gemaakt met bijvoorbeeld ggplot2.

# Plot de tijdreeks
ggplot(jaarcijfers, aes(x=Perioden_Date, y=Value)) +
  geom_line() + 
  ylim(0,250)+
  labs(title = "Opbrengst toeristenbelasting per jaar", x = "", y = "mln euro",
       caption = "Bron: CBS")

Grafiek van opbrengst toeristenbelasting tussen 1995 en 2017.

Tabel 84120NED bevat gegevens over de belastingopbrengsten en de ontvangsten van wettelijke sociale premies van de overheid. De gegevens zijn uitgesplitst naar soort belasting of wettelijke sociale premie. De kolomnaam van toeristenbelasting is op te zoeken in de metadata.

import pandas as pd
import requests
import datetime

def get_odata(target_url):
    data = pd.DataFrame()
    while target_url:
        r = requests.get(target_url).json()
        data = data.append(pd.DataFrame(r['value']))
        
        if '@odata.nextLink' in r:
            target_url = r['@odata.nextLink']
        else:
            target_url = None
            
    return data

# Bekijk welke metadata beschikbaar is
table_url = "https://odata4.cbs.nl/CBS/84120NED"
print(get_odata(table_url))

# Zoek de code van toeristenbelasting op
belastingcodes = get_odata(table_url + "/BelastingenEnWettelijkePremiesCodes")
print(belastingcodes[belastingcodes['Title'].str.contains("Toerist")])

De Key die hoort bij toeristenbelasting is A045081. Om alleen de juiste informatie op te halen voegen we het filter "BelastingenEnWettelijkePremies eq 'A045081'" toe aan get_data.

# Haal de data op
target_url = table_url + "/Observations?$filter=BelastingenEnWettelijkePremies eq 'A045081'"
data = get_odata(target_url)

# Verwijder overbodige kolommen
data = data[['Perioden','Value']]

De data die met de API wordt binnengehaald bevat een kolom Perioden die de verslagperiode in het formaat yyyyXXww aangegeeft, waarbij yyyy het jaar is, XX aangeeft of het jaarcijfers (JJ), kwartaalcijfers (KW) of maandcijfers (MM) zijn en ww het volgnummer is. Om de data verder te verwerken is het nodig om de perioden te converteren naar een standaardformaat en dit kan met de functie cbs_add_date_column die hieronder gedefinieerd wordt. Deze functie is gebaseerd op de gelijknamige functie uit het R-package cbsodataR en voegt een kolom frequency toe met het type van de periode (maand/kwartaal/jaar) en een kolom met de startdatum van de periode. Bij jaarcijfers wordt de letter Y in de kolom frequency ingevuld. Vervolgens kan er eenvoudig op jaarcijfers gefilterd worden met data['frequency'] == 'Y'.

""" 
Deze functie voegt drie kolommen toe aan het dataframe: year, frequency en
date. Standaard wordt de begindatum van de periode toegevoegd, zoals 01-03-2019
bij de periode 2019KW01.
"""
def cbs_add_date_column(data, period_name = "Perioden"):
    if not period_name in list(data):
        print("No time dimension found called " + period_name)
        return data
    
    regex = r'(\d{4})([A-Z]{2})(\d{2})'
    data[['year','frequency','count']] = data[period_name].str.extract(regex)
    
    freq_dict = {'JJ': 'Y', 'KW': 'Q', 'MM': 'M'}
    data = data.replace({'frequency': freq_dict})
    
     # Converteert van het CBS-formaat voor perioden naar een datetime.
    def convert_cbs_period(row):
        if(row['frequency'] == 'Y'):
            return datetime.datetime(int(row['year']),1,1)
        elif(row['frequency'] == 'M'):
            return datetime.datetime(int(row['year']),int(row['count']),1)
        elif(row['frequency'] == 'Q'):
            return datetime.datetime(int(row['year']),int(row['count'])*3-2,1)
        else:
            return None
        
    data['date'] = data.apply(convert_cbs_period, axis = 1)
    return data

# Voeg een kolom met de verwerkte datum toe
data = cbs_add_date_column(data)

# Selecteer jaarcijfers 
jaarcijfers = data[data['frequency'] == 'Y']

Tenslotte kan er een grafiek van de tijdreeks worden gemaakt met bijvoorbeeld matplotlib.

p = jaarcijfers.plot(x = 'date', y = 'Value',legend = False)
p.set_title("Opbrengst toeristenbelasting per jaar")
p.set_ylim([0,250])
p.set_xlabel("")
p.set_ylabel("mln euro")

Grafiek van opbrengst toeristenbelasting tussen 1995 en 2017.