7.3 Analyse factorielle des correspondances

Comme l’ACP s’intéresse à des corrélations linéaires entre variables quantitatives, elle n’est absolument pas utilisable pour traiter des variables qualitatives. L’Analyse Factorielle des Correspondances sera utile dans ce dernier cas (AFC, ou en anglais Correspondence Analysis ou CA).

À vous de jouer !
h5p

Tout comme pour l’ACP, nous aborderons l’AFC au travers d’un exemple concret en utilisant la fonction ca() (issue du package {ca}) dans SciViews::R("explore").

7.3.1 Enquête sur la science

L’idée de départ de l’AFC est de pouvoir travailler sur des données qualitatives en les transformant en variables quantitatives via une astuce de calcul, et ensuite de réaliser une ACP sur ce tableau quantitatif pour en représenter l’information visuellement sur une carte. Les enquêtes (mais pas seulement) génèrent souvent des quantités importantes de variables qualitatives qu’il faut ensuite analyser. En effet, des sondages proposent la plupart du temps d’évaluer une question sur une échelle de plusieurs niveaux du genre “tout à fait d’accord”, “d’accord”, “pas d’accord”, “pas du tout d’accord”, ce qui donnerait une variable facteur à quatre niveaux ordonnés.

Une grande enquête mondiale a été réalisée en 1993 pour déterminer (entre autres) ce que la population pense de la science en général. Quatre questions sont posées (section 4 de ce questionnaire, voir aussi ici) :

A. Les gens croient trop souvent à la science, et pas assez aux sentiments et à la foi

B. En général, la science moderne fait plus de mal que de bien

C. Tout changement dans la nature apporté par les êtres humains risque d’empirer les choses

D. La science moderne va résoudre nos problèmes relatifs à l’environnement sans faire de grands changements à notre mode de vie

Les réponses possibles sont : 1 = tout à fait d’accord à 5 = pas du tout d’accord. Le jeu de données wg93 du package {ca} reprend les réponses données par les Allemands de l’ouest à ces questions. De plus, les caractéristiques suivantes des répondants sont enregistrées :

  • le sexe (1 = homme, 2 = femme),
  • l’âge (1 = 18-24, 2 = 25-34, 3 = 35-44, 4 = 45-54, 5 = 55-64, 6 = 65+)
  • le niveau d’éducation (1 = primaire, 2 = second. partim, 3 = secondaire, 4 = univ. partim, 5 = univ. cycle 1, 6 = univ. cycle 2+)
wg <- read("wg93", package = "ca")
wg
# # A data.table: 871 x 7
# # Language:     en
#    A     B     C     D     sex   age   edu  
#    <fct> <fct> <fct> <fct> <fct> <fct> <fct>
#  1 2     3     4     3     2     2     3    
#  2 3     4     2     3     1     3     4    
#  3 2     3     2     4     2     3     2    
#  4 2     2     2     2     1     2     3    
#  5 3     3     3     3     1     5     2    
#  6 3     4     4     5     1     3     2    
#  7 3     4     2     4     2     5     2    
#  8 3     4     4     2     1     3     3    
#  9 3     2     2     1     1     3     2    
# 10 3     3     2     2     1     3     2    
# # … with 861 more rows

Ceci est un tableau cas par variables avec sept variables facteurs et 871 cas. Nous commençons par réencoder les niveaux des variables pour plus de clarté :

wg %>.%
  smutate(.,
    A = recode(A, `1` = "++", `2` = "+", `3` = "0", `4` = "-", `5` = "--"),
    B = recode(B, `1` = "++", `2` = "+", `3` = "0", `4` = "-", `5` = "--"),
    C = recode(C, `1` = "++", `2` = "+", `3` = "0", `4` = "-", `5` = "--"),
    D = recode(D, `1` = "++", `2` = "+", `3` = "0", `4` = "-", `5` = "--"),
    sex = recode(sex, `1` = "H", `2` = "F"),
    age = recode(age, `1` = "18-24", `2` = "25-34", `3` = "35-44",
      `4` = "45-54", `5` = "55-64", `6` = "65+"),
    edu = recode(edu, `1` = "primaire", `2` = "sec. part", `3` = "secondaire",
      `4` = "univ. part", `5` = "univ. cycle 1", `6` = "univ. cycle 2")
  ) %->% wg
