Visualiser une analyse géométrique des données avec ggplot2 (R/RStudio)
Ce tutoriel a pour objectif de montrer comment réaliser, avec R et RStudio, des graphiques et des tableaux statistiques sur-mesure, permettant de visualiser et de présenter les résultats d’une analyse géométrique des données.
Il a aussi pour objectif – secondaire ici mais loin d’être trivial – de fournir un exemple de script de programmation statistique structuré, commenté et reproductible.
Depuis un tutoriel ancien toujours disponible sur ce site, des nouveaux packages et nouvelles fonctionnalités ont été développés, qui facilitent et améliorent la production de graphiques. Par exemple :
- FactoMineR : ce package parmi les plus utilisés pour réaliser des analyses géométriques a très nettement amélioré ses fonctionnalités graphiques, qui permettent désormais (notamment) de sélectionner des modalités, et de limiter le chevauchement des labels (voir la vidéo réalisée par François Husson, qui explique comment paramétrer les graphiques avec les fonctions de FactoMineR) ;
- factoextra : ce package permet de générer avec peu de code des graphiques élégants et bien structurés (de type ggplot) ;
- GDAtools : le package de Nicolas Robette comporte aussi des fonctions permettant de réaliser et paramétrer finement des graphiques de type ggplot :
ggcloud_variables
,ggcloud_indiv
,ggadd_ellipses
,ggadd_interaction
,ggadd_supvar
.
Toutefois, ces packages ne permettent pas de tout paramétrer, et je me suis souvent heurté au problème de ne pas pouvoir sélectionner comme je l’aurais souhaité les modalités à représenter, les couleurs ou encore les symboles de mes graphiques.
Par ailleurs, le package ggplot2 permet, à moindre coût en termes de code, de réaliser des graphiques non seulement élégants mais infiniment paramétrables.
J’ai donc peu à peu mis en place les routines que je vous présente ici, afin d’automatiser autant que faire se peut le code et minimiser le travail d’écriture, tout en permettant d’adapter le code à une diversité de données d’enquête, et aux objectifs d’administration de la preuve.
Sommaire
- Une famille de méthodes graphiques
- Objet, données et script
- Préambules
- Réalisation de l’ACM, stockage et visualisation des premiers résultats
- Mise en forme des résultats statistiques
- Visualisations graphiques
- Références
Une famille de méthodes graphiques
On désigne par analyse géométrique des données la famille de méthodes incluant notamment l’analyse en composantes principales (ACP), l’analyse des correspondances multiples (ACM), les classifications (ascendantes hiérarchies notamment, CAH), ou encore l’analyse factorielle de tableaux multiples (AFM).
Ces méthodes ont en commun de fournir la meilleure description possible des corrélations dans un jeu de données. De nombreux manuels présentent ces méthodes, avec un degré varié de technicité (notamment, Volle, 1997 ; Cibois, 2000 ; Le Roux et Rouanet, 2014).
Un parti-pris graphique
Cette vaste famille de méthodes est souvent présentée de façon graphique. Dans les enseignements en sciences sociales, qu’elles soient juste commentées ou que l’on enseigne leurs principes et la façon d’en réaliser à l’aide d’un logiciel, il est fréquent que seul le fameux graphique en croix soit présenté. Il en va de même dans la plupart des publications scientifiques : généralement on publie un ou plusieurs plans factoriels où sont projetées les modalités actives – et le cas échéant illustratives ou supplémentaires -, plus rarement on publie aussi ces mêmes plans où sont projetés les nuages des individus, et encore plus rarement les tableaux présentant les principaux résultats statistiques permettant d’interpréter les axes (fréquences, contributions, coordonnées, cosinus carrés).
Ce parti-pris graphique n’est pas un problème en soi : c’est même la force et l’intérêt des méthodes d’analyse géométrique que de permettre une visualisation synthétique des corrélations à l’intérieur d’un jeu de données. Mais à deux conditions : 1/ que le graphique soit démonstratif et 2/ que l’on oublie jamais qu’une lecture uniquement graphique des résultats peut conduire à de sérieuses erreurs d’interprétation (Cibois, 1997).
Quelques règles pratiques
Avant de montrer, à partir d’un exemple simple, comment mettre en place les routines qui vous permettront d’être efficaces dans la création de vos graphiques et tableaux, on énoncera quelques “règles pratiques”. Ces règles, relatives aux graphiques et aux tableaux, n’ont pas vocation à faire office de tables de la loi. Je les ai réfléchies, au fil du temps, à force de chercher à interpréter des résultats d’AGD publiés par d’autres, de regarder des graphiques plus ou moins réussis… et d’essayer d’en faire moi-même avec plus ou moins de bonheur…
Bref, ce sont des règles essentiellement pratiques, dont on verra ensuite la traduction technique avec R/RStudio.
Les règles d’un graphique démonstratif ?
On pourrait dire schématiquement que pour qu’un graphique soit démonstratif, i.e utile à l’administration de la preuve, il faut qu’il soit lisible, structuré et esthétique.
- On doit pouvoir sélectionner quelles modalités représenter. La question de savoir quelles modalités sont éligibles à l’interprétation est l’objet de débats : ce n’est pas ici notre problème. Mais quel que soit le seuil que vous choisissiez… tout n’est pas bon à représenter !
- Les noms des variables et les libellés des modalités doivent être présentés en langage naturel et non en code hiéroglyphique compréhensible uniquement (et encore…) par les concepteurs de l’enquête ou les chercheurs qui ont mené l’analyse. Pratiquement, cela nécessite de travailler le matériau avec lequel graphiques et tableaux sont réalisés afin de renommer – si besoin – variables et modalités.
- Les libellés des modalités ne doivent pas se chevaucher. Ici, une bonne nouvelle : un package “magique” dans R (ggrepel associé à ggplot2) s’en charge. On verra comment l’utiliser.
- Les labels des axes doivent être clairement présentés, et indiquer le pourcentage d’inertie conservé par chaque axe.
- Une légende permettant de déchiffrer les symboles et couleurs éventuels doit être positionnée de façon à ne pas empiéter sur le graphique.
- Les types de variables (actives ou illustratives) doivent être clairement distingués, par des symboles ou des couleurs distincts.
- Les variables aussi doivent être distinguées entre elles, par un jeu de symboles adéquat.
- Enfin, l’esthétisme doit être pris en considération (notamment en vue d’une publication) : les gammes de couleur utilisées doivent être harmonieuses, et permettre de distinguer ce qui doit l’être ; on doit pouvoir aisément passer de la couleur au noir et blanc (ou nuances de gris).
C’est tout ? Non… Des graphiques démonstratifs ne suffisent pas à asseoir statistiquement l’argumentation : il faut aussi présenter lisiblement les principaux résultats statistiques de l’analyse géométrique. Pour un exemple de publication présentant les graphiques et statistiques d’une ACM, voir Denord et al. (2011).
Quels résultats statistiques présenter, et comment ?
Les informations qu’il s’agit de présenter diffèrent selon le type de méthode mise en œuvre. Pour une ACP, point de contributions pour les modalités actives, mais plutôt un tableau présentant les corrélations des variables aux axes. Pour une AFM, point de contributions non plus, puisque l’analyse consiste en une ACP d’ACM partielles : on privilégiera donc les coordonnées, et les v.test, par exemple.
Cela étant dit, si l’on s’en tient à l’ACM, méthode de la famille des analyses géométriques la plus couramment utilisée en sciences sociales, il convient sans doute de présenter :
- pour chaque modalité active : effectif brut (information importante : des modalités à petits effectifs peuvent parfois peser artificiellement lourd sur les axes : c’est le fameux effet “distinction” de l’ACM…) et fréquence, puis les coordonnées, contributions et cosinus carrés sur chacun des axes interprétés ;
- on peut aussi éventuellement sommer les contributions par variable, ou par groupe de variables, afin de voir quelles sont les variables ou les grandes thématiques qui structurent chaque axe ;
- pour chaque modalité illustrative : effectif brut, fréquence, puis coordonnées et cosinus carrés sur chacun des axes interprétés.
Je montrerai dans ce tutoriel comment générer directement dans R, un tableau comportant toutes les informations nécessaires. Je ne montrerai en revanche pas comment achever sa mise en forme afin de le publier. Les utilisateurs de LibreOffice Calc (ou son avatar non libre nommé Excel) exporteront le tableau en format .odt ou .xlsx afin de le manipuler à leur guise avant publication. Les utilisateurs de LateX pourront utiliser la fonction xtable
pour générer un code LateX, ou bien passer par la macro CalctoLatex (ou Excel2Latex). Les utilisateurs de RMarkdown pourront utiliser knitr::kable
et ses multiples réglages (kableExtra::kable_styling
).
Une bonne nouvelle, tout de même : structurer le tableau des résultats et rassembler les informations utiles à la réalisation des graphiques vont de pair. En faisant l’un, on se simplifie la vie pour l’autre…
Objet, données et script
L’objet sociologique que j’ai choisi pour ce tutoriel est l’intérim. Je tenterai une très modeste description du monde du travail intérimaire, sans autre ambition sociologique que de mettre en œuvre une “vraie” analyse, à partir de “vraies” données.
Les données utilisées dans ce tutoriel sont celles du fichier détail de l’Enquête emploi en continu de 2018. Elles sont librement téléchargeable sur le site de l’Insee.
Ce fichier détail comporte un nombre restreint de variables (par rapport au fichier production-recherche accessible via Quetelet-Progedo). L’analyse se limitera donc à une série assez frustre d’indicateurs, qui ne permettent sans doute pas de dire quoi que soit de très élaboré sur l’intérim et les intérimaires. On ne s’attardera donc pas sur l’interprétation des axes et plans factoriels, pour se concentrer sur les problèmes concrets d’une recherche sur des données d’enquête, non simulées.
Les données, les scripts ainsi que le matériel d’une présentation dédiée à ce tutoriel (@Vendredis Quanti, U. Grenoble, Pacte, 4/06/21) sont disponibles sur un dépôt GitHub.
Afin de simplifier la suite du tutoriel, on ne présentera pas les recodages et sélections de variables. Celles et ceux qui veulent sauter cette étape, peuvent télécharger directement le fichier prêt-à-jouer ee18_interim_recode.csv dans le dossier data_prod sur le dépôt GitHub mentionné supra.
Avant de commencer…
Une petite mise en garde : le script présenté ci-dessous n’est pas d’une grande complexité. J’ai essayé de l’écrire de la façon la plus simple et explicite possible. Ce n’est toutefois pas un script “niveau débutant”… et il faudra peut-être à celles et ceux qui débutent dans R un peu de patience pour pouvoir se l’approprier.
Le script est rédigé en mode Tidyverse. Pour celles et ceux qui ne sont pas familiers de cette façon de coder dans R, reportez-vous au manuel de Julien Barnier, en ligne ici : Introduction à R et au Tidyverse.
Il s’agit d’un script réplicable dans la mesure où si vous disposez des données (au bon endroit dans votre ordinateur), et si les packages convoqués en début de script sont bien installés, vous n’avez rien à modifier pour pouvoir l’exécuter de bout en bout.
Préambules
On part du principe que vous disposez d’une installation fonctionnelle de R et RStudio sur votre ordinateur. Si cela n’était pas le cas, rendez-vous ici.
Chargement des packages
Les packages utilisés dans ce tutoriel sont les suivants :
- tidyverse : le package des packages, incontournable pour programmer de façon efficace et “propre”. On se référera aussi utilement au site du projet Tidyverse.
- questionr : le package (développé par Julien Barnier) dont vous aurez besoin tout le temps, notamment pour la statistique descriptive ;
- FactoMineR : le package le plus usuel pour la réalisation d’analyses géométriques. On se référera aussi au site dédié à FactoMineR. et aux vidéos de François Husson
- RColorBrewer : pour disposer de gammes de couleurs et de nuanciers lisibles et non fantaisistes… Voir aussi ce tutoriel en ligne très utile.
- ggrepel : pour que les libellés des points ne se chevauchent pas (crucial !) ;
- explor : pour visualiser facilement et rapidement les résultats d’une analyse géométrique, dans une fenêtre graphique interactive. On peut consulter les didacticiels de Julien Barnier (créateur de ce package) : https://quanti.hypotheses.org/1171 et juba.github.io/explor/articles/introduction_fr.html.
On exécute aussi en début de script l’option scipen = 999
afin de désactiver l’écriture scientifique, qui gène un peu la lisibilité des sorties statistiques.
Pour une présentation synthétique des packages les plus usuels en sciences sociales, voir ici.
# 1. PACKAGES ET OPTIONS ----
library(tidyverse) # pour la manipulation des données
library(questionr) # pour la description des variables
library(FactoMineR) # pour l'analyse géométrique des données
library(RColorBrewer) # pour les palettes de couleurs
library(ggrepel) # pour que les libellés des points ne se chevauchent pas (crucial !)
library(explor) # pour une visualisation exploratoire des résultats de l'analyse
options(scipen=999) # pour désactiver l'écriture scientifique des nombres
Chargement des données
Si vous suivez le tutoriel à partir du script intégral, les instructions qui suivent sont inutiles : exécutez juste l’ensemble des instructions numérotées de 1 à 3 dans le script.
Si vous utilisez uniquement les données recodées et prêtes-à-jouer, voici la marche à suivre :
- Téléchargez les ici (si ce n’est déjà fait) ;
- enregistrez-les dans un dossier “data”, au même niveau dans votre ordinateur que le script sur lequel vous écrivez (ou du projet RStudio que vous avez créé) ;
- ne les ouvrez avec LibreOffice Calc (ou Excel) SOUS AUCUN PRETEXTE ! C’est une très mauvaise habitude, et vous risquez de les corrompre (et de devoir les télécharger à nouveau, etc.) ;
- suivez les instructions ci-dessous.
# chargement des données (à adapter en fonction de l'endroit où vous les avez stockées)
d <- read_csv2("ee18_interim_recode.csv")
Réalisation de l’ACM, stockage et visualisation des premiers résultats
C’est la partie “facile” de notre petite affaire… même si le diable sociologique se cache presque tout entier dans cette première étape de sélection des variables à faire entrer dans l’analyse. Une phrase de Jean-Paul Benzécri, créateur de l’analyse factoriel, est à réciter comme un mantra :
All in all, doing a data analysis, in good mathematics,
is simply searching eigenvectors (valeurs propres) ; all the science (or the
art) of it is just to find the right matrix to diagonalize.
Autrement dit : diagonaliser des matrices et chercher des valeurs propres est chose aisée (d’autant plus que c’est R qui le fera pour vous…). Mais savoir quelle matrice diagonaliser – et donc quelles variables et quels individus sélectionner – c’est tout un art !
# 4. REALISATION DE L'ACM ----
# 4.1. Sélection des variables de l'analyse ----
d_acm <- d %>%
select("age", "genre", "diplome", "nationalite", "gsp", "sociopro", "menage", "position", "choix", horaires", "heures_plus", "public_prive", "secteur") %>%
mutate_all(factor) %>% # car la fonction MCA ne mange que du "factor"
drop_na() # enlever toutes les NA (57 individus)
str(d_acm)
Nota bene concernant les NA : les cases vides sont un problème en analyse géométrique, surtout lorsqu’elles sont peu nombreuses, et potentiellement redondantes (ce sont parfois les mêmes individus qui ne répondent pas). En effet, l’analyse les considère comme des modalités comme les autres. C’est formidable si la non-réponse est un objet d’étude, mais potentiellement ennuyeux sinon. Une façon expéditive de régler le problème est d’enlever les individus qui ont des cases vides pour les variables actives. Attention à ne pas en abuser… au risque de déformer fortement votre échantillon. D’autres solutions sont possibles : ventiler les cases vides de façon aléatoire (argument level.ventil
de la fonction MCA
), les remplir en “imputant” les données manquantes à partir de ce que l’on sait par ailleurs des corrélations dans le jeu de données (package missMDA développé par François Husson et l’équipe de FactoMineR), ou encore faire de l’analyse spécifique. Ici comme toujours, il n’y a pas de solution technique miracle : la question essentielle est celle de la délimitation de votre objet et de votre population d’intérêt…
Réaliser l’ACM
# 4.2. Réalisation de l'analyse et première visualisation des résultats ----
res_acm <- MCA(d_acm, quali.sup = 5:7)
L’argument quali.sup = 5:7
permet de sélectionner les variables illustratives (ou supplémentaires). Ici, ce sont les variables de rang 5, 6 et 7 dans le jeu de données d_acm
. C’est à dire :
gsp
etsociopro
: deux codages différents, plus ou moins agrégés, de la profession ;menage
: le type de ménage dans lequel vit l’enquêté.e
On a stocké les résultats de l’analyse dans l’objet res_acm
. Cet objet (une liste) se subdivise en plusieurs sous objets, dont certains vont constituer le matériau de base de notre travail de mise en forme et de visualisation. Par défaut, la fonction MCA stocke les résultats des cinq premiers axes. Si vous en voulez plus, il faut paramétrer l’argument npc
.
res_acm$var
: liste comportant les informations relatives aux variables activesres_acm$var$coord
: tableau comportant les coordonnées de chaque modalité active ;res_acm$var$contrib
: tableau comportant les contributions de chaque modalité active ;res_acm$var$cos2
: tableau comportant les cosinus carrés de chaque modalité active ;res_acm$var$v.test
: tableau comportant les v-tests de chaque modalité active.
res_acm$ind
: liste comportant les informations relatives aux variables activesres_acm$ind$coord
: les coordonnées des individus sur chaque axe ;- Les contributions et cosinus carrés des individus sont aussi disponibles, mais on les utilisera pas.
res_acm$quali.sup
: liste comportant les informations relatives aux variables illustrative
res_acm$quali.sup$coord
: tableau comportant les coordonnées de chaque modalité illustrative ;res_acm$quali.sup$cos2
: tableau comportant les cosinus carrés de chaque modalité illustrative ;res_acm$quali.sup$v.test
: tableau comportant les v-tests de chaque modalité illustrative.
Premières visualisations
Prenez le temps, toujours, de regarder les premières sorties graphiques (même “moches” et peu lisibles) : elles fournissent des indications précieuses sur le caractère plus ou moins pertinent de votre analyse (si votre nuage des individus se scinde en deux patates bien disjointes, par exemple, c’est que vous avez un problème…).
On peut à cette fin utiliser le package factoextra, afin de produire rapidement les premiers graphiques exploratoires, ou encore le package explor avec la simple commande suivante :
explor(res_acm)
Description statistique des axes
Avant d’aller plus loin dans l’interprétation de l’ACM, il faut examiner les axes, et déterminer quels sont ceux que l’on retiendra pour l’analyse. Là encore, plusieurs critères sont possibles, certains préconisant même d’interpréter tous axes… pourvu qu’ils soient interprétables.
L’enjeu ici est de montrer :
- Comment structurer un tableau exportable comportant les informations relatives à l’inertie conservée dans les différents axes factoriels ;
- Comment réaliser un diagramme des sauts de variance.
# 4.3. Description statistique des axes ----
variances <- as.data.frame(res_acm$eig) %>%
rownames_to_column() %>% # récupérer les noms de lignes (dim 1, dim 2, etc) dans une colonne distincte
slice(1:10) %>% # conserver que les infos des 10 premiers axes
mutate(Axes = str_replace_all(rowname, "dim", "Axe")) %>% # créer une nouvelle variable à partir de rowname, qui prend les valeurs "Axe 1, Axe 2, etc" au lieu de "dim 1, dim 2, etc."
select(-rowname) %>%# on enlève cette colonne dont on n'a plus besoin
rename(`Valeurs propres` = eigenvalue) %>%
rename(`% de variance` = `percentage of variance`) %>% # on renomme les autres colonnes
rename(`% cumulé de variance` = `cumulative percentage of variance`) %>%
mutate(Axes = fct_relevel(Axes, paste("Axe", 1:10))) # pour que l'ordre de 1 à 10 soit bien respecté dans les graphiques
On obtient le tableau suivant :
Valeurs propres | % de variance | % cumulé de variance | Axes |
0,22 | 6,3 | 6,3 | Axe 1 |
0,16 | 4,6 | 11,0 | Axe 2 |
0,14 | 4,1 | 15,1 | Axe 3 |
0,14 | 3,9 | 19,0 | Axe 4 |
0,13 | 3,8 | 22,8 | Axe 5 |
0,13 | 3,6 | 26,4 | Axe 6 |
0,12 | 3,5 | 29,8 | Axe 7 |
0,12 | 3,3 | 33,1 | Axe 8 |
0,11 | 3,2 | 36,4 | Axe 9 |
0,11 | 3,2 | 39,5 | Axe 10 |
Pour générer les diagrammes des sauts d’inertie, il suffit de prendre l’information contenue dans la deuxième ou dans la troisième colonne de ce tableau.
Aparté concernant les taux de variance modifiés de Benzécri : la démarche graphique serait la même, il faudrait juste calculer les taux modifiés et utiliser cette information comme input de la fonction graphique ci-dessous. Le package GDAtools développé par Nicolas Robette propose une fonction modif.rate
qui permet de calculer aisément les taux de variance modifiés.
# Diagrammes des sauts de variance ----
ggplot(variances, aes(x = Axes)) + # initialiser du graphique et de l'axe horizontal
geom_bar(aes(y = `% de variance`), # indiquer le type de graphique (barplot) et la variable à représenter sur l'axe vertical
stat = "identity",
fill = "red") + # parce que j'aime bien le rouge
xlab("") + # on enlève le label de l'axe des x, pas très utile
ylab("% de variance") + # renommer proprement le label de l'axe des y
theme_minimal() # un des thèmes possibles dans ggplot, que j'aime bien car il est... minimaliste !
ggplot(variances, aes(x = Axes)) +
geom_bar(aes(y = `% cumulé de variance`),
stat = "identity",
fill = "blue") +
xlab("") +
ylab("% cumulé de variance") +
theme_minimal()
Enfin, il faut définir un seuil de contribution, permettant de déterminer quelles sont les modalités actives éligibles à l’interprétation, et qui devront donc être représentées graphiquement.
Ce seuil est bien évidemment arbitraire, on ne discutera pas ici de ce point. Le seuil ici retenu est la contribution moyenne (1 / nombre de modalités actives). Le principe est simple : une modalité est dite “contributive” si elle “pèse” plus lourd sur un axe que son poids moyen dans l’ensemble du nuage d’origine.
# Seuil de contribution ----
seuil <- 100 / nrow(res_acm$var$coord)
Mise en forme des résultats statistiques
L’objectif est de parvenir à créer un grand tableau qui contient autant de lignes que de modalités actives et/ou supplémentaires. Chacune de ces modalités est décrite par sa fréquence dans le jeu de données, et par les indicateurs usuels de l’AGD pour les premiers axes factoriels (contribution, coordonnée, cosinus carré, v.test).
Ce tableau peut être exporté dans un tableur afin de servir de support à l’interprétation des axes. Rappelons que l’interprétation statistique des axes doit impérativement précéder l’interprétation graphique, faute de quoi on s’expose à commettre de grosses erreurs d’interprétation.
Ce tableau peut aussi, en étant légèrement mis en forme, être utilisé dans une annexe de mémoire, de thèse, ou dans une publication. Pour la raison évoquée dans le paragraphe précédent et en introduction de cet article, cela devrait même être obligatoire…
Fréquences des modalités actives et supplémentaires
Première étape : récupérer les fréquences de chaque modalité. Ce faisant on récupère aussi la liste de toutes les modalités du jeu de données, ainsi que les variables auxquelles elles appartiennent. Ce sera très utile plus bas pour dessiner des symboles différents par variable…
# 5.1. Fréquences modalités actives et supplémentaires ----
frequences <- gather(d_acm, variables, modalites) %>% # étendre le jeu de données par variable et modalité
count(variables, modalites) %>% # compter le nombre de couples "variable/modalité" unique (donc le nombre d'individus par modalité du jeu de données)
group_by(variables) %>%
mutate(pourcentage = round(100 * n / nrow(d_acm), 1)) %>% # calculer des pourcentages pour chaque groupe de variable
ungroup() %>%
select(variables, modalites, n, pourcentage)
On obtient un le tableau suivant (extrait des premières lignes) :
variables | modalites | n | pourcentage |
age | +50a | 786 | 17 |
age | 15-29a | 1859 | 40,1 |
age | 30-39a | 1116 | 24,1 |
age | 40-49a | 871 | 18,8 |
genre | Femmes | 1355 | 29,3 |
genre | Hommes | 3277 | 70,7 |
Indicateurs statistiques des modalités actives
Deuxième étape : récupérer les indicateurs statistiques des modalités actives. On procède pas à pas : d’abord les coordonnées, puis les contributions, les cosinus carrés et les v.tests.
# 5.2. Modalités actives ----
# Coordonnées (modalités actives) ----
coordonnees <- as.data.frame(round(res_acm$var$coord, 2)) %>% # récupérer les coordonnées des modalités actives et arrondir à deux décimales (c'est bien suffisant)
rename_all(tolower) %>% # tout en minuscules
rename_all(~ str_replace(., " ", "")) %>% # renommer les variables en supprimant les espaces : par exemple : "dim 1" devient "dim1"
rename_all(~ str_c(., "coord", sep = "_")) %>% # ajouter le suffixe _coord à chaque nom de variable. On obtient ainsi par exemple "dim1_coord"
mutate(modalites = rownames(.)) # récupérer les noms des modalités, stockées dans le nom des lignes de res_acm$var$coord
# Contributions (modalités actives) ----
contributions <- as.data.frame(round(res_acm$var$contrib, 2)) %>%
rename_all(tolower) %>%
rename_all(~ str_replace(., " ", "")) %>%
rename_all(~ str_c(., "contrib", sep = "_")) %>% # idem sauf qu'ici on obtient "dim1_contrib"
mutate(modalites = rownames(.))
# Cosinus carrés (modalités actives) ----
cos2 <- as.data.frame(round(res_acm$var$cos2, 2)) %>%
rename_all(tolower) %>%
rename_all(~ str_replace(., " ", "")) %>%
rename_all(~ str_c(., "cos2", sep = "_")) %>% # idem avec "cos2"
mutate(modalites = rownames(.))
# vtest (modalités actives) ----
vtest <- as.data.frame(round(res_acm$var$v.test, 2)) %>%
rename_all(tolower) %>%
rename_all(~ str_replace(., " ", "")) %>%
rename_all(~ str_c(., "vtest", sep = "_")) %>% # idem avec vtest
mutate(modalites = rownames(.))
Enfin, on assemble ces cinq tableaux, afin d’obtenir un grand tableau (resultats_actives
) comportant toutes les informations dont on a besoin pour décrire les axes selon les modalités actives. On en profite pour faire un peu de “cosmétique” en renommant proprement les noms des variables (ce sera très utile par la suite pour construire la légende des graphiques).
resultats_actives <- frequences %>%
right_join(coordonnees) %>%
right_join(contributions) %>%
right_join(cos2) %>%
right_join(vtest) %>% # fusionner les jeux de données ; la clé de fusion (implicite) est la variable "modalites", qui est commune à tous.
mutate(type = "Variable active") %>% # ajout d'une colonne contenant la chaîne de caractères "Variable active" (pour pouvoir distinguer plus tard avec les variables supplémentaires)
select(type, variables, modalites, n, pourcentage,
contains("dim1"), contains("dim2"),
contains("dim3"), contains("dim4")) %>% # conserver et réorganiser les variables pertinentes axe par axe
mutate(variables = fct_recode(variables, # renommer les noms des variables actives afin qu'elles soient lisibles et "publiables"
"Age" = "age",
"Genre" = "genre",
"Diplôme" = "diplome",
"Nationalité" = "nationalite",
"Position dans l'emploi" = "position",
"Choix du contrat" = "choix",
"Horaire de travail" = "horaires",
"Souhaite travailler plus" = "heures_plus",
"Public/Privé" = "public_prive",
"Secteur d'activité" = "secteur"))
On obtient le tableau suivant (extraits) :
type | variables | modalites | n | pourcentage | dim1_coord | dim1_contrib | dim1_cos2 |
Variable active | Age | +50a | 786 | 17 | -0,23 | 0,41 | 0,01 |
Variable active | Age | 15-29a | 1859 | 40,1 | 0,2 | 0,69 | 0,03 |
Variable active | Age | 30-39a | 1116 | 24,1 | -0,06 | 0,04 | 0 |
Variable active | Age | 40-49a | 871 | 18,8 | -0,13 | 0,15 | 0 |
Variable active | Genre | Femmes | 1355 | 29,3 | 0,92 | 11,24 | 0,35 |
Variable active | Genre | Hommes | 3277 | 70,7 | -0,38 | 4,65 | 0,35 |
Indicateurs statistiques des modalités illustratives
On procède de la même façon pour les modalités illustratives. On assemble les résultats dans un objet resultats_sup
.
# 5.3. Modalités supplémentaires ----
# Coordonnées (modalités supplémentaires) ----
coordonnees_sup <- as.data.frame(round(res_acm$quali.sup$coord, 2)) %>% # la démarche est la même que supra, mais avec le sous-objet quali.sup qui stocke les informations sur les variables qualitatives supplémentaires
rename_all(tolower) %>%
rename_all(~ str_replace(., " ", "")) %>%
rename_all(~ str_c(., "coord", sep = "_")) %>%
mutate(modalites = rownames(.))
# Cosinus carrés (modalités supplémentaires) ----
cos2_sup <- as.data.frame(round(res_acm$quali.sup$cos2, 2)) %>%
rename_all(tolower) %>%
rename_all(~ str_replace(., " ", "")) %>%
rename_all(~ str_c(., "cos2", sep = "_")) %>%
mutate(modalites = rownames(.))
# vtest (modalités supplémentaires) ----
vtest_sup <- as.data.frame(round(res_acm$quali.sup$v.test, 2)) %>%
rename_all(tolower) %>%
rename_all(~ str_replace(., " ", "")) %>%
rename_all(~ str_c(., "vtest", sep = "_")) %>%
mutate(modalites = rownames(.))
# Assemblage du tableau des résultats (modalités actives) ----
resultats_sup <- frequences %>%
right_join(coordonnees_sup) %>%
right_join(cos2_sup) %>%
right_join(vtest_sup) %>%
mutate(type = "Variable supplémentaire") %>% # comme supra pour le tableau des résultats des modalités actives : on distingue ici le type de variable.
select(type, variables, modalites, n, pourcentage,
contains("dim1"), contains("dim2"),
contains("dim3"), contains("dim4")) %>%
mutate(variables = fct_recode(variables,
"Groupe socio-professionnel" = "gsp",
"Catégorie socio-professionnelle" = "sociopro",
"Type de ménage" = "menage"))
Assemblage du tableau complet des résultats
Afin d’obtenir un tableau comportant toutes les informations pour toutes les modalités actives et illustratives, on colle les lignes des tableaux resultats_actives
, resultats_sup
.
On a donc dans un seul et même objet intitulé resultats_complet
toutes les informations nécessaires pour 1/ interpréter correctement les axes et 2/ faire de beaux graphiques !
resultats_complet <- bind_rows(resultats_actives, resultats_sup)
Visualisations graphiques
Avant de se lancer dans la réalisation des graphiques, un point d’étape s’impose. De quoi dispose-t-on ?
- Des résultats de l’ACM stockés dans un objet res_acm, qui se subdivise en sous-objet permettant de décrire les axes, les individus, les variables actives et les variables illustratives ;
- un tableau synthétique comportant les fréquences, coordonnées, contributions, cosinus carrés et v.test des modalités actives :
resultats_actives
; - la même chose (sauf les contributions) pour les modalités illustratives :
resultats_sup
; - un grand tableau qui amalgame toutes ces informations :
resultats_complet
Ce sont ces informations qui nous serviront à réaliser nos graphiques.
On peut représenter les résultats d’une analyse géométrique de deux façons complémentaires :
- le nuage des modalités : on projette les modalités actives et éventuellement supplémentaires pertinentes sur le plan formé par la confrontation de deux axes ;
- le nuage des individus : on projette les individus sur ce même plan.
L’information la plus importante ce sont donc les coordonnées des individus ou des modalités sur les axes, qui permettent de positionner des points sur un plan.
On peut ensuite modifier ces graphiques de diverses façons :
- faire varier la taille des points en fonction du poids des individus ou des modalités dans l’inertie conservée dans le plan factoriel, ou bien en fonction de la fréquence de chaque point.
- “habiller” le nuage des individus en fonction d’une variable catégorielle, afin de visualiser comment se répartissent et se distinguent des individus dont les propriétés sociales diffèrent ;
- tracer des ellipses de concentration, qui permettent de mieux visualiser la forme de sous-nuages de points, et donc de mener une analyse structurale de l’information géométrique contenue dans ces nuages.
Le nuage des modalités actives
Commençons par le plus simple : sélectionner les modalités qui passent le seuil de la contribution moyenne, et les projeter dans un plan factoriel…
# 6.1. Nuage des modalités actives ----
# Nuage des modalités actives simple ----
resultats_actives %>%
filter(dim1_contrib > seuil |
dim2_contrib > seuil) %>% # on part du tableau de résultat des modalités actives et on filtre uniquement celles dont la contribution dépasse le seuil pour l'un ou l'autre des deux axes (| est l'opérateur logique OU).
ggplot(aes(x = dim1_coord, y = dim2_coord, # initialisation du graphique
label = modalites, # les labels des points sont les modalités
shape = variables)) + # les formes des points dépendent des variables : à chaque variable son symbole
geom_point() + # tracer les points
geom_text_repel(segment.alpha = 0.5) + # tracer les labels, en utilisant cette fonction du package ggrepel qui permet de s'assurer qu'il n'y a pas de chevauchement. segment.alpha : transparence du petit tiret qui relie si besoin le libellé au point
coord_fixed() + # pour que les échelles des axes soient identiques
geom_hline(yintercept = 0, colour = "darkgrey", linetype="longdash") + # ligne horizontale y = 0
geom_vline(xintercept = 0, colour = "darkgrey", linetype="longdash") + # ligne verticale x = 0
xlab(paste0("Axe 1 (", round(variances[1, 2], 1), " %)")) + # label de l'axe horizontal, qui intègre automatiquement le pourcentage de variance
ylab(paste0("Axe 2 (", round(variances[2, 2], 1), " %)")) + # idem pour l'axe vertical
scale_shape_manual(name="", values = 0:20) + # sélection des types de symboles
guides(shape=guide_legend(title="")) + # paramètres de la légende : pas de titre
theme_minimal() + # mise en forme globale du graphique ; theme_minimal est la plus "sobre" mais d'autres sont possibles...
theme(legend.position="bottom") # pour que la légende se place sous le graphique.
Il n’est pas inintéressant de faire figurer sur ce graphique une indication de l’importance de chaque modalité. On peut ainsi moduler la taille des symboles par l’effectif de chaque modalité
# Nuage des modalités actives : points proportionnels aux effectifs ----
resultats_actives %>%
filter(dim1_contrib > seuil |
dim2_contrib > seuil) %>%
ggplot(aes(x = dim1_coord, y = dim2_coord,
label = modalites,
shape = variables,
size = n)) + # il suffit d'ajouter ce paramètre : la taille des points s'ajuste à la variable n (effectifs)
geom_point() +
geom_text_repel(size = 3, segment.alpha = 0.5) +
coord_fixed() +
geom_hline(yintercept = 0, colour = "darkgrey", linetype="longdash") +
geom_vline(xintercept = 0, colour = "darkgrey", linetype="longdash") +
xlab(paste0("Axe 1 (", round(variances[1, 2], 1), " %)")) +
ylab(paste0("Axe 2 (", round(variances[2, 2], 1), " %)")) +
scale_shape_manual(name="", values = 0:20) +
guides(shape=guide_legend(title=""), size = FALSE) + # paramètres de la légende : pour ne pas que les échelles de taille s'affichent dans la légende
theme_minimal() +
theme(legend.position="bottom")
On pourrait multiplier les types de graphiques différents, en modifiant juste le paramètre “size” :
- points proportionnels aux contributions sur l’axe 1 ou l’axe 2, ou à la moyenne des contributions sur les deux axes
- points proportionnels aux cos2, etc.
On peut aussi colorer les symboles et les labels en fonction d’un gradient de couleurs proportionnel à une variable numérique (ici les effectifs). Il suffit de jouer sur le paramètre “color”.
resultats_actives %>%
filter(dim1_contrib > seuil |
dim2_contrib > seuil) %>%
ggplot(aes(x = dim1_coord, y = dim2_coord,
label = modalites,
shape = variables,
color = n)) + # il suffit de remplacer size par color
geom_point() +
geom_text_repel(size = 3, segment.alpha = 0.5) +
coord_fixed() +
geom_hline(yintercept = 0, colour = "darkgrey", linetype="longdash") +
geom_vline(xintercept = 0, colour = "darkgrey", linetype="longdash") +
xlab(paste0("Axe 1 (", round(variances[1, 2], 1), " %)")) +
ylab(paste0("Axe 2 (", round(variances[2, 2], 1), " %)")) +
scale_shape_manual(name="", values = 0:20) +
scale_color_gradient(name="",
# low = "#FFCC33", high = "#FF3300" # enlever le commentaire pour un exemple de modification de couleur du gradient
) +
guides(shape=guide_legend(title=""), size = FALSE) + # paramètres de la légende : pour ne pas que les échelles de taille s'affichent dans la légende
theme_minimal() +
theme(legend.position="bottom")
Les nuages des modalités actives et illustratives
On peut aussi représenter sur un même graphique les modalités actives et illustratives. J’ai arbitrairement décidé de ne représenter que les modalités illustratives dont la coordonnée dépasse 0.3 en valeur absolue.
L’enjeu important ici est de bien différencier les deux types de variables. On fait le choix ici de les distinguer par la couleur. C’est l’argument colour = type
qui permet de le faire (pour rappel, type est le nom de la colonne du tableau resultats_complet qui distingue entre variables actives et variables supplémentaires).
Il faut aussi évidemment paramétrer la légende afin qu’elle soit lisible et permette bien de distinguer entre les deux types de variables (en plus des symboles des modalités actives et supplémentaires).
# 6.2. Nuage des modalités actives et supplémentaires ----
resultats_complet %>%
filter(dim1_contrib > seuil |
dim2_contrib > seuil |
is.na(dim2_contrib) & dim1_coord > 0.29 |
is.na(dim2_contrib) & dim1_coord < -0.31) %>% # on part du tableau complet et on sélectionne, en plus des modalités actives qui passent le seuil, les modalités supplémentaires dont les coordonnées s'écartent de +/- 0.3 du barycentre (c'est parfaitement arbitraire...).
ggplot(aes(x = dim1_coord, y = dim2_coord,
label = modalites,
shape = variables,
colour = type, # on distingue par des couleurs différentes les variables actives et supplémentaires
size = n)) +
geom_point() +
geom_text_repel(size = 3, segment.alpha = 0.5) +
coord_fixed() +
geom_hline(yintercept = 0, colour = "darkgrey", linetype="longdash") +
geom_vline(xintercept = 0, colour = "darkgrey", linetype="longdash") +
xlab(paste0("Axe 1 (", round(variances[1, 2], 1), " %)")) +
ylab(paste0("Axe 2 (", round(variances[2, 2], 1), " %)")) +
scale_shape_manual(name="", values = 0:20) +
scale_color_manual(values = c("black", "darkgrey")) + # paramètres de couleur ; tout est possible ici... à vous de tester les lignes ci-dessous par exemple :
# scale_color_brewer(palette = "Set1") +
# scale_color_grey() +
# scale_color_brewer(palette = "Accent")
guides(shape = guide_legend(title="Nom des variables (actives et supplémentaires)", # titre de la légende des noms de variable
title.position = "top"),
colour = guide_legend(title= "Type de variable", # titre de la légende distinguant actives et supplémentaires
title.position = "top",
nrow = 2),
size = FALSE) + # toujours pas de légende pour les tailles de point
theme_minimal() +
theme(legend.position="bottom")
Le nuage des individus et les ellipses de concentration
L’un des intérêts des méthodes d’analyse géométrique est de pouvoir raisonner sur des variables et sur des individus. Il est donc important de pouvoir bien représenter le nuage des individus, et de maîtriser les outils graphiques permettant d’en analyser la structure.
Commençons par le plus simple : le nuage des individus dans son plus simple appareil… Il faut d’abord récupérer les coordonnées des individus, stockées dans res_acm_ind_coord
# 6.3. Nuage des individus ----
indiv12 <- as.data.frame(res_acm$ind$coord[,1:2]) # récupérer les coordonnées des individus sur les deux premiers axes.
ggplot(indiv12, aes(x = `Dim 1`, y = `Dim 2`)) + # initialisation du graphique
geom_point(alpha = 0.6, # alpha : permet une certaine transparence des points, pour mieux voir les superpositions. On pourrait aussi changer la taille des points avec l'argument size = 1 (par exemple).
colour = "#E41A1C") + # couleur hexadécimale tout à fait arbitraire... les goûts et les couleurs...
coord_fixed() +
geom_hline(yintercept = 0, colour = "darkgrey", linetype="longdash") +
geom_vline(xintercept = 0, colour = "darkgrey", linetype="longdash") +
xlab(paste0("Axe 1 (", round(variances[1, 2], 1), " %)")) +
ylab(paste0("Axe 2 (", round(variances[2, 2], 1), " %)")) +
guides(colour = FALSE) + # pas de légende pour le paramètre de couluer
theme_minimal()
Mais ce nuage peut aussi être “habillé”, selon les modalités d’une variable dont on aimerait bien savoir comment elle structure l’espace social représenté. Autrement dit, on aimerait pouvoir visualiser la façon dont ce nuage est structuré par le genre.
Une première étape consiste à coller ensemble les informations dont on a besoin pour faire le graphique : les coordonnées des individus et leur genre…
Ensuite, ce n’est que l’affaire d’un paramètre : colour = genre
.
# 6.4. Habillage du nuage des individus : points habillés selon le genre ----
indiv12_genre <- d_acm %>%
select(genre) %>%
bind_cols(indiv12) # pour habiller le nuage de points, on a besoin d'adjoindre aux coordonnées l'information avec laquelle on veut l'habiller. Ici le genre. On s'autorise cette manipulation uniquement car l'ordre des lignes n'a pas été modifié...
ggplot(indiv12_genre, aes(x = `Dim 1`, y = `Dim 2`,
colour = genre)) + # ce paramètre permet de colorier les points en fonction de la variable "genre"
geom_point(alpha = 0.6) +
coord_fixed() +
geom_hline(yintercept = 0, colour = "darkgrey", linetype="longdash") +
geom_vline(xintercept = 0, colour = "darkgrey", linetype="longdash") +
xlab(paste0("Axe 1 (", round(variances[1, 2], 1), " %)")) +
ylab(paste0("Axe 2 (", round(variances[2, 2], 1), " %)")) +
scale_color_brewer(palette = "Set2") + # idem ici : tout est possible, à partir de RColorBrewer notamment. Voir display.brewer.all()
guides(colour = guide_legend(title="")) + # pour enlever le titre de la légende
theme_minimal()
Enfin, on peut tracer très facilement des ellipses de concentration, afin de visualiser de façon synthétique la forme de ces nuages, et leur intersection éventuelle.
C’est la fonction stat_ellipse()
qui s’en charge. Par défaut, l’ellipse englobe 95% des points de chaque sous-nuage (c’est modifiable dans la fonction avec le paramètre level = …).
# 6.5. Habillage du nuage des individus : points habillés et ellipses de concentration----
ggplot(indiv12_genre, aes(x = `Dim 1`, y = `Dim 2`,
colour = genre)) +
geom_point(alpha = 0.6) +
coord_fixed() +
stat_ellipse() + # unique ajout par rapport au graphique précédent, permettant de tracer les ellipses
geom_hline(yintercept = 0, colour = "darkgrey", linetype="longdash") +
geom_vline(xintercept = 0, colour = "darkgrey", linetype="longdash") +
xlab(paste0("Axe 1 (", round(variances[1, 2], 1), " %)")) +
ylab(paste0("Axe 2 (", round(variances[2, 2], 1), " %)")) +
scale_color_brewer(palette = "Set2") +
guides(colour = guide_legend(title="")) +
theme_minimal()
Références
Cibois Philippe, 1997, « Les pièges de l’analyse des correspondances », Histoire & Mesure, 12(3), p. 299‑320.
Cibois Philippe, 2000, L’analyse factorielle. Analyse en composantes principales et analyse des correspondances, Paris, PUF.
Denord François, Lagneau-Ymonet Paul, Thine Sylvain, 2011, « Le champ du pouvoir en France », Actes de la recherche en sciences sociales, 190(5), p. 24‑57.
Volle Michel, 1997, Analyse des données, Paris, Economica.
Le Roux Brigitte, Rouanet Henry, 2014, Analyse géométrique des données multidimensionnelles, Paris, Dunod.
Remerciements
Cet article a fait l’objet d’une relecture attentive de la part de Gabriel Alcaras, Sofia Aouani, Samuel Coavoux et Pierre Mercklé. Qu’ils en soient ici bien amicalement remerciés. Les erreurs et autres inexactitudes qui y demeureraient sont bien évidemment de ma seule responsabilité…
OpenEdition vous propose de citer ce billet de la manière suivante :
Anton Perdoncin (6 avril 2020). Visualiser une analyse géométrique des données avec ggplot2 (R/RStudio). QUANTI / Sciences sociales. Consulté le 9 octobre 2024 à l’adresse https://doi.org/10.58079/t4c1
Bonjour,
merci bcp pour ce très clair exposé! Je travaille aussi à visualiser des ACM avec ggplot2 depuis quelques temps et je dois dire que si j’étais tombé sur un billet comme le votre plus tôt, j’aurai gagné bcp de temps.
Je m’interroge néanmoins sur une chose: les échelles des axes (ordonnées, abscisses) ne sont pas les même dans vos figures. Or, j’ai l’impression que cela peut fausser la lecture. Je crois aussi que dans les ouvrages de Leroux et Rouanet et dans d’autres packages R qui permettent de visualiser les des ACM (comme soc.ca), les échelles des x et des y sont les mêmes – dans ggplot2, il suffit d’ajouter “coord_fix()” à l’équation.
Qu’en pensez vous? Est-ce aussi nécessaire que l’idée que je m’en fais?
Meilleures salutations
Pierre Bataille
Bonjour. Merci à vous ! En effet, c’est important, et c’est un oubli de ma part. J’ajoute l’argument coord_fixed() et une petite ligne pour expliquer.
Bonjour,
Merci beaucoup pour ce billet, j’ai cependant un problème sur la fin. Tout va bien jusqu’au 5.2, et là tout se gâte et je n’ai pas trouvé de solution donc je vous sollicite. En gros, j’ai une ACM avec des valeurs manquantes, que je gère avec la matrice disjonctive, point qui a probablement son importance donc je le précise.
Arrivé au 5.2, je génère les tableaux, et là tout s’écroule : les tableaux de contributions, coordonnées, v-tests et cos2, excluent les non-réponses. Et je crois que c’est ce qui fait qu’ensuite quand je regroupe les tableaux, ça donne quelque chose d’étrange avec des variables fantômes. Est-ce que vous auriez une idée de comment régler ce problème ?
Merci en tout cas pour le billet
Bonjour,
Je suppose que le problème vient du contenu de l’objet stockant les résultats de l’ACM. Difficile néanmoins de vous répondre sans voir le script et les données… Vous m’envoyez par mail un extrait de vos données et votre script pour que je puisse y jeter un oeil ? Amicalement.
Je réponds en public si ça peut aider les personnes qui passeraient par là : Anton m’a indiqué que le problème était dû au fait que plusieurs variables partageaient les mêmes noms de modalités et m’a dit de recoder les noms en “variable_modalité”. Cela a réglé le problème.
Sinon merci beaucoup pour l’aide et pour le guide dans son ensemble ça m’a beaucoup aidé !