Séparer et unir des colonnes

Bienvenue

Les données sont plus faciles à analyser dans R lorsqu’elles sont stockées dans un format ordonné (on parle de tidy data en anglais). Dans le dernier module, vous avez appris à ordonner les données dont la présentation est désordonnée. Mais les jeux de données peuvent être désordonnés d’une autre manière : un ensemble de données peut combiner plusieurs valeurs dans une seule cellule ou répartir une seule valeur sur plusieurs cellules. Cela rend difficile l’extraction et l’utilisation de valeurs lors de votre analyse.

Ce module vous apprendra deux outils que vous pouvez utiliser pour réordonner ce type de données :

  • separate() - qui sépare une colonne en plusieurs colonnes
  • unite() - qui combine plusieurs colonnes en une seule colonne

Le module se termine par une étude de cas qui vous obligera à utiliser tous les outils de mise en ordre des données pour appréhender un jeu de données réelles désordonnées.

Ce module utilise les packages de base du tidyverse, notamment {tidyr}. Tous ces packages ont été préinstallés et préchargés.

Cliquez sur le bouton “Suivant” pour commencer.

separate()

hurricanes

Le jeu de données hurricanes contient les informations historiques de cinq ouragans. À première vue, il semble contenir quatre variables : name, wind_speed, pressure et date. Cependant, trois autres variables sont ‘cachées.’ dans le jeu de données. Pouvez-vous les repérer ?

Dates

Saviez-vous que les dates sont une combinaison de plusieurs variables ?

Vous afficherez presque toujours ces variables ensemble pour faire une date, car une date est elle-même une variable —et elle bien plus qu’uniquement la combinaison de ses trois parties.