wg
# # A data.table: 871 x 7
# # Language:     en
#    A     B     C     D     sex   age   edu       
#    <fct> <fct> <fct> <fct> <fct> <fct> <fct>     
#  1 +     0     -     0     F     25-34 secondaire
#  2 0     -     +     0     H     35-44 univ. part
#  3 +     0     +     -     F     35-44 sec. part 
#  4 +     +     +     +     H     25-34 secondaire
#  5 0     0     0     0     H     55-64 sec. part 
#  6 0     -     -     --    H     35-44 sec. part 
#  7 0     -     +     -     F     55-64 sec. part 
#  8 0     -     -     +     H     35-44 secondaire
#  9 0     +     +     ++    H     35-44 sec. part 
# 10 0     0     +     +     H     35-44 sec. part 
# # … with 861 more rows

Par exemple, si nous nous posons la question de l’impact de l’éducation sur l’impression que la science est néfaste (question B), nous pourrons faire l’analyse suivante :

  • Étape 1 : calcul du tableau de contingence à double entrée croisant les réponses à la question B avec le niveau d’éducation des répondants :
table(wg$B, wg$edu)
#     
#      primaire sec. part secondaire univ. part univ. cycle 1 univ. cycle 2
#   ++        6        34         19          6             4             2
#   +        10        93         47         12             5             7
#   0        11        95         55         18            11            15
#   -         7       112         82         37            16            27
#   --        4        44         39         21            13            19
  • Étape 2 : test de Chi2 d’indépendance, voir section 8.2.5 du cours de SDD I. L’hypothèse nulle du test est que les deux variables sont indépendantes l’une de l’autre. L’hypothèse alternative est qu’une dépendance existe. Si la réponse aux questions est indépendante du niveau d’éducation (pas de rejet de H0), notre analyse est terminée. Sinon, il faut approfondir… Choisissons notre seuil \(\alpha\) à 5% avant de réaliser notre test.
chisq.test(wg$B, wg$edu)
# Warning in chisq.test(wg$B, wg$edu): Chi-squared approximation may be incorrect
# 
#   Pearson's Chi-squared test
# 
# data:  wg$B and wg$edu
# X-squared = 42.764, df = 20, p-value = 0.002196

L’avertissement nous prévient qu’une approximation a dû être réalisée, mais le test reste utilisable si la valeur est loin du seuil \(/alpha\). La valeur p de 0,2% est très inférieure à \(\alpha\). Nous rejetons H0. Il y a dépendance entre le niveau d’éducation et la réponse à la question B. OK, mais comment se fait cette dépendance ? C’est ici que l’AFC nous vient en aide.

À vous de jouer !
h5p

-Étape 3 : Si l’hypothèse nulle est rejetée, il faut analyser plus en profondeur. L’AFC va présenter la dépendance de manière claire. La fonction ca() accepte un jeu de données dans l’argument data = et une formule qui spécifie dans le terme de droite les deux variables à croiser séparées par un signe + (~ fact1 + fact2). Donc :

