Verken de diversiteit van Nederlandse gemeenten

Auteur

Mattijn van Hoek

Publicatiedatum

26 januari 2024

In dit artikel duiken we diep in de gegevens van Nederlandse gemeenten op basis van geboortelanden, van de ruwe data tot een boeiende interactieve visualisatie.

Met behulp van de cbsodata-bibliotheek verzamelen we gedetailleerde bevolkingsgegevens per nationaliteit voor het jaar 2023. Dankzij slimme filtering en coding identificeren we vervolgens per gemeente de twee meest voorkomende Nationaliteiten.

Code
import cbsodata
import pandas as pd
import geopandas as gpd
import altair as alt
import topojson as tp


# list of nationaliteit codes
natcodes_landen = ['NAT9278','NAT9281','NAT9292','NAT9300','NAT9301','NAT9303','NAT9307','NAT9310','NAT9322','NAT9324','NAT9326','NAT9330','NAT9332','NAT9336','NAT9338','NAT9345','NAT9346','NAT9348','NAT9349','NAT9350','NAT9351','NAT9353','NAT9356','NAT9357','NAT9368','NAT9372','NAT9377','NAT9387','NAT9403','NAT9406','NAT9409','NAT9410','NAT9415','NAT9416','NAT9417','NAT9418','NAT9436','NAT9440','NAT9442','NAT9444','NAT9448','NAT9454','NAT9458','NAT9470','NAT9473','NAT9480']
filter_natcodes = ''.join([f"(Nationaliteit eq '{natcode}') or " for natcode in natcodes_landen]).rstrip(' or ')
filter_natcodes

# collect data for selection of nationalities
df = pd.DataFrame(cbsodata.get_data(
    table_id='85644NED', 
    select=['Bevolking_1', 'Nationaliteit', 'RegioS'], 
    filters=f"((Geslacht eq 'T001038')) and ((Leeftijd eq '10000')) and ({filter_natcodes}) and ((Perioden eq '2023JJ00')) and ((substringof('GM',RegioS)))"
))
df = df.dropna(subset='Bevolking_1', how='any')

# get 2nd largest groups for eacht gemeente
df_max_2per_gemeente = df.loc[df.groupby('RegioS')['Bevolking_1'].nlargest(2).reset_index(level=0, drop=True).index]

# split in two dataframes
df_pop_one = df.loc[df_max_2per_gemeente.groupby('RegioS')['Bevolking_1'].head(1).index]
df_pop_two = df.loc[df_max_2per_gemeente.groupby('RegioS')['Bevolking_1'].tail(1).index]

# load IDs of each gemeente
df_regioscodes = pd.read_csv('RegioSCodes.csv', sep=';')
df_gemcodes = df_regioscodes[df_regioscodes.Identifier.str.startswith('GM')]

# combine with our data from CBS
df_pop_one_gemeente_id = pd.merge(df_gemcodes[['Identifier', 'Title']], df_pop_one, right_on='RegioS', left_on='Title', how='left').drop('Title', axis=1)
df_pop_two_gemeente_id = pd.merge(df_gemcodes[['Identifier', 'Title']], df_pop_two, right_on='RegioS', left_on='Title', how='left').drop('Title', axis=1)

# load geometry of each gemeente
geodata_url = 'gemeenten_2023.geo.json'
gemeentegrenzen = gpd.read_file(geodata_url, driver='GeoJSON')
gemeentegrenzen = tp.Topology(gemeentegrenzen, toposimplify=1000, topoquantize=1000).to_gdf()

# connect CBS-data to geometroe wit gemcodes
df_pop_one_gemeente_id_geom = pd.merge(gemeentegrenzen[['statcode', 'statnaam', 'geometry']], df_pop_one_gemeente_id, right_on='Identifier', left_on='statcode', how='left').drop(['RegioS', 'Identifier', 'statcode'], axis=1)
df_pop_two_gemeente_id_geom = pd.merge(gemeentegrenzen[['statcode', 'statnaam', 'geometry']], df_pop_two_gemeente_id, right_on='Identifier', left_on='statcode', how='left').drop(['RegioS', 'Identifier', 'statcode'], axis=1)

