# French municipal elections 2008, 2014, 2020: sources and transformations

Assembled 2026-10-04. Every number comes from an official file of the
Ministère de l'Intérieur that was downloaded on that date and is kept in `raw/`.
`dune exec data/elections/build/fetch.exe` re-downloads each missing file and
checks the SHA-1 of every file, and `dune exec data/elections/build/build.exe`
writes `lists_*.csv`, `races.csv` and `nuances.csv` (it reads the 2008 `.xls`
with the `xlsx` library). `dune exec data/elections/build/outcomes.exe --
ACCOUNTS_CSV` writes `outcomes.csv` from the commune accounts
(`accounts.csv`, see `data/places/SOURCES.md`) and the Urssaf and IRCOM series
in `data/zrr`. All three run from the repository root and take the data
directory as an optional last argument (default `data/elections`).

| File | Rows | Content |
|---|---|---|
| `lists_2008.csv` | 11,594 | every list, rounds 1 and 2, communes of 3,500+ inhabitants |
| `lists_2014.csv` | 26,459 | every list, rounds 1 and 2, communes of 1,000+ inhabitants |
| `lists_2020.csv` | 24,754 | every list, rounds 1 and 2, communes of 1,000+ inhabitants |
| `races.csv` | 22,506 | one row per commune and year: 2,770 (2008), 9,787 (2014), 9,949 (2020) |
| `nuances.csv` | 58 | list nuance codes per year, label, bloc, source |

## 1. Result files

All results files are by commune, one row per commune (or Paris, Lyon or
Marseille sector, or electoral section) with one block of columns per list.
"Sous réserve de rectifications par le juge de l'élection" applies to all of
them.