wg_b_edu <- ca(data = wg, ~ B + edu)
wg_b_edu
# 
#  Principal inertias (eigenvalues):
#            1        2        3        4    
# Value      0.043989 0.004191 0.000914 4e-06
# Percentage 89.59%   8.54%    1.86%    0.01%
# 
# 
#  Rows:
#                ++         +         0        -        --
# Mass     0.081515  0.199770  0.235362 0.322618  0.160735
# ChiDist  0.286891  0.274685  0.095627 0.141176  0.341388
# Inertia  0.006709  0.015073  0.002152 0.006430  0.018733
# Dim. 1  -1.140162 -1.293374 -0.406059 0.591115  1.593838
# Dim. 2  -2.211930  0.641674 -0.409771 0.976000 -1.034695
# 
# 
#  Columns:
#          primaire sec. part secondaire univ. part univ. cycle 1 univ. cycle 2
# Mass     0.043628  0.433984   0.277842   0.107922      0.056257      0.080367
# ChiDist  0.427367  0.164446   0.036882   0.279471      0.341163      0.417941
# Inertia  0.007968  0.011736   0.000378   0.008429      0.006548      0.014038
# Dim. 1  -1.722505 -0.773101   0.115183   1.302120      1.428782      1.962905
# Dim. 2  -3.523982  0.381160   0.336612   0.235984     -2.516238      0.135512

Nous retrouvons les valeurs propres (eigenvalues) qui représentent l’inertie exprimée sur les différents axes, avec le pourcentage juste en dessous. Les deux tableaux suivants détaillent les calculs. Il n’est pas indispensable de comprendre tout ce qui s’y trouve, mais notez que les composantes du calcul du Chi2 pour chaque niveau des variables sont reprises à la ligne ChiDist. Nous y reviendrons. La méthode summary() ainsi que le graphique des éboulis via chart$scree() nous donnent une information plus claire sur la contribution de la variabilité totale sur les différents axes. Cela nous aide à décider si le plan réduit aux deux premiers axes que nous utiliserons ensuite est adéquat ou non (pourcentage suffisant = données bien représentées dans ce plan).

summary(wg_b_edu, scree = TRUE, rows = FALSE, columns = FALSE)
# 
# Principal inertias (eigenvalues):
# 
#  dim    value      %   cum%   scree plot               
#  1      0.043989  89.6  89.6  **********************   
#  2      0.004191   8.5  98.1  **                       
#  3      0.000914   1.9 100.0                           
#  4      4e-06000   0.0 100.0                           
#         -------- -----                                 
#  Total: 0.049097 100.0
chart$scree(wg_b_edu)

Ici, les deux premiers axes cumulent plus de 98% de la variation totale. Nous pouvons donc continuer en toute confiance. Le biplot va ici projeter les individus sur la carte (les individus étant en fait les différents niveaux des deux variables, obtenus après réalisation de deux ACP, l’une sur les colonnes et l’autre sur les lignes). Les deux groupes sont représentés par des couleurs différentes, mais sur une même carte. C’est la fonction chart$biplot() qui réalise ce graphique.

chart$biplot(wg_b_edu, choices = c(1, 2))

L’interprétation se fait d’abord sur les points d’une couleur (niveau de la première variable), puis sur ceux de l’autre (niveaux de la seconde variable), et enfin, conjointement. Voici ce que cela donne ici :

  • Les niveaux d’éducation en rouge sont globalement représentés essentiellement de gauche à droite dans un ordre croissant de “études primaires” jusqu’à “études universitaires de second cycle ou plus”.

  • Les réponses à la question B (en bleu turquoise) sont également rangés globalement de gauche à droite (avec une légère inversion, entre + et ++) depuis ceux qui sont tout à fait d’accord que la science moderne est néfaste à la gauche jusqu’à ceux qui ne le sont pas du tout à la droite.

  • Conjointement, on va comparer les points rouges et les bleus et regarder ceux qui se situent dans une direction similaire par rapport au centre d’inertie à la coordonnée {0, 0} matérialisée par la croix grise17. Ainsi, nous constatons que ceux qui ont le niveau éducatif le plus faible (niveau primaire) ont globalement plutôt répondu qu’ils sont tout à fait d’accord (++), alors qu’à l’opposé, les universitaires ne sont pas du tous d’accord avec l’affirmation (--).

L’essentiel se lit ici effectivement sur un seul axe (dimension 1) qui capture environ 90% de la variation totale. Le second axe vient moduler légèrement ce classement avec 8,5% de variabilité, mais sans tout bouleverser.

