9  Facteurs et forcats

Dans R, les facteurs sont utilisés pour représenter des variables catégorielles, c’est-à-dire des variables qui ont un nombre fixé et limité de valeurs possibles (par exemple une variable sexe ou une variable niveau d’éducation).

De telles variables sont parfois représentées sous forme textuelle (vecteurs de type character). Cependant, cela ne permets pas d’indiquer un ordre spécifique aux modalités, à la différence des facteurs.

Note

Lorsque l’on importe des données d’enquêtes, il est fréquent que les variables catégorielles sont codées sous la forme d’un code numérique (par exemple 1 pour femme et 2 pour homme) auquel est associé une étiquette de valeur. C’est notamment le fonctionnement usuel de logiciels tels que SPSS, Stata ou SAS. Les étiquettes de valeurs seront abordés dans un prochain chapitre (voir Chapitre 12).

Au moment de l’analyse (tableaux statistiques, graphiques, modèles de régression…), il sera nécessaire de transformer ces vecteurs avec étiquettes en facteurs.

9.1 Création d’un facteur

Le plus simple pour créer un facteur est de partir d’un vecteur textuel et d’utiliser la fonction factor().

x <- c("nord", "sud", "sud", "est", "est", "est")
x |> 
  factor()
[1] nord sud  sud  est  est  est 
Levels: est nord sud

Par défaut, les niveaux du facteur obtenu correspondent aux valeurs uniques du facteur textuel, triés par ordre alphabétique. Si l’on veut contrôler l’ordre des niveaux, et éventuellement indiquer un niveau absent des données, on utilisera l’argument levels de factor().

x |> 
  factor(levels = c("nord", "est", "sud", "ouest"))
[1] nord sud  sud  est  est  est 
Levels: nord est sud ouest

Si une valeur observée dans les données n’est pas indiqué dans levels, elle sera silencieusement convertie en valeur manquante (NA).

x |> 
  factor(levels = c("nord", "sud"))
[1] nord sud  sud  <NA> <NA> <NA>
Levels: nord sud

Si l’on veut être averti par un warning dans ce genre de situation, on pourra avoir plutôt recours à la fonction readr::parse_factor() du package readr, qui, le cas échéant, renverra un tableau avec les problèmes rencontrés.

x |> 
  readr::parse_factor(levels = c("nord", "sud"))
Warning: 3 parsing failures.
row col           expected actual
  4  -- value in level set    est
  5  -- value in level set    est
  6  -- value in level set    est
[1] nord sud  sud  <NA> <NA> <NA>
attr(,"problems")
# A tibble: 3 × 4
    row   col expected           actual
  <int> <int> <chr>              <chr> 
1     4    NA value in level set est   
2     5    NA value in level set est   
3     6    NA value in level set est   
Levels: nord sud

Une fois un facteur créé, on peut accéder à la liste de ses étiquettes avec levels().

f <- factor(x)
levels(f)
[1] "est"  "nord" "sud" 

Dans certaines situations (par exemple pour la réalisation d’une régression logistique ordinale), on peut avoir avoir besoin d’indiquer que les modalités du facteur sont ordonnées hiérarchiquement. Dans ce cas là, on aura simplement recours à ordered() pour créer/convertir notre facteur.

c("supérieur", "primaire", "secondaire", "primaire", "supérieur") |> 
  ordered(levels = c("primaire", "secondaire", "supérieur"))
[1] supérieur  primaire   secondaire primaire   supérieur 
Levels: primaire < secondaire < supérieur

Techniquement, les valeurs d’un facteur sont stockés de manière interne à l’aide de nombres entiers, dont la valeur représente la position de l’étiquette correspondante dans l’attribut levels. Ainsi, un facteur à n modalités sera toujours codé avec les nombre entiers allant de 1 à n.

class(f)
[1] "factor"
typeof(f)
[1] "integer"
as.integer(f)
[1] 2 3 3 1 1 1
as.character(f)
[1] "nord" "sud"  "sud"  "est"  "est"  "est" 

9.2 Changer l’ordre des modalités

Le package forcats, chargé par défaut lorsque l’on exécute la commande library(tidyverse), fournie plusieurs fonctions pour manipuler des facteurs. Pour donner des exemples d’utilisation de ces différentes fonctions, nous allons utiliser le jeu de données hdv2003 du package questionr.

