Le retour des abeilles

Suite à mon précédant billet, “Maudites Abeilles“, j’ai eu des commentaires sur le blog, mais aussi sur Twitter pour me dire que malgré tout, il y avait bien une chance sur deux pour avoir un chemin bleu permettant de connecter les deux régions bleues, celle au nord et celle au sud.

En effet, dans le problème de base, les régions sont colorées, et le problème n’est pas l’existence d’un chemin, mais l’existence d’un chemin de la bonne couleur. Il faut juste rajouter une petite ligne pour spécifier la couleur du chemin

> simu2=function(d){
+ C=sample(c(rep(1,d^2/2),rep(2,d^2/2)),size=d^2)
+ B=NULL
+ for(i in 1:(d^2)){
+ x=rep(i,6)
+ y=c(i-d,i-d+1,i-1,i+1,i+d-1,i+d)
+ if(i%%d==1) y=c(i-d,i-d+1,NA,i+1,NA,i+d)
+ if(i%%d==0) y=c(i-d,NA,i-1,NA,i+d-1,i+d)
+ D=data.frame(x,y)
+ D=D[(D$y>=1)&(D$y<=d^2),] + B=rbind(B,D[which((C[D$y]==C[D$x])&(C[D$x]==1)),]) + } + B=B[(B[,2]>=1)&(B[,2]<=d^2),] + G=as.vector(t(B)) + G_iter=connect(make_graph(G), d^2) + connectpt=function(x) as.numeric(adjacent_vertices(G_iter,x)[[1]]) + sum(unlist(Vectorize(connectpt)(1:d))%in%(d^2+1-(1:d)))>0}
> appr=function(d,nsimu) mean(Vectorize(simu2)(rep(d,nsimu)))
> appr(4,10000)
[1] 0.4993

et en effet, il y a une chance sur deux de trouver un chemin bleu permettant de connecter les deux régions bleues. Mais comme je le disais hier, j’ai du mal avec l’argument de symétrie évoqué dans la “correction”.

Maudites Abeilles

Pendant les “vacances”, j’étais avec les enfants (le plus grand et la plus petite) au palais de la découverte, à Paris, à l’exposition temporaire “faîtes vos jeux“. Comme toujours, je suis surpris quand je compare les musées de sciences, et les musées de sciences (j’avais fait un billet voilà quelques années en comparant le MoMaths et le Gugenheim, à New York City). Autant ma grande fille est autant intéressée par les tableaux que par les sciences, autant les deux autres préfèrent mille fois les seconds au premier. On a d’ailleurs passé plus de 4 heures au musée (dont une à faire toutes les animations de “faîtes vos jeux“), il a fallu y aller parce que j’avais une réunion de boulot (oui, les parisiens n’étaient pas en vacances, et j’avais une réunion impossible à déplacer). D’où les guillemets à “vacances” (le second jour de notre excursion, c’était une intervention de deux heures en fin d’après midi).

Mais je m’égare… A un des stands de l’animation, il avait le défi des abeilles,

Sur notre “damier” 8×8 (ici), on a 32 cases bleues et 32 rouges (ou oranges), et on cherche la probabilité qu’un chemin permette de passer d’un côté à l’autre. Sur le moment, le problème m’a pas mal pris la tête, et je me disais que la réponse devait être compliquée… en tout cas, que ça n’était pas aussi simple que ça…

En ligne, on peut trouver le guide de l’enseignant qui apporte des compléments aux divers ateliers, et qui propose, en l’occurrence, une solution

La réponse serait-ici 1/2. Mais la réponse ne me satisfait pas, en particulier la phrase “son absence à l’inverse impose un chemin orange” qui suppose qu’il existe forcément un chemin, soit rouge, soit bleu, pour passer d’un côté à l’autre, ce qui ne me semble pas trivial ! Quant

Par exemple sur le damier 4×4 ci-dessous,

il n’existe aucun chemin rouge allant  du bas vers le haut, et aucun chemin bleu non plus ! Et si on regarde, il existe (au moins) un chemin rouge allant de la gauche vers la droite, et un chemin bleu. Bref, la réponse 1/2 me semble douteuse….  Pour ceux qui veulent de vrai hasard (mais qui a autant de chance d’apparaître que celui au dessus), on a le graph ci-dessous

Mais maintenant essayons de la calculer cette probabilité. Par simulations au moins. Regardons un damier,

on peut le visualiser sous forme d’une matrice

ou en décalant un peu d’une matrice carrée 4×4 (ici)

Si on regarde ce que sont les voisins (afin de faire un chemin), sur le schéma initial, on cherchait les configuration suivantes

ce qui, dans notre matrice, correspond aux éléments ci-dessous,

Bref, on va pouvoir connecter des points d’un graph, et regarder si les 4 premiers éléments du graphs sont connectés aux 4 derniers

(le but est désormais de savoir si le nord et le sud connectent, peu importe la couleur). On peut commencer par créer le graph

