11  Manipuler du texte avec stringr

Les fonctions de forcats vues précédemment permettent de modifier des modalités d’une variables qualitative globalement. Mais parfois on a besoin de manipuler le contenu même du texte d’une variable de type chaîne de caractères : combiner, rechercher, remplacer…

On va utiliser ici les fonctions de l’extension stringr. Celle-ci fait partie du coeur du tidyverse, elle est donc automatiquement chargée avec :

library(tidyverse)
Note

stringr est en fait une interface simplifiée aux fonctions d’une autre extension, stringi. Si les fonctions de stringr ne sont pas suffisantes ou si vous manipulez beaucoup de chaînes de caractères, n’hésitez pas à vous reporter à la documentation de stringi.

Dans ce qui suit on va utiliser le court tableau d’exemple d suivant :

d <- tibble(
  nom = c("Mr Félicien Machin", "Mme Raymonde Bidule", "M. Martial Truc", "Mme Huguette Chose"),
  adresse = c("3 rue des Fleurs", "47 ave de la Libération", "12 rue du 17 octobre 1961", "221 avenue de la Libération"),
  ville = c("Nouméa", "Marseille", "Vénissieux", "Marseille")
)
nom adresse ville
Mr Félicien Machin 3 rue des Fleurs Nouméa
Mme Raymonde Bidule 47 ave de la Libération Marseille
M. Martial Truc 12 rue du 17 octobre 1961 Vénissieux
Mme Huguette Chose 221 avenue de la Libération Marseille

11.1 Expressions régulières

Les fonctions présentées ci-dessous sont pour la plupart prévues pour fonctionner avec des expressions régulières. Celles-ci constituent un mini-langage, qui peut paraître assez cryptique, mais qui est très puissant pour spécifier des motifs de chaînes de caractères.

Elles permettent par exemple de sélectionner le dernier mot avant la fin d’une chaîne, l’ensemble des suites alphanumériques commençant par une majuscule, des nombres de 3 ou 4 chiffres situés en début de chaîne, et beaucoup beaucoup d’autres choses encore bien plus complexes.

Pour donner un exemple concret, l’expression régulière suivante permet de détecter une adresse de courrier électronique1 :

[\w\d+.-_]+@[\w\d.-]+\.[a-zA-Z]{2,}

Par souci de simplicité, dans ce qui suit les exemples seront donnés autant que possible avec de simples chaînes de caractères, sans expression régulière. Mais si vous pensez manipuler des données textuelles, il peut être très utile de s’intéresser à cette syntaxe.

11.2 Concaténer des chaînes

La première opération de base consiste à concaténer des chaînes de caractères entre elles. On peut le faire avec la fonction paste.

Par exemple, si on veut concaténer l’adresse et la ville :

paste(d$adresse, d$ville)
#> [1] "3 rue des Fleurs Nouméa"              
#> [2] "47 ave de la Libération Marseille"    
#> [3] "12 rue du 17 octobre 1961 Vénissieux" 
#> [4] "221 avenue de la Libération Marseille"

Par défaut, paste concatène en ajoutant un espace entre les différentes chaînes. On peut spécifier un autre séparateur avec son argument sep :

paste(d$adresse, d$ville, sep = " - ")
#> [1] "3 rue des Fleurs - Nouméa"              
#> [2] "47 ave de la Libération - Marseille"    
#> [3] "12 rue du 17 octobre 1961 - Vénissieux" 
#> [4] "221 avenue de la Libération - Marseille"

Il existe une variante, paste0, qui concatène sans mettre de séparateur, et qui est légèrement plus rapide :

paste0(d$adresse, d$ville)
#> [1] "3 rue des FleursNouméa"              
#> [2] "47 ave de la LibérationMarseille"    
#> [3] "12 rue du 17 octobre 1961Vénissieux" 
#> [4] "221 avenue de la LibérationMarseille"
Note

À noter que paste et paste0 sont des fonctions R de base. L’équivalent pour stringr se nomme str_c.

Parfois on cherche à concaténer les différents éléments d’un vecteur non pas avec ceux d’un autre vecteur, comme on l’a fait précédemment, mais entre eux. Dans ce cas paste seule ne fera rien :

paste(d$ville)
#> [1] "Nouméa"     "Marseille"  "Vénissieux" "Marseille"

Il faut lui ajouter un argument collapse, avec comme valeur la chaîne à utiliser pour concaténer les éléments :