library(tidyverse)
data("hdv2003", package = "questionr")

Considérons la variable qualif qui indique le niveau de qualification des enquêtés. On peut voir la liste des niveaux de ce facteur, et leur ordre, avec levels(), ou en effectuant un tri à plat avec la fonction questionr::freq().

hdv2003$qualif |> 
  levels()
[1] "Ouvrier specialise"       "Ouvrier qualifie"        
[3] "Technicien"               "Profession intermediaire"
[5] "Cadre"                    "Employe"                 
[7] "Autre"                   
hdv2003$qualif |> 
  questionr::freq()
                           n    % val%
Ouvrier specialise       203 10.2 12.3
Ouvrier qualifie         292 14.6 17.7
Technicien                86  4.3  5.2
Profession intermediaire 160  8.0  9.7
Cadre                    260 13.0 15.7
Employe                  594 29.7 35.9
Autre                     58  2.9  3.5
NA                       347 17.3   NA

Parfois, on a simplement besoin d’inverser l’ordre des facteurs, ce qui peut se faire facilement avec la fonction forcats::fct_rev(). Elle renvoie le facteur fourni en entrée en ayant inverser l’ordre des modalités (mais sans modifier l’ordre des valeurs dans le vecteur).

hdv2003$qualif |> 
  fct_rev() |> 
  questionr::freq()
                           n    % val%
Autre                     58  2.9  3.5
Employe                  594 29.7 35.9
Cadre                    260 13.0 15.7
Profession intermediaire 160  8.0  9.7
Technicien                86  4.3  5.2
Ouvrier qualifie         292 14.6 17.7
Ouvrier specialise       203 10.2 12.3
NA                       347 17.3   NA

Pour plus de contrôle, on utilisera forcats::fct_relevel() où l’on indique l’ordre souhaité des modalités. On peut également seulement indiquer les premières modalités, les autres seront ajoutées à la fin sans changer leur ordre.

hdv2003$qualif |> 
  fct_relevel("Cadre", "Autre", "Technicien", "Employe") |> 
  questionr::freq()
                           n    % val%
Cadre                    260 13.0 15.7
Autre                     58  2.9  3.5
Technicien                86  4.3  5.2
Employe                  594 29.7 35.9
Ouvrier specialise       203 10.2 12.3
Ouvrier qualifie         292 14.6 17.7
Profession intermediaire 160  8.0  9.7
NA                       347 17.3   NA

La fonction forcats::fct_infreq() ordonne les modalités de celle la plus fréquente à celle la moins fréquente (nombre d’observations) :

hdv2003$qualif |> 
  fct_infreq() |> 
  questionr::freq()
                           n    % val%
Employe                  594 29.7 35.9
Ouvrier qualifie         292 14.6 17.7
Cadre                    260 13.0 15.7
Ouvrier specialise       203 10.2 12.3
Profession intermediaire 160  8.0  9.7
Technicien                86  4.3  5.2
Autre                     58  2.9  3.5
NA                       347 17.3   NA

Pour inverser l’ordre, on combinera forcats::fct_infreq() avec forcats::fct_rev().

hdv2003$qualif |> 
  fct_infreq() |> 
  fct_rev() |> 
  questionr::freq()
                           n    % val%
Autre                     58  2.9  3.5
Technicien                86  4.3  5.2
Profession intermediaire 160  8.0  9.7
Ouvrier specialise       203 10.2 12.3
Cadre                    260 13.0 15.7
Ouvrier qualifie         292 14.6 17.7
Employe                  594 29.7 35.9
NA                       347 17.3   NA

Dans certains cas, on souhaite créer un facteur dont les modalités sont triées selon leur ordre d'apparition dans le jeu de données. Pour cela, on aura recours à forcats::fct_inorder().

v <- c("c", "a", "d", "b", "a", "c")
factor(v)
[1] c a d b a c
Levels: a b c d
fct_inorder(v)
[1] c a d b a c
Levels: c a d b

La fonction forcats::fct_reorder() permets de trier les modalités en fonction d’une autre variable. Par exemple, si je souhaite trier les modalités de la variable qualif en fonction de l’âge moyen (dans chaque modalité) :