Ici la lecture de la carte générée par l’AFC est limpide… Et manifestement, nous pouvions légitimement conclure à l’époque qu’un effort éducatif et d’information était nécessaire auprès de la population la moins éduquée pour leur faire prendre conscience de l’intérêt de la science moderne (à moins que cette dernière remarque ne soit un parti pris… d’un universitaire :-).

7.3.2 Des acariens sinon rien

Un deuxième exemple illustre une utilisation légèrement différente de l’AFC. Il s’agit des tableaux de type “espèces par stations”. Ces tableaux contiennent différentes espèces ou autres groupes taxonomiques dénombrés dans plusieurs échantillons prélevés à des stations différentes. Le dénombrement des espèces peut être quantitatif (nombre d’individus observés par unité de surface ou de volume), semi-quantitatif (peu, moyen, beaucoup), ou même binaire (présence ou absence de l’espèce). Ces tableaux sont assimilables, en réalité, à des tableaux de contingence à double entrée qui croisent deux variables qualitatives “espèce” et “station”. C’est comme si la première étape avait déjà été réalisée dans l’analyse précédente du jeu de données wg.

Naturellement, comme il ne s’agit pas réellement d’un tableau de contingence, nous ne réaliserons pas le test Chi2 d’indépendance ici, et nous passerons directement à l’étape de l’AFC. L’exemple choisi concerne des populations d’acariens dans de la litière (acarien se dit “mite” en anglais, d’où le nom du jeu de données issu du package {vegan}).

mite <- read("mite", package = "vegan")
skimr::skim(mite)
Tableau 7.3: Data summary
Name mite
Number of rows 70
Number of columns 35
Key NULL
_______________________
Column type frequency:
numeric 35
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
Brachy 0 1 8.73 10.08 0 3.00 4.5 11.75 42 ▇▁▂▁▁
PHTH 0 1 1.27 2.17 0 0.00 0.0 2.00 8 ▇▂▁▁▁
HPAV 0 1 8.51 7.56 0 4.00 6.5 12.00 37 ▇▅▂▁▁
RARD 0 1 1.21 2.78 0 0.00 0.0 1.00 13 ▇▁▁▁▁
SSTR 0 1 0.31 0.97 0 0.00 0.0 0.00 6 ▇▁▁▁▁
Protopl 0 1 0.37 1.61 0 0.00 0.0 0.00 13 ▇▁▁▁▁
MEGR 0 1 2.19 3.62 0 0.00 1.0 3.00 17 ▇▁▁▁▁
MPRO 0 1 0.16 0.47 0 0.00 0.0 0.00 2 ▇▁▁▁▁
TVIE 0 1 0.83 1.47 0 0.00 0.0 1.00 7 ▇▁▁▁▁
HMIN 0 1 4.91 8.47 0 0.00 0.0 4.75 36 ▇▁▁▁▁
HMIN2 0 1 1.96 3.92 0 0.00 0.0 2.75 20 ▇▁▁▁▁
NPRA 0 1 1.89 2.37 0 0.00 1.0 2.75 10 ▇▂▁▁▁
TVEL 0 1 9.06 10.93 0 0.00 3.0 19.00 42 ▇▂▃▁▁
ONOV 0 1 17.27 18.05 0 5.00 10.5 24.25 73 ▇▂▁▁▁
SUCT 0 1 16.96 13.89 0 7.25 13.5 24.00 63 ▇▅▂▁▁
LCIL 0 1 35.26 88.85 0 1.25 13.0 44.00 723 ▇▁▁▁▁
Oribatl1 0 1 1.89 3.43 0 0.00 0.0 2.75 17 ▇▁▁▁▁
Ceratoz1 0 1 1.29 1.46 0 0.00 1.0 2.00 5 ▇▂▁▁▁
PWIL 0 1 1.09 1.71 0 0.00 0.0 1.00 8 ▇▂▁▁▁
Galumna1 0 1 0.96 1.73 0 0.00 0.0 1.00 8 ▇▁▁▁▁
Stgncrs2 0 1 0.73 1.83 0 0.00 0.0 0.00 9 ▇▁▁▁▁
HRUF 0 1 0.23 0.62 0 0.00 0.0 0.00 3 ▇▁▁▁▁
Trhypch1 0 1 2.61 6.14 0 0.00 0.0 2.00 29 ▇▁▁▁▁
PPEL 0 1 0.17 0.54 0 0.00 0.0 0.00 3 ▇▁▁▁▁
NCOR 0 1 1.13 1.65 0 0.00 0.5 1.75 7 ▇▁▁▁▁
SLAT 0 1 0.40 1.23 0 0.00 0.0 0.00 8 ▇▁▁▁▁
FSET 0 1 1.86 3.18 0 0.00 0.0 2.00 12 ▇▁▁▁▁
Lepidzts 0 1 0.17 0.54 0 0.00 0.0 0.00 3 ▇▁▁▁▁
Eupelops 0 1 0.64 0.99 0 0.00 0.0 1.00 4 ▇▃▁▁▁
Miniglmn 0 1 0.24 0.79 0 0.00 0.0 0.00 5 ▇▁▁▁▁
LRUG 0 1 10.43 12.66 0 0.00 4.5 17.75 57 ▇▃▂▁▁
PLAG2 0 1 0.80 1.79 0 0.00 0.0 1.00 9 ▇▁▁▁▁
Ceratoz3 0 1 1.30 2.20 0 0.00 0.0 2.00 9 ▇▂▁▁▁
Oppiminu 0 1 1.11 1.84 0 0.00 0.0 1.75 9 ▇▂▁▁▁
Trimalc2 0 1 2.07 5.79 0 0.00 0.0 0.00 33 ▇▁▁▁▁

