zal-analytics-core/dutch-law-enforcement-entity-resolution-dataset
Dutch Law Enforcement Entity Resolution Benchmark A collection of synthetic benchmark datasets for entity resolution and record linkage research, modelled on real Dutch and EU/EEA law enforcement data schemas. All data is 100% synthetic. No records correspond to real persons, vehicles, phone numbers, transactions, or criminal histories. Every name, date, plate number, IBAN, IMSI, and identifier is procedurally generated. The datasets are freely usable, shareable, and… See the full description on the dataset page: https://huggingface.co/datasets/zal-analytics-core/dutch-law-enforcement-entity-resolution-dataset.
Dutch Law Enforcement Entity Resolution Benchmark
A collection of synthetic benchmark datasets for entity resolution and record linkage research, modelled on real Dutch and EU/EEA law enforcement data schemas.
All data is 100% synthetic. No records correspond to real persons, vehicles, phone numbers, transactions, or criminal histories. Every name, date, plate number, IBAN, IMSI, and identifier is procedurally generated. The datasets are freely usable, shareable, and publishable without privacy or legal concerns.
Work in progress. This dataset collection is actively evolving. Generation logic, schemas, and demographic distributions are being progressively ported from the internalzsimulateandzbrokersimulation libraries maintained by ZAL Analytics. Not all providers have reached feature parity with those systems yet; schemas and ground truth formats may change between releases.
Datasets are generated from real-world schemas (KvK Handelsregister, Basisregistratie Personen, SIS II Article 26/36 specification, Dutch ANPR kentekencamera feed, HKS criminal antecedents, Interpol I-24/7 notice schema, telecom CDR / ETSI LI standard, FIU-NL GTR) combined with authentic Dutch demographic distributions from CBS (Centraal Bureau voor de Statistiek) statistics. Error patterns, name variants, OCR confusion, romanization differences, estimated dates of birth, SIM swap events, are injected to replicate the specific challenges found in Dutch and cross-Schengen identity data.
Why this dataset exists
Standard entity resolution benchmarks (DBLP-ACM, Febrl, Cora) do not reflect the challenges of Dutch law enforcement data:
- Dutch name complexity, tussenvoegsel prefixes (
van,de,van der,van den,van 't) are frequently omitted, abbreviated, or formatted inconsistently across registrations - Multicultural name variation, Dutch-Moroccan and Dutch-Turkish communities produce names with authentic spelling variants (
Mohammed/Mohamed/Muhammad, dropped Turkish diacritics likeÇelik→Celik) - Cross-Schengen identity, SIS II entries are registered by different member states in different scripts; the same person may appear as
Benabdallah Fatimain one state andFatima Benabdallahin another, with Arabic-script aliases alongside Latin ones - ANPR OCR errors, Dutch highway cameras produce predictable single-character plate confusions (0↔O, 1↔I, 8↔B, 5↔S, 2↔Z) that require fuzzy matching rather than exact lookup
- Estimated dates of birth, asylum and immigration records frequently use
YYYY-01-01(January 1st fallback) when the exact birth date is unknown, creating year-boundary matching challenges - Telecom identity churn, SIM swap events mean the same MSISDN can be associated with different persons over time, while IMEI reuse links different subscribers through a shared device
- Financial network structure, money mule rings and structuring patterns require graph-aware resolution rather than pairwise comparison
Versioning
v1.1 (current)
v1.1 changes how records are identified throughout the pipeline and in all generated datasets. The changes are breaking: v1.0 datasets and entity stores are not compatible with v1.1 and must be regenerated.
Record identity: natural keys replace sequential integers
In v1.0, records were assigned sequential integer IDs by the caller and ID offsets were required to prevent collisions between sources. In v1.1, every record is anchored to its natural key (the value from whichever column is its authoritative identifier: BSN for BRP, kvk_nummer for KvK, etc.). The internal numeric ID is derived deterministically via FNV-1a(source:key), so the same (source, key) pair always produces the same ID with no collision risk across sources.
Ground truth format: natural key pairs replace integer ID pairs
In v1.0, all ground truth files used record_id_a, record_id_b columns containing sequential integers. In v1.1, ground truth files use the natural key of each source directly:
Data directory layout: versioned subdirectory
Demo, example, and test datasets are now generated under data/v1.1/ to preserve v1.0 data in data/demos/, data/tests/, and data/examples/ for reference:
EntityMember `record_key` field
The entity_members table in .zes entity store files has a new record_key TEXT NOT NULL column. v1.0 .zes files cannot be opened by v1.1: delete and regenerate them.
`data/raw/` removed
The data/raw/ directory and its generator (generate_raw.py) have been removed. Raw provider datasets at multiple scale tiers are no longer part of the standard generation workflow. Benchmark scenarios in data/benchmarks/ cover the same providers at appropriate scales for evaluation purposes.
v1.0
Original release. Records identified by caller-managed sequential integers. Ground truth files used record_id_a, record_id_b columns. Multi-source pipelines required manual ID offsets to prevent collisions. Demo/test/example data under data/demos/, data/tests/, data/examples/ without version subdirectory.
Datasets
1. KvK Director Records
Company director registrations from the Dutch Chamber of Commerce (Kamer van Koophandel). The same person often directs multiple companies, with each registration entered independently, producing name variants, formatting differences, and address drift over time. Directors of BV and NV companies are registered as bestuurders; sole proprietors (Eenmanszaak) are both owner and entity.
Files: kvk_director_flat.csv, kvk_director_extract.json, ground_truth_pairs.csv
Schema, `kvk_director_flat.csv`:
Ground truth format: kvk_a, kvk_b, is_match, match_type
Match types: same_person_multi_company, duplicate_entry, name_variant
Default scale: ~2,000 director records · ~1,300 true pairs
Injected error patterns:
- Tussenvoegsel variants:
van der/v/d/vd/ omitted entirely - Abbreviated given names:
Johannes→J.orJ.A. - Case and whitespace inconsistencies across independent registrations
- Address drift between an individual's multiple company registrations
2. BRP Population Register
Person records modelled on the Basisregistratie Personen (BRP), the Dutch municipal population register. Every resident, Dutch national or foreign national with a registered address, has a BRP entry. The dataset tests string similarity matching, address normalisation, date of birth transcription errors, and dual-nationality scoring.
Files: brp_persons.csv, ground_truth_pairs.csv
Schema, `brp_persons.csv`:
Ground truth format: bsn_a, bsn_b, is_match, match_type
Match types: address_move, name_variant, dob_error, nationality_partial
Default scale: ~11,000 records · ~1,000 true pairs
Injected error patterns:
- Address moves: same person registered at two different addresses across time
- Name variants: abbreviated given names, tussenvoegsel capitalised or omitted
- Date of birth transcription errors: day and month transpositions
- Partial nationality: one record shows dual nationality, the other only one
3. SIS II Wanted and Missing Persons
Person records modelled on the Schengen Information System II (SIS II) Article 26 (wanted persons) and Article 36 (missing persons) schemas. The key challenge is cross-Schengen alias pairs: the same individual entered by different member states using different name order conventions, romanizations, or scripts.
Files: sis_persons.csv, ground_truth_alias_pairs.csv
Schema, `sis_persons.csv`:
Ground truth format: sis_id_a, sis_id_b, is_match, match_type
Match types: alias_romanization_variant, name_transposition, estimated_dob_variant, cross_script_alias
Default scale: ~9,000 records · ~4,000 true pairs
Injected error patterns:
- Name transposition:
Benabdallah Fatima↔Fatima Benabdallah(given/surname order varies by country convention) - Romanization variants:
Mohammed/Mohamed/Muhammed;Youssef/Yousef/Yusuf - Estimated date of birth: ~30% of records use
YYYY-01-01(January 1st fallback) - Non-Latin script aliases: Arabic-script aliases alongside Latin primary names
- Document number prefix variation: same serial with different country-code prefixes
4. HKS Criminal Antecedents
Records modelled on the Herkenningsdienstsysteem (HKS), the Dutch National Police criminal antecedents system. HKS records are subject-oriented rather than incident-oriented: a single individual may appear under their registered name in the BRP while their HKS entry carries a street alias or romanization variant. The BSN is present for ~60% of records.
Files: hks_records.csv, ground_truth_pairs.csv
Schema, `hks_records.csv`:
Ground truth format: hks_id_a, hks_id_b, is_match, match_type
Match types: alias_vs_registered_name, bsn_linked, street_alias, romanization_variant
Default scale: ~6,400 records · ~1,400 true pairs
Injected error patterns:
- Registered name vs. street alias: BRP name differs from HKS operational alias
- BSN linkage: direct BSN match despite all name fields differing
- Street alias abbreviation:
Mohammed El Haddad→Mo Haddad - Romanization: Arabic-origin surnames appear in multiple Latin transcriptions
5. Interpol Notices
Records modelled on the Interpol I-24/7 notice schema (Red, Blue, Yellow, and Green notices). The primary cross-source challenge is that the same individual may appear in SIS II under a Dutch or German romanization convention and in Interpol under a French or English convention, producing the same person with systematically different spellings across two datasets.
Files: interpol_notices.csv, ground_truth_pairs.csv
Schema, `interpol_notices.csv`:
Ground truth format: notice_id_a, notice_id_b, is_match, match_type
Match types: cross_source_romanization_variant, alias_match, partial_dob
Default scale: ~3,600 records · ~630 true pairs
Injected error patterns:
- Cross-source romanization: different transliteration conventions per requesting country
- Name-order transposition between Western (given-surname) and Eastern (surname-given) conventions
- Partial date of birth: year only, or year + month, without day
6. ANPR Vehicle Passages
Highway camera passage events modelled on the Dutch ANPR (Automatische NummerPlaat Herkenning) kentekencamera feed operated by Dienst Verkeerspolitie. The primary challenge is single-character OCR confusion: a camera may read CX-I80-W when the true plate is CX-180-W. Vehicle trajectories link the same vehicle across multiple cameras on a corridor.
Files: anpr_passages.csv, ground_truth_vehicle_pairs.csv
Schema, `anpr_passages.csv`:
Ground truth format: passage_id_a, kenteken_true, kenteken_ocr, is_match, match_type
Match types: ocr_confusion (with specific substitution noted, e.g. 1_to_I)
Default scale: ~50,000 passages · ~2,490 true pairs
Injected OCR confusion pairs (single substitution per plate, bidirectional):
The confusion table was extended beyond the original five pairs using documented sources on ANPR character confusion under Dutch highway conditions (low-angle cameras, motion blur, wet conditions). The default OCR error rate is calibrated at 1% per plate, consistent with published accuracy figures for modern Dutch kentekencamera systems on dry, well-lit motorways. Earlier synthetic datasets used a 5% rate, which overrepresents errors by a factor of 5–25× relative to operational data.
Dutch license plate formats used: DD-LLL-D, LL-DD-LL, DD-LL-DD, L-DDD-LL, LL-DDD-L (current RDW series). Camera coordinates are sampled from bounding boxes around real Dutch highway corridors (A1, A2, A4, A10, A12, A15, A20, A28).
7. CDR Call Detail Records
Call detail records modelled on the telecom CDR / ETSI LI standard as used in Dutch lawful interception. The primary resolution challenge is identity across the MSISDN/IMSI/IMEI triple: a SIM swap leaves the MSISDN the same but changes the IMSI; a device sale leaves the IMEI the same but changes both MSISDN and IMSI. Graph-annotated variants include structured subscriber clusters (10–40 nodes, 85% intra-cluster calling) for community detection tests.
Files: cdr_records.csv, ground_truth_clusters.csv
Schema, `cdr_records.csv`:
Ground truth format: msisdn, imsi, cluster_id, is_hub
The ground truth table is a cluster-membership list rather than a pair list. Each row assigns a subscriber (MSISDN + IMSI) to a cluster and flags whether they are the communication hub of that cluster.
Default scale: ~50,000 records · ~6,300 cluster-membership rows
Call duration calibration: Call duration distributions are grounded in two real-world CDR datasets from Kaggle (see Base data sources). Distributions were derived from approximately 101,000 records across both sources and show the characteristic Dutch mobile calling pattern: median call ~86 seconds, long tail up to ~76 minutes for extended calls. Cluster sizes for social-graph mode (10th–75th percentile: 1–29 contacts per subscriber) are likewise derived from the network degree distribution observed in the same sources.
Injected identity patterns:
- SIM swap: same MSISDN, old IMSI replaced by new IMSI mid-dataset (~15% of clusters)
- IMEI reuse: same device IMEI appears under two different MSISDNs (device resold / shared)
- Burner handset: new IMEI + new MSISDN registered within existing communication cluster
8. SIM Subscriber Registrations
Subscriber registration records modelled on Dutch telecom subscriber databases as maintained by KPN, T-Mobile, Vodafone, Tele2, and associated MVNOs. Pairs the identity data collected at point-of-sale (name, date of birth, document number) with the technical subscriber identity (MSISDN, IMSI, ICCID). A companion swap-events table records IMSI changes on the same SIM card.
Files: sim_subscribers.csv, sim_swap_events.csv, ground_truth_swaps.csv
Schema, `sim_subscribers.csv`:
Schema, `sim_swap_events.csv`:
Ground truth format: sim_id_a, sim_id_b, msisdn, is_match, match_type
Match types: sim_swap_same_person, shared_device, identity_document_reuse
Default scale: ~10,800 subscriber records · ~800 swap events · ~800 true pairs
Injected identity patterns:
- SIM swap: same subscriber activates a replacement SIM, same MSISDN, new ICCID and IMSI
- Prepaid registration gap: prepaid subscribers registered without BSN, matched via name + DOB
- Document reuse: same identity document number associated with two subscriber registrations (stolen / cloned document scenario)
9. FIU Unusual Transaction Reports
Transaction records modelled on FIU-NL (Financial Intelligence Unit Nederland) Geldtransactie Reporting (GTR) reports, as required under the Wwft (Anti-Money Laundering Act). Graph-annotated variants include structured money mule rings (5–15 accounts per ring, feeder → mule hub → offshore IBAN pattern) for graph analytics and network-aware entity resolution.
Files: fiu_transactions.csv, ground_truth_clusters.csv
Schema, `fiu_transactions.csv`:
Ground truth format: iban, cluster_id, rol, is_mule
The ground truth table is a cluster-membership list. Each row assigns an IBAN to a money mule ring (cluster_id), specifies the role (feeder, hub, offshore), and flags whether the account is a mule account.
Default scale: ~20,000 transaction records · ~490 ring-membership rows
Transaction amount calibration: Transaction amounts are calibrated against the real financial transactions network dataset published by Cerqueira et al. (2022), a directed graph of 1.6 million accounts and 3.8 million transactions derived from a European banking dataset (see Base data sources). The empirical amount distribution shows a strongly right-skewed pattern: median transaction ~€2,000, 75th percentile ~€18,200, 90th percentile ~€275,000. Cash transaction structuring (amounts clustered just below the €10,000 mandatory reporting threshold) is modelled from the same source. Feeder-to-hub ring topology (number of feeders per mule account) is derived from the weakly connected component analysis of that network.
Injected network patterns:
- Structuring: large amounts split across multiple small transfers just below reporting thresholds
- Layering: feeder accounts transfer to a central mule hub, which then moves funds offshore
- Name discrepancy: IBAN holder name does not match the name on the FIU report (third-party account)
- IBAN prefix clustering: offshore IBANs cluster around a small set of high-risk jurisdictions
Benchmark Scenarios
The data/benchmarks/ directory contains pre-built evaluation scenarios derived from the schemas described above. Each scenario pairs one or more source files with a ground truth file that lists every true match pair, enabling precision/recall evaluation without requiring an exhaustive pairwise scan.
Task types
Standard scale (~10,000 records per source)
Micro scale (~1,000 records per source)
Reduced-scale versions of a subset of the standard scenarios. Schemas, error patterns, and generation logic are identical to their standard counterparts; only record counts differ. Intended as fast smoke tests.
How benchmark datasets are generated
Each benchmark scenario is generated by data_generator/generate_bench.py using the same _common.py name, address, and demographic helpers that underpin all other generators. Generation proceeds in three steps:
- Anchor population sampling: A pool of unique synthetic persons is drawn for the appropriate schema (BRP, KvK, SIS II, or HKS) using the same name distributions, demographic compositions, and field constraints described in the Datasets section above.
- Duplicate and variant injection: For each true match pair, one record is treated as the canonical version and one or more perturbed variants are generated. Perturbations are drawn from the same error pattern catalogue as the raw datasets: tussenvoegsel variants, given name abbreviations, date-of-birth day errors (day shifted ±1, year and month preserved), address drift, name-order transpositions, romanization variants, estimated dates of birth, and alias substitutions. The specific patterns applied depend on the schemas involved: cross-schema scenarios apply the patterns that reflect real challenges between those two sources.
- Confounder padding: Non-matching records are added to reach the target total. Confounders are always drawn from the most frequent surnames (following the same Zipf-weighted distribution as the raw data) so that same-surname, different-person pairs occur at realistic rates and are not trivially rejected.
The result is a scenario where true matches are scattered among a realistic population of non-matches, reproducing the difficulty of operational Dutch administrative data without including any real personal information.
Each dataset group in data/benchmarks/ (e.g. brp/, brp_kvk/) also contains a mapping.toml file that declares field comparison settings (comparison type, null handling, blocking keys) for use with splink. See data/benchmarks/README.md for the full format description.
Cross-schema field coverage
Cross-source scenarios are harder than single-schema scenarios because not all fields are available in both sources. A missing field should be treated as absent information, not as evidence of non-match:
Ground truth format
All ground truth files share the same structure:
Any pair of records not listed in the ground truth file should be treated as a non-match.
Demographic composition
Name distributions reflect Dutch population demographics (CBS 2025, source: cbs.nl, Population by origin):
Surname sampling follows a Zipf (power-law) distribution, the most common Dutch surnames (de Jong, Jansen, de Vries, van den Berg, …) are sampled proportionally more often than rare ones, matching the actual CBS surname frequency table. This ensures that confounders (records that share a common surname but are different persons) occur at a realistic rate and are not drawn from artificially uniform distributions.
Arabic and Turkish names are generated with authentic spelling variants to replicate the romanization challenges found in real Dutch administrative data. SIS II and Interpol records include a proportion of non-Latin script aliases.
File formats
All files are UTF-8 encoded CSV with a header row. Pipe (|) is used as the internal delimiter for multi-valued fields (e.g. alias_namen, delict_types). Ground truth files always include an is_match column (True / False) and a match_type column describing the specific error pattern injected. Cluster ground truth files use a membership-list format rather than a pair list.
Base data sources
Name and address pools
Name and address pools are derived from the following publicly available Kaggle datasets:
The Zipf weighting means that the top-ranked surnames are sampled far more often than rare ones, mirroring the real Dutch population register where de Jong is roughly 60× more common than a mid-ranked surname. This is important for confounder injection: confounders are always drawn from the most common surnames, matching real operational conditions where common names cause genuine ambiguity.
CDR call statistics
Call duration and network topology statistics are derived from two real-world CDR datasets published on Kaggle:
FIU financial network topology
Money mule ring topology and transaction amount distributions are grounded in the Financial Transactions Network dataset:
Cerqueira, V., Torgo, L., & Moreira-Matias, L. (2022). Financial Transactions Network [Data set]. Available at the data accompanying "Fraud Detection under Privileged Information", IEEE TNNLS.
The dataset contains two directed graphs (GN and GT) of 1.6 million accounts and 3.8 million labelled financial transactions. The following statistics were extracted and used to calibrate generation:
- Transaction amounts, empirical percentiles (p25 ≈ €300, p50 ≈ €2,000, p75 ≈ €18,200, p90 ≈ €275,000) set the amount ranges for each transaction type. Cash structuring is calibrated so that ~40% of cash transactions cluster in the €8,500–€9,999 range, consistent with documented structuring patterns in FIU-NL reports.
- Ring (cluster) size, weakly connected component analysis of the labelled graph determines realistic feeder-per-hub counts (range: 2–30 feeders, consistent with the p50 WCC size of 2 and max of 200).
- Hub out-degree, p90 hub out-degree from the labelled graph (2.0) calibrates how many downstream offshore accounts a mule hub forwards to.
The source dataset is not redistributed in this repository. The extracted statistics are stored in data/base/fiu/fiu_topology.json.
Domain logic and error pattern definitions are being progressively ported from the zsimulate and zbroker simulation libraries developed at ZAL Analytics. Providers ported: BRP, SIS II, HKS, ANPR, KvK, CDR, FIU (all with calibrated distributions). Providers still in progress: Interpol, SIM.
License
All data is fully synthetic and released under CC BY 4.0. No real personal data, criminal records, vehicle information, or financial records are included. Attribution: cite this dataset as Dutch Law Enforcement Entity Resolution Benchmark (synthetic), arsalan-anwari, 2026.