paste(d$ville, collapse = ", ")
#> [1] "Nouméa, Marseille, Vénissieux, Marseille"

11.3 Convertir en majuscules / minuscules

Les fonctions str_to_lower, str_to_upper et str_to_title permettent respectivement de mettre en minuscules, mettre en majuscules, ou de capitaliser les éléments d’un vecteur de chaînes de caractères :

str_to_lower(d$nom)
#> [1] "mr félicien machin"  "mme raymonde bidule" "m. martial truc"    
#> [4] "mme huguette chose"
str_to_upper(d$nom)
#> [1] "MR FÉLICIEN MACHIN"  "MME RAYMONDE BIDULE" "M. MARTIAL TRUC"    
#> [4] "MME HUGUETTE CHOSE"
str_to_title(d$nom)
#> [1] "Mr Félicien Machin"  "Mme Raymonde Bidule" "M. Martial Truc"    
#> [4] "Mme Huguette Chose"

11.4 Découper des chaînes

La fonction str_split permet de “découper” une chaîne de caractère en fonction d’un délimiteur. On passe la chaîne en premier argument, et le délimiteur en second :

str_split("un-deux-trois", "-")
#> [[1]]
#> [1] "un"    "deux"  "trois"

On peut appliquer la fonction à un vecteur, dans ce cas le résultat sera une liste :

str_split(d$nom, " ")
#> [[1]]
#> [1] "Mr"       "Félicien" "Machin"  
#> 
#> [[2]]
#> [1] "Mme"      "Raymonde" "Bidule"  
#> 
#> [[3]]
#> [1] "M."      "Martial" "Truc"   
#> 
#> [[4]]
#> [1] "Mme"      "Huguette" "Chose"

Ou un tableau (plus précisément une matrice) si on ajoute simplify = TRUE.

str_split(d$nom, " ", simplify = TRUE)
#>      [,1]  [,2]       [,3]    
#> [1,] "Mr"  "Félicien" "Machin"
#> [2,] "Mme" "Raymonde" "Bidule"
#> [3,] "M."  "Martial"  "Truc"  
#> [4,] "Mme" "Huguette" "Chose"

Si on souhaite créer de nouvelles colonnes dans un tableau de données en découpant une colonne de type texte, on pourra utiliser la fonction separate de l’extension tidyr. Celle-ci est expliquée Section 12.3.3.

Voici juste un exemple de son utilisation :

library(tidyr)
d %>% separate(nom, c("genre", "prenom", "nom"), sep = " ")
#> # A tibble: 4 × 5
#>   genre prenom   nom    adresse                     ville     
#>   <chr> <chr>    <chr>  <chr>                       <chr>     
#> 1 Mr    Félicien Machin 3 rue des Fleurs            Nouméa    
#> 2 Mme   Raymonde Bidule 47 ave de la Libération     Marseille 
#> 3 M.    Martial  Truc   12 rue du 17 octobre 1961   Vénissieux
#> 4 Mme   Huguette Chose  221 avenue de la Libération Marseille

11.5 Extraire des sous-chaînes par position

La fonction str_sub permet d’extraire des sous-chaînes par position, en indiquant simplement les positions des premier et dernier caractères :

str_sub(d$ville, 1, 3)
#> [1] "Nou" "Mar" "Vén" "Mar"

11.6 Détecter des motifs

str_detect permet de détecter la présence d’un motif parmi les élements d’un vecteur. Par exemple, si on souhaite identifier toutes les adresses contenant “Libération” :

str_detect(d$adresse, "Libération")
#> [1] FALSE  TRUE FALSE  TRUE

str_detect renvoie un vecteur de valeurs logiques et peut donc être utilisée, par exemple, avec le verbe filter de dplyr pour extraire des sous-populations.

Une variante, str_count, compte le nombre d’occurrences d’une chaîne pour chaque élément d’un vecteur :

str_count(d$ville, "s")
#> [1] 0 1 2 1
Avertissement

Attention, les fonctions de stringr étant prévues pour fonctionner avec des expressions régulières, certains caractères n’auront pas le sens habituel dans la chaîne indiquant le motif à rechercher. Par exemple, le . ne sera pas un point mais le symbole représentant “n’importe quel caractère”.

La section sur les modificateurs de motifs explique comment utiliser des chaîne “classiques” au lieu d’expressions régulières.

On peut aussi utiliser str_subset pour ne garder d’un vecteur que les éléments correspondant au motif :