# rename columns
df_pop_one_gemeente_id_geom.columns = ['Gemeente', 'geometry', 'Aantal', 'Nationaliteit']
df_pop_two_gemeente_id_geom.columns = ['Gemeente', 'geometry', 'Aantal', 'Nationaliteit']

Met Altair zijn we in staat om levendige kaarten en staafdiagrammen te creëren. Kleurrijke details, vormen en interactieve elementen maken de data niet alleen toegankelijk, maar ook boeiend. Verken de gegevens op jouw eigen tempo met legenda’s en selectiemogelijkheden.

Code
# build chart
selection = alt.selection_point(fields=['Nationaliteit'], bind='legend', empty=True)
fill_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf', '#aec7e8', '#ffbb78', '#98df8a', '#ff9896', '#c5b0d5', '#c49c94', '#f7b6d2', '#c7c7c7', '#dbdb8d', '#9edae5', '#ad494a', '#8c6d31']

left_geom = alt.Chart(df_pop_one_gemeente_id_geom, title=alt.Title(text=' ', subtitle='1e meestvoorkomende nationaliteit')).mark_geoshape(tooltip=True, stroke='black').encode(
    fill=alt.Color('Nationaliteit').scale(range=fill_colors).legend(orient='bottom', columns=9).sort(field='Aantal', op='count', order='descending'),
    strokeWidth=alt.condition(selection, alt.value(0.4), alt.value(0)),   
    tooltip=[alt.Tooltip('Gemeente'), alt.Tooltip('Nationaliteit'), alt.Tooltip('Aantal')],
    opacity=alt.condition(selection, alt.value(1), alt.value(0.2))    
).project(type='identity', reflectY=True)

left_bar = alt.Chart(
    df_pop_one_gemeente_id_geom, 
    title=alt.Title(text=' ', subtitle='1e meestvoorkomende nationaliteit')
).mark_bar(tooltip=True, stroke='black').encode(
    x=alt.X('count()').title('Aantal gemeenten'),
    y=alt.Y('Nationaliteit').sort('-x').title(None), 
    fill=alt.Fill('Nationaliteit').scale(range=fill_colors).sort(field='Aantal', op='count', order='descending'),
    opacity=alt.condition(selection, alt.value(1), alt.value(0.2))    
)

right_bar = alt.Chart(
    df_pop_two_gemeente_id_geom, 
    title=alt.Title(text=' ', subtitle='2e meestvoorkomende nationaliteit')
).mark_bar(tooltip=True, stroke='black').encode(
    x=alt.X('count()').scale(reverse=True).title('Aantal gemeenten'),
    y=alt.Y('Nationaliteit').sort('-x').axis(orient='right').title(None), 
    fill=alt.Fill('Nationaliteit').scale(range=fill_colors).sort(field='Aantal', op='count', order='descending'),
    opacity=alt.condition(selection, alt.value(1), alt.value(0.2))    
)

right_geom = alt.Chart(df_pop_two_gemeente_id_geom, title=alt.Title(text=' ',subtitle='2e meestvoorkomende nationaliteit')).mark_geoshape(tooltip=True, stroke='black').encode(
    fill=alt.Color('Nationaliteit').scale(range=fill_colors).sort(field='Aantal', op='count', order='descending'),
    strokeWidth=alt.condition(selection, alt.value(0.4), alt.value(0)),   
    tooltip=[alt.Tooltip('Gemeente'), alt.Tooltip('Nationaliteit'), alt.Tooltip('Aantal')],
    opacity=alt.condition(selection, alt.value(1), alt.value(0.2))    
).project(type='identity', reflectY=True)

alt.renderers.set_embed_options(actions=False, renderer='svg')
alt.hconcat(
    left_bar.properties(height=400, width=100),
    left_geom.properties(height=400, width=350),
    right_geom.properties(height=400, width=350),
    right_bar.properties(height=400, width=100)
).properties(
    title=alt.TitleParams(text='Nederlandse gemeenten op basis van nationaliteit.', subtitle='CBS, PDOK (2023)', anchor='start')
).configure_legend(
    titleFontSize=15,
    labelFontSize=15,
    labelLimit=200
).configure_title(
    fontSize=15,
    subtitleFontSize=15
).add_params(
    selection
)

