La mise à disposition de données ouvertes (open data) se fait souvent dans des formats non standard et avec des organisations de données toujours surprenantes.
Le ministère de l’intérieur vient de mettre à disposition, via le portail data.gouv.fr, les données du 1er tour des élections régionales 2015 sous forme d’un joli fichier xlsx (merci le référentiel général d’interopérabilité) à la construction sans doute très ergonomique pour celui qui l’a conçu mais pas quand on veut l’utiliser sous R, Python ou QGIS. Ce qui est amusant, c’est qu’on sent que les données d’origine étaient dans un bon format, sans doute issues d’une base de données, et qu’on a fait un effort pour les publier sous forme de fichier Excel avec plein de redondances d’informations.
Il va donc falloir remettre les données en forme. Et pour celà, on va utiliser R et l’excellent package data.table, écrit par Matt Dowle, qui permet des traitements très rapides.
Tout d’abord on part de la version csv du fichier Excel. Certes il existe des modules pour lire directement les fichiers .xlsx mais ils sont lents et peu commodes. J’ai donc chargé le fichier sous LibreOffice, supprimé les 2 premières lignes et sauvé en csv avec délimiteur tabulation.
Avec data.table, la fonction fread lit directement et très vite les fichier csv. Elle est en général capable de déviner le délimiteur et le type des données. Ici on a 13 warnings car il faut attendre 7000 lignes pour tomber sur un département corse (2A) qui oblige fread a passer la colonne en character et non pas integer. Idem pour d’autres colonnes où le type est deviné très tard.
options(width=100)
library("data.table")
df1 <- fread('reg2015t1.csv')
Les lignes sont très longues avec beaucoup de variables. On ne conserve que celles qui sont intéressantes (en gros les entiers et les nuances politiques). Comme les entetes de lignes sont les mêmes, on va renommer les colonnes V1, V2, V3…
On calcule également un code commune INSEE sur 5 positions (sauf pour les DOM où on n’a fait le recodoge des codes en ZA, ZB non officiels)
df2<-df1[,c(2,4,6,7,9,11,14,17,seq(21,117,8), seq(25,121,8)), with=F]
setnames(df2, paste0('V', 1:length(df2)))
df2[,dc:=paste0(ifelse(nchar(V1)==1, paste0('0',V1), V1), sprintf("%03d", V2))]
cols <- names(df2)
vars=c('ins', 'abs', 'vot', 'blc', 'nul', 'exp')
cols[3:8]=vars
setnames(df2, cols)
setkey(df2, dc)
# ligne correspondant à Besançon
df2[dc=='25056']
## V1 V2 ins abs vot blc nul exp V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21
## 1: 25 56 69057 37445 31612 623 395 30594 LFN LDIV LUD LECO LVEC LFG LEXG LDLF LMDM LUG
## V22 V23 V24 V25 V26 V27 V28 V29 V30 V31 V32 V33 V34 dc
## 1: 6538 273 6314 669 1795 1964 404 1021 1024 10592 NA NA NA 25056
C’est pas encore terrible mais par rapport au point de départ, c’est déjà mieux !
On va maintenant remettre ensemble les nuances (variables V9 à V21) et le nombre de votes (variables V22 à V34).
Pour cela on va utiliser la fonction melt du package data.table qui fait exactement ce qu’on attend avec une syntaxe finalement très simple. Les anglais disent qu’on passe d’un format wide à un format long.
nuance=paste0('V', 9:21)
voix=paste0('V', 22:34)
m2 <- melt(df2, measure=list(nuance, voix), value.name=c('nuance','voix'))
m2[dc=='25056']
## V1 V2 ins abs vot blc nul exp dc variable nuance voix
## 1: 25 56 69057 37445 31612 623 395 30594 25056 1 LFN 6538
## 2: 25 56 69057 37445 31612 623 395 30594 25056 2 LDIV 273
## 3: 25 56 69057 37445 31612 623 395 30594 25056 3 LUD 6314
## 4: 25 56 69057 37445 31612 623 395 30594 25056 4 LECO 669
## 5: 25 56 69057 37445 31612 623 395 30594 25056 5 LVEC 1795
## 6: 25 56 69057 37445 31612 623 395 30594 25056 6 LFG 1964
## 7: 25 56 69057 37445 31612 623 395 30594 25056 7 LEXG 404
## 8: 25 56 69057 37445 31612 623 395 30594 25056 8 LDLF 1021
## 9: 25 56 69057 37445 31612 623 395 30594 25056 9 LMDM 1024
## 10: 25 56 69057 37445 31612 623 395 30594 25056 10 LUG 10592
## 11: 25 56 69057 37445 31612 623 395 30594 25056 11 NA
## 12: 25 56 69057 37445 31612 623 395 30594 25056 12 NA
## 13: 25 56 69057 37445 31612 623 395 30594 25056 13 NA
Reste à supprimer les lignes où le nombre de voix est manquant et à remettre les variables dans le bon ordre. On en profite pour indexer (trier) la table par commune et variable qui correspond au numéro de la liste.
m2 <- m2[!is.na(voix)]
reg2015l <- m2[, c('dc', vars, 'variable', 'nuance', 'voix'), with=F]
setkey(reg2015l, dc, variable)
reg2015l[dc=='25056']
## dc ins abs vot blc nul exp variable nuance voix
## 1: 25056 69057 37445 31612 623 395 30594 1 LFN 6538
## 2: 25056 69057 37445 31612 623 395 30594 2 LDIV 273
## 3: 25056 69057 37445 31612 623 395 30594 3 LUD 6314
## 4: 25056 69057 37445 31612 623 395 30594 4 LECO 669
## 5: 25056 69057 37445 31612 623 395 30594 5 LVEC 1795
## 6: 25056 69057 37445 31612 623 395 30594 6 LFG 1964
## 7: 25056 69057 37445 31612 623 395 30594 7 LEXG 404
## 8: 25056 69057 37445 31612 623 395 30594 8 LDLF 1021
## 9: 25056 69057 37445 31612 623 395 30594 9 LMDM 1024
## 10: 25056 69057 37445 31612 623 395 30594 10 LUG 10592
Il peut y avoir plusieurs liste de la même nuance dans une région. On peut donc sommer si on veut pour obtenir une table par commune et nuance.
w1 <- m2[, .(voix=sum(voix)), by=.(dc, nuance)]
w1[order(dc, nuance)]
## dc nuance voix
## 1: 01001 LCOM 8
## 2: 01001 LDIV 1
## 3: 01001 LDLF 7
## 4: 01001 LDVD 0
## 5: 01001 LEXG 4
## ---
## 337028: ZD424 LFN 33
## 337029: ZD424 LMDM 540
## 337030: ZD424 LREG 15
## 337031: ZD424 LUD 1175
## 337032: ZD424 LUG 382
A partir de cette table, on peut également reconstituer une table en format long avec une colonne par nuance en utilsant la fonction réciproque de melt : dcast.
On refait ensuite une jointure sur dc avec les variables de niveau commune : ‘ins’, ‘abs’, ‘vot’, ‘blc’, ‘nul’ et ‘exp’. La syntaxe data.table de la jointure est particulièrement compacte : df3[df4].
df3 <- dcast(w1, dc ~ nuance, value.var='voix', fill=0)
setkey(df3, dc)
df4 <- copy(df2[, c('dc', vars), with=F])
setkey(df4, dc)
reg2015w <- df3[df4]
reg2015w[dc=='25056']
## dc LCOM LDIV LDLF LDVD LDVG LECO LEXD LEXG LFG LFN LLR LMDM LRDG LREG LSOC LUD LUDI LUG
## 1: 25056 0 273 1021 0 0 669 0 404 1964 6538 0 1024 0 0 0 6314 0 10592
## LVEC LVEG ins abs vot blc nul exp
## 1: 1795 0 69057 37445 31612 623 395 30594
setkey(reg2015w, dc)
On termine en écrivant les fichiers csv correspondants.
write.table(reg2015l, 'reg2015l.tsv', sep='\t', row.names = F)
write.table(reg2015w, 'reg2015w.tsv', sep='\t', row.names = F)
La table reg2015w est téléchargeable sur le site data.gouv.fr en suivant ce lien