Quiz électoraux 2022

Étude initiale: quiz du Figaro

C’est initialement le quiz du Figaro qui m’a inspiré cette étude. Mais il était tellement biaisé qu’une approche quantitative n’aurait pas valu la peine.

Je me suis contenté d’en faire un Tweet qui constitue une bon introduction au problème posé.

Étude qualitative des items: quiz du Monde

Avant de commencer à coder mais en sachant déjà quelles réponses étaient associées à quels candidats, j’ai analysé attentivement les différents items et j’ai rapporté un certain nombre d’anomalie dans cet autre thread Twitter.

Code

Tous les scripts et toutes les données nécessaires pour reproduire cette analyse sont sur disponibles sur Github.

A noter: dans le dossier “sources” du dossier Github se trouvent également les quiz du Figaro, du Télégramme et du Parisien au format brut, extraits le 2/04, pour ceux que ça intéresserait de généraliser l’approche ci-dessous. Vous pouvez aussi télécharger le quiz du Monde dans un format Excel ici.

Malheureusement, j’ai analysé ces données avec Matlab (non open-source). Promis, tous les prochains projets utiliseront Python 😇.

Le script lemonde_analyse.m extrait les informations du quiz et simule un grand nombre de réponses aléatoires pour évaluer l’équilibre du test. Un test non-biaisé devrait aboutir à une répartition homogène des probabilités d’être classé premier d’un candidat à l’autre. Cette analyse peut se faire sur la base des 20 premières questions ou du quiz entier, en corrigeant ou non pour le nombre d’affectations possibles par candidats. Le script lemonde_plot.m crée les figures. Les barres d’erreur indiquent les intervalles de confiance à 95%.

Le but de ces analyses est de montrer qu’aucune version du quiz n’est équilibrée. Il y a deux facteurs en jeu. Premièrement, tous les candidats n’ont pas le même nombre d’affectations possibles, ce qui diminue mécaniquement le nombre de points qu’ils peuvent se voir attribuer. Deuxièmement, même lorsque l’on corrige pour le nombre d’affectations possibles par candidat (de manière à ce que chaque candidat reçoive en moyenne exactement le même nombre de points), le jeu complexe des co-affectations fait que certains candidats sont avantagés par rapport à d’autres.

Prétraitement des données

Version du quiz utilisée: https://www.lemonde.fr/les-decodeurs/article/2022/03/31/quiz-sur-la-presidentielle-2022-de-quel-candidat-etes-vous-le-plus-proche_6119971_4355770.html

Affectations par candidat

clear all

% Le quiz a été importé a partir de cette addresse sous la forme d'un
% fichier JSON
% https://assets-decodeurs.lemonde.fr/sheets/XusEdnYa3E1Jjl5VdrKGA2z9bH1uwA_887
% Ce fichier JSON a ensuite été converti au format excel/csv en utilisant
% cet outil: https://data.page/json/csv
% Puis il a été importé (à partir du fichier excel) dans matlab pour
% produire ce fichier:
quiz=importdata('sources/quizlemonde.mat')

% spécifications de l'analyse
nom_analyse='analyses';

nquestions=20;
nsimulations=10000;
correction_nquestion=true;
candidats={'Arthaud', 'Poutou','Roussel', 'Mélenchon', 'Jadot', 'Hidalgo', 'Macron', 'Pécresse', 'Lassalle', 'Dupont-Aignan', 'Le Pen', 'Zemmour'};

% prétraitement du quiz pour obtenir les affectations par candidat et par
% question.
noptions=nan(nquestions,1);
for i=1:nquestions
    for a=1:6
        if (iscell(quiz{i+1,9+a})==0 | isempty(cell2mat(quiz{i+1,9+a}))) & isnan(noptions(i,1))
            noptions(i,1)=a-1;
        end
        for c=1:length(candidats)
            exist_str=strfind(quiz{i+1,9+a},candidats{c});
            try
                if isempty(exist_str{1})==0
                    mat_affect(c,i)=a;
                end
            end
        end
    end
