├── .gitignore ├── Python ├── basics.py ├── thematic_map.py └── time_series_graph.py ├── R ├── basics.R ├── thematic_map.R └── time_series_graph.R └── README.md /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/statistiekcbs/CBS-Open-Data-v4/e7a845df3504ab8a64d72c7f44cde2d26cfdbd59/.gitignore -------------------------------------------------------------------------------- /Python/basics.py: -------------------------------------------------------------------------------- 1 | """ 2 | Voorbeelden gebruik van beta-versie CBS Open Data in Python 3 | https://beta-odata4.cbs.nl 4 | Auteur: Jolien Oomens 5 | Centraal Bureau voor de Statistiek 6 | 7 | Minimale voorbeelden van het ophalen van een tabel, het koppelen van metadata 8 | en het filteren van data voor het downloaden. 9 | """ 10 | 11 | import pandas as pd 12 | import requests 13 | 14 | def get_odata(target_url): 15 | data = pd.DataFrame() 16 | while target_url: 17 | r = requests.get(target_url).json() 18 | data = data.append(pd.DataFrame(r['value'])) 19 | 20 | if '@odata.nextLink' in r: 21 | target_url = r['@odata.nextLink'] 22 | else: 23 | target_url = None 24 | 25 | return data 26 | 27 | table_url = "https://beta-odata4.cbs.nl/CBS/83765NED" 28 | 29 | # Downloaden van gehele tabel, duurt ongeveer 2 minuten 30 | # target_url = table_url + "/Observations" 31 | # Downloaden van eerste 100 rijen uit de tabel 32 | target_url = table_url + "/Observations?$top=100" 33 | data = get_odata(target_url) 34 | print(data.head()) 35 | 36 | # Koppelen van metadata aan tabel 37 | groups = get_odata(table_url + "/MeasureGroups") 38 | codes = get_odata(table_url + "/MeasureCodes") 39 | 40 | data = pd.merge(data, codes, left_on = "Measure", right_on = "Identifier") 41 | data = pd.merge(data, groups, left_on = "MeasureGroupId", right_on = "Id") 42 | print(data.head()) 43 | 44 | # Selectie downloaden van tabel 45 | wijken_en_buurtencodes = get_odata(table_url + "/WijkenEnBuurtenCodes") 46 | ams = wijken_en_buurtencodes[wijken_en_buurtencodes['Title'].str.contains("Amsterdam")] 47 | print(ams[['Title','Identifier']]) 48 | 49 | target_url = table_url + "/Observations?$filter=WijkenEnBuurten eq 'GM0363'" 50 | data_amsterdam = get_odata(target_url) 51 | print(data_amsterdam.head()) -------------------------------------------------------------------------------- /Python/thematic_map.py: -------------------------------------------------------------------------------- 1 | """ 2 | Voorbeelden gebruik van beta-versie CBS Open Data in R 3 | https://beta-odata4.cbs.nl 4 | Auteur: Jolien Oomens 5 | Centraal Bureau voor de Statistiek 6 | 7 | In dit voorbeeld worden gemeentegrenzen gekoppeld aan geboortecijfers om een 8 | thematische kaart te maken. 9 | """ 10 | 11 | import pandas as pd 12 | import geopandas as gpd 13 | import requests 14 | 15 | def get_odata(target_url): 16 | data = pd.DataFrame() 17 | while target_url: 18 | r = requests.get(target_url).json() 19 | data = data.append(pd.DataFrame(r['value'])) 20 | 21 | if '@odata.nextLink' in r: 22 | target_url = r['@odata.nextLink'] 23 | else: 24 | target_url = None 25 | 26 | return data 27 | 28 | # De geodata wordt via de API van het Nationaal Georegister van PDOK opgehaald. 29 | # Een overzicht van beschikbare data staat op https://www.pdok.nl/datasets. 30 | geodata_url = "https://geodata.nationaalgeoregister.nl/cbsgebiedsindelingen/wfs?request=GetFeature&service=WFS&version=2.0.0&typeName=cbs_gemeente_2017_gegeneraliseerd&outputFormat=json" 31 | gemeentegrenzen = gpd.read_file(geodata_url) 32 | 33 | # Zoek op welke codes bij geboortecijfers horen 34 | table_url = "https://beta-odata4.cbs.nl/CBS/83765NED" 35 | codes = get_odata(table_url + "/MeasureCodes") 36 | geb = codes[codes['Title'].str.contains("Geboorte")] 37 | print(geb[['Title','Unit','Identifier']]) 38 | 39 | target_url = table_url + "/Observations?$filter=Measure eq 'M000173_2' and startswith(WijkenEnBuurten,'GM')" 40 | geboorten_per_gemeente = get_odata(target_url) 41 | geboorten_per_gemeente['WijkenEnBuurten'] = geboorten_per_gemeente['WijkenEnBuurten'].str.strip() 42 | geboorten_per_gemeente = geboorten_per_gemeente.rename({'Value':'relatieve_geboorte'}, axis='columns') 43 | gemeentegrenzen = pd.merge(gemeentegrenzen, geboorten_per_gemeente, left_on = "statcode", right_on = "WijkenEnBuurten") 44 | 45 | p = gemeentegrenzen.plot(column='relatieve_geboorte', figsize = (10,8)) 46 | p.axis('off') 47 | p.set_title("Levend geborenen per 1000 inwoners, 2017") -------------------------------------------------------------------------------- /Python/time_series_graph.py: -------------------------------------------------------------------------------- 1 | """ 2 | Voorbeelden gebruik van beta-versie CBS Open Data in R 3 | https://beta-odata4.cbs.nl 4 | Auteur: Jolien Oomens 5 | Centraal Bureau voor de Statistiek 6 | 7 | In dit voorbeeld worden cijfers over de opbrengst van toeristenbelasting per 8 | jaar opgehaald, bewerkt en weergegeven in een grafiek. 9 | """ 10 | 11 | import pandas as pd 12 | import requests 13 | import datetime 14 | 15 | def get_odata(target_url): 16 | data = pd.DataFrame() 17 | while target_url: 18 | r = requests.get(target_url).json() 19 | data = data.append(pd.DataFrame(r['value'])) 20 | 21 | if '@odata.nextLink' in r: 22 | target_url = r['@odata.nextLink'] 23 | else: 24 | target_url = None 25 | 26 | return data 27 | 28 | # Bekijk welke metadata beschikbaar is 29 | table_url = "https://beta-odata4.cbs.nl/CBS/84120NED" 30 | print(get_odata(table_url)) 31 | 32 | # Zoek de code van toeristenbelasting op 33 | belastingcodes = get_odata(table_url + "/BelastingenEnWettelijkePremiesCodes") 34 | print(belastingcodes[belastingcodes['Title'].str.contains("Toerist")]) 35 | 36 | # Haal de data op 37 | target_url = table_url + "/Observations?$filter=BelastingenEnWettelijkePremies eq 'A045081'" 38 | data = get_odata(target_url) 39 | 40 | # Verwijder overbodige kolommen 41 | data = data[['Perioden','Value']] 42 | 43 | """ 44 | Deze functie voegt drie kolommen toe aan het dataframe: year, frequency en 45 | date. Standaard wordt de begindatum van de periode toegevoegd, zoals 01-03-2019 46 | bij de periode 2019KW01. 47 | """ 48 | def cbs_add_date_column(data, period_name = "Perioden"): 49 | if not period_name in list(data): 50 | print("No time dimension found called " + period_name) 51 | return data 52 | 53 | regex = r'(\d{4})([A-Z]{2})(\d{2})' 54 | data[['year','frequency','count']] = data[period_name].str.extract(regex) 55 | 56 | freq_dict = {'JJ': 'Y', 'KW': 'Q', 'MM': 'M'} 57 | data = data.replace({'frequency': freq_dict}) 58 | 59 | # Converteert van het CBS-formaat voor perioden naar een datetime. 60 | def convert_cbs_period(row): 61 | if(row['frequency'] == 'Y'): 62 | return datetime.datetime(int(row['year']),1,1) 63 | elif(row['frequency'] == 'M'): 64 | return datetime.datetime(int(row['year']),int(row['count']),1) 65 | elif(row['frequency'] == 'Q'): 66 | return datetime.datetime(int(row['year']),int(row['count'])*3-2,1) 67 | else: 68 | return None 69 | 70 | data['date'] = data.apply(convert_cbs_period, axis = 1) 71 | return data 72 | 73 | data = cbs_add_date_column(data) 74 | 75 | # Selecteer jaarcijfers en plot een grafiek 76 | jaarcijfers = data[data['frequency'] == 'Y'] 77 | 78 | p = jaarcijfers.plot(x = 'date', y = 'Value',legend = False) 79 | p.set_title("Opbrengst toeristenbelasting per jaar") 80 | p.set_ylim([0,300]) 81 | p.set_xlabel("") 82 | p.set_ylabel("mln euro") -------------------------------------------------------------------------------- /R/basics.R: -------------------------------------------------------------------------------- 1 | # Voorbeelden gebruik van beta-versie CBS Open Data in R 2 | # https://beta-odata4.cbs.nl 3 | # Auteur: Jolien Oomens 4 | # Centraal Bureau voor de Statistiek 5 | 6 | # Minimale voorbeelden van het ophalen van een tabel, het koppelen van metadata 7 | # en het filteren van data voor het downloaden. 8 | 9 | library(tidyverse) 10 | library(jsonlite) 11 | 12 | get_odata <- function(targetUrl) { 13 | data <- data.frame() 14 | 15 | while(!is.null(targetUrl)){ 16 | response <- fromJSON(url(targetUrl)) 17 | data <- bind_rows(data,response$value) 18 | targetUrl <- response[["@odata.nextLink"]] 19 | } 20 | return(data) 21 | } 22 | 23 | tableUrl <- "https://beta-odata4.cbs.nl/CBS/83765NED" 24 | 25 | # Downloaden van gehele tabel, duurt ongeveer 2 minuten 26 | # targetUrl <- paste0(tableUrl,"/Observations") 27 | # Downloaden van eerste 100 rijen uit de tabel 28 | targetUrl <- paste0(tableUrl,"/Observations?$top=100") 29 | data <- get_odata(targetUrl) 30 | head(data) 31 | 32 | # Koppelen van metadata aan tabel 33 | groups <- get_odata(paste0(tableUrl,"/MeasureGroups")) 34 | codes <- get_odata(paste0(tableUrl,"/MeasureCodes")) 35 | 36 | data <- data %>% 37 | left_join(codes, by=c("Measure"="Identifier"))%>% 38 | left_join(groups, by=c("MeasureGroupId"="Id")) 39 | 40 | # Selectie downloaden van tabel 41 | wijken_en_buurtencodes <- get_odata(paste0(tableUrl,"/WijkenEnBuurtenCodes")) 42 | wijken_en_buurtencodes %>% filter(str_detect(Title, "Amsterdam")) 43 | 44 | targetUrl <- paste0(tableUrl,"/Observations?$filter=WijkenEnBuurten eq \'GM0363\'") 45 | data_amsterdam <- get_odata(targetUrl) -------------------------------------------------------------------------------- /R/thematic_map.R: -------------------------------------------------------------------------------- 1 | # Voorbeelden gebruik van beta-versie CBS Open Data in R 2 | # https://beta-odata4.cbs.nl 3 | # Auteur: Jolien Oomens 4 | # Centraal Bureau voor de Statistiek 5 | 6 | # In dit voorbeeld worden gemeentegrenzen gekoppeld aan geboortecijfers om een 7 | # thematische kaart te maken. 8 | 9 | library(jsonlite) 10 | library(geojsonio) 11 | library(tidyverse) 12 | library(sp) 13 | 14 | get_odata <- function(targetUrl) { 15 | response <- fromJSON(url(targetUrl)) 16 | data <- response$value 17 | targetUrl <- response[["@odata.nextLink"]] 18 | 19 | while(!is.null(targetUrl)){ 20 | response <- fromJSON(url(targetUrl)) 21 | data <- bind_rows(data,response$value) 22 | targetUrl <- response[["@odata.nextLink"]] 23 | } 24 | return(data) 25 | } 26 | 27 | # De geodata wordt via de API van het Nationaal Georegister van PDOK opgehaald. 28 | # Een overzicht van beschikbare data staat op https://www.pdok.nl/datasets. 29 | geoUrl <- "https://geodata.nationaalgeoregister.nl/cbsgebiedsindelingen/wfs?request=GetFeature&service=WFS&version=2.0.0&typeName=cbs_gemeente_2017_gegeneraliseerd&outputFormat=json" 30 | fileName <- "gemeentegrenzen2017.geojson" 31 | download.file(geoUrl, fileName) 32 | gemeentegrenzen <- geojson_read(fileName, what = "sp") 33 | 34 | # Zoek op welke codes bij geboortecijfers horen 35 | tableUrl <- "https://beta-odata4.cbs.nl/CBS/83765NED" 36 | codes <- get_odata(paste0(tableUrl,"/MeasureCodes")) 37 | codes %>% filter(str_detect(Title,"Geboorte")) 38 | 39 | targetUrl <- paste0(tableUrl,"/Observations?$filter=Measure eq \'M000173_2\' and startswith(WijkenEnBuurten,\'GM\')") 40 | 41 | geboorten_per_gemeente <- get_odata(targetUrl) %>% 42 | mutate(WijkenEnBuurten = str_trim(WijkenEnBuurten)) %>% 43 | rename(relatieve_geboorte = Value) 44 | 45 | gemeentegrenzen@data <- gemeentegrenzen@data %>% 46 | left_join(geboorten_per_gemeente,by=c("statcode"="WijkenEnBuurten")) 47 | 48 | g <- fortify(gemeentegrenzen, region = "id") 49 | gemeentegrenzenDF <- merge(g, gemeentegrenzen@data, by = "id") 50 | 51 | ggplot(data = gemeentegrenzenDF) + 52 | geom_polygon(aes(x=long, y=lat, group = group, fill = relatieve_geboorte)) + 53 | coord_equal()+ 54 | ggtitle("Levend geborenen per 1000 inwoners, 2017") + 55 | theme_void() -------------------------------------------------------------------------------- /R/time_series_graph.R: -------------------------------------------------------------------------------- 1 | # Voorbeelden gebruik van beta-versie CBS Open Data in R 2 | # https://beta-odata4.cbs.nl 3 | # Auteur: Jolien Oomens 4 | # Centraal Bureau voor de Statistiek 5 | 6 | # In dit voorbeeld worden cijfers over de opbrengst van toeristenbelasting per 7 | # jaar opgehaald, bewerkt en weergegeven in een grafiek. 8 | 9 | library(tidyverse) 10 | library(jsonlite) 11 | 12 | get_odata <- function(targetUrl) { 13 | data <- data.frame() 14 | 15 | while(!is.null(targetUrl)){ 16 | response <- fromJSON(url(targetUrl)) 17 | data <- bind_rows(data,response$value) 18 | targetUrl <- response[["@odata.nextLink"]] 19 | } 20 | return(data) 21 | } 22 | 23 | # Bekijk welke metadata beschikbaar is 24 | tableUrl <- "https://beta-odata4.cbs.nl/CBS/84120NED" 25 | get_odata(tableUrl) %>% select(name,kind) 26 | 27 | # Zoek de code van toeristenbelasting op 28 | belastingCodes <- get_odata(paste0(tableUrl,"/BelastingenEnWettelijkePremiesCodes")) 29 | belastingCodes %>% filter(str_detect(Title, "Toerist")) %>% select(Identifier,Title,Description) 30 | 31 | # Haal de data op 32 | targetUrl <- paste0(tableUrl,"/Observations?$filter=BelastingenEnWettelijkePremies eq 'A045081'") 33 | data <- get_odata(targetUrl) 34 | 35 | # Verwijder overbodige kolommen 36 | data <- data %>% select(Perioden, Value) 37 | 38 | # Definieer een functie voor datumconversie 39 | # Dit is een aangepaste versie van de functie cbs_add_date_column uit cbsodataR 40 | cbs_add_date_column <- function(x, date_type = c("Date", "numeric"), period_name="Perioden",...){ 41 | if (!period_name %in% colnames(x)){ 42 | warning(paste("No time dimension found called",period_name)) 43 | return(x) 44 | } 45 | 46 | period <- x[[period_name]] 47 | PATTERN <- "(\\d{4})(\\w{2})(\\d{2})" 48 | 49 | year <- as.integer(sub(PATTERN, "\\1", period)) 50 | number <- as.integer(sub(PATTERN, "\\3", period)) 51 | type <- factor(sub(PATTERN, "\\2", period)) 52 | 53 | # year conversion 54 | is_year <- type %in% c("JJ") 55 | is_quarter <- type %in% c("KW") 56 | is_month <- type %in% c("MM") 57 | 58 | 59 | # date 60 | date_type <- match.arg(date_type) 61 | 62 | if (date_type == "Date"){ 63 | period <- as.POSIXct(character()) 64 | period[is_year] <- ISOdate(year, 1, 1, tz="")[is_year] 65 | period[is_quarter] <- ISOdate(year, 1 + (number - 1) * 3, 1, tz="")[is_quarter] 66 | period[is_month] <- ISOdate(year, number, 1, tz="")[is_month] 67 | period <- as.Date(period) 68 | } else if (date_type == "numeric"){ 69 | period <- numeric() 70 | period[is_year] <- year[is_year] + 0.5 71 | period[is_quarter] <- (year + (3*(number - 1) + 2) / 12)[is_quarter] 72 | period[is_month] <- (year + (number - 0.5) / 12)[is_month] 73 | if (all(is_year)){ 74 | period <- as.integer(period) 75 | } 76 | } 77 | 78 | type1 <- factor(levels=c("Y","Q", "M")) 79 | type1[is_year] <- "Y" 80 | type1[is_quarter] <- "Q" 81 | type1[is_month] <- "M" 82 | type1 <- droplevels(type1) 83 | 84 | # put the column just behind the period column 85 | i <- which(names(x) == period_name) 86 | x <- x[c(1:i, i, i:ncol(x))] 87 | idx <- c(i+1, i+2) 88 | x[idx] <- list(period, type1) 89 | names(x)[idx] <- paste0(period_name, paste0("_", c(date_type,"freq"))) 90 | x 91 | } 92 | 93 | # Datumconversie en selectie van jaarcijfers 94 | data <- data %>% cbs_add_date_column() 95 | jaarcijfers <- data %>% filter(Perioden_freq == "Y") 96 | 97 | # Plot de tijdreeks 98 | ggplot(jaarcijfers, aes(x=Perioden_Date, y=Value)) + 99 | geom_line() + 100 | ylim(0,300)+ 101 | labs(title = "Opbrengst toeristenbelasting per jaar", x = "", y = "mln euro", 102 | caption = "Bron: CBS") 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CBS Open Data v4 2 | 3 | In deze repository zijn codevoorbeelden te vinden van het gebruik van de [beta-versie van CBS Open Data v4](https://beta.opendata.cbs.nl). Deze nieuwe versie van de CBS Open Data API is gebaseerd op het [OData 4](https://www.odata.org/)-protocol. 4 | 5 | Er wordt beschreven hoe datasets kunnen worden gedownload met metadata, hoe deze gekoppeld kunnen worden, hoe filters werken en er is een voorbeeld uitgewerkt van een thematische kaart. 6 | 7 | ## Codevoorbeelden 8 | Voor elke taal is er een aparte map met daarin codevoorbeelden. De talen die momenteel ondersteund worden zijn 9 | * R, 10 | * Python. 11 | 12 | Elke map bevat 13 | * basics: het downloaden van een tabel, het koppelen van metadata en het downloaden van een selectie van een tabel. 14 | * thematic map: het koppelen van geodata van PDOK met CBS-data om een kaart te maken. 15 | * time series graph: het bewerken en het maken van grafieken van tijdreeksen. 16 | 17 | ## Werken met geodata 18 | De geobestanden die nodig zijn voor thematische kaarten worden door het CBS gepubliceerd via [PDOK (Publieke Dienstverlening Op de Kaart)](https://www.pdok.nl/datasets). Deze geodata is te downloaden in verschillende bestandsformaten zoals Shapefile en GeoJSON en het is ook mogelijk om de bestanden geautomatiseerd op te halen met de API. In de codevoorbeelden wordt gebruik gemaakt van de API. Meer informatie over de geo-API is te vinden in [de documentatie](https://pdok-ngr.readthedocs.io/). 19 | 20 | ## Licentie 21 | Op alle datasets van het CBS en alle codevoorbeelden is de licentie Creative Commons Naamsvermelding van toepassing. Als onderdeel van Creative Commons Naamsvermelding is het bij hergebruik verplicht te vermelden dat de gegevens afkomstig zijn van het CBS. Meer informatie is te vinden in de [Disclaimer Open Data](https://www.cbs.nl/-/media/statline/documenten/disclaimer-open-data-v-2.pdf?la=nl-nl). 22 | --------------------------------------------------------------------------------