De kaart is hoverbaar en de legenda items zijn klikbaar. Ergens in de legenda niet op een naam klikken maakt de selectie ongedaan.

Deze analyse onthult inzichten in de diversiteit van nationaliteiten binnen Nederlandse gemeenten. De interactieve visualisatie nodigt je uit om verder te ontdekken, waardoor je een levendig beeld krijgt van de diverse culturen in Nederland.

Klaar om de diversiteit binnen Nederlandse gemeenten te verkennen? Ontdek de diversiteit van de Nederlandse samenleving per gemeente.

Code
options = df.RegioS.unique().tolist()
input_dropdown = alt.binding_select(
    options=options,
    name='Gemeente: '
)
selection = alt.selection_point(
    fields=['RegioS'],
    bind=input_dropdown,
    empty=False,
    value='Amsterdam'
)

alt.data_transformers.disable_max_rows()
alt.renderers.set_embed_options(actions=False, renderer='svg')
alt.Chart(df, width=1325).mark_bar(tooltip=True, stroke='black', opacity=1).encode(
    y=alt.Y('Bevolking_1:Q').title('Aantal in gemeente'),
    x=alt.X('Nationaliteit').sort('-y').title(None),
    fill=alt.Fill('Bevolking_1')
        .legend(title='Aantal in gemeente', orient='top')
        .title('Aantal in gemeente')
        .scale(domain=[0, 75000], range=['#FEFBE9', '#FCF7D5', '#F5F3C1', '#EAF0B5', '#DDECBF', '#D0E7CA', '#C2E3D2', '#B5DDD8', '#A8D8DC', '#9BD2E1', '#8DCBE4', '#81C4E7', '#7BBCE7', '#7EB2E4', '#88A5DD', '#9398D2', '#9B8AC4', '#9D7DB2', '#9A709E', '#906388', '#805770', '#684957', '#46353A'], type='pow', exponent=0.2, interpolate='rgb',)
).add_params(
    selection
).transform_filter(
    selection
).properties(
    title=alt.TitleParams(text='Nationaliteiten per gemeente op basis van geboorteland buiten Nederland.', subtitle='CBS, PDOK (2023)', anchor='start')
)

De staafdiagram is hoverbaar en het selectie menu biedt mogelijkheden om een andere gemeente te selecteren.

En om het helemaal af te maken, de gemeenten gesorteerd per nationaliteit.

Code
options = df.Nationaliteit.unique().tolist()
input_dropdown = alt.binding_select(
    options=options,
    name='Nationaliteit: '
)
selection = alt.selection_point(
    fields=['Nationaliteit'],
    bind=input_dropdown,
    empty=False,
    value='Pools'
)

alt.data_transformers.disable_max_rows()
alt.renderers.set_embed_options(actions=False, renderer='svg')
alt.Chart(df, width=1325).mark_bar(tooltip=True, stroke='black', opacity=1).encode(
    y=alt.Y('Bevolking_1:Q').title('Aantal in gemeente'),
    x=alt.X('RegioS').sort('-y').title(None),
    fill=alt.Fill('Bevolking_1')
        .legend(title='Aantal in gemeente', orient='top')
        .title('Aantal in gemeente')
        .scale(domain=[0, 75000], range=['#FEFBE9', '#FCF7D5', '#F5F3C1', '#EAF0B5', '#DDECBF', '#D0E7CA', '#C2E3D2', '#B5DDD8', '#A8D8DC', '#9BD2E1', '#8DCBE4', '#81C4E7', '#7BBCE7', '#7EB2E4', '#88A5DD', '#9398D2', '#9B8AC4', '#9D7DB2', '#9A709E', '#906388', '#805770', '#684957', '#46353A'], type='pow', exponent=0.2, interpolate='rgb',)
).add_params(
    selection
).transform_filter(
    selection
).transform_window(
    rank='rank(RegioS)',
    sort=[alt.SortField('Bevolking_1', order='descending')]
).transform_filter(
    (alt.datum.rank < 30)
).properties(
    title=alt.TitleParams(text='Gemeenten per nationaliteit op basis van geboorteland buiten Nederland.', subtitle='CBS, PDOK (2023)', anchor='start')
)

De staafdiagram is hoverbaar en het selectie menu biedt mogelijkheden om een andere nationaliteit te selecteren.