d=4
set.seed(1)
C=sample(c(rep(1,d^2/2),rep(2,d^2/2)),size=d^2)
M=matrix(C,d,d)
> M
[,1] [,2] [,3] [,4]
[1,] 1 1 2 2
[2,] 1 2 1 1
[3,] 2 2 1 1
[4,] 2 2 1 2

ce qui correspond bien à notre matrice ci-dessous

On va alors créer le graph, c’est à dire chercher les noeuds qui communiquent

library(igraph)
B=NULL
for(i in 1:(d^2)){
x=rep(i,6)
y=c(i-d,i-d+1,i-1,i+1,i+d-1,i+d)
if(i%%d==1) y=c(i-d,i-d+1,NA,i+1,NA,i+d)
if(i%%d==0) y=c(i-d,NA,i-1,NA,i+d-1,i+d)
D=data.frame(x,y)
D=D[(D$y>=1)&(D$y<=d^2),] B=rbind(B,D[which(C[D$y]==C[D$x]),]) } B=B[(B[,2]>=1)&(B[,2]<=d^2),]
G=as.vector(t(B))
m=make_graph(G)

On peut ainsi voir ici que 1 communique avec 2, et 5

> B[B$x==1,]
x y
4 1 2
6 1 5

On peut d’ailleurs visualiser le graph, et voir qu’il est constitué de 4 graphs disjoints,

> plot(m)

(le coin 16 étant isolé, sans connection avec le reste – étrangement – il n’apparait pas sur le graph)

Pour trouver tous les points qui communiquent entre eux, on peut itérer un peu (voire beaucoup, peu importe)

G_iter=connect(make_graph(G), d^2)
connectpt=function(x) as.numeric(adjacent_vertices(G_iter,x)[[1]])

Ici, le nord et le sud communiquent s’il y a au moins un chemin (peu importe la longueur)

sum(unlist(Vectorize(connectpt)(1:d))%in%(d^2+1-(1:d)))>0

On peut alors une petite fonction, qui va générer une répartition des couleurs sur un damier carré (de longueur paire)

simu=function(d){
C=sample(c(rep(1,d^2/2),rep(2,d^2/2)),size=d^2)
B=NULL
for(i in 1:(d^2)){
x=rep(i,6)
y=c(i-d,i-d+1,i-1,i+1,i+d-1,i+d)
if(i%%d==1) y=c(i-d,i-d+1,NA,i+1,NA,i+d)
if(i%%d==0) y=c(i-d,NA,i-1,NA,i+d-1,i+d)
D=data.frame(x,y)
D=D[(D$y>=1)&(D$y<=d^2),] B=rbind(B,D[which(C[D$y]==C[D$x]),]) } B=B[(B[,2]>=1)&(B[,2]<=d^2),] G=as.vector(t(B)) G_iter=connect(make_graph(G), d^2) connectpt=function(x) as.numeric(adjacent_vertices(G_iter,x)[[1]]) sum(unlist(Vectorize(connectpt)(1:d))%in%(d^2+1-(1:d)))>0}

On peut alors construire la fonction suivante, pour faire un peu de monte carlo

appr=function(d,nsimu) mean(Vectorize(simu)(rep(d,nsimu)))

Par exemple sur notre damier 4×4, on a environ 3 chances sur 4 d’avoir un chemin (peu importe sa couleur) qui permette de rejoindre le sud depuis le nord

> appr(4,10000)
[1] 0.7446

alors que sur le 8×8 du palais de la découverte, on a

> appr(8,10000)
[1] 0.7805

78% de chances (un peu plus). Ce qui semble confirmer qu’il n’y a pas forcément un chemin permettant d’aller du nord au sud.

On peut d’ailleurs faire un petit dessin pour noter que quand la dimension du damier augmente (je n’ai pas tenté ici le cas impair – avec une case de plus d’une certaine couleur), la probabilité de trouver au moins un chemin semblerait augmenter (même si ça reste à prouver, et en particulier, je serais curieux de voire vers quoi ça converge…)

 

Visualiser les résultats du premier tour

Juste quelques lignes de code, pour visualiser les résultats du premier tour en France. L’idée est de faire une carte assez minimaliste, avec des cercles centrés sur les centroïdes des départements. On commence par récupérer les données pour le fond de carte, un fichier 7z sur le site de l’ign.

download.file("https://wxs-telechargement.ign.fr/oikr5jryiph0iwhw36053ptm/telechargement/inspire/GEOFLA_THEME-DEPARTEMENTS_2016$GEOFLA_2-2_DEPARTEMENT_SHP_LAMB93_FXX_2016-06-28/file/GEOFLA_2-2_DEPARTEMENT_SHP_LAMB93_FXX_2016-06-28.7z",destfile = "dpt.7z")

On a dans ce fichier des informations sur les centroïdes

library(maptools)
library(maps)
departements<-readShapeSpatial("DEPARTEMENT.SHP")
plot(departements)
points(departements@data$X_CENTROID,departements@data$Y_CENTROID,pch=19,col="red")

Comme ça ne marche pas très bien, on va le refaire à la main, par exemple pour l’Ille-et-Vilaine,