35 espèces d’acariens sont dénombrés en 70 stations différentes, et il n’y a aucune valeur manquante. Comme il ne peut y avoir aucun total de ligne ou de colonne égal à zéro pour le calcul des Chi2 (sinon, on aurait une division par zéro), nous vérifions cela de la façon suivante :

rowSums(mite)
#  [1] 140 268 186 286 199 209 162 126 123 166 216 213 177 269 100  97  90 118 118
# [20] 184 117 172  81  80 123 120 173 111 111  96 130  93 136 194 111 133 139 189
# [39]  94 157  81 140 148  60 158 154 121 113 107 148  91 112 145  49  58 108   8
# [58] 121  90 127  42  13  86  88 112 116 781 111 184 121
colSums(mite)
#   Brachy     PHTH     HPAV     RARD     SSTR  Protopl     MEGR     MPRO 
#      611       89      596       85       22       26      153       11 
#     TVIE     HMIN    HMIN2     NPRA     TVEL     ONOV     SUCT     LCIL 
#       58      344      137      132      634     1209     1187     2468 
# Oribatl1 Ceratoz1     PWIL Galumna1 Stgncrs2     HRUF Trhypch1     PPEL 
#      132       90       76       67       51       16      183       12 
#     NCOR     SLAT     FSET Lepidzts Eupelops Miniglmn     LRUG    PLAG2 
#       79       28      130       12       45       17      730       56 
# Ceratoz3 Oppiminu Trimalc2 
#       91       78      145

De plus, l’AFC est très sensible à la présence de valeurs extrêmes. Il faut être particulièrement attentif à ne pas avoir trop de disparité, surtout en présence d’espèces très abondantes versus d’autres, très rares. Si quelques échantillons contiennent une quantité particulièrement large d’items, cela peut aussi être problématique. Pour des dénombrements ou du semi-quantitatif à plus d’une dizaine de niveaux, la boite de dispersion parallèle est un bon graphique, mais nous devons transformer le tableau de données en format long avec spivot_longer() pour pouvoir le faire, et ensuite, nous présentons les espèces sur l’axe des ordonnées avec coord_flip() :