end

Calculs complémentaires

% calcule la similarité entre candidats (réception de points pour les mêmes réponses)
for c=1:length(candidats)
    for cc=1:length(candidats)
        coaffectation(c,cc)=(sum(mat_affect(c,mat_affect(c,:)>0)==mat_affect(cc,mat_affect(c,:)>0)))/sum(mat_affect(c,:)>0);
    end
end

% correction pour le nombre de questions concernant chaque candidat
if correction_nquestion
    point_correction=1./(mean(mat_affect>0,2));
else
    point_correction=ones(length(candidats),1);
end

% simulation des réponses aléatoires
scores=zeros(length(candidats),nsimulations);
for s=1:nsimulations
    for i=1:nquestions
         choix=randi(noptions(i));
         points=find(choix==mat_affect(:,i));
         scores(points,s)=scores(points,s)+point_correction(points);
    end
    [dum, dum, invrank] = unique(scores(:,s));
    rang(:,s)=1+max(invrank)-invrank;
end

% calcule des moyennes et des intervalles de confiance à 95%
for c=1:length(candidats)
    proportion_premier(c,1)=mean(rang(c,:)==1);
    confidence_interval(c,:)=bootci(1000,@(x) mean(x), [rang(c,:)==1]');
end

% crée le dossier si besoin
if exist('analyses', 'dir')==0
    mkdir('analyses')
end

% sauvegarde les données
save(['analyses/' nom_analyse '.mat'])

% fichiers présauvegardés
% court = 20 questions: première partie du quiz
% long = 40 questions: inclusion des questions supplémentaires

Figures

Initialization


% nom de l'analyse
analyse_source='analyses_court_correction';

% charge l'analyse souhaitée
load(['analyses/' analyse_source '.mat'])

% crée le dossier si besoin
if exist('figures', 'dir')==0
    mkdir('figures')
end
% plot biais
f1=figure('name', 'bias', 'color', 'w', 'position',  [246   328   560   645] )
% code couleur récupéré sur le site du monde
hexcolors={'#9D0D16', '#CE2929', '#F1001C', '#FF3333', '#23B73D', '#FF668B', '#FF9F0E', '#0890C5', '#617B75', '#02279C', '#8D6026','#654519'};
hm=bar([1:12]',diag(100*proportion_premier(:,1)),'stacked');
hold on
he=errorbar(1:12,100*proportion_premier,100*(proportion_premier-confidence_interval(:,1)),100*(confidence_interval(:,2)-proportion_premier));
he.LineStyle='none';
he.Color='k';
he.LineWidth=1;
hold off
for c=1:12
    cartecouleur(c,:)=sscanf(hexcolors{c}(2:end),'%2x%2x%2x',[1 3])/255;
    hm(c).FaceColor=cartecouleur(c,:);
    hm(c).FaceAlpha=0.8;
end
xlim([0 13])
ylabel('premier ou copremier (%)')
set(gca, 'xticklabel', candidats, 'fontsize', 14)
box off
camroll(-90)
saveas(f1, ['figures/figure_' analyse_source '_biais.png'])
Biais non-corrigé Biais corrigé
La simulation de nombreux participants dont les choix sont aléatoires permet de révéler de très forts biais dans la construction du quiz, certains candidats étant par défault avantagés par rapport à d’autres, avec ou sans correction pour le nombre de réponses les concernant.

Coaffectation et similarité

% similarité
f2=figure('color', 'w')
imagesc(100\*coaffectation) 
set(gca,'xtick',1:12); 
set(gca,'ytick',1:12); 
set(gca,'xticklabel',candidats); 
set(gca,'yticklabel',candidats, 'fontsize', 14); 
xticklabel_rotate(\[\],45,\[\], 'fontsize', 14) 
title('pourcentage de co-affectations') 
colorbar 
set(gcf, 'position', \[ 808 328 984 645\]) 
saveas(f2, \['figures/figure\_' analyse_source(10:end) '\_similarité.png'\])

Coaffectations