pos=which(departements@data[,"CODE_DEPT"]==35)
Poly_35=departements[pos,]
plot(departements)
plot(Poly_35,col="yellow",add=TRUE)
departements@data[pos,c("X_CENTROID","Y_CENTROID")]
points(departements@data[pos,c("X_CENTROID","Y_CENTROID")],pch=19,col="red")
library(rgeos)
(ctd=gCentroid(Poly_35,byid=TRUE))
points(ctd,pch=19,col="blue")

Comme ça marche mieux, on va utiliser ces centroïdes.

ctd=as.data.frame(gCentroid(departements,byid=TRUE))
plot(departements)
points(ctd,pch=19,col="blue")

Maintenant, il nous faut les résultats des élections, par département. On peut aller scraper le site du ministère d’ intérieur. On a une page par département, donc c’est facile de parcourir. Par contre, dans l’adresse url, il faut le code région. Comme je suis un peu fainéant, au lieu de faire une table de correspondance, on teste tout, jusqu’à ce que ça marche. L’idée est d’aller cherche le nombre de voix obtenues par un des candidats.

candidat="M. Emmanuel MACRON"
library(XML)
voix=function(no=35){
testurl=FALSE
i=1
vect_reg=c("084","027","053","024","094","044","032","028","075","076","052",
"093","011","001","002","003","004")
region=NA
while((testurl==FALSE)&(i<=20)){
reg=vect_reg[i]
nodpt=paste("0",no,sep="")
# if(!is.na(as.numeric(no))){if(as.numeric(no)<10) nodpt=paste("00",no,sep="")}
url=paste("http://elections.interieur.gouv.fr/presidentielle-2017/",reg,"/",nodpt,"/index.html",sep="")
test=try(htmlParse(url),silent =TRUE)
if(!inherits(test, "try-error")){testurl=TRUE
region=reg}
i=i+1
}
tabs tab=tabs[[2]]
nb=tab[tab[,1]==candidat,"Voix"]
a<-unlist(strsplit(as.character(nb)," "))
as.numeric(paste(a,collapse=""))}

On peut alors tester

> voix(35)
[1] 84648

Comme ça semble marcher, on le fait pour tous les départements

liste_dpt=departements@data$CODE_DEPT
nbvoix=Vectorize(voix)(liste_dpt)

On peut alors visualiser sur une carte.

plot(departements,border=NA)
points(ctd,pch=19,col=rgb(1,0,0,.25),cex=nbvoix/50000)

Et on peut aussi tenter pour une autre candidate,

candidat="Mme Marine LE PEN"

et on obtient la carte suivante

plot(departements,border=NA)
points(ctd,pch=19,col=rgb(0,0,1,.25),cex=nbvoix/50000)

CartoStats, la Cartographie Statistique

Début juin, une jolie conférence sera organisée à Paris-Diderot, intitulée CartoStats. Au programme:

1. Nouvelles compositions graphiques
– Gilles Palsky (Université Paris 1)
– Karine Hurel (CGET)
– Philippe Rekacewicz (Visions Carto)
2. Nouvelles méthodes, nouveaux outils
– Hervé Paris (Alter Carto)
– Claire Cunty (Université de Lyon 2)
– Boris Mericksay (Université de Rennes 2)
– Timothée Giraud et Nicolas Lambert (CNRS )
3. Nouveaux usages
– Myriam Baron (Université Paris-Est Créteil)
– Arthur Charpentier (Université de Rennes 1)
– Etienne Côme (IFSTTAR)

 

Visualizing (censored) lifetime distributions

There are now more than 10,000 R packages available from CRAN, much more if you include those available only on github. So, to be honest, it become difficult to know all of them. But sometimes, you discover a nice function in one of them, and that is really awesome. Consider for instance some (standard) censored lifetime data,

n=10000
idx=sample(1:4,size=n,replace=TRUE)
pd=LETTERS[idx]
lambda=1+(idx-1)/3
t=rexp(n,lambda)
x=rexp(n)
c=t>x
y=pmin(t,x)
df=data.frame(time=y,status=c,product=pd)

(yes, I will generate them here). Consider Kaplan-Meier estimator of the survival function,

library(survival)
km.base = survfit( Surv(time,status) ~ 1  , data = df )
plot(km.base)

This week end, Anat (currently finishing the Data Science for Actuaries program) made me discover a nice R function, to add information to that graph (well, not that graph, since it will be a ggplot version, but the same survival distribution plot)

library(ggplot2)
library(survminer)
ggsurvplot(km.base, main = "", color = "blue" , censor = FALSE, xlim = c(0,3), risk.table = TRUE ,
risk.table.col = "blue" , risk.table.height = 0.2, risk.table.title = "" , legend.labs = "All" , legend.title = "" , break.time.by = 1, xlab = "" , ylab = "")

This is more interesting when we have different lifetimes

km.prod = survfit( Surv(time,status) ~ product  , data = df )
ggsurvplot(km.prod, main = "", censor = FALSE, xlim = c(0,3), risk.table = TRUE , risk.table.col = "strata" , risk.table.height = 0.3, risk.table.title = "" , legend.labs = LETTERS[1:4] , legend.title = "" , break.time.by = 1, xlab = "" , ylab = "")