Cependant, il y a des moments où il est commode de traiter chaque élément d’une date séparément. Par exemple, que se passe-t-il si vous souhaitez filtrer les ouragans uniquement pour les tempêtes qui se sont produites en juin (c’est-à-dire `month == 6) ? Il serait alors pratique de réorganiser les données pour ressembler à ceci :

Mais comment pouvez-vous faire cela ?

separate()

Vous pouvez séparer les éléments de date avec la fonction separate(). separate() divise une colonne de valeurs en plusieurs colonnes qui contiennent chacune une partie des valeurs d’origine.

Exécutez le code ci-dessous pour voir separate() en action. Cliquez ensuite sur “Continue” pour en savoir plus sur la syntaxe.

hurricanes %>% 
  separate(col = date, into = c("year","month","day"), sep = "-")

Syntaxe

Réécrivons notre commande ci-dessus sans le pipe (%>%). Cela permettra de décrypter plus facilement la syntaxe de separate().

separate(data = hurricanes, 
         col = date, 
         into = c("year","month","day"),
         sep = "-")

separate() prend en arguments un jeu de données, puis le nom de la colonne à séparer. Ici, notre code séparera la colonne date du jeu de données hurricane.

L’argument sep = "-" indique à la fonction separate() qu’il faut séparer chaque valeur de la colonne date à chaque fois qu’un - apparaît. Vous pouvez choisir de séparer une colonne selon n’importe quel caractère.

La séparation selon le symbole - divisera chaque date en trois dates : une année, un mois et un jour. Ainsi, separate() ajoutera trois nouvelles colonnes au résultat. L’argument into donne à separate() un vecteur de caractères de noms à utiliser pour les nouvelles colonnes. Puisque le résultat aura trois nouvelles colonnes, ce vecteur devra contenir trois nouveaux noms. separate() fournira un message d’erreur s’il lui est demandé de créer moins ou plus de colonnes qu’il n’y a de noms de colonnes renseignés.

Fonctionnement par défaut

Par défaut, separate() séparera les valeurs à l’emplacement de chaque caractère non alphanumérique, comme -, ,,/, etc. Ainsi, nous aurions pu exécuter notre code précédent sans l’argument sep = "-" et obtenir le même résultat. Lancer le code ci-dessous pour voir si c’est bien le cas.

hurricanes %>% 
  separate(col = date, into = c("year","month","day"))

Séparation grâce à la position

Si vous définissez sep égal à un (ou des) entier(s), separate() séparera les valeurs à l’emplacement indiqué par les entiers Par exemple :

  • sep = 1 séparera les valeurs après le premier caractère
  • sep = -2 séparera les valeurs après l’ avant dernier caractère, et ce quel que soit le nombre de caractères apparaissant dans la valeur. En d’autres termes, il séparera le dernier caractère de chaque valeur du reste.
  • sep = c(2, 4, 6) séparera les valeurs après les deuxième, quatrième et sixième caractères, créant quatre sous-valeurs.

Vous pensez avoir compris ce fonctionnement ? Alors créez cette version de hurricanes en ajoutant un deuxième appel à separate() qui utilise un séparateur entier :

hurricanes %>% 
  separate(col = date, into = c("year","month","day"))
hurricanes %>% 
  separate(col = date, into = c("year","month","day")) %>% 
  separate(col = year, into = c("century", "year"), sep = 2)

Quiz - Et si…

Ces deux commandes renverraient-elles le même résultat ? Selon vous, pourquoi ? Une fois que vous pensez avoir la réponse, exécutez le code ci-dessous pour voir si vous aviez raison.

hurricanes %>% 
  separate(col = pressure, into = c("first", "last"), sep = 1)
hurricanes %>% 
  separate(col = pressure, into = c("first", "last"), sep = "1")

Conversion

Vous avez peut-être remarqué que separate() renvoie ses résultats sous forme de colonnes de chaînes de caractères. Cependant, dans certains cas (comme le nôtre), les colonnes contiendront des entiers, des doubles ou d’autres types de données sans caractère.

Vous pouvez demander à separate() de convertir les nouvelles colonnes en un type de données approprié en ajoutant convert = TRUE lors de votre appel separate().

Identifiez les types de données de year, month, et day. Ils apparaissent sous les noms de colonnes lorsque vous affichez le jeu de données (comme dans la sortie ci-dessous). Ajoutez ensuite convert = TRUE et ré-exécutez le code. Quels changements observez-vous ?

hurricanes %>% 
  separate(col = date, into = c("year", "month", "day"))
hurricanes %>% 
  separate(col = date, into = c("year", "month", "day"), convert = TRUE)

Suppression

Jetons maintenant un coup d’œil à un dernier argument de separate(). Si vous ajoutez remove = FALSE à votre appel separate(), R conservera la colonne d’origine dans les résultats.

hurricanes %>% 
  separate(col = date, into = c("year", "month", "day"), convert = TRUE, remove = FALSE)

unite()

unite()

Vous pouvez faire l’inverse de separate() avec unite(). unite() utilise plusieurs colonnes en entrée pour créer une seule colonne en sortie. Cette fonction construit cette colonne en collant ensemble les cellules des colonnes d’origine avec un séparateur.

hurricanes %>%
  separate(date, c("year", "month", "day"), sep = "-") %>%
  unite(col = "date", month, day, year, sep = ":")

Syntaxe

hurricanes %>%
  separate(date, c("year", "month", "day"), sep = "-") %>%
  unite(col = "date", month, day, year, sep = ":")

Notez que la syntaxe de unite() est l’inverse de celle de separate() :

  • Le premier argument est une chaîne de caractères : le nom de la nouvelle colonne que unite () créera
  • Les arguments qui suivent sont les colonnes à combiner dans la nouvelle colonne. Vous pouvez répertorier autant de colonnes que vous le souhaitez. Leurs noms n’ont pas besoin d’être entre guillemets et chaque nom est répertorié comme son propre argument.

Exercice - Séparer et Unir des colonnes

Utilisez separate() et unite() pour ré-écrire les dates de hurricanes avec le format suivant :

  • month/day/year, par ex : 1/27/2020
hurricanes %>%
  separate(date, c("year", "month", "day"), sep = "-") %>%
  unite(col = "date", month, day, year, sep = "/")

Exercice - Séparer et Unir des colonnes 2

Utilisez le bloc ci-dessous pour :

  1. Utilisez separate() pour isoler les deux premiers chiffres de chaque date et ainsi créer une nouvelle colonne “century” (siècle)
  2. Filtrez les données sur les lignes où century == 19. Cela vous permettra d’identifier les tempêtes survenues dans les années 1900.
  3. Utilisez unite() pour ramener les résultats au format de date d’origine. Conseil : vous pouvez définir sep = "" pour éviter d’inclure un caractère de séparation lors de l’union.
hurricanes %>%
  separate(col = date, c("century", "rest"), sep = 2) %>%
  filter(century == 19) %>% 
  unite(col = "date", century, rest, sep = "")

Tidy data (Données ordonnées)

Jusqu’à présent, nous avons séparé et uni date, une variable qui contient des sous-variables pertinentes. En effet, il n’est pas très logique de combiner des valeurs indépendantes et sans relation entre elles au sein des mêmes cellules. Cependant, de nombreux jeux de données suivent cette pratique insensée. Si vous vous retrouvez confronté à ce problème, vous pouvez utiliser separate() et unite() pour réorganiser les valeurs de manière ordonnée.

Dans l’étude de cas qui suit, c’est justement ce que vous allez faire. Vous vous exercerez également à utiliser plusieurs fonctions du package {tidyr}.

Etude de cas

who

Le jeu de données who contient un sous-ensemble de données du rapport mondial sur la tuberculose de l’Organisation Mondiale de la Santé (OMS). Il est disponible ici.

Dans leur format d’origine, les données sont très désordonnées

who

Variables disponibles dans who

Les quatre premières colonnes de who contiennent chacune une variable :

  • country - nom de pays
  • iso2 - code pays composé de deux lettres
  • iso3 - code pays composé de trois lettres
  • year - année

Les colonnes restantes sont nommées d’après des codes qui contiennent plusieurs variables.

Codes disponibles dans who

Chaque nom de colonne après la quatrième contient un code composé de trois valeurs provenant de trois variables : type of TB, gender, et age.

Objectif

Pour que who soit plus facile à utiliser dans R, nous devons l’agencer dans le format ci-dessous. Ce jeu de données contient six variables non redondantes : country, year, type, sex, age (group), et n (le nombre de cas de tuberculose signalés pour chaque groupe).

Échauffement

Il faudra un certain nombre d’opérations pour ordonner le jeu de données who. Lorsque vous regardez le résultat final, la tâche peut sembler longue et difficile, mais chaque opération individuelle sera assez concise (et vous y êtes déjà familier). Nous utiliserons un chaînage d’opérations avec le pipe %>% pour enchaîner ces opérations.

Commençons le processus en supprimant les variables redondantes iso2 etiso3 de who. En d’autres termes, utilisons une fonction de {dplyr} pour sélectionner chaque colonne sauf iso2 etiso3. Rappelez-vous qu’il existe un moyen de le faire sans avoir à taper beaucoup de code.

who %>% 
  select(-iso2, -iso3)

Stratégie

Ensuite, nous devons déplacer les variables type, sex et age hors des noms de colonne et les mettre dans leur propre colonne. C’est vrai que l’objectif final est de séparer ces valeurs dans leurs propres cellules, mais ce sera plus facile à faire une fois qu’elles seront dans leur propre colonne.

En bref, nous voulons faire quelque chose comme ceci :

Exercice - Restructurer les données

Continuez le code ci-dessous. Utilisez une fonction du package {tidyr} pour restructurer les données en rassemblant les noms de colonne dans leur propre colonne, nommée “codes”. Placez les cellules de la colonne dans une colonne nommée “n”. Conseil : ça peut vous être utile : il y a maintenant 58 colonnes dans l’ensemble de données.

Vous pouvez considérer chaque nom de colonne comme une clé (key) qui combine les valeurs (values) de plusieurs variables. Nous voulons déplacer ces clés dans leur propre colonne de clés.

who %>% 
  select(-iso2, -iso3)
who %>%
  select(-iso2, -iso3) %>% 
  pivot_longer(cols = 3:58, names_to = "codes", values_to = "n")

Exercice - Séparer les colonnes

Ajoutez au processus le moyen de séparer les codes contenus dans codes en trois colonnes nommées “new”, “type”, et “sexage”. Sur quel type de séparateur devez-vous effectuer la séparation ?

who %>%
  select(-iso2, -iso3) %>% 
  pivot_longer(cols = 3:58, names_to = "codes", values_to = "n")
who %>%
  select(-iso2, -iso3) %>% 
  pivot_longer(cols = 3:58, names_to = "codes", values_to = "n") %>% 
  separate(codes, into = c("new", "type", "sexage"), sep = "_")

Exercice - Séparer les données (encore !)

Notre dernière opération a permis d’isoler les variables new et type dans leur propre colonne. Mais elle n’a pas permis de séparer les variables de sexe et d’âge.

Regardez la structure de la colonne sexage d’un peu plus près. Vous pouvez voir que chaque cellule commence par une seule lettre (qui représente un sexe, m ou f) suivie de trois chiffres ou plus (qui représentent un groupe d’âge). Utilisez ces informations pour effectuer une seconde séparation qui isole les variables sex et age :

who %>%
  select(-iso2, -iso3) %>% 
  pivot_longer(cols = 3:58, names_to = "codes", values_to = "n") %>% 
  separate(codes, into = c("new", "type", "sexage"), sep = "_")
who %>%
  select(-iso2, -iso3) %>% 
  pivot_longer(cols = 3:58, names_to = "codes", values_to = "n") %>% 
  separate(codes, into = c("new", "type", "sexage"), sep = "_") %>% 
  separate(sexage, into = c("sex", "age"), sep = 1)

Exercice - Sélectionner des colonnes

Continuez le processus pour supprimer la variable new, qui ne fournit aucune information utile. (Chaque ligne du jeu de données montre de nouveaux cas de tuberculose et a la même valeur de new).

who %>%
  select(-iso2, -iso3) %>% 
  pivot_longer(cols = 3:58, names_to = "codes", values_to = "n") %>% 
  separate(codes, into = c("new", "type", "sexage"), sep = "_") %>% 
  separate(sexage, into = c("sex", "age"), sep = 1)
who %>%
  select(-iso2, -iso3) %>% 
  pivot_longer(cols = 3:58, names_to = "codes", values_to = "n") %>% 
  separate(codes, into = c("new", "type", "sexage"), sep = "_") %>% 
  separate(sexage, into = c("sex", "age"), sep = 1) %>% 
  select(-new)

n

Notez que la colonne n de who est celle qui contient les informations les plus pertinentes. Vous n’avez pas besoin de prendre de mesures particulières pour répertorier le pays, l’année, le type, le sexe et les combinaisons d’âge dans le jeu de données. Dans un sens, vous connaissez ces combinaisons à l’avance. La colonne n, elle, indique le nombre de cas de tuberculose signalés pour chaque combinaison. Contrairement aux autres colonnes, vous ne connaissez pas ces informations à l’avance et vous ne pouvez les obtenir que par le biais de travaux sur le terrain - les vôtres ou ceux de quelqu’un d’autre. C’est en ce sens que la colonne n est celle qui contient les informations les plus pertinentes, et c’est celle sur laquelle vous allez focaliser vos analyses. Par conséquent, il est préoccupant de voir que cette colonne contient autant de valeurs NA.

NA

NA est le symbole de R qui indique une information manquante. Il est courant d’avoir plusieurs NA lorsque vous remodelez vos données d’un format large vers un format long. En effet, la structure de table rectangulaire imposée par des données larges nécessite un espace réservé pour chaque combinaison variable-valeur —même si aucune donnée n’a été collectée pour cette combinaison.

En revanche, le format de données long ne nécessite pas d’espace réservé pour chaque combinaison variable-valeur. Étant donné que chaque combinaison est enregistrée sur sa propre ligne, vous ne pouvez tout simplement pas inclure de lignes contenant une information manquante NA.

drop_na()

Le package {tidyr} fournit une fonction très pratique pour supprimer des lignes contenant un NA dans une colonne spécifique : drop_na(). Pour l’utiliser, donnez à drop_na() un jeu de données (qui peut provenir d’un pipe %>%), puis listez une ou plusieurs colonnes de ce jeu de données. Par exemple :

data %>% 
  drop_na(colonne1, colonne2)

drop_na() supprimera chaque ligne contenant un NA dans une ou plusieurs des colonnes répertoriées.

Ajoutez drop_na() au pipe ci-dessous pour supprimer chaque ligne qui a un NA dans la colonne n.

who %>%
  select(-iso2, -iso3) %>% 
  pivot_longer(cols = 3:58, names_to = "codes", values_to = "n") %>% 
  separate(codes, into = c("new", "type", "sexage"), sep = "_") %>% 
  separate(sexage, into = c("sex", "age"), sep = 1) %>% 
  select(-new)
who %>%
  select(-iso2, -iso3) %>% 
  pivot_longer(cols = 3:58, names_to = "codes", values_to = "n") %>% 
  separate(codes, into = c("new", "type", "sexage"), sep = "_") %>% 
  separate(sexage, into = c("sex", "age"), sep = 1) %>% 
  select(-new) %>% 
  drop_na(n)

Récapitulatif

Bon travail ! Vous avez transformer le jeu de données who en un jeu de données ordonné et prêt à être exploré, modélisé et analysé.

La différence entre les versions initiale et finale de who est radical, mais chaque étape de notre processus de transformation a utilisé un changement minime, logique et familier. C’est la conception même du tidyverse ! Le tidyverse contient un vocabulaire de fonctions qui font chacune une chose simple, mais qui peuvent être combinées pour effectuer des tâches plus sophistiquées. De cette façon, le tidyverse est comme un langage écrit, il est composé de mots (fonctions) qui peuvent être combinés en phrases qui ont une signification sophistiquée (pipes).

Cette approche facilite également la résolution des problèmes de code. Vous pouvez aborder n’importe quel problème en le décomposant en une série de petites étapes.