mite %>.%
  spivot_longer(., everything(), names_to = "species", values_to = "n") %>.%
  chart(., n ~ species) +
    geom_boxplot() +
  labs(x = "Espèces", y = "Observations") +
  coord_flip()

Aïe ! Nous avons ici une magnifique valeur extrême, ainsi que des différences trop nettes entre les espèces les plus abondantes et les plus rares. Nous devons y remédier avant de faire notre AFC. Deux solutions sont possibles :

  1. Soit nous dégradons l’information vers du semi-quantitatif en définissant des niveaux d’abondance, du genre 0, 1-10, 10-20, 20-30, 31+. Nous pouvons réaliser cela avec cut() comme illustré ici sur un exemple fictif.
sample <- c(0, 12, 500, 25, 5)
cut(sample, breaks = c(-1, 0, 10, 20, 30, Inf))
# [1] (-1,0]   (10,20]  (30,Inf] (20,30]  (0,10]  
# Levels: (-1,0] (0,10] (10,20] (20,30] (30,Inf]

Nous voyons que cut() a ici remplacé nos données quantitatives dans sample en une variable qualitative à cinq niveaux clairement libellés. Notez l’astuce du groupe (-1,0] pour capturer les valeurs nulles dans un groupe séparé.

  1. Autre solution, nous transformons les données pour réduire l’écart entre les extrêmes. Typiquement, une transformation \(log(x + 1)\) est efficace pour cela, et fonctionne bien en présence de valeurs nulles aussi. Nous utiliserons cette dernière technique pour notre jeu de données mite. Enfin, nous nous assurons que les stations sont bien numérotées de 1 à 70 en lignes (rownames()).
mite2 <- log1p(as_dtf(mite)) # We want a data.frame to use row names
# Ajouter le numéro des stations explicitement
rownames(mite2) <- 1:nrow(mite2)

Le graphique en boite de dispersion parallèle montre plus d’homogénéité maintenant :

mite2 %>.%
  spivot_longer(., everything(), names_to = "species", values_to = "log_n_1") %>.%
  chart(., log_n_1 ~ species) +
  geom_boxplot() +
  labs(x = "Espèces", y = "Logarithme des observations") +
  coord_flip()

A noter que ce type de graphique ne convient pas pour les données semi-quantitatives ou qualitatives. Nous pouvons réaliser le graphique suivant à la place dans ce cas pour visualiser la distribution des espèces dans les différentes stations (qui fonctionne aussi en quantitatif) :

mite2 %>.%
  smutate(., station = 1:nrow(mite2)) %>.%
  spivot_longer(., Brachy:Trimalc2, names_to = "species", values_to = "n") %>.%
  chart(., species ~ station %fill=% n) +
  geom_raster()

Maintenant que nos données sont vérifiées, nettoyées (phase de préparation), et décrites correctement (phase descriptive), nous pouvons réaliser notre AFC et explorer les particularités de ce jeu de données en vue d’une étude plus approfondie ultérieure (phase exploratoire). L’utilisation de ca() sur un tableau de type contingence à double entrée déjà prêt est ultra-simple. Il suffit de fournir le tableau en question comme seul argument à la fonction.

mite2_ca <- ca(mite2)

Examinons la variabilité sur les différents axes, numériquement et à l’aide du graphe des éboulis :