or, with a different time granularity

ggsurvplot(km.prod, main = "", censor = FALSE, xlim = c(0,3), risk.table = TRUE , risk.table.col = "strata" , risk.table.height = 0.3, risk.table.title = "" , legend.labs = LETTERS[1:4] , legend.title = "" , break.time.by = .5, xlab = "" , ylab = "")

Nice, isn’t it?

Sondages, intentions de vote et “vote utile”

Il y a plusieurs semaines, Ashley Kirk et Patrick Scott publiaient French presidential election: How the polls are shaping up in the race to become president. Force est de constater qu’ils avaient raison: les sondages prennent une place essentielle dans cette période électorale.

Avant, une précaution d’usage s’impose, je pense. Les instituts de sondages le rappellent sans cesse, et ils ont raison : leur métier n’est pas véritablement de faire des sondages d’intention de vote. L’essentiel de leurs revenus vient des études de marché,
et d’opinion, généralement pour le compte d’entreprises privées. Néanmoins, les élections sont une fabuleuse vitrine publicitaire. On peut alors imaginer que l’institut qui aura “prédit” le scrutin avec la plus grande précision devrait s’assurer une rente pour les mois – voire les années – à venir. Cette vitrine publicitaire serait importante car elle empêcherait aux instituts de prendre parti : ils ont intérêt à prédire aussi précisément que possible, probablement plus que chercher à influencer les votes. Mais est-ce aussi simple ?

Depuis une quizaine d’années, on voit régulièrement ressurgir le concept de « vote utile ». Ce concept est particulièrement important en France, pour les élections présidentielles, compte tenu du système de vote : un vote en deux tours, avec seulement les deux meilleurs au second tour. Qui dit “meilleur” dit positionnement relatif, et donc importance du vote des autres. Quelque part, mon vote est important, mais celui des autres l’est tout autant (sinon plus). Pour voter de manière rationnelle, il faut savoir comment les autres vont voter. Et comme souvent dans un jeu statégique – et les élections en sont – l’information joue un rôle essentiel.

Lorsqu’un élécteur ne sait pas quel sera le vote des autres, il aura – a priori – intérêt à voter pour son candidat préféré, Mais si ce même électeur a des croyances quant au vote des autres, il peut avoir intérêt à modifier stratégiquement son choix, en votant pour son second, voire son troisième choix : on parlera alors de « vote utile ». Sans information sur les choix des autres (ou leurs stratégie), il ne peut pas y avoir de comportement stratégique. Et force est de constater que les “sondages” (terme générique pour désigner les estimations d’intension de vote) sont devenus des instruments essentiels pour déterminer justement quel sera le choix des autres électeurs.

Les sondages sont devenus l’élément central du vote stratégique, surtout s’ils sont présenté comme fournissant une information précise, voire “statistiquement rigoureuse” des intentions de vote. Les sondages deviennent alors auto-réalisateurs s’ils poussent à voter pour des candidats qui affichent le plus de chances de gagner, ce qu’on appelle communément le « vote utile ». Mais on peut aussi imaginer des stratégies inverses : si mon candidat préféré a 40% d’intention de votes, dans les derniers sondages, a-t-il vraiment besoin de ma voix ? Ne pourrais-je pas éviter de perdre du temps en allant voter ?

Bref, j’ai encore du mal à comprendre le rôle exact des sondages dans les élections, mais ils sont loin d’être neutres. Et j’ai l’impression que comprendre ce mécanisme est plus important pour quantifier les “marges d’erreurs” que toutes les méthodes statistiques que l’on peut imaginer (dont je parlais dans un précédent billet). Et les sondages en période électorale semblent relever davantage de la théorie des jeux que des problèmes d’inférence statistique.

C’est normal ! (partie 1) et si la normalité n’existait pas?

Il y a quelques semaines, je discutais avec une collègue juriste (alors que j’essayais de glaner des statistiques juridiques), et alors que l’on parlait alors de la lenteur de l’instruction, ou de l’inversion de la charge de la preuve (je ne me souviens plus) j’ai été supris qu’elle me dise que “c’est normal“. Je sais qu’un juriste et un statisticien ne donnent pas forcément le même sens aux mots, et cette phrase m’a dérangé parce qu’une situation qui pourrait sembler “normale” (car observée régulièrement) n’est pas pour autant “juste” (c’est le paradoxe is/ought de Hume, mais j’en parlerais dans un autre billet).

Le point de départ est de comprendre ce qu’est la normalité “empirique”, telle qu’observée dans une population. Ce qui pourrait être “normal” pour un statisticien. Pour commencer, je pensais revenir sur un exemple raconté dans The End of Average de Todd Rose, qui essaye de montrer, exemples à l’appui, que l’homme moyen n’existe pas.

  • L’homme moyen de Quételet

