20  Échelles de Likert

Les échelles de Likert tirent leur nom du psychologue américain Rensis Likert qui les a développées. Elles sont le plus souvent utilisées pour des variables d’opinion. Elles sont codées sous forme de variable catégorielle et chaque item est codé selon une graduation comprenant en général cinq ou sept choix de réponse, par exemple : Tout à fait d’accord, D’accord, Ni en désaccord ni d’accord, Pas d’accord, Pas du tout d’accord.

Pour les échelles à nombre impair de choix, le niveau central permet d’exprimer une absence d’avis, ce qui rend inutile une modalité « Ne sait pas ». Les échelles à nombre pair de modalités voient l’omission de la modalité neutre et sont dites « à choix forcé ».

20.1 Exemple de données

Générons un jeu de données qui nous servira pour les différents exemples.

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(labelled)
niveaux <- c(
  "Pas du tout d'accord",
  "Plutôt pas d'accord",
  "Ni d'accord, ni pas d'accord",
  "Plutôt d'accord",
  "Tout à fait d'accord"
)
set.seed(42)
df <-
  tibble(
    groupe = sample(c("A", "B"), 150, replace = TRUE),
    q1 = sample(niveaux, 150, replace = TRUE),
    q2 = sample(niveaux, 150, replace = TRUE, prob = 5:1),
    q3 = sample(niveaux, 150, replace = TRUE, prob = 1:5),
    q4 = sample(niveaux, 150, replace = TRUE, prob = 1:5),
    q5 = sample(c(niveaux, NA), 150, replace = TRUE),
    q6 = sample(niveaux, 150, replace = TRUE, prob = c(1, 0, 1, 1, 0))
  ) |> 
  mutate(across(q1:q6, ~ factor(.x, levels = niveaux))) |> 
  set_variable_labels(
    q1 = "Première question",
    q2 = "Seconde question",
    q3 = "Troisième question",
    q4 = "Quatrième question",
    q5 = "Cinquième question",
    q6 = "Sixième question"
  )

20.2 Tableau de fréquence

On peut tout à fait réaliser un tableau de fréquence classique avec gtsummary::tbl_summary().

library(gtsummary)
df |> 
  tbl_summary(include = q1:q6)

Characteristic

N = 150

1
Première question
    Pas du tout d'accord 39 (26%)
    Plutôt pas d'accord 32 (21%)
    Ni d'accord, ni pas d'accord 25 (17%)
    Plutôt d'accord 30 (20%)
    Tout à fait d'accord 24 (16%)
Seconde question
    Pas du tout d'accord 56 (37%)
    Plutôt pas d'accord 44 (29%)
    Ni d'accord, ni pas d'accord 19 (13%)
    Plutôt d'accord 26 (17%)
    Tout à fait d'accord 5 (3.3%)
Troisième question
    Pas du tout d'accord 8 (5.3%)
    Plutôt pas d'accord 17 (11%)
    Ni d'accord, ni pas d'accord 29 (19%)
    Plutôt d'accord 43 (29%)
    Tout à fait d'accord 53 (35%)
Quatrième question
    Pas du tout d'accord 11 (7.3%)
    Plutôt pas d'accord 19 (13%)
    Ni d'accord, ni pas d'accord 31 (21%)
    Plutôt d'accord 40 (27%)
    Tout à fait d'accord 49 (33%)
Cinquième question
    Pas du tout d'accord 33 (26%)
    Plutôt pas d'accord 25 (20%)
    Ni d'accord, ni pas d'accord 28 (22%)
    Plutôt d'accord 25 (20%)
    Tout à fait d'accord 16 (13%)
    Unknown 23
Sixième question
    Pas du tout d'accord 50 (33%)
    Plutôt pas d'accord 0 (0%)
    Ni d'accord, ni pas d'accord 50 (33%)
    Plutôt d'accord 50 (33%)
    Tout à fait d'accord 0 (0%)
1

n (%)

Cependant, cela produit un tableau inutilement long, d’autant plus que les variables q1 à q6 ont les mêmes modalités de réponse. La fonction gtsummary::tbl_likert() offre un affichage plus compact.

df |> 
  tbl_likert(
    include = q1:q6
  )

Characteristic

Pas du tout d’accord

Plutôt pas d’accord

Ni d’accord, ni pas d’accord

Plutôt d’accord

Tout à fait d’accord

Première question 39 (26%) 32 (21%) 25 (17%) 30 (20%) 24 (16%)
Seconde question 56 (37%) 44 (29%) 19 (13%) 26 (17%) 5 (3.3%)
Troisième question 8 (5.3%) 17 (11%) 29 (19%) 43 (29%) 53 (35%)
Quatrième question 11 (7.3%) 19 (13%) 31 (21%) 40 (27%) 49 (33%)
Cinquième question 33 (26%) 25 (20%) 28 (22%) 25 (20%) 16 (13%)
Sixième question 50 (33%) 0 (0%) 50 (33%) 50 (33%) 0 (0%)