summary(mite2_ca, scree = TRUE, rows = FALSE, columns = FALSE)
# 
# Principal inertias (eigenvalues):
# 
#  dim    value      %   cum%   scree plot               
#  1      0.366209  31.5  31.5  ********                 
#  2      0.132783  11.4  42.9  ***                      
#  3      0.072315   6.2  49.1  **                       
#  4      0.065787   5.7  54.7  *                        
#  5      0.055872   4.8  59.5  *                        
#  6      0.048122   4.1  63.7  *                        
#  7      0.041834   3.6  67.3  *                        
#  8      0.039072   3.4  70.6  *                        
#  9      0.032183   2.8  73.4  *                        
#  10     0.031300   2.7  76.1  *                        
#  11     0.028809   2.5  78.6  *                        
#  12     0.026949   2.3  80.9  *                        
#  13     0.024701   2.1  83.0  *                        
#  14     0.022729   2.0  84.9                           
#  15     0.020618   1.8  86.7                           
#  16     0.018029   1.5  88.3                           
#  17     0.016836   1.4  89.7                           
#  18     0.014850   1.3  91.0                           
#  19     0.014217   1.2  92.2                           
#  20     0.012553   1.1  93.3                           
#  21     0.010879   0.9  94.2                           
#  22     0.010412   0.9  95.1                           
#  23     0.009703   0.8  96.0                           
#  24     0.008187   0.7  96.7                           
#  25     0.007294   0.6  97.3                           
#  26     0.006689   0.6  97.9                           
#  27     0.005343   0.5  98.3                           
#  28     0.004576   0.4  98.7                           
#  29     0.004020   0.3  99.1                           
#  30     0.003828   0.3  99.4                           
#  31     0.002732   0.2  99.6                           
#  32     0.001955   0.2  99.8                           
#  33     0.001656   0.1  99.9                           
#  34     0.000776   0.1 100.0                           
#         -------- -----                                 
#  Total: 1.163821 100.0
chart$scree(mite2_ca, fill = "cornsilk")

La variabilité décroit rapidement sur les deux premiers axes, et ensuite plus lentement. Les deux premières dimensions ne capturent qu’environ 43% de la variabilité totale. Toutefois, la variation moins brutale par après justifie de ne garder que deux axes. Malgré la représentativité relativement faible dans ce plan, nous verrons que l’AFC reste interprétable et peut fournir des informations utiles, même dans ce cas. Voici la carte (biplot) :

chart$biplot(mite2_ca, choices = c(1, 2))

Nous observons une forme en fer à cheval de la distribution des points. Attention ! Ceci est un artéfact lié au fait que la distance du Chi2 a tendance à rapprocher les extrêmes (“qui se ressemblent dans la différence”, en quelque sorte) alors que notre souhait aurait plutôt été de les placer aux extrémités du graphique. Il faut donc analyser les données comme une transition progressive d’un état A vers un état B le long de la forme en fer à cheval.

Lorsqu’il y a beaucoup de points, ce graphique tend à être très encombré et illisible. L’argument repel = TRUE vient parfois aider en allant placer les labels de telle façon qu’ils se chevauchent le moins possible. Par contre, la forme du nuage de point est du coup un peu moins visible.

chart$biplot(mite2_ca, choices = c(1, 2), repel = TRUE)
# Warning: ggrepel: 22 unlabeled data points (too many overlaps). Consider
# increasing max.overlaps

Nous vous laissons le soin d’effectuer l’analyse complète des points bleus (stations) entre eux, ensuite les points rouges (espèces) entre eux et enfin, conjointement. Notez les stations 44, 59, 65 et 67 qui se détachent et qui sont caractérisées par des espèces qu’on trouve préférentiellement là-bas : Trhypch1 et Trimalc2. Les espèces et stations proches du centre d’inertie, au contraire, ne montrent aucune particularité.

7.3.3 Principe de l’AFC

Maintenant que nous avons découvert la façon de réaliser et d’interpréter une AFC, il est temps d’en comprendre les rouages. Rappelez-vous que la distance du Chi2 quantifie l’écart entre des effectifs observés \(a_i\) et des effectifs théoriques \(\alpha_i\) comme :

\[\chi^2=\sum{\frac{(a_i - \alpha_i)^2}{\alpha_i}}\]

Il y a un terme par cellule du tableau de contingence (que nous appellerons les contributions au Chi2), et par ailleurs, sous l’hypothèse nulle d’indépendance des variables, les \(\alpha_i\) sont connus et dépendent uniquement des sommes des lignes et des colonnes du tableau. En effet, nous avons :