str_subset(d$adresse, "Libération")
#> [1] "47 ave de la Libération"     "221 avenue de la Libération"

11.7 Extraire des motifs

str_extract permet d’extraire les valeurs correspondant à un motif. Si on lui passe comme motif une chaîne de caractère, cela aura peu d’intérêt :

str_extract(d$adresse, "Libération")
#> [1] NA           "Libération" NA           "Libération"

C’est tout de suite plus intéressant si on utilise des expressions régulières. Par exemple la commande suivante permet d’isoler les numéros de rue.

str_extract(d$adresse, "^\\d+")
#> [1] "3"   "47"  "12"  "221"

str_extract ne récupère que la première occurrence du motif. Si on veut toutes les extraire on peut utiliser str_extract_all. Ainsi, si on veut extraire l’ensemble des nombres présents dans les adresses :

str_extract_all(d$adresse, "\\d+")
#> [[1]]
#> [1] "3"
#> 
#> [[2]]
#> [1] "47"
#> 
#> [[3]]
#> [1] "12"   "17"   "1961"
#> 
#> [[4]]
#> [1] "221"
Note

Si on veut faire de l’extraction de groupes dans des expressions régulières (identifiés avec des parenthèses), on pourra utiliser str_match.

À noter que si on souhaite extraire des valeurs d’une colonne texte d’un tableau de données pour créer de nouvelles variables, on pourra utiliser la fonction extract de l’extension tidyr, décrite Section 12.3.6.

Par exemple :

library(tidyr)
d %>% extract(adresse, "type_rue", "^\\d+ (.*?) ", remove = FALSE)
#> # A tibble: 4 × 4
#>   nom                 adresse                     type_rue ville     
#>   <chr>               <chr>                       <chr>    <chr>     
#> 1 Mr Félicien Machin  3 rue des Fleurs            rue      Nouméa    
#> 2 Mme Raymonde Bidule 47 ave de la Libération     ave      Marseille 
#> 3 M. Martial Truc     12 rue du 17 octobre 1961   rue      Vénissieux
#> 4 Mme Huguette Chose  221 avenue de la Libération avenue   Marseille

11.8 Remplacer des motifs

La fonction str_replace_all permet de remplacer une chaîne ou un motif par une autre.

Par exemple, on peut remplacer les occurrence de “Mr” par “M.” dans notre variable nom :

str_replace_all(d$nom, "Mr", "M.")
#> [1] "M. Félicien Machin"  "Mme Raymonde Bidule" "M. Martial Truc"    
#> [4] "Mme Huguette Chose"

On peut également spécifier plusieurs remplacements en une seule fois :

str_replace_all(
    d$adresse,
    c("avenue" = "Avenue", "ave" = "Avenue", "rue" = "Rue")
)
#> [1] "3 Rue des Fleurs"            "47 Avenue de la Libération" 
#> [3] "12 Rue du 17 octobre 1961"   "221 Avenue de la Libération"

11.9 Modificateurs de motifs

Par défaut, les motifs passés aux fonctions comme str_detect, str_extract ou str_replace sont des expressions régulières classiques.

On peut spécifier qu’un motif n’est pas une expression régulière mais une chaîne de caractères normale en lui appliquant la fonction fixed. Par exemple, si on veut compter le nombre de points dans les noms de notre tableau, le paramétrage par défaut ne fonctionnera pas car dans une expression régulière le . est un symbole signifiant “n’importe quel caractère” :

str_count(d$nom, ".")
#> [1] 18 19 15 18

Il faut donc spécifier que notre point est bien un point avec fixed :

str_count(d$nom, fixed("."))
#> [1] 0 0 1 0

On peut aussi modifier le comportement des expressions régulières à l’aide de la fonction regex. On peut ainsi rendre les motifs insensibles à la casse avec ignore_case :

str_detect(d$nom, "mme")
#> [1] FALSE FALSE FALSE FALSE
str_detect(d$nom, regex("mme", ignore_case = TRUE))
#> [1] FALSE  TRUE FALSE  TRUE

On peut également permettre aux regex d’être multilignes avec l’option multiline = TRUE, etc.

11.10 Ressources

L’ouvrage R for Data Science, accessible en ligne, contient un chapitre entier sur les chaînes de caractères et les expressions régulières (en anglais).

Le site officiel de stringr contient une liste des fonctions et les pages d’aide associées, ainsi qu’un article dédié aux expressions régulières.