Au XIXème siècle, si plusieurs astronomes mesuraient la vitesse d’un même objet céleste, ils obtenaient (souvent) plusieurs mesures différentes. Pour savoir “laquelle” utiliser dans leurs calculs, l’idée d’utiliser “la méthode des moyennes” s’est rapidement imposée – comme le rappelle Stahl (2006), et surtout Sheynin (1973) – cette “moyenne” ayant une précision plus grande que n’importe quelle autre grandeur (ou dirait aujourd’hui “statistique”).

Adolphe Quetelet fut, semble-t-il, le premier à appliquer ce calcul de moyennes à des mesures humaines, introduisant son fameux concept d'”homme moyen”. Comme j’en avais parlé dans un précédant billet, la moyenne est une grande particulière, dont le sens n’est pas forcément clair. Si on définit la moyenne à l’aide d’une minimisation d’erreur quadratique, on a une interprétation en terme de prévision (on retrouve ici la notion de mesure elicitable dont je parlais dans mon dernier cours): la taille moyenne est la taille de “devrait” mesurer une personne tirée au hasard (à une variation aléatoire – et imprévisible – près). En 1846 dans une lettre (publiée dans l’ouvrage Lettres sur la théorie des probabilités, appliquée aux sciences morales) Adolphe Quételet utilise l’image de la statue du gladiateur pour expliquer ce que peut être l’homme moyen.

  • L’interprétation de Francis Galton

Ce homme moyen a beaucoup plu à l’époque, en particulier au sein de l’école anglaise eugéniste, dirigée à l’époque par Francis Galton, même si ce dernier s’intéresse surtout aux déviations par rapport à cette norme (déviation vers le haut et déviation vers le bas). Comme le rappelle Bulmer (2004),the deviations from that average—upwards towards genius, and downwards towards stupidity—must follow the law that governs deviations from all true averages“. Les travaux de Galton ont visé à comprendre ces déviations. Si Florence Nightingale affirmait que “the Average Man is God’s Will“, Galton de son côté s’intéressait davantage au caractère héréditaire. Mais cet “homme moyen” a-t-il pour autant du sens ?

  • L’être humain moyen n’existe pas

Une anecdote intéressante est celle de deux statues, à Cleveland, celles de Norma et Normann. L’artiste Abram Belskie et l’obstetricien Robert Latou Dickinson ont réalisé ensemble ces statues, en 1943. La particularité est qu’aucun modèle n’a été représenté. En fait, il s’agissait de représenter une femme et un homme qui avaient les mensurations moyennes de l’époque.

Une fois ces statues réalisées, un concours a été organisé pour trouver qui ces statues pouvaient bien représenter. Plusieurs milliers de personnes de l’Ohio ont envoyé leurs mensurations, mais aucun ne correspondaient à celles des statues. Certes, plusieurs centaines avaient la même taille. Plusieurs centaines avaient le même tour de poitrine. Mais aucune n’avait toutes les bonnes mesures. Car comme l’explique Todd Rose, l’homme n’est pas unidimensionnel: c’est sur plusieurs dimensions qu’on le mesure.

Et chercher à le résumer en une grandeur unidimensionnel est beaucoup trop réducteur. C’est ce qu’il montre dans son livre sur les tests d’intelligence, par exemple, où un même QI peut être associé à deux personnes très différentes.

Pareil pour décider de recruter quelqu’un, se focaliser sur un seul indicateur n’a pas de sens. Le soucis quand on travaille dans un contexte multivarié, c’est que la moyenne perd de son sens. Pour reprendre le titre d’un billet mis en ligne voilà 6 mois, être moyen peut être extraordinaire.

  • La malédiction de la dimension (en grande dimension, l’espace est très vide…)

En fait, ce problème est bien connu par les statisticiens, sous le nom de “fléau de la dimension“. Prenons un exemple simple: supposons qu’une grandeur d’intérêt suive une loi normale https://latex.codecogs.com/gif.latex?\mathcal{N}(\mu,\sigma^2), par exemple le poids, la taille, le tour de poitrine, etc. On pourrait dire que la norme, c’est se trouver dans un intervalle https://latex.codecogs.com/gif.latex?[\mu\pm%201.5%20\cdot\sigma]. Si on a une loi normale, cette situation survient dans 85% des cas,

> (1-2*pnorm(-1.5))
[1] 0.8663856

Et 15% seront vues comme “anormales”. Elles peuvent être anormalement petites, ou anormalement grandes. C’est le dessin ci-dessous: on regarde ici seulement une dimension

On peut maintenant regarder deux dimensions, le poids et la taille, par exemple. La norme serait ici que dans les deux dimensions, on soit dans l’intervalle https://latex.codecogs.com/gif.latex?[\mu\pm%201.5%20\cdot\sigma]. Si les grandeurs sont indépendantes, la probabilité que les deux grandeurs soient “normale” est de 75%

> (1-2*pnorm(-1.5))^2
[1] 0.750624

En dimension deux, 75% des observations sont “normales”, et 25% sont “anormales”

En dimension 3, on passe à 65%

> (1-2*pnorm(-1.5))^3
[1] 0.6503298