hdv2003$qualif_tri_age <-
  hdv2003$qualif |> 
  fct_reorder(hdv2003$age, .fun = mean)
hdv2003 |> 
  dplyr::group_by(qualif_tri_age) |> 
  dplyr::summarise(age_moyen = mean(age))
# A tibble: 8 × 2
  qualif_tri_age           age_moyen
  <fct>                        <dbl>
1 Technicien                    45.9
2 Employe                       46.7
3 Autre                         47.0
4 Ouvrier specialise            48.9
5 Profession intermediaire      49.1
6 Cadre                         49.7
7 Ouvrier qualifie              50.0
8 <NA>                          47.9
Astuce

questionr propose une interface graphique afin de faciliter les opérations de ré-ordonnancement manuel. Pour la lancer, sélectionner le menu Addins puis Levels ordering, ou exécuter la fonction questionr::iorder() en lui passant comme paramètre le facteur à réordonner.

Interface graphique de questionr::iorder()

Une démonstration en vidéo de cet add-in est disponible dans le webin-R #05 (recoder des variables) sur [YouTube](https://youtu.be/CokvTbtWdwc?t=3934).

9.3 Modifier les modalités

Pour modifier le nom des modalités, on pourra avoir recours à forcats::fct_recode() avec une syntaxe de la forme "nouveau nom" = "ancien nom".

hdv2003$sexe |> 
  questionr::freq()
         n  % val%
Homme  899 45   45
Femme 1101 55   55
hdv2003$sexe <- 
  hdv2003$sexe |> 
  fct_recode(f = "Femme", m = "Homme")
hdv2003$sexe |> 
  questionr::freq()
     n  % val%
m  899 45   45
f 1101 55   55

On peut également fusionner des modalités ensemble en leur attribuant le même nom.

hdv2003$nivetud |> 
  questionr::freq()
                                                                  n    % val%
N'a jamais fait d'etudes                                         39  2.0  2.1
A arrete ses etudes, avant la derniere annee d'etudes primaires  86  4.3  4.6
Derniere annee d'etudes primaires                               341 17.0 18.1
1er cycle                                                       204 10.2 10.8
2eme cycle                                                      183  9.2  9.7
Enseignement technique ou professionnel court                   463 23.2 24.5
Enseignement technique ou professionnel long                    131  6.6  6.9
Enseignement superieur y compris technique superieur            441 22.0 23.4
NA                                                              112  5.6   NA
hdv2003$instruction <- 
  hdv2003$nivetud |> 
  fct_recode(
    "primaire" = "N'a jamais fait d'etudes",
    "primaire" = "A arrete ses etudes, avant la derniere annee d'etudes primaires",
    "primaire" = "Derniere annee d'etudes primaires",
    "secondaire" = "1er cycle",
    "secondaire" = "2eme cycle",
    "technique/professionnel" = "Enseignement technique ou professionnel court",
    "technique/professionnel" = "Enseignement technique ou professionnel long",
    "supérieur" = "Enseignement superieur y compris technique superieur"
  )
hdv2003$instruction |> 
  questionr::freq()
                          n    % val%
primaire                466 23.3 24.7
secondaire              387 19.4 20.5
technique/professionnel 594 29.7 31.5
supérieur               441 22.0 23.4
NA                      112  5.6   NA
Interface graphique

Le packagequestionr propose une interface graphique facilitant le recodage des modalités d’une variable qualitative. L’objectif est de permettre à la personne qui l’utilise de saisir les nouvelles valeurs dans un formulaire, et de générer ensuite le code R correspondant au recodage indiqué.

Pour utiliser cette interface, sous RStudio vous pouvez aller dans le menu Addins (présent dans la barre d’outils principale) puis choisir Levels recoding. Sinon, vous pouvez lancer dans la console la fonction questionr::irec() en lui passant comme paramètre la variable à recoder.

Interface graphique de questionr::iorder()

Astuce

Une démonstration en vidéo de cet add-in est disponible dans le webin-R #05 (recoder des variables) sur [YouTube](https://youtu.be/CokvTbtWdwc?t=3387).

La fonction forcats::fct_collapse() est une variante de forcats::fct_recode() pour indiquer les fusions de modalités. La même recodification s’écrirait alors :

