Interactive visualizations for analyzing voting patterns and vote transfers between Israeli Knesset elections (2003-2022, Knesset 16-25).
קולות נודדים - Visit the Website
This project provides a suite of interactive tools to explore Israeli election data at the ballot-box level (~10,000-12,000 polling stations per election). It answers questions like:
All pages are fully bilingual (Hebrew/English) and have both desktop and mobile-optimized versions.
A central hub with key stats and card navigation to all visualizations. Shows live visitor count.
Visualizes the flow of votes between consecutive elections using a Sankey diagram.
The transfer matrix is computed using convex optimization to find the best-fit stochastic matrix M where Votes_before × M ≈ Votes_after.
A 2D visualization of all polling stations, positioned by similarity of voting patterns.
Interactive Leaflet map showing all ballot stations across Israel.
Compare support for two parties (or the same party across elections) at the ballot-box level.
Interactive calculator showing how Knesset seats are allocated using the Bader-Ofer (modified D’Hondt) method.
Identifies polling stations with unusual voting patterns compared to their surroundings.
Simulates a hypothetical regional election system for Israel using Voronoi district mapping and D’Hondt seat allocation.
Deep-dive page for party families showing their evolution across elections:
Accessible via party.html?name=<party_family_name>.
Deep-dive page for individual settlements showing:
Accessible via settlement.html?name=<settlement_name> or by clicking settlement links in the map, T-SNE, and scatter views.
The vote transfer matrix is estimated by solving a constrained optimization problem:
minimize ||V_from × M - V_to||²
subject to:
- M[i,j] ≥ 0 for all i,j (non-negative transfers)
- Σⱼ M[i,j] = 1 for all i (rows sum to 1, i.e., stochastic matrix)
This assumes voters transfer between parties in a consistent pattern across all polling stations—a simplification, but one that produces interpretable results.
Solver: CVXPY with SCS backend
When comparing elections, ballot boxes are matched by settlement name and ballot number. Israeli ballot boxes sometimes get subdivided between elections (e.g., box 14 becomes 14.1, 14.2, 14.3). The matching logic:
Polling stations are embedded in 2D using t-SNE on the vector of party vote proportions. Stations with similar voting profiles cluster together, often revealing geographic and demographic patterns.
| Election | Knesset | Date | Turnout |
|---|---|---|---|
| 16 | 16th Knesset | January 2003 | 67.8% |
| 17 | 17th Knesset | March 2006 | 63.6% |
| 18 | 18th Knesset | February 2009 | 64.7% |
| 19 | 19th Knesset | January 2013 | 67.8% |
| 20 | 20th Knesset | March 2015 | 72.3% |
| 21 | 21st Knesset | April 2019 | 68.5% |
| 22 | 22nd Knesset | September 2019 | 69.8% |
| 23 | 23rd Knesset | March 2020 | 71.5% |
| 24 | 24th Knesset | March 2021 | 67.4% |
| 25 | 25th Knesset | November 2022 | 70.6% |
# Clone the repository
git clone https://github.com/harelc/elections-vote-transfer.git
cd elections-vote-transfer
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Start local server
cd site
python3 -m http.server 8888
Then open http://localhost:8888 in your browser.
source venv/bin/activate
# Vote transfer matrices
python generate_transfer_data.py
cp data/transfer_*.json site/data/
# T-SNE clustering
python generate_tsne_data.py
python add_locations_to_tsne.py
cp data/tsne_*.json site/data/
# Geographic map data (writes directly to site/data/)
python generate_map_data.py
# Wikipedia enrichment for settlement profiles (writes to site/data/)
python enrich_settlements_wikipedia.py
├── site/ # Static website files
│ ├── index.html # Dashboard landing page
│ ├── sankey.html # Sankey vote transfer diagram
│ ├── tsne.html # T-SNE ballot clustering
│ ├── geomap.html # Geographic map
│ ├── scatter.html # Party scatter plot
│ ├── dhondt.html # Bader-Ofer seat calculator
│ ├── irregular.html # Irregular ballot analysis
│ ├── regional.html # Regional elections simulator
│ ├── settlement.html # Settlement profile page
│ ├── discussions.html # Community discussions (Giscus)
│ ├── i18n.js # Internationalization & navigation
│ ├── og-image.png # Social preview image
│ ├── m/ # Mobile-optimized versions of all pages
│ └── data/ # JSON data files for frontend
├── data/ # Source and intermediate data
├── party_config.py # Election metadata & party config (K16-K25)
├── generate_transfer_data.py # Transfer matrix computation
├── generate_tsne_data.py # T-SNE embedding computation
├── generate_map_data.py # Geographic data generation
├── download_statistical_zones.py # CBS 2011 zone matching pipeline
├── process_statistical_zones.py # CBS socioeconomic data processing
├── download_historical_ballots.py # K16-K20 ballot data from CEC CKAN API
├── enrich_settlements_wikipedia.py # Wikipedia data enrichment
├── prepare_election_26.py # Election 26 data workflow
└── ballot*.csv # Raw election results per election (16-25)
This project is licensed under CC BY-NC-SA 4.0 (Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International).
You are free to share and adapt this work for non-commercial purposes, with attribution.
Contributions are welcome! Please feel free to submit issues or pull requests.