pour 35% d’observations “anormales” (plus du tiers)

Etc. En dimension cinq, on passe en dessous de 50%

> (1-2*pnorm(-1.5))^5
[1] 0.4881532

autrement dit, être dans la norme dans les 5 dimensions, ce n’est plus le cas de la majorité. Et en dimension vingt, ceux qui sont “normaux” sont plutôt atypiques, avec une proportion de l’ordre de 5%,

> (1-2*pnorm(-1.5))^20
[1] 0.0567838

Bref, la normalité est un concept particulièrement étrange sur le plan empirique, car intuitivement associé à l’idée d’une majorité. Alors que ce n’est pas le cas, la normalité étant justement atypique.

Supprimer des données ?

Il y a une dizaine de jours, je participais à la conférence Sciences XXL, à l’INED, où nous parlions des utilisations des données massives en Sciences Sociales, et au même moment, j’ai découvert – en discutant avec plusieurs assureurs – qu’ils envisageaient de supprimer des données, de manière également massive. Quand on veut faire de l’économétrie de la finance, on peut utiliser des données de cours boursiers, qu’on peut trouver en ligne, ou – pour une granularité plus faible – acheter à des fournisseurs de données. Mais quand on fait de l’économétrie de l’assurance, avoir des données est plus compliqué. Dans mon tout premier cours d’assurance dommage, j’enseignais la théorie, faute de données. Depuis, j’ai réussi à récupérer des données. On a mis ces données dans un package R (casdatasets), et mieux: avec des jeux de données (prêtées par des assureurs) on a pu lancer notre troisième pricing game, qui devrait nous permettre de mieux comprendre la dynamique des marchés d’assurance.

Bref, j’ai été particulièrement surpris d’apprendre que des assureurs envisageaient de détruire des données (individuelles). J’avais entendu de tels propos lors qu’avec Arnaud on commencé à travaillé sur le risque de catastrophes naturelles, et qu’on essayait de mieux comprendre l’impact des tempêtes Lothar et Martin, en décembre 2009. En particulier, au commencement de nos recherches, nous voulions avoir un ordre de grandeur du nombre de polices d’assurance (multirisques habitation) dans un portefeuille, ou plutôt le taux de polices touchées, si possible avec une granularité spatiale assez faible. Des assureurs ont pu nous aider à avoir des ordres de grandeur, par département

mais d’autres nous ont dit qu’il serait difficile de retrouver les données, qui remontent à près de 20 ans. Voire impossible. Et c’est là qu’on a commencé à entendre parler de suppression de données personnelles. Et si les données n’ont pas été agrégées au niveau que l’on souhaite, c’est impossible de faire la requête, 20 ans après.

Continue reading Supprimer des données ?

Toi aussi, raconte ce que tu veux avec des graphiques

L’autre jour, les décodeurs publiaient sur le site du Monde un article sur l’utilisation des graphiques en base 100,

Je reprends le commentaire

Celui-ci indique, sans appel selon Mme Le Pen, le déclin des économies françaises, italiennes et espagnoles face à l’industrie allemande. Marine Le Pen n’hésite ainsi pas à affirmer que le déclin de la production française est lié à l’introduction de la monnaie unique en 2001.

On peut reconstruire le graphique assez facilement, les données étant en ligne sur https://data.oecd.org/

base=read.table(loc,sep=",",header=TRUE)
extract=function(nom="FRA"){
b=base[base[,1]==nom,c("TIME","Value")]
names(b)=c("TIME",nom)
return(b)
}
B=extract("FRA")
B=merge(B,extract("DEU"),all.x=TRUE,all.y=TRUE)
B=merge(B,extract("ESP"),all.x=TRUE,all.y=TRUE)
B=merge(B,extract("ITA"),all.x=TRUE,all.y=TRUE)
dim(B)
B$TIME
B$t=seq(1950+1/8,2016+7/8,by=1/4)
plot(B$t,B$FRA,type="l",col="blue",lwd=2)
i=which(B$T=="2001-Q1")
Bi=B
for(j in 2:5) Bi[,j]=Bi[,j]*100/Bi[i,j]
plot(B$t,Bi$FRA,type="l",col="blue",lwd=2,ylim=range(Bi[which(B$T=="1990-Q1"):which(B$T=="2016-Q4"),2:5],na.rm=TRUE),xlim=c(1990,2020))
text(2019,Bi$FRA[max(which(is.na(Bi$FRA)==FALSE))],"FRA",col="blue")
lines(B$t,Bi$DEU,lwd=2)
text(2019,Bi$DEU[max(which(is.na(Bi$DEU)==FALSE))],"DEU")
lines(B$t,Bi$ESP,lwd=2,col="red")
text(2019,Bi$ESP[max(which(is.na(Bi$ESP)==FALSE))],"ESP",col="red")
lines(B$t,Bi$ITA,lwd=2,col="green")
text(2019,Bi$ITA[max(which(is.na(Bi$ITA)==FALSE))],"ITA",col="green")

Maintenant, on peut aussi changer la référence de la base 100. Par exemple, la victoire de la France à la Coupe du Monde Foot