\[\alpha_i = \frac{\textrm{total ligne} . \textrm{total colonne}}{\textrm{total général}}\]

Donc, lors du calcul du Chi2, nous passons par une étape de transformation du tableau de contingence à double entrée où nous substituons les effectifs observés (des entiers, donc du quantitatif discret) par les contributions respectives au Chi2. Or ces contributions sont des valeurs réelles (continues) nulles ou positives. Nous obtenons donc un tableau contenant des valeurs numériques continues qui peut être traité par une ACP classique.

L’ACP va reprojeter les lignes du tableau (qui, rappelons-le, sont les différents niveaux d’une des deux variables qualitatives étudiées) dans un espace réduit. Dans ce contexte, le graphique représentant les variables n’a pas grand intérêt, puisque ces variables sont en réalité fictives, ou si vous préférez, “bricolées”. Par contre, la position des individus les uns par rapport aux autres, et par rapport au centre d’inertie du graphique a une signification importante et interprétable.

Enfin, comme il n’y a aucune raison a priori d’utiliser une variable en ligne et l’autre en colonne, nous pouvons également transposer la table de contingence (les lignes deviennent les colonnes et vice versa). Si nous réalisons une nouvelle ACP sur ce tableau transposé, nous allons reprojeter les niveaux de l’autre variable qualitative sur un autre espace réduit.

Si nous prenons le même nombre de composantes principales des deux côtés, nous aurons la même part de variance reprise dans les deux projections. De plus, nous pouvons superposer les deux représentations en faisant coïncider les deux centres d’inertie en {0, 0}. La difficulté, comme pour tout biplot, est d’arriver à mettre à l’échelle les points d’une représentation par rapport à ceux de l’autre. Cette mise à l’échelle permet d’interpréter les points de couleurs différentes conjointement : des points proches sur le biplot dénotent des correspondances entre les niveaux respectifs des deux variables étudiées, d’où le nom de la méthode, analyse factorielle des correspondances.

Au final, l’AFC apparaît donc comme une variante de l’ACP qui utilise la distance du Chi2 à la place de la distance euclidienne dans l’ACP classique, et qui reflète la symétrie du tableau de contingence (lignes/colonnes versus colonnes/lignes). Il en résulte la possibilité d’analyser ainsi des données qualitatives pour lesquelles la distance et la statistique du Chi2 ont précisément été inventées.

À vous de jouer !

Effectuez maintenant les exercices du tutoriel B07Lb_ca (Analyse factorielle des correspondances).

BioDataScience2::run("B07Lb_ca")

Réalisez le travail B07Ia_acp_afc, partie II.

Travail individuel pour les étudiants inscrits au cours de Science des Données Biologiques II à l’UMONS (Q2 : analyse) à terminer avant le 2023-02-27 23:59:59.

Initiez votre projet GitHub Classroom

Voyez les explications dans le fichier README.md, partie II.

Le projet suivant est un travail par groupe de quatre personnes et se poursuivra au module 8.

Réalisez en groupe le travail B07Ga_doubs, partie I.

Travail en groupe de 4 pour les étudiants inscrits au cours de Science des Données Biologiques II à l’UMONS (Q2 : analyse) à terminer avant le 2023-04-17 23:59:59.

Initiez votre projet GitHub Classroom

Voyez les explications dans le fichier README.md, partie I.

Pour en savoir plus
  • Une courte introduction de l’AFC en vidéo avec résolution d’un exemple volontairement simpliste pour faire comprendre la signification du graphique obtenu et la façon de l’interpréter.

  • Une autre explication qui utilise les packages {FactoMineR} et {factoextra} et qui va plus loin dans les concepts (notamment la qualité de la représentation des points via leur “cos2”, les différents types de biplots, et l’ajout de données supplémentaires).


  1. Attention : les valeurs sur les axes ne sont pas à interpréter quantitativement. Elles ne portent pas une information utile ici.↩︎