2  Préparation des données

Au sein de ce chapitre, on cherche à atteindre le double objectif de/d’ :

On cherche dans la mesure du possible à toujours travailler avec des tidy datas et à utiliser en priorité les fonctions de la série de packages de tidyverse.

2.1 Bonnes pratiques dans le système de récolte des données (Qualtrics, …)

Chaque participant·e reçoit un id partiellement anonymisé. Il est fourni par l’équipe de recherche (ce n’est pas construit par le ou la participant·e). L’id ne contient que des chiffres pour simplifier le problème des majuscules/minuscules à la saisie et éviter les confusions entre le zéro et la lettre o (oui oui, c’est du vécu). Ceci signifie que l’id dispose d’une structure qui permet de/d’ :

  • évaluer son authenticité
  • faciliter la catégorisation des données
  • repérer efficacement les id et leur catégorisation dans les traitement ultérieurs

Exemple : un id comme 12.47.694 est structuré comme suit groupe12.constantearbitraire47.nombrealéatoireà3chiffres.

Chaque id est vérifié dans Qualtrics, ce qui signifie qu’un·e participant·e doit confirmer l’id pour que le système passe à la suite. On relève aussi l’intérêt de demander à l’utilisateur·trice de confirmer son groupe d’appartenance en cochant un facteur dans une liste imposée dans Qualtrics. Ceci a l’avantage de repérer les saisies fantasques (une personne met un id bidon / un·e participant·e s’inscrit avec le bon id mais dans le mauvais groupe, …). Dans la préparation des données, il semble que l’on gagne ainsi un temps important pour trier les données valables. La confiance dans les données s’en trouve augmentée. On relève toutefois une faiblesse : le code n’est pas 100% anonyme et on peut toujours remonter à un groupe de participant·es. Il faut alors en amont s’engager à détruire le document qui a permis la génération des id.

Chaque questionnaire est identifié par 3 premières lettre significatives, le nombre d’items et un _:

Exemple : Questionnaire sur la motivation scolaire en 13 items est identifié par mot13.

Ensuite, chacun des items est proprement nommé par ordre d’apparition, ce qui est fondamental, notamment pour le traitement des items inversés :

Exemple : mot13_1, mot13_2, …, mot13_13 (il semble que Qualtrics ajoute par défaut le underscore_ )

2.2 Premières étapes dans R (en principe, les noms des variables sont sains)

On commence par le chargement des packages et l’importation du ou des fichiers de données. Dans cet exemple, on prend le cas (plus complexe) où nous devons gérer et associer deux data frames.

library(tidyverse)
library(readxl)

# Importation des données Qualtrics à disposition ----
d_t1_raw <- read_excel("crips2019_lv_raw_t1.xlsx")
d_t2_raw <- read_excel("crips2019_lv_raw_t2.xlsx")

2.2.1 ajout de la variable de temps dans chaque df (cas simple)

Chaque fichier représente un temps de mesure. Il nous suffit d’ajouter une variable indiquant cette information.

Dans cet exemple, on règle en même temps le problème des id qui contiennent éventuellement des majuscules.

#Ajout de la variable temps sur chaque df et normalisation des id en minuscule.

d_t1 <- d_t1_raw %>% 
  mutate(temps="temps 1",
         id=tolower(id)) 

d_t2 <- d_t2_raw %>% 
  mutate(temps="temps 2",
         id=tolower(id))

2.2.2 catégorisation de la variable de temps (cas avec horodatage préalable)

Quand on possède un unique fichier regroupant toutes les observations, on peut catégoriser les données à partir de l’horodateur.

#modification de la variable "RecordedDate" en "date" en temps 1 et temps 2 tout en filtrant les id enregistrés hors temps 1 et temps 2.
#mais on commence par la renommer.

d <- d %>% 
  rename(date = RecordedDate) %>%  #dans cet ordre.
  mutate(id = tolower(id)) #gestion de la casse.

d <- d %>% 
  mutate(
    date = case_when(date >= as.POSIXct("01.08.2019", format="%d.%m.%Y", tz="utc") & date <= as.POSIXct("31.08.2019", format="%d.%m.%Y", tz="utc") ~ "temps 1",
                     date >= as.POSIXct("01.06.2020", format="%d.%m.%Y", tz="utc") & date <= as.POSIXct("30.06.2020", format="%d.%m.%Y", tz="utc") ~ "temps 2",
                     TRUE ~ "autre temps")) #on privilégie case_when car on a 3 conditions et on va gérer les dates.

#Au passage, R a modifié le type de variable "date". On le laisser respirer... et on filtre... (si j'intègre filter dans le pipe, ça bug...)

d <- d %>% filter(date =="temps 1" | date == "temps 2")

On est pas encore confiant sur la qualité du code ci-dessus.

2.2.3 création d’un data frame unique

A ce stade, à partir du cas simple, il ne semble pas y avoir de raison au maintien de 2 df différents. On peut créer un df unique à l’aide, simplement, de bind_rows. Cela nous évite de doubler les manipulations au temps 1 et au temps 2.

Au préalable, on s’est assuré que les noms des variables correspondaient à notre nomenclature, et que l’ordre des questions dans chaque questionnaire était le même au temps 1 et au temps 2.

Les éventuelles nouvelles variables du temps 2 sont bien traitées grâce à l’ajout de NA pour les observations du temps 1.

#Mise en formation long sur un df

d_long <- bind_rows(d_t1,d_t2)

On choisit la mise en format long (au lieu de wide) pour correspondre au attentes du traitement tidy des données. Cela signifie que chaque ligne est une observation et chaque colonne est une variable. Ceci implique que chaque participant·e se retrouve dans deux lignes : une concernant la modalité (facteur) temps 1 et l’autre selon la modalité temps 2. C’était déroutant au début mais on a adopté cette manière de faire.

2.2.4 gestion des items à inverser

Les items à inverser sont traités en étant strictement remplacés via la fonction mutate() dans le cadre de l’enregistrement d’un nouveau data frame. R permet de remonter les changements donc cet écrasement n’empêche pas la vérification des processus, étape par étape.

#Recondage des variables au score inversé

d_long <- d_long %>% 
  mutate(hbs20__7 = 6 - hbs20__7,
         pec5__5 = 6 - pec5__5,
         be8__4 = 8 - be8__4,
         be8__5 = 8 - be8__5,
         be8__8 = 8 - be8__8,
         est10__3 = 5 - est10__3,
         est10__5 = 5 - est10__5,
         est10__7 = 5 - est10__7,
         est10__10 = 5 - est10__10,
         sou13__6 = 6 - sou13__6,
         sou13__9 = 6 - sou13__9,
         mot16__4 = 8 - mot16__4,
         mot16__8 = 8 - mot16__8,
         mot16__12 = 8 - mot16__12,
         mot16__15 = 8 - mot16__15,
         mot16__16 = 8 - mot16__16)

Le code paraît fastidieux et potentiellement source d’erreurs. De plus, ce recodage s’accompagne d’une feuille annexe sur laquelle on a noté avec prudence les items en cause et la formule pour inverser les scores… On ne sait mieux faire…

Ce procédé d’écrasement facilite l’analyse de la cohérence interne ou le calcul des scores par la suite (cf. chapitre description).

2.2.5 calcul du score de chaque questionnaire par observation

La variable qui contient le score global de chaque questionnaire par participant·e porte l’extension *_sco* :

Exemple : mot13_sco est la variable de score total de mot13_1 à mot13_13 avec les variables qui ont été inversées (sans conservation dans le df de l’item non-inversé).

#Recondage des variables au score inversé

#Création des moyennes de chaque questionnaire pour chaque observation

d_long <- d_long %>% 
  mutate(hbs_sco = rowMeans(select(.,starts_with("hbs")),na.rm =T),
         pec_sco = rowMeans(select(.,starts_with("pec")),na.rm =T),
         be_sco  = rowMeans(select(.,starts_with("be")) ,na.rm =T),
         est_sco = rowMeans(select(.,starts_with("est")),na.rm =T),
         cli_sco = rowMeans(select(.,starts_with("cli")),na.rm =T),
         sou_sco = rowMeans(select(.,starts_with("sou")),na.rm =T),
         mot_sco = rowMeans(select(.,starts_with("hbs")),na.rm =T))

De nouveau, on ne crée pas d’objet intermédiaire sachant que R sait très bien nous permettre de vérifier le processus, étape par étape.

Ce code paraît très efficace. Il se base sur notre nomenclature sans risque d’erreur, indépendamment du nombre de variable ou de la position des colonnes.

On apprécie, même si on ne comprend pas encore à satisfaction la fonction ´select´et son fonctionnement.

2.2.6 filtrage des observations

Ici, on a essayé de développer une technique pour ne garder que les id qui se retrouvent strictement au temps 1 et au temps 2.

Les enjeux sont cruciaux :

  • on doit s’assurer qu’un même id ne se retrouve pas plusieurs fois dans le même temps (un·e participant·e peut avoir rempli 2 fois le questionnaire du temps, p.ex.)
  • on doit aussi s’assurer que nous ferons nos comparaisons temps 1 / temps 2 sur des données complètes

C’est notre présomption de base. Il est dès lors important de filtrer les observations de manière stricte.

Pour cela, nous avons :

  • créé un df de comparaison pour pouvoir dire à R quoi sélectionner. Ce df va, en deux étapes, ne garder que (1) les id uniques dans chaque temps puis (2) constituant une paire (t1, t2).
d_comp <- d_long %>% 
  drop_na(id) %>% #par sécurité, on supprime les lignes dont l'id est vide
  arrange(id) %>% #visuel uniquement
  group_by(id, temps) %>% 
  count(id) %>% 
  filter(n==1) %>% #On s'est assuré que chaque id est unique dans chaque modalité de temps. On doit encore être sûrs qu'on a maintenant exactement une paire (t1,t2). Donc on continue le processus que R réalise dans cet ordre.
  ungroup() %>% 
  group_by(id) %>% 
  count(id) %>% 
  filter(n==2) %>% #on ne garde que les paires de id qui se retrouvent dans t1 et t2. C'est notre grosse perte de données de ce traitement
  ungroup()

On devrait vérifier la validité de cette méthode, sur le plan des bonnes pratiques stat, et aussi sur le plan du codage dans R…et aussi s’assurer qu’elle fonctionne bien comme on le pense en la mettant à l’épreuve.

  • créé un nouveau df qui ne contient plus que les paires souhaitées
#Notre df de comparaison est prêt. On peut procéder à l'élagage de d_long.

d_long_paired <- d_long %>% 
  filter(id %in% d_comp$id) %>% 
  mutate(group=ifelse(group=="con", "contrôle","expérimental"))

On a été un peu sauvé avec le logique %in% mais sans bien savoir pourquoi == ne convient pas. Dans l’example, on en a profité pour modifier les facteurs de la variable group. Les forums ne sont pas clairs sur la différence entre ifelseet if_elseou encore case_when. On doit clarifier cela.

2.2.7 gestion des données manquantes

pas encore de bonnes pratiques.

2.2.8 gestion des données extrêmes

pas encore de bonnes pratiques.