i=which(B$T=="1998-Q2")

Damned,

Celui-ci indique, le déclin des économies françaises, italiennes et espagnoles face à l’industrie allemande. Ce déclin de la production française est lié à la victoire de la France à la Coupe du Monde de Football.

ou la dernière victoire de la France à l’Eurovision

i=which(B$T=="1977-Q3")

Damned,

Celui-ci indique, le déclin des économies françaises et espagnoles face à l’industrie allemande. Ce déclin de la production française est lié à la victoire de la France à l’Eurovision.

ou encore, la naissance de Mme Le Pen

i=which(B$T=="1968-Q3")

Damned,

Celui-ci indique, le déclin de l’économie française face aux industries allemande et espagnole. Ce déclin de la production française est lié à la naissance de Mme Le Pen.

Le Master Statistique & Econométrie est mort

Après 10 ans, le master Statistique & Econométrie de l’Université de Rennes 1 va disparaître…. pour renaître de ses cendres sous le nom Mathématiques Appliquées, Statistique, cohabilité par l’Université de Rennes 1, Rennes 2, Agrocampus Ouest, l’ENSAI et l’INSA Rennes, à la rentrée. En particulier, il y aura un parcours “Prévision et Prédiction Economiques“. Il y aura beaucoup de nouveaux cours, mais j’aurais l’occasion d’en reparler…

The U.S. Has Been At War 222 Out of 239 Years

This morning, I discovered an interesting statistic, America Has Been At War 93% of the Time – 222 Out of 239 Years – Since 1776,  i.e. the U.S. has only been at peace for less than 20 years total since its birth. I wanted to check, get a better understanding and look at other countries in the world.

As always, we can try to extract information from wikipedia, since there are pages dedicated to that information

url="https://en.wikipedia.org/wiki/List_of_wars_involving_the_United_States"
download.file(url,destfile = "warUS.html")
url="https://en.wikipedia.org/wiki/List_of_wars_involving_France"
download.file(url,destfile = "warFR.html")
url="https://fr.wikipedia.org/wiki/Liste_des_guerres_de_la_France#Premi.C3.A8re_R.C3.A9publique"
download.file(url,destfile = "guerre.html")
url="https://en.wikipedia.org/wiki/List_of_wars_involving_Canada"
download.file(url,destfile = "warCAN.html")

If we look at the US page, there are tables, so it should be easy to extract it. For instance,

Even if the war did last 1 day, we will say that the US were at war in 1811. The information we want to confirm can be “there were 21 full years – from Jan 1st till Dec 31st – where the US were not at war, once, during those years“. From the row above, we can claim that the US were at war in 1811. Most of the time, we have

I.e. there is a beginning (here 1775) and an end (1783). So here, the US are said to be at war in 1775, 1776, 1777, 1778, 1779, 1780, 1781, 1782, 1783. To extract the information, we look for regular expressions in the first column, with number, on 4 digits.

https://freakonometrics.hypotheses.org/files/2017/03/guerre-us1.png

Well, sometimes it can be a bit tricky, since we have 3 dates, 1941, 1945 and (in the legend) 1944. But if we consider the minimal and the maximal dates, we have our range of dates.

Now that we we how to extract information, let’s do it. The code will be

library(stringr)
ext_date=function(x){
dates12="[0-9]{4}"
#grep(pattern = dates2, x = col1[1])
L=str_extract_all(as.character(x),dates12)
return_L=list()
if(length(L)>0){
for(j in 1:length(L))
if(length(L[[j]])==1) return_L[[j]]=as.numeric(L[[j]])
if(length(L[[j]])>=2) return_L[[j]]=seq(min(as.numeric(L[[j]])),max((as.numeric(L[[j]]))))
}
return(return_L)}

For the US, we get the following years

library(XML)
tables=readHTMLTable("warUS.html")
list_dates=list()
for(i in 1:length(tables)){
if(!is.null(dim(tables[[i]]))){
if(ncol(tables[[i]])>1){
col1=tables[[i]][,1]
list_dates[[i]]=lapply(col1,ext_date)
}
}}
d=unique(unlist(list_dates))

(red means at war, while green means no-war) and indeed,

> length(d)
[1] 222

there were 222 years with war.  Now, what about another country. Like France. Here I use the French wiki page, since information is not in tables in the English one.

tables=readHTMLTable("guerre.html")
list_dates=list()
for(i in 1:length(tables)){
if(!is.null(dim(tables[[i]]))){
if(ncol(tables[[i]])>1){
col1=tables[[i]][,1]
col2=tables[[i]][,2]
col12=paste(col1,col2)
list_dates[[i]]=lapply(col12,ext_date)
}
}}
d=unique(unlist(list_dates))

On the same period of time (starting in 1775), France was also on war most of the time.

Less than the US, but still: 185 years with war,

> length(d[d>=1775])
[1] 185

And on a longer period of time? Why not start, say, around the Hundred Years’s War,

meaning that since 1337, there were (only) 174 years without a single war where France was involved.