Pour des besoins plus pointus, on pourra aussi utiliser l’extension stringi sur laquelle est elle-même basée stringr.

11.11 Exercices

Dans ces exercices on utilise un tableau d, généré par le code suivant :

d <- tibble(
  nom = c("M. rené Bézigue", "Mme Paulette fouchin", "Mme yvonne duluc", "M. Jean-Yves Pernoud"),
  naissance = c("18/04/1937 Vesoul", "En 1947 à Grenoble (38)", "Le 5 mars 1931 à Bar-le-Duc", "Marseille, juin 1938"),
  profession = c("Ouvrier agric", "ouvrière qualifiée", "Institutrice", "Exploitant agric")
)
nom naissance profession
M. rené Bézigue 18/04/1937 Vesoul Ouvrier agric
Mme Paulette fouchin En 1947 à Grenoble (38) ouvrière qualifiée
Mme yvonne duluc Le 5 mars 1931 à Bar-le-Duc Institutrice
M. Jean-Yves Pernoud Marseille, juin 1938 Exploitant agric

Exercice 1

Capitalisez les noms des personnes avec str_to_title :

#> [1] "M. René Bézigue"      "Mme Paulette Fouchin" "Mme Yvonne Duluc"    
#> [4] "M. Jean-Yves Pernoud"
str_to_title(d$nom)

Exercice 2

Dans la variable profession, remplacer toutes les occurrences de l’abbréviation “agric” par “agricole” :

#> [1] "Ouvrier agricole"    "ouvrière qualifiée"  "Institutrice"       
#> [4] "Exploitant agricole"
str_replace(d$profession, "agric", "agricole")

Exercice 3

À l’aide de str_detect, identifier les personnes de catégorie professionnelle “Ouvrier”. Indication : pensez au modificateur ignore_case.

#> [1]  TRUE  TRUE FALSE FALSE
str_detect(d$profession, regex("Ouvr", ignore_case = TRUE))

Exercice 4

À l’aide de case_when et de str_detect, créer une nouvelle variable sexe identifiant le sexe de chaque personne en fonction de la présence de M. ou de Mme dans son nom.

#> # A tibble: 4 × 4
#>   nom                  naissance                   profession         sexe 
#>   <chr>                <chr>                       <chr>              <chr>
#> 1 M. rené Bézigue      18/04/1937 Vesoul           Ouvrier agric      Homme
#> 2 Mme Paulette fouchin En 1947 à Grenoble (38)     ouvrière qualifiée Femme
#> 3 Mme yvonne duluc     Le 5 mars 1931 à Bar-le-Duc Institutrice       Femme
#> 4 M. Jean-Yves Pernoud Marseille, juin 1938        Exploitant agric   Homme
d %>%
  mutate(sexe = case_when(
          str_detect(nom, fixed("Mme")) ~ "Femme",
          str_detect(nom, fixed("M.")) ~ "Homme"
      )
  )

Exercice 5

Extraire l’année de naissance de chaque individu avec str_extract. Vous pouvez utiliser le regex "\\d\\d\\d\\d" qui permet d’identifier les nombres de quatre chiffres.

Vous devez obtenir le vecteur suivant :

#> [1] "1937" "1947" "1931" "1938"
str_extract(d$naissance, "\\d\\d\\d\\d")

À l’aide de la fonction extract de l’extension tidyr et du regex précédent, créez une nouvelle variable annee dans le tableau, qui contient l’année de naissance (pour plus d’informations sur extract, voir Section 12.3.6).

#> # A tibble: 4 × 4
#>   nom                  naissance                   annee profession        
#>   <chr>                <chr>                       <chr> <chr>             
#> 1 M. rené Bézigue      18/04/1937 Vesoul           1937  Ouvrier agric     
#> 2 Mme Paulette fouchin En 1947 à Grenoble (38)     1947  ouvrière qualifiée
#> 3 Mme yvonne duluc     Le 5 mars 1931 à Bar-le-Duc 1931  Institutrice      
#> 4 M. Jean-Yves Pernoud Marseille, juin 1938        1938  Exploitant agric
library(tidyr)
d %>%
    extract(
        naissance,
        "annee",
        "(\\d\\d\\d\\d)",
        remove = FALSE
    )

  1. Il s’agit en fait d’une version très simplifiée, la “véritable” expression permettant de tester si une adresse mail est valide fait plus de 80 lignes…↩︎