| Year | Round | Dataset page (data.gouv.fr, Ministère de l'Intérieur) | File URL | SHA-1 (= data.gouv `analysis:checksum`) | Local copy |
|---|---|---|---|---|---|
| 2008 | 1 and 2 | https://www.data.gouv.fr/datasets/elections-municipales-2008-resultats-572150 ("communes de plus de 3 500 habitants (scrutin de liste ...)", created 2013-07-08, modified 2016-03-12) | https://static.data.gouv.fr/18/22c3d2b3c8e3abb0fb447515ce23ad4858ad8b3522749d9924acc218f7b7a9.xls | 00ffa79d6ed4e20979b6e6b99ac96af0d1f1ef71 | `raw/mn2008_3500plus.xls` (sheets `Tour 1`, `Tour 2`) |
| 2008 | 1 and 2, by polling station | https://www.data.gouv.fr/datasets/elections-municipales-2008-communes-de-plus-de-3-500-habitants-resultats-par-bureaux-de-vote-1 (created 2015-09-25) | https://static.data.gouv.fr/resources/elections-municipales-2008-communes-de-plus-de-3-500-habitants-resultats-par-bureaux-de-vote-1/20150925-122851/MN08_BVot_T1T2.txt | 897f1b91e90c39c954be3a915657e64a91c6aabc | `raw/mn2008_bvot_t1t2.txt` (used only to fill one truncated record, see 5) |
| 2014 | 1 | https://www.data.gouv.fr/datasets/elections-municipales-2014-resultats-1er-tour (created 2014-03-25) | https://www.data.gouv.fr/storage/f/2014-03-25T16-06-23/muni-2014-resultats-com-1000-et-plus-t1.txt | 35eafb2aa825e3d154e4979586eaf2881efcd315 | `raw/mn2014_t1_1000plus.txt` |
| 2014 | 2 | https://www.data.gouv.fr/datasets/elections-municipales-2014-resultats-2eme-tour (created 2014-03-31) | https://www.data.gouv.fr/storage/f/2014-03-31T09-49-28/muni-2014-resultats-com-1000-et-plus-t2.txt | 4daf897c613f5de57b355df92c3217232ae79271 | `raw/mn2014_t2_1000plus.txt` |
| 2020 | 1 | https://www.data.gouv.fr/datasets/elections-municipales-2020-resultats-1er-tour (created 2020-03-17, modified 2020-06-29) | https://static.data.gouv.fr/resources/elections-municipales-2020-resultats/20200525-133704/2020-05-18-resultats-communes-de-1000-et-plus.txt | 6b1197e9c9ca53792322e3df069b34613fdbb8d3 | `raw/mn2020_t1_1000plus.txt` (version of 18 May 2020) |
| 2020 | 2 | https://www.data.gouv.fr/datasets/municipales-2020-resultats-2nd-tour (created 2020-06-29) | https://static.data.gouv.fr/resources/municipales-2020-resultats-2nd-tour/20200629-192435/2020-06-29-resultats-t2-communes-de-1000-hab-et-plus.txt | ecf1182cc5b0f13c7f7b181986ccbe81c0e53853 | `raw/mn2020_t2_1000plus.txt` |

The dataset metadata returned by the data.gouv.fr API
(`https://www.data.gouv.fr/api/1/datasets/<id>/`) on 2026-10-04 is kept in
`raw/meta/<id>.json`. The text files are ISO-8859-1. The 2014 and 2020 round-2
files are `;`-separated; the 2020 round-1 file is tab-separated.

Not used: the 2008 files for communes under 3,500 (majority vote for
individuals, no lists), the 2014 and 2020 files for communes under 1,000 (same
reason), and the 2026 municipal elections, which are only available in the
aggregated by-polling-station file of data.gouv.fr ("Données des élections
agrégées"), too large to fetch here.

## 2. Nuance grids (`nuances.csv`)

Columns: `year, code, label, bloc, bloc_ministry, bloc_basis, source`. Labels
are copied as printed in the source.

| Year | Document | URL | SHA-1 of local copy | Local copy |
|---|---|---|---|---|
| 2008 | Ministère de l'Intérieur, "Résultats des élections municipales", page "Nuances" (15 list nuances) | https://www.interieur.gouv.fr/sections/a_votre_service/resultats-elections/MN2008/nuances.html, archived copy https://web.archive.org/web/20080409215657/https://www.interieur.gouv.fr/sections/a_votre_service/resultats-elections/MN2008/nuances.html | 81d107809a84dca34b43afa91f2e98afe853c6a0 | `raw/nuances/mn2008_nuances_wayback.html` |
| 2014 | Ministère de l'Intérieur, "Résultats des élections municipales et communautaires 2014", page "Nuances des listes" (17 list nuances) | https://www.archives-resultats-elections.interieur.gouv.fr/resultats/MN2014/nuances.php (archived 2026-03-09: https://web.archive.org/web/20260309230858/https://www.archives-resultats-elections.interieur.gouv.fr/resultats/MN2014/nuances.php); same table at https://www.interieur.gouv.fr/Elections/Les-resultats/Municipales/elecresult__MN2014/(path)/MN2014/nuances.html (archived 2023-08-27) | b72cbfab4cf2781eb85e1beb0d98adf4f815cb96 and ccb50d776ac8de5097f1a5db86cd6014ed333c29 | `raw/nuances/mn2014_nuances_archives.html`, `raw/nuances/mn2014_nuances_interieur.html` |
| 2020 | Circulaire NOR INTA1931378J du 3 février 2020 "relative à l'attribution des nuances politiques aux candidats aux élections municipales et communautaires des 15 et 22 mars 2020" (Légifrance circulaire 44929, published 04/02/2020; annexe 2: grille des nuances des listes, 23 nuances; annexe 3: blocs de clivages) | https://www.legifrance.gouv.fr/circulaire/id/44929, PDF http://circulaires.legifrance.gouv.fr/pdf/2020/02/cir_44929.pdf (archived 2020-02-22: https://web.archive.org/web/20200222232701/http://circulaires.legifrance.gouv.fr/pdf/2020/02/cir_44929.pdf) | 96ffc1a31915470a620021abd67b57e623b437ce | `raw/nuances/cir_44929.pdf` |

legifrance.gouv.fr and archives-resultats-elections.interieur.gouv.fr return
403 to scripted downloads, so the Internet Archive copies were downloaded. The
2014 page served by the ministry's archive site in 2026 is the one archived.
This 3 February 2020 circulaire replaced an earlier version after the Conseil
d'État's juge des référés suspended parts of it in January 2020 (context
only; that order was not downloaded).

For reference only, `raw/nuances_agregees_2025.csv` and
`raw/nuances_agregees_2026.csv` are the nuance dictionaries of the data.gouv.fr
dataset "Données des élections agrégées"
(https://static.data.gouv.fr/resources/donnees-des-elections-agregees/20250723-154646/nuances.csv
and
https://static.data.gouv.fr/resources/donnees-des-elections-agregees/20260324-132009/nuances.csv,
the second from circulaire INTP2602966C of February 2026). They are not used:
the 2026 grid puts LFI in the far-left bloc (EXG), whereas the 2020 grid puts
it in GAU.

### Blocs

`bloc` takes the values `far_left`, `left`, `centre`, `right`, `far_right`,
`other` and `unlabelled`.

**2020: the ministry's own grouping.** Annexe 3 of circulaire INTA1931378J
groups the list nuances into six blocs. The circulaire says, verbatim (OCR
spacing in the PDF scan corrected):

> 3. Définition des clivages (annexe 3)
>
> Vous voudrez bien vous référer à la répartition des nuances en 6 blocs
> précisée dans la grille annexée à la présente circulaire, afin d'agréger les
> résultats des différentes nuances qui vous permettront notamment d'évaluer
> rapidement les rapports de force en vue du second tour.

and section 2(b) lists the same groups:

> Cette grille est composée des 23 nuances suivantes: 1 nuance pour les listes
> d'extrême gauche: LEXG (liste d'extrême gauche) ; 7 nuances pour les listes
> de gauche: LCOM (liste du Parti communiste français), LFI (liste La France
> insoumise), LSOC (liste socialiste), LRDG (liste du Parti radical de gauche),
> LDVG (liste divers gauche), LUG (liste union de la gauche), LVEC (liste
> Europe Ecologie Les Verts) ; 4 nuances pour les listes de courants politiques
> divers: LECO (autre liste écologiste), LDIV (liste divers), LREG (liste
> régionaliste), LGJ (liste gilets jaunes) ; 5 nuances pour les listes du
> centre: LREM (liste La République en marche), LMDM (liste Modem), LUDI (liste
> UDI), LUC (liste union du centre), LDVC (liste divers centre) ; 4 nuances
> pour les listes de droite: LLR (liste Les Républicains), LUD (liste union de
> la droite), LDVD (liste divers droite), LDLF (liste Debout la France) ; 2
> nuances pour les listes d'extrême droite: LRN (liste Rassemblement national),
> LEXD (liste d'extrême droite).

Annexe 3 names the blocs EXG, GAU, AUT, CENT, DTE and EXD; they are copied in
`bloc_ministry` and mapped to `far_left`, `left`, `other`, `centre`, `right`
and `far_right`.

**2008 and 2014: no ministry grouping.** The 2008 and 2014 nuance pages list
the codes in left-to-right order with no blocs, so the grouping follows the
usual academic convention, aligned on the ministry's 2020 blocs:

- far_left: LEXG.
- left: LCOM, LFG, LPG, LSOC, LUG, LDVG, LVEC (2014); LCOM, LUG, LSOC, LVEC,
  LDVG and LGC "gauche-centristes", left-led lists with centrist partners
  (2008).
- centre: LMDM, LUC, LUDI (2014); LCMD "centre-MoDem" and LMC
  "majorité-centristes" (2008).
- right: LUMP, LUD, LDVD (2014); LMAJ "de la majorité" (UMP), LDVD (2008).
- far_right: LFN, LEXD.
- other: LDIV (2014); LAUT "inclassable", LREG (2008).

**unlabelled**: `NC` and `LNC`. `NC` occurs only in rows that are dropped
(the 2008 nuance-total rows and the 2014 Polynesian `PL2` rows, see 5), so
no kept 2008 or 2014 list is unlabelled. `LNC` (2020) labels 11,967 list rows.
`LNC` means "non communiqué". The 2020 circulaire nuances lists only "dans les
communes de 3500 habitants et plus et dans toutes les communes chefs-lieux
d'arrondissement", so 2020 communes of 1,000 to 3,499 inhabitants have no
label: 6,776 of the 9,949 races of 2020 have an `LNC` winner.

## 3. Definitions (`races.csv`)

- **Decisive round**: 2 if the commune is in the round-2 file, otherwise 1.
- **Winner**: the list with the most votes in the decisive round.
- **Best left list, best right list**: the list with the most decisive-round
  votes whose bloc is `far_left` or `left`, and the one whose bloc is `centre`
  or `right`. `left_votes` and `right_votes` are their votes, blank when no
  such list stood.
- **top_two_left_right** = 1 when the two lists with the most votes in the
  decisive round are one left and one right (as just defined). Otherwise 0,
  including single-list races.
- **margin_left_minus_right** = 100 x (left_votes - right_votes) /
  (left_votes + right_votes), in **percentage points** of the votes cast for
  those two lists. Filled only when `top_two_left_right` = 1. A positive value
  means the left list won.
- Since the centre is put with the right here, the file also carries a
  stricter variant in which the right is the `right` bloc alone (2008: LMAJ,
  LDVD; 2014: LUMP, LUD, LDVD; 2020: LLR, LUD, LDVD, LDLF): see
  `right_excl_centre_votes`, `margin_left_minus_right_excl_centre` and
  `top_two_left_right_excl_centre`. It matters for 2008 (MoDem stood alone)
  and 2020 (La République en marche).
- `expressed` (exprimés), `registered` (inscrits) and `voters` (votants) are
  for the decisive round.
- Extra columns: `commune_name`, `decisive_round`, `n_lists`, `winner_list`,
  `winner_votes`, `winner_seats`, `second_nuance`, `second_bloc` and
  `second_votes`.

`lists_<year>.csv` columns: `insee_code, year, round, list_name, nuance, bloc,
votes, seats, constituency, commune_name, panel, head_of_list, registered,
voters, expressed`. `seats` is "Sièges / Elu" (municipal council seats). It is 0
in round 1 when a round 2 followed. `constituency` is empty for a whole
commune, `SRnn` for a Paris, Lyon or Marseille sector (`ARnn` in 2008), and
`SNnn` for an electoral section. `panel` is the 2020 "N.Pan." panel number.

## 4. Commune codes

`insee_code` is the five-character code of the year's own commune map: the
ministry's department code, left-padded to two digits, followed by its commune
code, left-padded to three digits (the 2020 round-1 file drops the leading
zeros; Corsica is `2A` or `2B`). The overseas codes in the ministry files are
converted as follows. The three-digit commune code's last two digits are the
INSEE commune number.

| Ministry | INSEE prefix | Note |
|---|---|---|
| ZA Guadeloupe | 971 | |
| ZB Martinique | 972 | |
| ZC Guyane | 973 | |
| ZD La Réunion | 974 | |
| ZS Saint-Pierre-et-Miquelon | 975 | |
| ZM Mayotte | 976 (2014, 2020); **985 in 2008** | Mayotte was a collectivité d'outre-mer coded 985 until it became department 976 in 2011 (the ministry file uses 5xx in 2008 and 2014, 6xx in 2020) |
| ZP Polynésie française | 987 | 2014 file uses 0xx, 2020 file 7xx, both ending in the INSEE number |
| ZN Nouvelle-Calédonie | 988 | **2008** numbers communes with a scheme of its own (Nouméa = 800); the code is taken from the 2014 file by commune name (all 12 names match) |

## 5. Gaps, exclusions and anomalies

- **Coverage and labels.** List voting with nuances covers communes of 3,500+
  inhabitants in 2008 and 1,000+ in 2014. In 2020 lists stand in 1,000+
  communes, but nuances exist only for 3,500+ and chefs-lieux d'arrondissement
  (see 2). An RD on 2008 and 2020 therefore covers 3,500+ communes only, and
  2014 covers 1,000+.
- **Paris, Lyon, Marseille and sectioned communes** vote by sector or electoral
  section. Their rows are kept in `lists_<year>.csv` with `constituency` set,
  but they are **not in `races.csv`** (2008: 138 sector or section rows across
  both rounds; 2014: 55; 2020: 34).
- **2008 nuance-total rows dropped.** For 84 commune-round rows of sectioned
  communes (Paris, Lyon, Marseille, Oyonnax, Louhans, Saumur, ...), the 2008
  `.xls` has an extra row that totals the sections' votes by nuance, with all
  16 nuances, no list name and no head of list. These rows are not lists and
  are dropped from both files. Communes whose only whole-commune row is such a
  total (Louhans, Troarn, Gallardon, ...) have no race.
- **2014 French Polynesia sections.** 89 rows (63 in round 1, 26 in round 2)
  of type `PL2`, a plurinominal majority vote for individual candidates in the
  sections of Polynesian communes, are not list votes and are dropped.
- **Round 2 missing from the official file.** In 7 commune-years no list has
  an absolute majority in round 1 and the commune is absent from the round-2
  file. These are dropped from `races.csv`: 2014 Papara (98734); 2020
  Iracoubo (97303), Matoury (97307), Remire-Montjoly (97309), Roura (97310),
  Awala-Yalimapo (97361) and Papaichton (97362). The 2020 round-2 file has no
  Guyane (ZC) row at all.
- **2008 records packed in one cell.** In 20 round-1 and 5 round-2 rows of the
  2008 `.xls`, a list name beginning with `"` made the rest of the record land
  in one cell as `;`-separated text. `build.exe` splits it back. One of them
  (Bonson, 42022, round 1) is cut at 255 characters, so its last list (LDVD, "A
  BONSON ENSEMB AUTREMENT") has no seats and no votes. Its votes (241) are the
  sum over Bonson's polling stations in the 2008 by-bureau file, and its seats
  are 0 because Bonson went to a round 2. The polling-station sums equal the
  `.xls` votes for every other Bonson list.
- **2008 list name lost to a formula error.** The Orthez (64430) round-1 list
  of Bernadette Prada (LDVG) is the error cell `#NAME?` in the 2008 `.xls`:
  its name, "+ VRAIE + PROCHE + JUSTE" in the by-bureau file, was read as a
  formula. `lists_2008.csv` leaves its `list_name` empty.
- **Split list names rejoined.** Lille 2008 (rounds 1 and 2: "SÉBASTIEN
  HUYGHE......." followed by an empty cell) and Le Lardin-Saint-Lazare 2020
  round 1 (a tab inside the list name).
- **Seats inconsistent with votes in the official 2008 file.** In
  Beaumont-lès-Valence (26037; LSOC 1,056 votes, 10 seats; LDVD 831 votes, 17
  seats) and Aubignan (84004; LDVG Bernard Rey 861 votes, 22 seats; LDVG Guy Rey
  1,428 votes, 5 seats), the list with fewer round-1 votes has the majority of
  seats. The votes match the by-bureau file. The values are kept as published,
  and the winner is the list with the most votes.
- **Seats not reported.** 2014 round 2 of Arue (98712) and Papeete (98735):
  `seats` is blank. 2020 round 1 of Egreville (77168), Saint-Mammès (77419)
  and Villenoy (77513): a list has an absolute majority but every list has 0
  seats in the file.
- **Votes do not add up.** 2020 round 2, Doingt (80240): the list votes sum to
  696 against 697 expressed in the file.
- Every other commune-round has list votes summing exactly to `expressed`.

## 6. Counts

| Year | Races | Decided in round 2 | Winner nuanced | Top two left v right | of which \|margin\| < 5 pts | < 2 pts | Top two left v right, centre excluded | of which < 5 pts |
|---|---|---|---|---|---|---|---|---|
| 2008 | 2,770 | 936 | 2,770 | 1,902 | 274 | 114 | 1,731 | 245 |
| 2014 | 9,787 | 1,731 | 9,787 | 2,848 | 425 | 153 | 2,659 | 397 |
| 2020 | 9,949 | 1,396 | 3,173 | 1,027 | 108 | 39 | 691 | 68 |
