1.9 MiB
Data gemeenteraadsverkiezingen 2022 Nederland¶
Dit notebook is voor het verwerken van de data van de gemeenteraadsverkiezingen van 2022. Er zal hier stap voor stap door de data gelopen worden om het proces reproduceerbaar te maken voor latere verkiezingen. De eerste stap was de data ophalen van de bronnen, zowel de overheid als waar is mijn stemlokaal (voor geografische data van de stemlokalen). De bronnen gebruikt voor de data zijn voor de verkiezingen van 2022 is als volgt:
- Verkiezingsuitslagen Gemeenteraad 2022
- Waar is mijn stemlokaal stembureau data
- CBS Wijk- en buurtkaart 2021
- CBS bevolkingsdichtheid kaart 100 bij 100 meter
- CBS bevolkingsdichtheid kaart 500 bij 500 meter
- CBS schatting van kiesgerechtigden per gemeente
De eerste stap die we moeten maken is de data importeren voor de analyse, daarna kunnen we kijken hoe goed de data is, hoe we het aan kunnen vullen, en wat er mee te doen. De makkelijkste structuur die we vonden was het geojson bestand van open state en de Volkskrant, daar staan alle stemlokalen al in een lijst, en we hebben een makkelijk framework om het te importeren; geopandas. We laden deze dan ook als eerste in.
import pandas as pd
import geopandas as gpd
crs = {'init':'EPSG:4326'}
df_geojson = gpd.read_file(r'../data/2022gr.geo.json', crs=crs)
We importeren nu de gemeente shapefile kaart van het CBS, om de overlay van onze stemlokalen eroverheen te kunnen doen.
nl_map = gpd.read_file(r'../data/shape/Netherlands_shapefile/gemeente_2021_v1.shp')
nl_map.to_crs(epsg=4326).plot()
print(nl_map)
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize = (10,10))
nl_map.to_crs(epsg=4326).plot(ax=ax, color='lightgrey')
df_geojson.plot(ax=ax)
We hebben hier een probleem dat sommige punten de coördinaten 0,0 hebben, dit is niet iets wat we willen weergeven. Laten we eerst kijken waarom dit het geval is in de data voordat we het er helemaal uithalen.
for index, row in df_geojson.iterrows():
if (row.geometry.centroid.x == 0.00000):
print(row)
Het zijn dus duidelijk allemaal mobiele stemlokalen, die kunnen we in een aparte dataset houden voor later, voor nu zijn ze onbelangrijk voor de kaartweergave, laten we de dataset dus opsplitsen in drie dataframes, de originele, één frame zonder de mobiele stemlokalen, en de mobiele stemlokalen alleen.
#de waarschuwing over de projectie die niet klopt kan genegeerd worden, we zoeken naar data die x = 0 is, de projectie gaat geen effect hebben op die data in Nederland
#filter de data en print welke waardes 0 hebben op x
mobiel_mask = df_geojson['geometry'].centroid.x == 0
i = 0
for item in mobiel_mask:
if item:
print(i)
i = i + 1
df_geojson_clean = df_geojson[~mobiel_mask]
df_geojson_mobiel = df_geojson[mobiel_mask]
print(df_geojson_clean)
#let's also check if none fell through the cracks, it might be some mobile polls did actually have a location posted erroneously.
#mobiel_mask_name = df_geojson_clean['description'].str.contains('Mobiel')
#df_geojson_clean = df_geojson_clean[~mobiel_mask_name]
#df_geojson_clean
df_wims = pd.read_csv(r'../data/wims-3.csv')
df_wims
gdf_wims = gpd.GeoDataFrame(
df_wims, geometry=gpd.points_from_xy(df_wims.Longitude, df_wims.Latitude)).set_crs(epsg=4326, inplace=True)
gdf_wims
gdf_wims_dedupe = gdf_wims.drop_duplicates(subset ="geometry", keep = False)
df_geojson_dedupe = df_geojson_clean.drop_duplicates(subset ="geometry", keep = False)
gdf_wims_dedupe
fig, ax = plt.subplots(figsize = (15,15))
nl_map.to_crs(epsg=4326).plot(ax=ax, color='lightgrey')
gdf_wims_dedupe.plot(ax=ax)
fig, ax = plt.subplots(figsize = (15,15))
nl_map.to_crs(epsg=4326).plot(ax=ax, color='lightgrey')
df_geojson_clean.plot(ax=ax)
Nu we een mooie kaart hebben van nederland met de stemlokalen erop, kunnen we gaan kijken naar hoe dit verhoud met de bevolkingsdichtheid als eerste voorbeeld. De kaart is opgehaald van het CBS, eerst gaan we kijken hoe de kaart er op zichzelf met dezelfde projectie uitziet.
nl_map_cbs = gpd.read_file(r'../data/shape/Netherlands_shapefile/cbs_vk500_2021_v1.gpkg')
nl_map_cbs.to_crs(epsg=4326).plot()
Het verwerken van de 100m bij 100m vierkanten kaar duurde heel lang (langer dan een half uur) op de laptop, en is dus niet ideaal tenzij we dit niveau van detail écht nodig hebben. Er is namelijk nog een andere kaart met dezelfde gegevens beschikbaar, maar met een grid van 500 bij 500 meter in plaats van 100 bij 100. Laten we dus kijken hoe vaak een stemlokaal dichterbij dan 100 meter van de dichtstbijzijnde andere is. De makkelijkste manier om dat te doen zonder alle punten met alle andere te vergelijken (heel veel moeite), is een extra dataframe maken als een kopie, alle indexen 1 opschuiven (want alle stemlokalen zijn al in een volgorde van clustering), en dan de laagste afstand bekijken.
df_shifted = df_geojson_clean.to_crs('EPSG:28992')
df_shifted['geometry (shifted)'] = df_shifted['geometry'].shift(periods=1)
df_shifted['distance'] = df_shifted['geometry'].distance(df_shifted['geometry (shifted)'])
df_shifted.sort_values(['distance'], inplace=True, ascending=True)
zero_mask_booth = df_shifted['distance'] == 0.000000
df_shifted = df_shifted[~zero_mask_booth]
print(df_shifted['distance'])
We kunnen dus vaststellen dat afstanden van onder de 500 meter waarschijnlijk zeldzaam zijn, en we verder kunnen gaan met de 500 meter bij 500 meter kaart. Laten we nu dus een projectie proberen te maken met de bevolkingsdichtheid erop om het te vergelijken met de stemlokalen en hun posities en clustering.
print(list(nl_map_cbs))
nl_map_dst = nl_map_cbs.drop(columns=['aantal_mannen', 'aantal_vrouwen', 'aantal_inwoners_0_tot_15_jaar', 'aantal_inwoners_15_tot_25_jaar', 'aantal_inwoners_25_tot_45_jaar', 'aantal_inwoners_45_tot_65_jaar', 'aantal_inwoners_65_jaar_en_ouder', 'percentage_nederlandse_achtergrond', 'percentage_westerse_migr_achtergr', 'percentage_niet_westerse_migr_achtergr', 'aantal_personen_met_uitkering_onder_aowlft', 'aantal_part_huishoudens', 'aantal_eenpersoonshuishoudens', 'aantal_meerpersoonshuishoudens_zonder_kind', 'aantal_eenouderhuishoudens', 'aantal_tweeouderhuishoudens', 'gemiddelde_huishoudensgrootte', 'aantal_woningen', 'aantal_woningen_bouwjaar_voor_1945', 'aantal_woningen_bouwjaar_45_tot_65', 'aantal_woningen_bouwjaar_65_tot_75', 'aantal_woningen_bouwjaar_75_tot_85', 'aantal_woningen_bouwjaar_85_tot_95', 'aantal_woningen_bouwjaar_95_tot_05', 'aantal_woningen_bouwjaar_05_tot_15', 'aantal_woningen_bouwjaar_15_en_later', 'aantal_meergezins_woningen', 'percentage_koopwoningen', 'percentage_huurwoningen', 'aantal_huurwoningen_in_bezit_woningcorporaties', 'aantal_niet_bewoonde_woningen', 'gemiddelde_woz_waarde_woning', 'omgevingsadressendichtheid'])
nl_map_dst
We kunnen zien dat er gemeente zijn die geen stemlokalen hebben, dit klopt. In de verkiezingen van 2022 hebben 12 gemeentes niet meegedaan. Er zijn dus in totaal 333 gemeentes met stemlokalen. We willen de bevolkingsdichtheiddata van de gemeentes die niet meededen dus weggooien, hiervoor moeten we eerst kijken welke alleen binnen de gemeentes vallen die we niet willen houden.
print(df_geojson['electionName'])
#get all municipalities that are in the election
gemeentes = df_geojson['electionName'].str.slice(13, -5).drop_duplicates()
print(gemeentes)
#First let's remove the municipalities that don't have an Election from the municipality map
remove_lst = ["Boxtel", "Eemsdelta", "Oisterwijk", "Vught", "Heerhugowaard", "Langedijk", "Boxmeer", "Cuijk", "Sint Anthonis", "Mill en Sint Hubert", "Grave", "Landerd", "Uden", "Brielle", "Hellevoetsluis", "Westvoorne", "Purmerend", "Beemster", "Weesp"]
nl_map_clean = nl_map[~nl_map["GM_NAAM"].isin(remove_lst)]
nl_map_clean = nl_map_clean.query("H2O != 'JA'")
nl_map_clean = nl_map_clean.query("GM_NAAM != 'Buitenland'")
nl_map_election = nl_map[nl_map["GM_NAAM"].isin(gemeentes)]
nl_map_election = nl_map_election.query("H2O != 'JA'")
print("Shape of clean: {}".format(nl_map_clean.shape))
print("Shape of election: {}".format(nl_map_election.shape))
diff = pd.concat([nl_map_election, nl_map_clean]).drop_duplicates(keep=False)
diff
Het is een andere vorm omdat deze drie gemeentes een andere format hadden voor de naam van hun verkiezingen, en dus niet op dezelfde manier gefilterd konden worden, ze staan wel in de lijst, dit is dus correct, we gebruiken de clean dataframe. Nu we dezelfde lijst hebben van gemeentes die in de verkiezingen meededen kunnen we verder.
fig, ax = plt.subplots(figsize = (15,15))
nl_map_clean.to_crs(epsg=4326).plot(ax=ax, color='lightgrey')
df_geojson_clean.plot(ax=ax)
nl_map_dst['savedindex'] = nl_map_dst.index #Save the index values as a new column
intersecting = nl_map_clean.sjoin(nl_map_dst, how='inner')['savedindex'] #Find the polygons that intersect. Keep savedindex as a series
nl_map_dst = nl_map_dst.loc[nl_map_dst.savedindex.isin(intersecting)] #Filter away these, "savedindex is in intersecting"
nl_map_dst.to_crs(epsg=4326).plot()
import folium
from folium import plugins
#Location is Amersfoort, the central point of the continental Netherlands
map = folium.Map(location = [52.155, 5.3875], zoom_start = 9, tiles="cartodbdark_matter", prefer_canvas=True)
nl_map_dst.to_crs(epsg=4326)
#Make sure the index is a string so folium can read it correctly as a key.
nl_map_dst['crs28992res500m'] = nl_map_dst['crs28992res500m'].apply(lambda x: str(x))
nl_map_dst_zero = nl_map_dst.copy()
zero_mask_pop = nl_map_dst['aantal_inwoners'] == -99997
nl_map_dst = nl_map_dst[~zero_mask_pop]
zero_mask_pop2 = nl_map_dst['aantal_inwoners'] == 0
nl_map_dst = nl_map_dst[~zero_mask_pop2]
folium.Choropleth(
geo_data = nl_map_dst,
name="Bevolkingsdichtheid",
data = nl_map_dst,
columns = ["crs28992res500m", "aantal_inwoners"],
key_on = 'feature.properties.crs28992res500m',
fill_color = 'RdPu',
nan_fill_color= 'white',
fill_opacity = 0.7,
nan_fill_opacity = 0.7,
line_opacity = 0,
legend_name = 'Bevolkingsdichtheid',
smooth_factor = 1.0,
show=False
).add_to(map)
map
# Renders the map to an HTML file and displays it in an embed.
def embed_map(m):
#from IPython.display import IFrame
m.save('index.html')
#return IFrame('index.html', width='100%', height='750px')
gdf_wims_hmp = [[point.xy[1][0], point.xy[0][0]] for point in gdf_wims.geometry]
plugins.HeatMap(gdf_wims_hmp, name="Stemlokalen Heatmap").add_to(map)
folium.LayerControl().add_to(map)
embed_map(map)
nl_map_dst_points = nl_map_dst.copy()
nl_map_dst_points['geometry'] = nl_map_dst_points['geometry'].centroid
nl_map_dst_zero_points = nl_map_dst_zero.copy()
nl_map_dst_zero_points['geometry'] = nl_map_dst_zero_points['geometry'].centroid
nl_map_dst_zero_points
import numpy as np
from scipy.spatial import cKDTree
from shapely.geometry import Point
#Found on https://gis.stackexchange.com/questions/222315/finding-nearest-point-in-other-geodataframe-using-geopandas
def ckdnearest(gdA, gdB):
nA = np.array(list(gdA.geometry.apply(lambda x: (x.x, x.y))))
nB = np.array(list(gdB.geometry.apply(lambda x: (x.x, x.y))))
btree = cKDTree(nB)
dist, idx = btree.query(nA, k=1)
gdB_nearest = gdB.iloc[idx].drop(columns="geometry").reset_index(drop=True)
gdf = pd.concat(
[
gdA.reset_index(drop=True),
gdB_nearest,
pd.Series(dist, name='dist')
],
axis=1)
return gdf
#error bar is +- sqrt(250²*2)m ≈ +-353.553390593m
error = np.sqrt(250**2 + 250**2)
df_nearest = ckdnearest(nl_map_dst_points.to_crs(epsg=28992), gdf_wims_dedupe.to_crs(epsg=28992))
df_nearest.head()
Het was mooier geweest als we het konden filteren op alleen stemgerechtigden en niet op de gehele populatiedichtheid, maar het CBS geeft geen data vrij die daarbij helpt. Er is een categorie voor 0-14 jaar oude mensen, maar niet alle minderjarigen of niet stemgerechtigde. Het moet dus maar met totale bevolkingsdichtheid om een idee te krijgen.
nl_map_dst_zero_points["aantal_inwoners"].mask(nl_map_dst_zero_points["aantal_inwoners"] == -99997, 0, inplace=True)
nl_map_dst_zero_points.head()
df_nearest_zero = ckdnearest(nl_map_dst_zero_points.to_crs(epsg=28992), gdf_wims_dedupe.to_crs(epsg=28992))
df_nearest_zero["aantal_inwoners"].dropna()
df_nearest_zero["dist"].dropna()
df_nearest_zero
df_nearest.sort_values(by=['dist'], inplace=True)
df_nearest[['Gemeente', 'Naam stembureau', 'dist']]
def plot_dist(df, label):
plt.figure(figsize=(13,13))
df['dist'].plot(kind="hist", weights=df['aantal_inwoners'], figsize=(13,13), density=False,title="Afstand tot Stemlokaal per 500 x 500 m vierkant (minimaal 5 mensen in het vierkant) Staffel {}".format(label), log=True).set_ylabel('Bevolking (Millioenen)')
plt.figure(figsize=(13,13))
df.plot.scatter(x = 'aantal_inwoners', y = 'dist', figsize=(13,13), title="Afstand tot Stemlokaal per 500 x 500 m vierkant (minimaal 5 mensen in het vierkant) Staffel {}".format(label), xlabel="Inwoners per 500m² (laagste is 5)", ylabel="Afstand tot stemlokaal (meter)")
df_nearest_zero.plot.scatter(x = 'aantal_inwoners', y = 'dist', figsize=(13,13), xlabel="Inwoners per 500m² (inclusief lege (<5 inwoners) vierkanten)", ylabel="Afstand tot stemlokaal (meter)")
FIGSIZE = (13, 13)
def plot_bargraph_with_groupings(df, groupby, colourby, title, xlabel, ylabel):
import matplotlib.patches as mpatches
# Makes the bargraph.
ax = df[groupby].value_counts().plot(kind='bar',
figsize=FIGSIZE,
title=title)
# display the graph.
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
plot_bargraph_with_groupings(gdf_wims_dedupe, 'Gebruiksdoel van het gebouw', 'Gemeente', 'Gebruiksdoelen van Stemlokalen', 'Gebruiksdoel', 'Hoeveelheid Stemlokalen')
Dit is onbruikbaar, de categorieën moeten elk één balkje zijn, niet zoals dit.
def plot_use (df, label):
plt.figure(figsize=(13,13))
df_exploded = df.drop('Gebruiksdoel van het gebouw', axis=1).join(df['Gebruiksdoel van het gebouw'].str.split(',', expand=True).stack().reset_index(level=1,drop=True).rename('Gebruiksdoel van het gebouw'))
#print(df_exploded)
plot_bargraph_with_groupings(df_exploded, 'Gebruiksdoel van het gebouw', 'Gemeente', 'Gebruiksdoelen van Stemlokalen Staffel {}'.format(label), 'Gebruiksdoel', 'Hoeveelheid Stemlokalen')
wims_count = gdf_wims_dedupe['Gemeente'].value_counts()
geojson_rename = df_geojson_dedupe.rename(columns = {'electionName': 'Gemeente'})
geojson_count = geojson_rename['Gemeente'].str.slice(13, -5).value_counts()
pd.concat([geojson_count, wims_count]).drop_duplicates(keep=False)
gdf_wims_times = gdf_wims_dedupe.loc[:, ('Openingstijden 14-03-2022','Openingstijden 15-03-2022','Openingstijden 16-03-2022', 'UUID', 'Gemeente', 'CBS gemeentecode')]
gdf_wims_split = gdf_wims_times
gdf_wims_split[['Openingstijden 14-03-2022 van', 'Openingstijden 14-03-2022 tot']] = gdf_wims_times['Openingstijden 14-03-2022'].str.split(' tot ',1 , expand=True)
gdf_wims_split[['Openingstijden 15-03-2022 van', 'Openingstijden 15-03-2022 tot']] = gdf_wims_split['Openingstijden 15-03-2022'].str.split(' tot ',1 , expand=True)
gdf_wims_split[['Openingstijden 16-03-2022 van', 'Openingstijden 16-03-2022 tot']] = gdf_wims_split['Openingstijden 16-03-2022'].str.split(' tot ',1 , expand=True)
gdf_wims_split
gdf_wims_split[['Openingstijden 14-03-2022 van','Openingstijden 15-03-2022 van','Openingstijden 16-03-2022 van', 'Openingstijden 14-03-2022 tot', 'Openingstijden 15-03-2022 tot','Openingstijden 16-03-2022 tot']] = gdf_wims_split[['Openingstijden 14-03-2022 van','Openingstijden 15-03-2022 van','Openingstijden 16-03-2022 van','Openingstijden 14-03-2022 tot','Openingstijden 15-03-2022 tot','Openingstijden 16-03-2022 tot']].apply(pd.to_datetime, errors='coerce')
gdf_wims_split
import datetime as dt
wims_datemerge = pd.DataFrame()
def datesplit(df, column):
df = df.dropna(subset = ['{} van'.format(column)])
df1 = df.rename(columns={'{} van'.format(column):'vandate','{} tot'.format(column):'totdate', 'UUID':'UUID'})
return (pd.concat([pd.Series(r.UUID,pd.date_range(r.vandate, r.totdate, freq='0.5H'))
for r in df1.itertuples()])
.rename_axis('Openingstijden')
.reset_index(name='UUID'))
def plot_opening(df, label):
plt.figure(figsize=(13,13))
wims_datesplit_14 = df.loc[:, ('Openingstijden 14-03-2022 van','Openingstijden 14-03-2022 tot', 'UUID')]
wims_datesplit_15 = df.loc[:, ('Openingstijden 15-03-2022 van','Openingstijden 15-03-2022 tot', 'UUID')]
wims_datesplit_16 = df.loc[:, ('Openingstijden 16-03-2022 van','Openingstijden 16-03-2022 tot', 'UUID')]
wims_datesplit_14 = datesplit(wims_datesplit_14, 'Openingstijden 14-03-2022')
wims_datesplit_15 = datesplit(wims_datesplit_15, 'Openingstijden 15-03-2022')
wims_datesplit_16 = datesplit(wims_datesplit_16, 'Openingstijden 16-03-2022')
wims_datesplit_14.reset_index()
wims_datesplit_15.reset_index()
wims_datesplit_16.reset_index()
#wims_datesplit_14.info()
#wims_datesplit_15.info()
#wims_datesplit_16.info()
wims_datestack = pd.concat([wims_datesplit_14, wims_datesplit_15], ignore_index=True)
wims_datestack = pd.concat([wims_datestack, wims_datesplit_16], ignore_index=True)
wims_datestack.set_index('Openingstijden', drop=False, inplace=True)
#print(wims_datestack['Openingstijden'].unique())
xticks = []
xticklabels = []
datetimecount = 0
for datetime in wims_datestack.groupby(pd.Grouper(freq='30Min'))['Openingstijden'].unique():
if (datetime.size > 0):
xticks.append(datetime.ravel()[0])
if(datetimecount % 6 == 0):
ts = pd.to_datetime(datetime.ravel()[0])
#hardcoded for the data, sloppy code, quick fix, only applies to timestamps with 7 minute changes to reality
if (ts.minute == 7):
ts = ts - dt.timedelta(minutes=7)
xticklabels.append(ts.strftime("%m-%d %H:%M"))
else:
xticklabels.append(" ")
else:
xticks.append("")
xticklabels.append("")
datetimecount += 1
wims_datestack.groupby(pd.Grouper(freq='30Min')).count().plot(kind='bar', y='Openingstijden',width=1, figsize=(25, 25), title="Openingstijden stemlokalen staffel {} kiesgerechtigden".format(label)).set_xticklabels(xticklabels, rotation=-45, ha="left", rotation_mode="anchor")
#TODO: export excel file
#wims_datesplit_16.groupby(wims_datesplit_16["Openingstijden 16-03-2022"].dt.hour).count().plot(kind="bar", y='Openingstijden 16-03-2022')
#wims_datesplit_16.groupby(pd.Grouper(freq='30Min')).count().plot(kind='bar', y='Openingstijden 16-03-2022')
#wims_datesplit_15.groupby(wims_datesplit_15["Openingstijden 15-03-2022"].dt.hour).count().plot(kind="bar", y='Openingstijden 15-03-2022')
#wims_datesplit_14.groupby(wims_datesplit_14["Openingstijden 14-03-2022"].dt.hour).count().plot(kind="bar", y='Openingstijden 14-03-2022')
df_stemger = pd.read_excel('../data/Maatwerk_kiesgerechtigd_per_gemeente_def.xlsx', sheet_name='Tabel 1', header=0, skiprows= [0,1,3,5,6,7,344,345])
df_stemger_clean = df_stemger[df_stemger['gemeentecode'].isin(nl_map_clean['GM_CODE'])]
bins = [0, 1, 3, 6, 10, 35, float("inf")]
labels = ['0-10.000', '10.000-30.000', '30.000-60.000', '60.000-100.000', '100.000-350.000', '350.000+']
df_stemger_clean['binned'] = pd.cut(df_stemger_clean['Kiesgerechtigde bevolking'], bins, labels=labels)
def filter_binned(df1, df2):
return df1[df1['CBS gemeentecode'].isin(df2['gemeentecode'])]
def make_graphs_opening(label):
df_stemger_label = df_stemger_clean[df_stemger_clean['binned'].str.contains(label)]
#df_stemger_label.head()
graph_wims = filter_binned(gdf_wims_dedupe, df_stemger_label)
graph_wims_split = filter_binned(gdf_wims_split, df_stemger_label)
plot_opening(graph_wims_split, label)
for label in labels:
make_graphs_opening(label)
def make_graphs_use(label):
df_stemger_label = df_stemger_clean[df_stemger_clean['binned'].str.contains(label)]
plot_use(filter_binned(gdf_wims_dedupe, df_stemger_label), label)
for label in labels:
make_graphs_use(label)
def make_graphs_dist(label):
df_stemger_label = df_stemger_clean[df_stemger_clean['binned'].str.contains(label)]
plot_dist(filter_binned(df_nearest, df_stemger_label), label)
for label in labels:
make_graphs_dist(label)
#de eerste vijf rijen zijn meta-data en kunnen we gerust weghalen voor nu
df_gr_gr = pd.read_csv(r'../data/stemmen/01_Groningen/osv4-3_telling_gr2022_groningen.csv', skiprows=5, header=None, delimiter=';')
print(df_gr_gr)
De data is heel gecompliceerd, dus we moeten het bruikbaar maken in pandas. Met hoe de data eruit ziet in een CSV in libre office willen we een constructie maken van de verschillende onderdelen. We hebben de data van de stemlokalen apart al, die kunnen we dus negeren. We willen de data van de lijsten apart hebben.
#TODO:mobiele stemlokalen eruit filteren die NIET 0,0 zijn
#TODO:datastandaard duitse verkiezingen stemlokaalafstand opzoeken