On peut utiliser add_n() pour ajouter les effectifs totaux.

df |> 
  tbl_likert(
    include = q1:q6,
    statistic = ~ "{p}%"
  ) |> 
  add_n()

Characteristic

N

Pas du tout d’accord

Plutôt pas d’accord

Ni d’accord, ni pas d’accord

Plutôt d’accord

Tout à fait d’accord

Première question 150 26% 21% 17% 20% 16%
Seconde question 150 37% 29% 13% 17% 3.3%
Troisième question 150 5.3% 11% 19% 29% 35%
Quatrième question 150 7.3% 13% 21% 27% 33%
Cinquième question 127 26% 20% 22% 20% 13%
Sixième question 150 33% 0% 33% 33% 0%
Astuce

Dans certains contextes, il est envisageable de traiter notre variable ordinale comme un score numérique. Ici, nous allons attribuer les valeurs -2, -1, 0, +1 et +2 à nos 5 modalités. Dès lors, nous pourrions être intéressé de rajouter à notre tableau le score moyen. Cela est possible en quelques étapes :

  1. transformer nos facteurs en scores : les fonctions as.integer() ou unclass() permettent de transformer un facteur en valeurs numériques (1 pour la première modalité, 2 pour la seconde, etc.). Dans le cas présent, il est préférable d’utiliser unclass() qui préserve les étiquettes de variables ce qui n’est pas le cas de as.integer(). Il ne faut pas oublier de retirer 3 pour obtenir des scores allant de -2 à +2. La fonction dplyr::across() permet d’effectuer l’opération sur plusieurs variables en même temps.

  2. calculer / générer un tableau statistique avec une colonne par statistique, ce qui se fait avec gtsummary::tbl_wide_summary() qui est très similaire à gtsummary::tbl_summary().

  3. mettre les deux tableaux dans une liste et les fusionner avec gtsummary::tbl_merge().

list(
  df |> tbl_likert(include = q1:q6),
  tbl_wide_summary(
    df|> mutate(across(q1:q6, \(x) unclass(x) - 3)),
    statistic = c("{mean}", "{sd}"),
    type = ~ "continuous",
    include = q1:q6,
    digits = ~ 1
  )
) |>
  tbl_merge(tab_spanner = FALSE)

Characteristic

Pas du tout d’accord

Plutôt pas d’accord

Ni d’accord, ni pas d’accord

Plutôt d’accord

Tout à fait d’accord

Mean

SD

Première question 39 (26%) 32 (21%) 25 (17%) 30 (20%) 24 (16%) -0.2 1.4
Seconde question 56 (37%) 44 (29%) 19 (13%) 26 (17%) 5 (3.3%) -0.8 1.2
Troisième question 8 (5.3%) 17 (11%) 29 (19%) 43 (29%) 53 (35%) 0.8 1.2
Quatrième question 11 (7.3%) 19 (13%) 31 (21%) 40 (27%) 49 (33%) 0.6 1.3
Cinquième question 33 (26%) 25 (20%) 28 (22%) 25 (20%) 16 (13%) -0.3 1.4
Sixième question 50 (33%) 0 (0%) 50 (33%) 50 (33%) 0 (0%) -0.3 1.3

20.3 Représentations graphiques

Le package ggstats propose une fonction ggstats::gglikert() pour représenter des données de Likert sous la forme d’un diagramme en barres centré sur la modalité centrale.

library(ggstats)
gglikert(df, include = q1:q6)

Par défaut, les pourcentages totaux ne prennent pas en compte la modalité centrale (lorsque le nombre de modalité est impair). On peut inclure la modalité centrale avec totals_include_center = TRUE, auquel cas la modalité centrale seront comptabilisée pour moitié de chaque côté. Le paramètre sort permet de trier les modalités (voir l’aide de ggstats::gglikert() pour plus de détails sur les différentes méthodes de tri).

df |> 
  gglikert(
    include = q1:q6,
    totals_include_center = TRUE,
    sort = "ascending"
  ) +
  guides(
    fill = guide_legend(nrow = 2)
  )

Il est possible de séparer les résultats par sous-groupe avec des facettes.

df |> 
  gglikert(
    include = q1:q6,
    facet_cols = vars(groupe)
  )

df |> 
  gglikert(
    include = q1:q6,
    y = "groupe",
    facet_rows = vars(.question),
    facet_label_wrap = 15
  )

Une représentation alternative consiste à réaliser un graphique en barres classiques, ce que l’on peut aisément obtenir avec ggstats::gglikert_stacked().

df |>
  gglikert_stacked(
    include = q1:q6,
    sort = "ascending",
    add_median_line = TRUE
  )