Skip to content

Instantly share code, notes, and snippets.

@jvhaarst
Created March 29, 2026 20:53
Show Gist options
  • Select an option

  • Save jvhaarst/363f236a3dad0d812fc108aec8cdd7f3 to your computer and use it in GitHub Desktop.

Select an option

Save jvhaarst/363f236a3dad0d812fc108aec8cdd7f3 to your computer and use it in GitHub Desktop.
Electric taxi analysis (Netherlands, RDW open data)

Electric Taxi Analysis (Netherlands)

Analyse the percentage of electric vehicles among active Dutch taxis using RDW open data.

Data sources

You need two CSV exports from RDW Open Data:

  1. Gekentekende voertuigen (registered vehicles) — download from opendata.rdw.nl Or from https://opendata.rdw.nl/Voertuigen/taxi-analyse/9rdy-zgxi/data_preview → Export → CSV
  2. Gekentekende voertuigen brandstof (fuel data) — download from opendata.rdw.nl → Export → CSV

Update the filenames in analyse_electric_taxis.py to match your downloads.

Filtering

A taxi is considered active when all of the following hold:

  • Taxi indicator = Ja
  • Vervaldatum APK >= today (valid MOT)
  • WAM verzekerd = Ja (insured)
  • Export indicator = Nee (not exported)

Running

Requires Python 3.9+. No virtualenv needed — uv fetches DuckDB on the fly:

uv run --with duckdb analyse_electric_taxis.py

Example output (29 March 2026)

Total active taxis:  17,971
Fully electric:      5,961 (33.17%)
Plug-in hybrid:      660 (3.67%)
Any electric drive:  6,621 (36.84%)

Diesel still dominates at 60.75%, but over a third of active Dutch taxis now have some form of electric drive.

#!/usr/bin/env python3
"""Analyse the percentage of electric vehicles among active Dutch taxis.
Uses DuckDB to query RDW open data CSV exports.
Run with: uv run --with duckdb analyse_electric_taxis.py
"""
import duckdb
TAXI_CSV = "taxi_analyse_20260329.csv"
BRANDSTOF_CSV = "Open_Data_RDW__Gekentekende_voertuigen_brandstof_20260329.csv"
TODAY = 20260329
result = duckdb.sql(f"""
WITH active_taxis AS (
SELECT "Kenteken"
FROM '{TAXI_CSV}'
WHERE "Taxi indicator" = 'Ja'
AND "Vervaldatum APK" >= {TODAY}
AND "WAM verzekerd" = 'Ja'
AND "Export indicator" = 'Nee'
),
taxi_fuels AS (
SELECT
a."Kenteken",
string_agg(
b."Brandstof omschrijving",
' + '
ORDER BY b."Brandstof volgnummer"
) AS fuel_combo
FROM active_taxis a
JOIN '{BRANDSTOF_CSV}' b ON a."Kenteken" = b."Kenteken"
GROUP BY a."Kenteken"
)
SELECT
fuel_combo,
count(*) AS cnt,
round(100.0 * count(*) / sum(count(*)) OVER (), 2) AS pct
FROM taxi_fuels
GROUP BY 1
ORDER BY cnt DESC
""")
print(f"Active taxis (valid APK, insured, not exported) as of {TODAY}:\n")
result.show()
totals = duckdb.sql("""
SELECT
sum(cnt) AS total,
sum(CASE WHEN fuel_combo = 'Elektriciteit' THEN cnt ELSE 0 END) AS fully_electric,
sum(CASE WHEN fuel_combo LIKE '%Elektriciteit%'
AND fuel_combo != 'Elektriciteit' THEN cnt ELSE 0 END) AS plugin_hybrid
FROM result
""").fetchone()
total, electric, hybrid = totals
print(f"\nSummary:")
print(f" Total active taxis: {total:,}")
print(f" Fully electric: {electric:,} ({100*electric/total:.2f}%)")
print(f" Plug-in hybrid: {hybrid:,} ({100*hybrid/total:.2f}%)")
print(f" Any electric drive: {electric+hybrid:,} ({100*(electric+hybrid)/total:.2f}%)")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment