7  Manipulations élémentaires de données

Voyons à présent quelques opérations basiques dans le dataframe importé dans la section précédente.

7.1 Travailler sur une variable précise

Dans le jeu de données précédemment importé, imaginons que l’on veuille calculer l’écart-type (fonction sd()) de la variable TM6, qui est en colonne 4. Il existe plusieurs façons équivalentes de procéder.

  1. En utilisant le numéro de colonne :

    sd(dat[, 4])
    [1] 5.457152
  2. En utilisant le nom de la colonne :

    sd(dat[, "TM6"])
    [1] 5.457152
  3. En utilisant la syntaxe $ :

    sd(dat$TM6)
    [1] 5.457152

Il existe de nombreuses manières différentes d’accéder à une colonne donnée d’un dataframe. On peut aussi utiliser la fonction with(), qui permet de spécifier en amont le jeu de données sur lequel on travaille (cela s’avère utile s’il y a de nombreuses instructions derrière, afin de ne pas avoir à rattacher le nom du jeu de données à chaque variable à laquelle on fait appel) :

with(dat, sd(TM6))
[1] 5.457152

7.2 Prendre en compte les données manquantes

Calculons maintenar l’écart-type de la variable FM1 :

sd(dat$FM1) # un essai "naïf" (mais naturel !)
[1] NA

Par défaut, de nombreuses fonctions R (mean(), max(), sd(), etc.) retournent NA si on leur soumet un vecteur comprenant des données manquantes. Pour forcer le calcul, on peut utiliser l’argument na.rm = TRUE (cf. l’aide) :

sd(dat$FM1, na.rm = TRUE)
[1] 26.08093

Si on veut supprimer tous les individus incomplets (i.e. comportant au moins une valeur manquante) dans un dataframe, on utilise na.omit()… mais cela peut être radical !

net <- na.omit(dat)
summary(net)
 Site   Sexe        FM1             TM6             RM1        Hypoplasie
 A:20   F:16   Min.   :393.7   Min.   :37.34   Min.   :207.3   Non:31    
 B:16   M:20   1st Qu.:409.9   1st Qu.:44.23   1st Qu.:222.6   Oui: 5    
               Median :428.5   Median :48.88   Median :231.4             
               Mean   :430.9   Mean   :48.63   Mean   :233.2             
               3rd Qu.:444.7   3rd Qu.:51.71   3rd Qu.:244.2             
               Max.   :508.9   Max.   :59.53   Max.   :257.1             
 Presence_parure Orientation_corps
 Non:22          Est  :13         
 Oui:14          Nord : 7         
                 Ouest:10         
                 Sud  : 6         
                                  
                                  

7.3 Ajouter ou supprimer des variables

Pour ajouter une nouvelle variable dans un dataframe, on la déclare “comme si elle existait déjà”, en spécifiant simplement l’opération qui la définit :

dat$Somme <- dat$FM1 + dat$RM1
head(dat) # visualiser le résultat
    Site Sexe    FM1   TM6    RM1 Hypoplasie Presence_parure Orientation_corps
A_1    A    M 420.11 51.75 251.15        Non             Non              Nord
A_2    A    M 508.93 57.98 255.82        Non             Non              Nord
A_3    A    M 465.19 53.36 251.41        Non             Non               Est
A_4    A    F 425.74 49.27 244.36        Oui             Oui             Ouest
A_5    A    M 465.42 59.53 251.21        Non             Non               Est
A_6    A    M 426.22 52.55 242.83        Non             Non               Est
     Somme
A_1 671.26
A_2 764.75
A_3 716.60
A_4 670.10
A_5 716.63
A_6 669.05

Par défaut, la variable est toujours ajoutée en dernière colonne (il est toujours possible de réordonner ensuite).

Pour supprimer une variable, on la remplace par NULL (attention : ce n’est pas équivalent à la remplacer par NA !) :

dat$Somme <- NULL
head(dat) # la variable a bien disparu
    Site Sexe    FM1   TM6    RM1 Hypoplasie Presence_parure Orientation_corps
A_1    A    M 420.11 51.75 251.15        Non             Non              Nord
A_2    A    M 508.93 57.98 255.82        Non             Non              Nord
A_3    A    M 465.19 53.36 251.41        Non             Non               Est
A_4    A    F 425.74 49.27 244.36        Oui             Oui             Ouest
A_5    A    M 465.42 59.53 251.21        Non             Non               Est
A_6    A    M 426.22 52.55 242.83        Non             Non               Est

7.4 Sélectionner des sous-ensembles d’individus

Il est possible de “filtrer” (en lignes) un dataframe selon un ou plusieurs critères en utilisant la fonction subset(). Essayer et interpréter les instructions suivantes (demander éventuellement un summary() de chaque sous-ensemble créé) :

## Filtrer sur une modalité d'un facteur :
femmes <- subset(dat, Sexe == "F")
print(femmes)
## Filtrer selon les modalités de deux facteurs :
femmes_A <- subset(dat, Sexe == "F" & Site == "A")
print(femmes_A)
## Filtrer selon *plusieurs* modalités d'un facteur :
est_sud <- subset(
    dat,
    Orientation_corps %in% c("Est", "Sud")
)
print(est_sud)
## Filtrer selon une variable numérique :
grands <- subset(dat, FM1 > 445)
print(grands)
Table 7.1: Récapitulatif des symboles utilisés en R dans la syntaxe de filtrage de données.
Symbole Sens logique
& “et”
| “ou”
%in% \(\in\), “appartient à”
! “non”, négation logique
!= \(\neq\), “n’est pas égal à”
<= \(\leq\)
>= \(\geq\)

7.5 Aparté : le “tidyverse”

R étant en soi un langage assez ancien, et ayant à l’origine été conçu en partie par des mathématiciens plutôt que par des informaticiens, son design peut parfois être un peu daté par rapport aux langages de programmation plus modernes, comme Julia.

Le “tidyverse” est une collection de packages créés par l’entreprise Posit, et visant à introduire un “mini-langage” plus moderne à l’intérieur du langage R. Ses concepts sont abondamment documentés dans Wickham et al. (2023) et sur le site web dédié.

En particulier, le “tidyverse” inclut le package {dplyr}, qui facilite un certain nombre d’opérations de manipulations de données, et où chaque fonction est un verbe très explicite.

Pour charger le package{dplyr}, et plus généralement le meta-package {tidyverse} :

library(tidyverse)

Parfois, comme ici, cela ne change pas grand chose par rapport aux fonctions R de base :

## Sélectionner un sous-ensemble d'individus (p. ex., les femmes) :
subset(dat, Sexe == "F") # fonction R de base
filter(dat, Sexe == "F") # équivalent dplyr

mais parfois, cela introduit au contraire une syntaxe beaucoup plus commode à manipuler, comme ci-dessous :

## Sélectionner des variables en spécifiant leur nom :
num <- dat[ , c("FM1", "TM6", "RM1")] # R-base
num <- select(dat, FM1, TM6, RM1)     # équivalent dplyr
head(num)

Le package {dplyr}, et les autres packages du “tidyverse”, seront ponctuellement utilisés d’ici la fin de la formation. N’hésitez pas à consulter la documentation pour découvrir davantage de fonctionnalités !

7.6 Sélectionner des variables

Nous venons de voir une instruction pour sélectionner certaines variables en spécifiant leur nom. Mieux encore, on peut aisément sélectionner toutes les variables d’un certain type dans le jeu de données : par exemple, toutes les variables numériques. Avec {dplyr}, c’est presque immédiat :

num <- select(dat, where(is.numeric))
head(num)
       FM1   TM6    RM1
A_1 420.11 51.75 251.15
A_2 508.93 57.98 255.82
A_3 465.19 53.36 251.41
A_4 425.74 49.27 244.36
A_5 465.42 59.53 251.21
A_6 426.22 52.55 242.83

En R-base, cela aurait nécessité une syntaxe nettement moins lisible et agréable, telle que :

bool_num <- unlist(lapply(dat, is.numeric))
num <- dat[, bool_num]

7.7 Trier selon une variable donnée

Pour réordonner toutes les lignes d’un dataframe selon les valeurs d’une variable donnée, on peut utiliser la fonction de base order() pour retrier les lignes du tableau :

triBase <- dat[order(dat$TM6), ]
head(triBase)

ou bien, de façon plus explicite là encore, la fonction arrange() du package {dplyr} :

triDplyr <- arrange(dat, TM6)
head(triDplyr)
     Site Sexe    FM1   TM6    RM1 Hypoplasie Presence_parure Orientation_corps
B_5     B    F     NA 36.13 223.80        Non             Non               Est
B_16    B    F 401.72 37.34 229.78        Non             Oui              Nord
B_7     B <NA> 428.41 40.66 200.69        Non             Non               Sud
B_14    B    F 410.32 40.85 209.79        Non             Oui             Ouest
B_11    B    F     NA 41.91 226.28        Non             Oui               Sud
B_12    B    F 393.67 42.33 222.92        Non             Non               Sud

Exercice. En consultant l’aide de ces fonctions :

  • comment trier par ordre décroissant de TM6 ?
  • pour arrange(), comment trier selon plusieurs variables (pour départager de possibles égalités) : selon TM6 puis selon FM1 ?

7.8 Appliquer une fonction en lignes ou en colonnes

L’instruction apply() permet d’appliquer la même fonction à toutes les lignes ou toutes les colonnes d’une matrice. L’argument MARGIN = 1 permet d’agir sur les lignes ; et MARGIN = 2 agit sur les colonnes.

On rappelle qu’on a précédemment défini un objet R num, contenant uniquement les variables numériques du jeu de données. Il est donc assimilable à une matrice. Consultons-le à nouveau :

head(num)
       FM1   TM6    RM1
A_1 420.11 51.75 251.15
A_2 508.93 57.98 255.82
A_3 465.19 53.36 251.41
A_4 425.74 49.27 244.36
A_5 465.42 59.53 251.21
A_6 426.22 52.55 242.83

Pour calculer l’écart-type de toutes les colonnes de ce nouveau tableau (attention aux données manquantes !) :

apply(num, MARGIN = 2, FUN = sd, na.rm = TRUE)
      FM1       TM6       RM1 
26.080928  5.457152 14.573473 
Itérer efficacement avec le package {purrr}

Le package {purrr}, qui fait partie du “tidyverse”, propose de nombreuses fonctions, rapides et explicites, pour itérer efficacement des opérations en R en une seule instruction. Les fonctions de {purrr}, qui ont presque toutes un nom commençant par map_*(), ont vocation à remplacer la fonction apply() et ses fonctions dérivées en R.

Par exemple :

map_dbl(.x = num, .f = \(x) sd(x, na.rm = TRUE))
      FM1       TM6       RM1 
26.080928  5.457152 14.573473 

Notez aussi, dans le bloc ci-dessus, la définition d’une fonction anonyme.

7.9 Exporter des dataframes

Il est possible d’écrire dans un fichier (par exemple un fichier CSV ou TXT) le contenu d’un dataframe, à l’aide des fonctions write.table(), write.csv(), ou encore write.csv2(). On pourra consulter l’aide de chaque fonction pour connaître leurs particularités et leurs arguments par défaut.

Par exemple, pour exporter le dataframe net créé dans la Section 7.2, on exécutera :

write.csv2(net, "./output/donnees_nettoyees.csv")

Puisqu’on a spécifié un chemin relatif vers ce fichier, il sera écrit dans un sous-dossier output du répertoire courant de la session.