Let’s try another one. Like Canada,

tables=readHTMLTable("warCAN.html")
list_dates=list()
for(i in 1:length(tables)){
if(!is.null(dim(tables[[i]]))){
if(ncol(tables[[i]])>1){
col1=tables[[i]][,1]
list_dates[[i]]=lapply(col1,ext_date)
}
}}
d=unique(unlist(list_dates))

Guess what… there’s a lot of green on that graph. Surprised?

Data XXL et Démographie Participative

Petite virée parisienne pour trois jours. Je serais à Science Po mercredi pour travailler un peu, et à l’INED jeudi et vendredi pour la conférence Sciences XXL – Ce que l’abondance et la diversité des données numériques font aux sciences sociales. Jeudi, je serais discutant pour la session “outils”, et vendredi après-midi, nous allons clôture la conférence avec un rapide retour sur un travail joint avec Olivier et Ewen, sur ‘Les données collaboratives généalogiques au service de la démographie historique’. Le travail est très vaste, et nous présenterons quelques résultats de réconciliation entre les tables de mortalité historique de l’INED, et celle que nous avons reconstituées sur des données démographiques…. Par exemple avec les naissances de 1850

et celles de 1899

La suite…. vendredi (ou plus tard, on n’a pas fini de jouer avec ces données).

 

Reading text automatically

It is now very easy to read (automatically) some text that can be found in a pdf file. For instance, consider the program of the conference we had yesterday – and today – in Rennes

> library(pdftools)
> scan_pdf <- pdf_text("http://crem.univ-rennes1.fr/Documents/Docs_sem_divers/2017_03_10-11_JJD/JDD_prog.pdf")
> cat(scan_pdf)
Journées Jeunes Docteurs
Programme du jeudi 9 mars 2017
Faculty of Economics - Rennes - Amphi Henri Krier
9h- 9h30 - Accueil
9h30-10h15 :      Présentation du CREM, de la faculté et des activités de recherche liées du ou laboratoire
10h15-10h50 :     Emmanuel LORENZON (Université de Bordeaux, GREThA)
Collusion with a rent seeking agency in sponsored search auctions
10h50-11h25 :     Julien BERTHOUMIEU (Université de Bordeaux, GREThA)
The Impact of “At-the-Border” and “Behind-the-Border” Policies on Cost-Reducing Research
and Development
Co-écrit avec Antoine Bouët

(etc). As you can see, it is working well, even in French, where we have those weird letters (with accents). Here, it is working well because the pdf is vectorized, i.e. it was generated properly, by open office.

But sometimes, we can have only a scanned version of a letter

or just a picture with some typed text. I will not mention hand-writing because it is much more complex.

The other day, my friend Fleur did show me a picture, and some very simple lines of code,

> library('tesseract')
> pic1="https://f.hypotheses.org/wp-content/blogs.dir/253/files/2017/03/pic1.png"
> text_fr <- ocr(pic1, engine = tesseract("fra"))
> cat(text_fr)
Près de 14.400 décès

Si [épidémie de grippe est un phénomène récurrent. celle de
2016—2017 présente plusieurs spécificités. outre sa virulence :
une survenue plus précoce que d‘habitude. une activité
modérée en médecine ambulatoire. mais un impact fort en
milieu hospitalier.

It looks like we’ve be able to extract typed text from a picture ! I want to check. I have to admit, first of all that installation on a linux machine is tricky: one has to install first leptonica, and then follow some guidelines to install tesseract (see also Artem‘s advices). It took me some time, but I’ve been able to install the package.

The first important step, it to train the algorithm with some texts in French (because it is in French in my picture)

> library('tesseract')
> tesseract_download("fra")

Then, I did try with the picture that Fleur did send me (the picture was inserted in the core of the message)

> pic2="https://f.hypotheses.org/wp-content/blogs.dir/253/files/2017/03/pic2.png"
> text_fr <- ocr(pic2, engine = tesseract("fra"))
> cat(text_fr)
Près de 14400 decès

s. mm….agw«… ………«…m ……
a……u…u Dhs—ur; ;pmum…. ;: u……
… W»: »… w…q…na… … ……
…… ………u…_ …… mm……
…… nwm/u

… … mm…—mg…»— sa…… su a.…….…
: :mmræwesdæ ; ; m…decnflwtülflws WWW…
un»… M on m…… . … … .. m...… wma:
.,… … V, …… … …;………yg…gn…
…… pe- le…samemeuuœwpwv m…
mum

Clearly, something went wrong here. When I got that output, I thought that I did not train properly the function. But it was not the answer. As described in that post (in French) it is necessary to have a clean picture, to read it properly

And actually, if we zoom in our picture – the first one, used by Fleur, to show me that package – we have

while for the second one – with a lower resolution – we have

It is necessary to have a scan of a typed text with high resolution… And you have to admit that it is awesome….

The good thing is that I have to work with a judge, in France, to assess quality of experts. And since most of the reports are typed, and then scanned, I am glad to have such a function. I just have to make sure that the resolution is high enough…

An Open Lab-Notebook Experiment