hdv2003$instruction <- 
  hdv2003$nivetud |> 
  fct_collapse(
    "primaire" = c(
      "N'a jamais fait d'etudes",
      "A arrete ses etudes, avant la derniere annee d'etudes primaires",
      "Derniere annee d'etudes primaires"
    ),
    "secondaire" = c(
      "1er cycle",
      "2eme cycle"
    ),
    "technique/professionnel" = c(
      "Enseignement technique ou professionnel court",
      "Enseignement technique ou professionnel long"
    ),
    "supérieur" = "Enseignement superieur y compris technique superieur"
  )

Pour transformer les valeurs manquantes (NA) en une modalité explicite, on pourra avoir recours à forcats::fct_na_value_to_level()1.

  • 1 Cette fonction s’appelait précédemment forcats::fct_explicit_na() et a été renommée depuis la version 1.0.0 de {forcats}.

  • hdv2003$instruction <-
      hdv2003$instruction |> 
      fct_na_value_to_level(level = "(manquant)")
    hdv2003$instruction |> 
      questionr::freq()
                              n    % val%
    primaire                466 23.3 23.3
    secondaire              387 19.4 19.4
    technique/professionnel 594 29.7 29.7
    supérieur               441 22.0 22.0
    (manquant)              112  5.6  5.6

    Plusieurs fonctions permettent de regrouper plusieurs modalités dans une modalité autres.

    Par exemple, avec forcats::fct_other(), on pourra indiquer les modalités à garder.

    hdv2003$qualif |> 
      questionr::freq()
                               n    % val%
    Ouvrier specialise       203 10.2 12.3
    Ouvrier qualifie         292 14.6 17.7
    Technicien                86  4.3  5.2
    Profession intermediaire 160  8.0  9.7
    Cadre                    260 13.0 15.7
    Employe                  594 29.7 35.9
    Autre                     58  2.9  3.5
    NA                       347 17.3   NA
    hdv2003$qualif |> 
      fct_other(keep = c("Technicien", "Cadre", "Employe")) |> 
      questionr::freq()
                 n    % val%
    Technicien  86  4.3  5.2
    Cadre      260 13.0 15.7
    Employe    594 29.7 35.9
    Other      713 35.6 43.1
    NA         347 17.3   NA

    La fonction forcats::fct_lump_n() permets de ne conserver que les modalités les plus fréquentes et de regrouper les autres dans une modalité autres.

    hdv2003$qualif |> 
      fct_lump_n(n = 4, other_level = "Autres") |> 
      questionr::freq()
                         n    % val%
    Ouvrier specialise 203 10.2 12.3
    Ouvrier qualifie   292 14.6 17.7
    Cadre              260 13.0 15.7
    Employe            594 29.7 35.9
    Autres             304 15.2 18.4
    NA                 347 17.3   NA

    Et forcats::fct_lump_min() celles qui ont un minimum d’observations.

    hdv2003$qualif |> 
      fct_lump_min(min = 200, other_level = "Autres") |> 
      questionr::freq()
                         n    % val%
    Ouvrier specialise 203 10.2 12.3
    Ouvrier qualifie   292 14.6 17.7
    Cadre              260 13.0 15.7
    Employe            594 29.7 35.9
    Autres             304 15.2 18.4
    NA                 347 17.3   NA

    Il peut arriver qu’une des modalités d’un facteur ne soit pas représentée dans les données.

    v <- factor(
      c("a", "a", "b", "a"),
      levels = c("a", "b", "c")
    )
    questionr::freq(v)
      n  % val%
    a 3 75   75
    b 1 25   25
    c 0  0    0

    Pour calculer certains tests statistiques ou faire tourner un modèle, ces modalités sans observation peuvent être problématiques. forcats::fct_drop() permet de supprimer les modalités qui n’apparaissent pas dans les données.

    v
    [1] a a b a
    Levels: a b c
    v |> fct_drop()
    [1] a a b a
    Levels: a b

    À l’inverse, forcats::fct_expand() permet d’ajouter une ou plusieurs modalités à un facteur.

    v
    [1] a a b a
    Levels: a b c
    v |> fct_expand("d", "e")
    [1] a a b a
    Levels: a b c d e

    9.4 Découper une variable numérique en classes

    Il est fréquent d’avoir besoin de découper une variable numérique en une variable catégorielles (un facteur) à plusieurs modalités, par exemple pour créer des groupes d’âges à partir d’une variable age.

    On utilise pour cela la fonction cut() qui prend, outre la variable à découper, un certain nombre d’arguments :

    • breaks indique soit le nombre de classes souhaité, soit, si on lui fournit un vecteur, les limites des classes ;
    • labels permet de modifier les noms de modalités attribués aux classes ;
    • include.lowest et right influent sur la manière dont les valeurs situées à la frontière des classes seront inclues ou exclues ;
    • dig.lab indique le nombre de chiffres après la virgule à conserver dans les noms de modalités.

    Prenons tout de suite un exemple et tentons de découper la variable age en cinq classes :

    hdv2003 <-
      hdv2003 |> 
      mutate(groupe_ages = cut(age, 5))
    hdv2003$groupe_ages |> questionr::freq()
                  n    % val%
    (17.9,33.8] 454 22.7 22.7
    (33.8,49.6] 628 31.4 31.4
    (49.6,65.4] 556 27.8 27.8
    (65.4,81.2] 319 16.0 16.0
    (81.2,97.1]  43  2.1  2.1

    Par défaut R nous a bien créé cinq classes d’amplitudes égales. La première classe va de 17,9 à 33,8 ans (en fait de 17 à 32), etc.

    Les frontières de classe seraient plus présentables si elles utilisaient des nombres ronds. On va donc spécifier manuellement le découpage souhaité, par tranches de 20 ans :

    hdv2003 <-
      hdv2003 |> 
      mutate(groupe_ages = cut(age, c(18, 20, 40, 60, 80, 97)))
    hdv2003$groupe_ages |> questionr::freq()
              n    % val%
    (18,20]  55  2.8  2.8
    (20,40] 660 33.0 33.3
    (40,60] 780 39.0 39.3
    (60,80] 436 21.8 22.0
    (80,97]  52  2.6  2.6
    NA       17  0.9   NA

    Les symboles dans les noms attribués aux classes ont leur importance : ( signifie que la frontière de la classe est exclue, tandis que [ signifie qu’elle est incluse. Ainsi, (20,40] signifie « strictement supérieur à 20 et inférieur ou égal à 40 ».

    On remarque que du coup, dans notre exemple précédent, la valeur minimale, 18, est exclue de notre première classe, et qu’une observation est donc absente de ce découpage. Pour résoudre ce problème on peut soit faire commencer la première classe à 17, soit utiliser l’option include.lowest=TRUE :

    hdv2003 <-
      hdv2003 |> 
      mutate(groupe_ages = cut(
        age, 
        c(18, 20, 40, 60, 80, 97),
        include.lowest = TRUE
      ))
    hdv2003$groupe_ages |> questionr::freq()
              n    % val%
    [18,20]  72  3.6  3.6
    (20,40] 660 33.0 33.0
    (40,60] 780 39.0 39.0
    (60,80] 436 21.8 21.8
    (80,97]  52  2.6  2.6

    On peut également modifier le sens des intervalles avec l’option right=FALSE :

    hdv2003 <-
      hdv2003 |> 
      mutate(groupe_ages = cut(
        age, 
        c(18, 20, 40, 60, 80, 97),
        include.lowest = TRUE,
        right = FALSE
      ))
    hdv2003$groupe_ages |> questionr::freq()
              n    % val%
    [18,20)  48  2.4  2.4
    [20,40) 643 32.1 32.1
    [40,60) 793 39.6 39.6
    [60,80) 454 22.7 22.7
    [80,97]  62  3.1  3.1
    Interface graphique

    Il n’est pas nécessaire de connaître toutes les options de cut(). Le package questionr propose là encore une interface graphique permettant de visualiser l’effet des différents paramètres et de générer le code R correspondant.

    Pour utiliser cette interface, sous RStudio vous pouvez aller dans le menu Addins (présent dans la barre d’outils principale) puis choisir Numeric range dividing. Sinon, vous pouvez lancer dans la console la fonction questionr::icut() en lui passant comme paramètre la variable numérique à découper.

    Interface graphique de questionr::iorder() Une démonstration en vidéo de cet add-in est disponible dans le webin-R #05 (recoder des variables) sur [YouTube](https://youtu.be/CokvTbtWdwc?t=2795).