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