Dans le champ contemporain de la recherche empirique et du traitement de données quantitatives, l’intégrité des corpus d’information constitue le fondement sur lequel repose toute inférence statistique valide. Pourtant, les praticiens de la science des données, les psychométriciens et les analystes quantitatifs sont perpétuellement confrontés à l’omniprésence des données incomplètes. Qu’il s’agisse de questionnaires psychométriques administrés en ligne, d’enquêtes épidémiologiques longitudinales ou d’essais cliniques multicentriques, l’absence de réponse est une réalité inévitable du processus de recueil de données humaines. Au sein de l’environnement de calcul statistique R, la gestion de ces vides informationnels requiert une compréhension technique et conceptuelle particulièrement rigoureuse, dans la mesure où l’interpréteur traite l’absence d’information comme un état logique singulier et non comme une simple chaîne textuelle.
La transformation des valeurs manquantes, désignées formellement par le symbole réservé NA (pour Not Available), en chaînes de caractères explicites représente une étape pivot dans les flux de préparation et de nettoyage des données (ou data wrangling). Si l’imputation numérique vise traditionnellement à remplacer des manques par des valeurs plausibles en vue de calculs paramétriques, la conversion d’un statut d’absence en une étiquette textuelle telle que « Non renseigné », « Refus de répondre » ou « Inapplicable » répond à un besoin fondamentalement différent. Cette opération vise la lisibilité documentaire, la standardisation catégorielle pour les visualisations descriptives, et la qualification qualitative des motifs d’absence au sein des protocoles de recherche.
Ce guide exhaustif a été conçu pour offrir une analyse approfondie et systématique des méthodes permettant de substituer des chaînes de caractères aux valeurs manquantes dans l’écosystème R. En parcourant les mécanismes sous-jacents du R de base, les fonctions spécialisées du métapackage Tidyverse telles que replace_na() de tidyr et mutate() de dplyr, ainsi que les subtilités inhérentes aux vecteurs de facteurs gérés par forcats, ce manuel propose une démarche méthodique éprouvée. Au-delà des simples lignes de commande, les enjeux épistémologiques, méthodologiques et statistiques de ces transformations sont examinés afin de garantir des analyses reproductibles et conformes aux exigences académiques les plus strictes.
- 1. Introduction à la gestion des données manquantes en sciences psychologiques et statistiques
- 2. Fondements techniques : La structure des valeurs NA dans les vecteurs R
- 3. La méthode moderne avec tidyr : Maîtriser la fonction replace_na()
- 4. Remplacement des NA par une chaîne dans une colonne unique
- 5. Remplacement simultané des NA dans plusieurs colonnes textuelles
- 6. Méthodes traditionnelles en R de base (Base R) sans dépendances
- 7. Intégration au sein de l’écosystème dplyr et manipulation avancée
- 8. Le piège des variables catégorielles : Facteurs versus Chaînes de caractères
- 9. Traitements textuels avancés et nettoyage conditionnel avec stringr
- 10. Études de cas appliquées en psychologie quantitative et sciences sociales
- 11. Bonnes pratiques méthodologiques, reproductibilité et pièges statistiques
- 12. Guide décisionnel récapitulatif pour les utilisateurs de R
- Références
1. Introduction à la gestion des données manquantes en sciences psychologiques et statistiques
1.1 Le défi épistémologique des données manquantes en psychométrie
Dans le domaine des sciences psychologiques, l’administration d’inventaires de personnalité, d’échelles de bien-être ou d’évaluations cognitives confronte systématiquement le chercheur à l’incomplétude des observations. Contrairement aux sciences physiques où l’absence de mesure résulte souvent d’une défaillance d’instrumentation facilement modélisable, la non-réponse humaine engage des processus psychologiques complexes. Un participant confronté au questionnaire d’inventaire de dépression de Beck (BDI-II) ou au modèle en cinq facteurs (NEO-PI-R) peut omettre délibérément un item en raison de la désirabilité sociale, de l’anxiété déclenchée par un contenu perçu comme intrusif, ou d’une incompréhension syntaxique de l’énoncé. Dans d’autres situations, l’omission relève d’une simple fatigue cognitive à la fin d’une longue batterie de tests psychométriques.
La taxonomie fondamentale théorisée par Donald Rubin établit une distinction cruciale entre trois mécanismes régissant les données manquantes : le mécanisme entièrement aléatoire (Missing Completely at Random ou MCAR), le mécanisme aléatoire (Missing at Random ou MAR) et le mécanisme non aléatoire (Missing Not at Random ou MNAR). Dans le cas du MCAR, la probabilité qu’une donnée soit manquante est totalement indépendante des variables observées et non observées. Dans le cas du MAR, cette probabilité dépend d’autres caractéristiques mesurées chez le participant (comme l’âge ou le niveau d’anxiété de base), mais pas de la valeur de la réponse omise elle-même. Enfin, dans le schéma MNAR, la non-réponse est directement corrélée à la variable latente que l’item cherche à mesurer ; par exemple, un individu manifestant un niveau sévère d’idéation suicidaire refusant spécifiquement de répondre à l’item évaluant ce symptôme.
L’impact de la présence de valeurs non attribuées sur la fidélité et la validité des échelles de mesure est majeur. L’omission de réponses altère le calcul de la cohérence interne mesurée par le coefficient alpha de Cronbach ou l’oméga de McDonald, et introduit des biais systématiques dans les analyses factorielles confirmatoires. Toutefois, dans les phases de restitution descriptive, les rapports cliniques individualisés et les analyses qualitatives mixtes, ignorer la cause de l’absence en éliminant les lignes incomplètes (analyse par suppression de liste) constitue une erreur méthodologique sévère. Il devient impératif d’opérer une standardisation textuelle qui qualifie expressément la nature de l’omission, transformant le vide informatique en une modalité informative intelligible pour le clinicien et l’auditeur statistique.
1.2 Représentation formelle de l’absence de données dans l’écosystème R
Dans l’architecture interne du langage R, l’absence de données est conceptualisée au niveau le plus fondamental de l’interpréteur par la constante logique NA. Cette constante n’est pas une simple chaîne de caractères contenant les lettres « N » et « A », mais un indicateur scalaire désignant une valeur inconnue ou non disponible. Pour maintenir l’intégrité du système de typage strict des vecteurs atomiques, R implémente en réalité plusieurs déclinaisons internes de cette valeur, notamment NA_character_, NA_integer_, NA_real_ et NA_complex_. Bien que l’utilisateur n’écrive habituellement que NA, l’interpréteur assigne ou coerce dynamiquement cette entité vers la variante correspondant au type du conteneur parent.
Il est primordial de distinguer rigoureusement NA des autres concepts d’absence ou d’anomalie numérique sous R. L’objet NULL représente l’absence totale de valeur ou un objet de longueur nulle, souvent renvoyé par des fonctions dont l’exécution ne produit aucun résultat. La valeur spéciale NaN (Not a Number) désigne une indétermination mathématique issue d’une opération arithmétique illicite, comme la division de zéro par zéro ou la racine carrée d’un nombre négatif dans le corps des réels. Quant à la chaîne de caractères vide, notée "", elle constitue une donnée textuelle parfaitement définie et existante d’une longueur de zéro caractère, mais dotée d’une réalité en mémoire. Face à ces différentes entités, les fonctions statistiques standard telles que mean(), sd() ou cor() adoptent une règle stricte : en présence d’un seul élément NA, le résultat de l’évaluation renvoie inévitablement NA, à moins que le paramètre na.rm = TRUE ne soit formellement spécifié.
Transformer un indicateur logique NA en une étiquette textuelle explicite répond à des exigences précises de communication et d’ingénierie des données. Lorsqu’une variable qualitative doit figurer dans un tableau récapitulatif destiné à des praticiens non statisticiens, la mention littérale « Donnée non renseignée » évite l’ambiguïté inhérente à l’affichage brut de l’interpréteur. De surcroît, lors de l’exportation de données vers des formats tabulaires intermédiaires ou des systèmes de gestion de bases de données relationnelles, la conversion textuelle prévient les distorsions d’interprétation où un NA pourrait être erronément assimilé par un logiciel tiers à une chaîne blanche ou au mot « NA » sans valeur sémantique d’absence.
1.3 Objectifs pédagogiques et organisation méthodologique de ce manuel
L’objectif principal de ce manuel est de conférer au lecteur une autonomie technique totale pour identifier, manipuler et recoder les valeurs manquantes sous forme de chaînes de caractères au sein de jeux de données complexes. La maîtrise de ces compétences exige l’acquisition raisonnée des outils de l’écosystème Tidyverse, avec une focalisation prononcée sur les packages tidyr, dplyr et forcats. Ces extensions modernes de R ont transformé l’ingénierie logicielle statistique en introduisant des syntaxes unifiées, prévisibles et hautement lisibles, indispensables à la maintenance de pipelines analytiques au sein d’équipes de recherche multidisciplinaires.
Afin de garantir une pratique scientifique rigoureuse, ce document s’articule autour d’une approche comparative systématique. Les solutions contemporaines offertes par le Tidyverse seront minutieusement confrontées aux techniques du R de base. Cette mise en perspective permettra d’évaluer les compromis entre lisibilité syntaxique, dépendance logicielle et efficacité computationnelle sur des structures de données de taille massive. Une attention particulière sera allouée à la résolution des anomalies classiques, telles que l’injection accidentelle de chaînes textuelles au sein de colonnes structurées sous forme de facteurs ordonnés ou catégoriels, une source fréquente de blocages dans les scripts d’analyse.
La progression méthodologique de l’ouvrage s’appuie sur des cas d’usage directement inspirés de cohortes empiriques en psychologie quantitative. Qu’il s’agisse de gérer des données sociodémographiques incomplètes, d’harmoniser les non-réponses d’inventaires psychométriques multidimensionnels ou d’assainir des verbatim textuels issus d’entretiens cliniques structurés, chaque procédé technique sera illustré par des exemples complets, reproductibles et immédiatement transférables à des protocoles de recherche fondamentale ou appliquée.
2. Fondements techniques : La structure des valeurs NA dans les vecteurs R
2.1 Typologie interne et coercition des valeurs manquantes
Pour appréhender sereinement le remplacement des valeurs manquantes par du texte, il convient d’examiner la manière dont le moteur de R gère l’allocation mémoire des vecteurs atomiques. Tout vecteur dans R possède un type unique et invariable une fois alloué. Les principaux types de base sont les types logique (logical), entier (integer), réel (double) et chaîne de caractères (character). La constante générique NA est, par défaut, de type logique. Lorsqu’elle est insérée dans un vecteur numérique ou textuel, l’interpréteur effectue une coercition implicite et silencieuse vers la variante interne correspondante. Dans le cadre précis d’un vecteur textuel, l’élément manquant devient formellement NA_character_.
Cette distinction interne est cruciale lors de l’application de fonctions bas niveau. La commande typeof(NA) renvoie la chaîne "logical", tandis que typeof(NA_character_) confirme le type "character". Les fonctions diagnostiques class() et mode() permettent également d’inspecter la nature des objets manipulés. Lorsque l’analyste décide d’injecter une chaîne de caractères, par exemple le mot « Absent », dans un vecteur préexistant contenant des valeurs NA, le mécanisme de coercition automatique de R s’active. Si le vecteur d’origine était numérique (contenant des entiers ou des réels), la tentative d’insertion d’un texte entraîne la conversion irréversible de l’ensemble du vecteur vers le type chaîne de caractères, privant l’analyste de toute possibilité ultérieure d’exécuter des calculs arithmétiques sans reconversion préalable.
Les risques d’incompatibilité de type lors d’assignations non contrôlées constituent l’une des causes les plus courantes d’instabilité des scripts scientifiques. Si une colonne d’âge au sein d’une base de données médicale contient des entiers et des NA, substituer ces derniers par la chaîne « Inconnu » transformera l’intégralité de la colonne en vecteur textuel. L’exécution subséquente d’une commande d’ajustement de modèle linéaire comme lm() échouera ou traitera l’âge comme une variable qualitative non ordonnée à multiples niveaux, faussant dramatiquement les degrés de liberté et les estimations de variance. Dès lors, toute substitution textuelle d’un NA doit faire l’objet d’une réflexion préalable sur la vocation de la colonne concernée.
2.2 L’évaluation logique avec is.na() versus les opérateurs d’égalité
Une incompréhension fréquente chez les utilisateurs débutants et intermédiaires de R concerne l’application des opérateurs de comparaison relationnelle face aux données manquantes. Il est mathématiquement et logiquement incorrect de tenter d’isoler des valeurs manquantes au moyen de l’expression x == NA. Dans la logique ternaire adoptée par R (vrai, faux, inconnu), comparer une entité inconnue à une autre entité inconnue produit nécessairement une indétermination logique. En conséquence, l’expression x == NA n’évalue aucun élément à TRUE ou FALSE, mais retourne systématiquement un vecteur de même dimension entièrement composé de valeurs NA.
Pour évaluer de manière fiable et déterministe la présence de valeurs non attribuées, R met à disposition la primitive vectorisée is.na(). Cette fonction parcourt le vecteur passé en argument et teste l’état d’absence de chaque élément au niveau binaire interne. Elle renvoie invariablement un vecteur booléen où la valeur TRUE signale une case vide et FALSE une case occupée par une donnée valide. Par exemple, si l’on applique cette commande à un vecteur combinant du texte et des manques, l’expression is.na(c("Paris", NA, "Lyon")) produira sans ambiguïté c(FALSE, TRUE, FALSE).
L’utilisation combinée de is.na() avec l’opérateur de négation logique, représenté par le point d’exclamation !is.na(x), constitue la base de tout filtrage visant à extraire exclusivement les observations complètes. Sur le plan de la performance computationnelle, is.na() est une fonction native écrite en langage C, hautement optimisée pour le traitement par lots en mémoire vive. Son exécution sur des matrices ou des data frames comportant des millions de lignes s’opère en une fraction de seconde, minimisant l’empreinte processeur par rapport à des boucles itératives d’inspection scalaire. Cette efficacité fait de is.na() la pierre angulaire des méthodes traditionnelles de remplacement de données que nous détaillerons dans les sections consacrées au R de base.
3. La méthode moderne avec tidyr : Maîtriser la fonction replace_na()
3.1 Architecture et syntaxe générale de replace_na()
Le package tidyr, composant central du métapackage Tidyverse maintenu par Posit, a été spécialement développé pour ordonner et restructurer les données tabulaires selon les principes du tidy data énoncés par Hadley Wickham. Au sein de cette bibliothèque, la fonction replace_na() se positionne comme l’outil dédié par excellence à l’imputation de remplacement. Sa signature formelle est d’une grande sobriété : elle accepte en premier argument data, représentant un vecteur atomique ou un data frame (ou tibble), et en second argument replace, spécifiant la ou les valeurs de substitution.
L’une des forces majeures de replace_na() réside dans son polymorphisme maîtrisé. Lorsqu’elle est appliquée à un vecteur isolé, l’argument replace requiert un scalaire unique du même type que le vecteur d’entrée. En revanche, lorsqu’elle traite un tableau de données dans son ensemble, elle attend une liste nommée associant chaque désignation de colonne à sa valeur d’imputation spécifique. Cette conception modulaire s’articule parfaitement avec les flux de travail utilisant l’opérateur de chaînage classique %>% du package magrittr ou l’opérateur de pipe natif |> introduit dans la version 4.1.0 du moteur R.
Dans le contexte de projets de recherche clinique et d’études translationnelles, la lisibilité du code conditionne directement l’auditabilité des résultats par des tiers. L’emploi de replace_na() évite les syntaxes verbeuses et répétitives d’indexation matricielle qui encombrent traditionnellement les scripts. La clarté déclarative offerte par cette fonction garantit que toute personne examinant le pipeline d’analyse peut identifier immédiatement quelle colonne fait l’objet d’une substitution, quelle valeur textuelle spécifique est insérée et comment les typages de sortie sont préservés.
3.2 Environnement d’exécution et prérequis d’installation
Pour mettre en œuvre les instructions présentées dans ce guide, l’installation préalable des bibliothèques logicielles requises doit être validée. Le moyen le plus direct consiste à installer la suite Tidyverse via le gestionnaire officiel du Comprehensive R Archive Network (CRAN). Cette procédure s’effectue au moyen de la commande install.packages("tidyverse") ou, si l’on souhaite limiter le téléchargement aux composants strictement indispensables, par l’installation isolée de tidyr et de dplyr via install.packages(c("tidyr", "dplyr", "forcats", "stringr")).
Dans un cadre académique soucieux de reproductibilité à long terme, la gestion stricte de l’arbre des dépendances logicielles représente un impératif méthodologique. L’utilisation du package renv est vivement recommandée pour capturer l’état exact des versions de packages mobilisées au moment des calculs. L’initialisation d’un environnement hermétique avec renv::init() assure que les scripts exécutés sur une machine locale conserveront un comportement absolument identique lorsqu’ils seront compilés ultérieurement sur un serveur de calcul ou une station de travail indépendante.
Afin de concrétiser notre apprentissage à travers des manipulations vérifiables, construisons un jeu de données synthétique représentatif d’une cohorte évaluée en psychologie de la santé. Ce data frame, baptisé cohorte_psy, regroupe des variables textuelles, catégorielles et numériques comportant diverses formes d’incomplétude intentionnelle :
Pour initialiser ce tableau de démonstration dans une session active de R, l’analyste exécute le script suivant :
library(tidyr)
library(dplyr)
cohorte_psy <- data.frame(id = 101:105, statut_marital = c("Marié", NA, "Séparé", "Célibataire", NA), diagnostic = c("Anxiété", "Dépression", NA, NA, "Trouble bipolaire"), score_bdi = c(14, 28, NA, 9, 32), stringsAsFactors = FALSE)
L’inspection préalable de la matrice d’incomplétude s’opère immédiatement en sollicitant les fonctions standard str(cohorte_psy) et summary(cohorte_psy). La commande str() confirme que les colonnes statut_marital et diagnostic sont bien stockées comme des vecteurs de chaînes de caractères (type chr), tandis que summary() quantifie la proportion de NA par colonne, fournissant un bilan descriptif avant toute tentative de manipulation corrective.
4. Remplacement des NA par une chaîne dans une colonne unique
4.1 Application ciblée sur un vecteur isolé ou une variable de data frame
Lorsqu’une procédure de traitement impose de ne corriger qu’une seule dimension qualitative au sein d’une structure rectangulaire, replace_na() offre une solution syntaxique directe. Considérons la variable statut_marital de notre tableau témoin. Dans de nombreuses enquêtes démographiques, laisser ces cellules vides crée une incertitude analytique quant à la réception du questionnaire. Pour remplacer ces manques par une étiquette non ambiguë telle que « Information non communiquée », la syntaxe élémentaire requiert de cibler le vecteur par l’opérateur d’extraction $.
L’exécution de la commande prend la forme suivante :
cohorte_psy$statut_marital <- replace_na(cohorte_psy$statut_marital, "Information non communiquée")
Cette instruction isole le vecteur textuel, identifie l’ensemble des positions où is.na() est vérifié, substitue la chaîne fournie en second argument aux indices correspondants, et réassigne le vecteur transformé au sein du data frame d’origine. Il convient d’observer que cette manipulation s’exécute de manière vectorisée et n’altère en rien la longueur initiale de la colonne ni l’alignement ordinal des lignes adjacentes.
Pour les praticiens qui privilégient un flux linéaire continu évitant la répétition du nom de l’objet, l’opérateur de pipe permet d’obtenir un résultat fonctionnellement équivalent :
cohorte_psy$statut_marital <- cohorte_psy$statut_marital |> replace_na("Information non communiquée")
La validation empirique du recodage s’effectue sans délai au moyen de la fonction table(), en prenant soin d’expliciter l’argument useNA = "always" :
table(cohorte_psy$statut_marital, useNA = "always")
Ce diagnostic croisé atteste de la disparition complète des occurrences <NA> dans la table de fréquences et de l’apparition distincte de la modalité textuelle nouvellement créée, confirmant l’imputation sans compromission de la structure globale du tableau.
4.2 Exemple pratique détaillé : Recodage du statut démographique
Analysons en détail les effets d’une telle opération au moyen d’un cas empirique d’évaluation clinique. Considérons une cohorte de patients admis dans un service de consultation pour troubles de l’humeur. La variable diagnostic présente plusieurs valeurs manquantes, correspondant à des cas où l’investigation psychiatrique est toujours en cours d’évaluation diagnostique au moment du gel de la base de données. Conserver la mention NA pourrait induire le lecteur d’un rapport intermédiaire en erreur en laissant supposer une omission de saisie.
Procédons au remplacement méthodique des valeurs manquantes par la mention explicite « Bilan en attente » :
cohorte_psy$diagnostic <- replace_na(cohorte_psy$diagnostic, "Bilan en attente")
Examinons l’état comparatif de notre jeu de données. Avant l’exécution, les lignes 3 et 4 du tableau présentaient la valeur NA dans la colonne diagnostic. Après l’assignation, l’impression du tableau via print(cohorte_psy) révèle la présence effective de la chaîne « Bilan en attente » aux indices 3 et 4. Pendant ce temps, la colonne score_bdi, qui mesure l’intensité de la symptomatologie dépressive à l’aide d’une échelle continue, demeure strictement intacte avec sa valeur NA d’origine préservée à la ligne 3.
Cet exemple met en lumière un principe directeur fondamental de la gestion des données scientifiques : l’imputation sélective. Les colonnes textuelles ou descriptives peuvent être enrichies de libellés sémantiques explicites sans contaminer ni forcer la modification des colonnes numériques contiguës. L’intégrité du vecteur score_bdi garantit que si des modélisations statistiques ultérieures (telles que des analyses de régression ou des comparaisons de moyennes par test de Student) sont exécutées, les paramètres mathématiques d’exclusion des valeurs manquantes continueront d’opérer rigoureusement sans générer d’erreurs de calcul arithmétique.
4.3 Pièges courants lors de l’application unidirectionnelle
En dépit de son apparente simplicité, l’application unidirectionnelle de replace_na() est le théâtre de maladresses syntaxiques fréquentes chez les chercheurs. Le premier piège réside dans l’omission pure et simple de la réassignation du résultat. Dans l’écosystème R, la très grande majorité des fonctions sont dépourvues d’effets de bord par mutation directe en place (in-place modification), conformément au paradigme de la programmation fonctionnelle pure. Ainsi, exécuter simplement :
replace_na(cohorte_psy$statut_marital, "Non renseigné")
affiche le vecteur modifié dans la console interactive sans pour autant mettre à jour l’objet cohorte_psy stocké dans la mémoire de la session R. Si l’analyste consulte ensuite son tableau avec head(cohorte_psy), il constatera avec stupeur que les NA subsistent. L’assignation formelle via l’opérateur <- est impérative pour acter la persistance des modifications en mémoire vive.
Un deuxième écueil concerne la confusion structurelle entre un vecteur isolé et un objet de type tibble ou data.frame à colonne unique. Si l’on applique accidentellement une syntaxe d’extraction par doubles crochets ou sélection tidyselect sans extraire la colonne sous forme atomique, replace_na() interprétera l’objet d’entrée comme un tableau de données et non comme un vecteur simple. Dans cette situation, passer une chaîne de caractères scalaire en second argument au lieu d’une liste déclenchera une erreur explicite signalant que pour un data frame, l’argument de remplacement doit impérativement être une structure en liste.
Le troisième piège, et certainement le plus déroutant, découle de la manipulation de colonnes implicitement encodées comme des facteurs catégoriels. Dans les versions de R antérieures à la version 4.0.0, l’argument stringsAsFactors était configuré par défaut sur TRUE lors de l’importation de fichiers textes avec read.table() ou read.csv(). Si une colonne de statut est un facteur et que l’analyste tente de substituer un NA par une chaîne de caractères qui ne figure pas déjà dans les niveaux prédéfinis du facteur (les levels), l’opération échoue silencieusement ou génère l’avertissement redouté invalid factor level, NA generated, recréant un nouveau NA à la place même de la modification attendue. Nous résoudrons exhaustivement cette problématique dans la section dédiée aux facteurs.
5. Remplacement simultané des NA dans plusieurs colonnes textuelles
5.1 Utilisation de structures en liste dans replace_na()
Lorsque le corpus de données comprend de multiples dimensions qualitatives nécessitant un assainissement concerté, procéder par assignations individuelles successives devient laborieux, inélégant et source de duplications de code contraires aux principes du génie logiciel statistique. La fonction replace_na() résout élégamment cette contrainte par sa capacité à recevoir un tableau de données entier comme premier argument, couplé à une liste de paramètres de substitution nommée en second argument.
L’architecture interne de cette modalité d’utilisation repose sur l’association directe entre les clés de la liste et les noms stricts des colonnes du tableau. Si une variable présente dans le data frame n’apparaît pas dans la liste de configuration fournie, replace_na() la traverse simplement sans y apporter la moindre altération. À l’inverse, toute variable désignée dans la liste se voit appliquer la chaîne ou la valeur spécifique qui lui a été allouée. La signature conceptuelle de cette instruction s’écrit de la manière suivante :
tableau_nettoye <- tableau_brut |> replace_na(list(colonne_A = "Remplacement A", colonne_B = "Remplacement B"))
Cette approche présente l’avantage considérable d’autoriser une différenciation sémantique fine selon la nature épistémologique de chaque variable. L’analyste n’est pas contraint d’appliquer une étiquette générique indistincte à l’ensemble du fichier. Une colonne d’antécédents médicaux pourra ainsi se voir attribuer la mention « Aucun antécédent répertorié », tandis qu’une colonne mesurant le niveau d’instruction recevra l’étiquette « Degré scolaire non communiqué ». Le traitement global gagne à la fois en cohérence documentaire et en précision terminologique.
5.2 Exemple pratique multi-variables : Statut socio-économique et niveau d’études
Illustrons cette méthodologie avancée à l’aide d’un cas représentatif d’une enquête épidémiologique en population générale. Imaginons un jeu de données contenant les variables démographiques profession, diplome et revenu_annuel mesurées auprès de six participants, tel que structuré ci-dessous :
enquete_socio <- data.frame(participant = paste0("P", 1:6), profession = c("Enseignant", NA, "Ouvrier", "Cadre", NA, "Retraité"), diplome = c("Master", "Baccalauréat", NA, "Doctorat", NA, "Licence"), revenu_annuel = c(34000, 22000, NA, 58000, 19000, NA), stringsAsFactors = FALSE)
Dans ce protocole, nous souhaitons que les valeurs manquantes de la colonne profession soient codées par l’intitulé « Sans activité ou non déclarée », tandis que les manques de la variable diplome doivent porter la mention « Donnée éducative absente ». Crucialement, la variable revenu_annuel, qui est de nature strictement quantitative continue (relevé en euros), ne doit en aucun cas être transformée en texte afin de préserver l’aptitude ultérieure à calculer le revenu moyen ou l’écart-type de la cohorte.
La commande groupée s’exécute avec une parfaite limpidité :
enquete_nettoyee <- enquete_socio |> replace_na(list(profession = "Sans activité ou non déclarée", diplome = "Donnée éducative absente"))
L’examen du nouvel objet enquete_nettoyee démontre l’efficacité chirurgicale du processus. Les colonnes qualitatives ciblées ont intégré leurs libellés explicites respectifs là où siégeaient auparavant des NA. Simultanément, la colonne revenu_annuel conserve ses cellules NA natives de type double, parfaitement identifiables par les fonctions d’estimation paramétrique. Le tableau final respecte à la perfection le format attendu pour une restitution documentaire institutionnelle ou pour la transition vers des modèles d’équations structurelles où les indicateurs catégoriels et continus sont spécifiés distinctement.
5.3 Scalabilité et maintenance du code pour grands questionnaires
Dans le cadre de projets d’envergure impliquant des questionnaires psychométriques de grande échelle — tels que le Minnesota Multiphasic Personality Inventory (MMPI) qui compte plus de cinq cents items, ou des cohortes épidémiologiques longitudinales compilant des dizaines de mesures subjectives —, la rédaction manuelle d’une liste de substitution pour chaque variable devient insoutenable et hautement vulnérable aux fautes typographiques.
Pour résoudre cette contrainte de scalabilité logicielle, le chercheur peut générer la liste de remplacement de façon entièrement programmatique en combinant les fonctions de manipulation de noms de colonnes et les utilitaires fonctionnels du package purrr. Supposons que l’on dispose d’une base de données contenant cinquante colonnes textuelles d’items subjectifs dont les noms partagent le même préfixe conventionnel, par exemple item_01 jusqu’à item_50. Nous pouvons identifier automatiquement ces colonnes et leur associer un dictionnaire de remplacement unifié :
colonnes_cibles <- grep("^item_", names(base_volumineuse), value = TRUE)
liste_remplacement <- as.list(rep("Non évalué", length(colonnes_cibles)))
names(liste_remplacement) <- colonnes_cibles
base_volumineuse <- base_volumineuse |> replace_na(liste_remplacement)
Cette approche d’ingénierie logicielle garantit une maintenance optimale du code de recherche. Si de nouvelles variables qualitatives répondant au même motif syntaxique sont ajoutées ultérieurement lors d’une vague d’évaluation subséquente (évaluation longitudinale à T2 ou T3), le script traitera l’ensemble du corpus sans nécessiter la moindre retouche manuelle. De plus, les variables omises involontairement dans la structure de liste continuent d’exister sans altération destructrice, offrant un filet de sécurité structurel indispensable à l’analytique reproductible.
6. Méthodes traditionnelles en R de base (Base R) sans dépendances
6.1 Remplacement par indexation logique directe
Bien que l’élégance syntaxique du Tidyverse séduise une majorité de praticiens contemporains, la maîtrise des mécanismes du R de base demeure indispensable. Dans de nombreux environnements de calcul haute performance, sur des serveurs sécurisés d’hôpitaux interdisant l’installation de bibliothèques tierces, ou lors du développement de packages R destinés à être publiés sur le CRAN où la minimisation des dépendances est une exigence absolue de stabilité, l’indexation logique native s’impose comme la solution privilégiée.
Le principe fondamental de l’imputation en R de base repose sur le sous-ensemble logique vectorisé combinant l’opérateur d’extraction crochets [ ] et le prédicat is.na(). Considérons un vecteur de données textuelles extrait d’une base de dossiers médicaux :
reponse <- c("Favorable", NA, "Défavorable", NA, "Réservé")
reponse[is.na(reponse)] <- "Indéterminé"
L’évaluation s’exécute de façon séquentielle et extrêmement directe : l’expression is.na(reponse) génère un vecteur logique c(FALSE, TRUE, FALSE, TRUE, FALSE). Lorsque ce vecteur logique est passé à l’intérieur des crochets d’indexation du vecteur d’origine, l’interpréteur R filtre la mémoire pour cibler uniquement les adresses physiques correspondant aux positions évaluées à TRUE. L’opérateur d’assignation <- inscrit alors directement la chaîne de substitution « Indéterminé » dans ces positions cibles, en laissant toutes les autres cellules inchangées.
Lorsqu’elle est appliquée à une colonne logée au sein d’un data frame, la commande prend la forme suivante :
df$statut[is.na(df$statut)] <- "Indéterminé"
Cette méthodologie se distingue par sa frugalité computationnelle et son exécution native sans aucune surcharge liée au chargement de métadonnées ou d’environnements d’exécution complexes. Elle comporte néanmoins un risque ergonomique : la répétition nécessaire du nom de l’objet (df$statut figure deux fois dans l’instruction) accroît la probabilité d’erreurs de saisie, notamment lorsqu’un copié-collé hâtif conduit l’analyste à filtrer sur une colonne tout en assignant le résultat dans une autre variable adjacente.
6.2 Application de la fonction ifelse() vectorisée
Une seconde approche traditionnelle en R de base fait appel à la fonction d’arbitrage conditionnel ifelse(). Cette primitive vectorisée accepte trois arguments indispensables : un test logique sous forme de condition booléenne, la valeur à retourner si le test est validé (yes), et la valeur alternative à restituer si le test échoue (no). Dans notre champ d’application, la syntaxe prend la forme suivante :
df$statut <- ifelse(is.na(df$statut), "Donnée absente", df$statut)
L’opérateur évalue la condition is.na(df$statut) pour chaque ligne du tableau. Pour chaque composante où le test renvoie TRUE, l’élément correspondant de la sortie prend la valeur « Donnée absente ». Dans le cas inverse où la condition renvoie FALSE, la fonction recopie la valeur préexistante présente dans df$statut. Bien que cette formulation présente une logique narrative intuitive, elle dissimule des limitations techniques substantielles qui doivent inciter l’analyste à la circonspection.
En premier lieu, ifelse() est notoire dans l’écosystème R pour sa propension à dépouiller les vecteurs de leurs attributs de classe sous-jacents. Si la colonne d’origine porte des attributs temporels (comme des dates de classe Date ou POSIXct) ou des métadonnées spécifiques attribuées par des outils de capture de données cliniques (comme les objets étiquetés labelled de SPSS ou SAS importés via le package haven), ifelse() élimine fréquemment ces attributs pour renvoyer un vecteur atomique brut dépouillé, provoquant des anomalies de comportement dans les visualisations ultérieures.
En second lieu, sur le plan des performances algorithmiques pures, ifelse() évalue systématiquement les deux branches d’arguments (l’expression vraie et l’expression fausse) dans leur intégralité avant de construire le vecteur final. Sur des cohortes massives de plusieurs millions d’enregistrements, cette double évaluation engendre un coût mémoire et processeur largement supérieur à celui de l’indexation directe par [is.na()]. Ainsi, si le chercheur choisit d’opérer en R de base pur, l’indexation directe doit être systématiquement préférée à l’usage de ifelse() pour les tâches de simple imputation.
7. Intégration au sein de l’écosystème dplyr et manipulation avancée
7.1 Combinaison de mutate() avec replace_na()
L’intégration harmonieuse des opérations de nettoyage dans un flux d’ingénierie de données fluide repose principalement sur le package dplyr. Grâce au verbe grammatical mutate(), spécialisé dans l’altération et la dérivation de colonnes, le remplacement des valeurs manquantes s’inscrit organiquement dans une chaîne de traitement standardisée. Au lieu d’isoler des commandes éparses dans son script, le chercheur consolide l’ensemble des transformations dans un pipeline logique lisible de haut en bas.
L’association paradigmatique de mutate() et de replace_na() s’articule comme suit :
cohorte_harmonisee <- cohorte_psy |>
filter(id > 100) |>
mutate(statut_marital = replace_na(statut_marital, "Non renseigné")) |>
arrange(id)
L’intérêt conceptuel de cette articulation tient à la gestion fluide de la portée lexicale : à l’intérieur de l’expression mutate(), le nom de la variable statut_marital est directement accessible sans qu’il soit nécessaire de répéter la référence au data frame parent via le symbole $. L’interpréteur résout la colonne dans le contexte précis de l’objet transmis par le pipe. Cette syntaxe se prête idéalement aux opérations enchaînées regroupant sélections de colonnes avec select(), regroupements stratifiés avec group_by() et résumés statistiques consolidés avec summarise().
Sur le plan de la gouvernance de la mémoire, les versions contemporaines de dplyr et du moteur R optimisent le comportement de mutate() en évitant la duplication intégrale de l’objet tant que cela n’est pas strictement nécessaire (mécanisme de copy-on-modify). Cela garantit une empreinte RAM minimale, même lors de l’enchaînement de plusieurs dizaines d’imputations successives au cours d’un même pipeline de validation scientifique.
7.2 Usage de if_else() et case_when() pour imputations nuancées
Dans de nombreux protocoles d’évaluation psychologique, le remplacement d’un NA ne peut se résoudre par une étiquette fixe universelle, mais dépend intimement d’informations contextuelles fournies par d’autres variables de l’enquête. Pour répondre à ces exigences logiques conditionnelles, dplyr propose les fonctions if_else() et case_when(). Contrairement au ifelse() permissif du R de base, dplyr::if_else() impose une stricte cohérence de type entre la branche vraie et la branche fausse, empêchant toute coercition accidentelle qui corromprait la structure du jeu de données.
La puissance d’arbitrage conditionnel s’exprime pleinement à travers case_when(), qui permet de formaliser des systèmes d’équations logiques complexes sous une forme vectorisée élégante. Imaginons une enquête dans laquelle la variable frequence_alcool présente des valeurs manquantes, mais où nous disposons par ailleurs d’une variable binaire consommateur_alcool (indiquant « Non » ou « Oui »). Si un participant coche « Non » à la première question filtre, l’item secondaire de fréquence est logiquement laissé vide par le répondant. Traiter ce manque comme une simple non-réponse négligente (« Donnée manquante ») constituerait une déformation de la réalité empirique.
Le recodage contextuel s’opère méthodiquement de la façon suivante :
enquete_clinique <- enquete_clinique |>
mutate(frequence_alcool = case_when(
!is.na(frequence_alcool) ~ frequence_alcool,
is.na(frequence_alcool) & consommateur_alcool == "Non" ~ "Non applicable (abstème)",
is.na(frequence_alcool) & consommateur_alcool == "Oui" ~ "Omission involontaire ou refus",
TRUE ~ "Statut indéterminé"
))
Dans cette structure, l’opérateur tilde ~ connecte la condition logique de gauche à la valeur de remplacement textuelle attribuée à droite. Les règles sont évaluées séquentiellement pour chaque ligne de données. Cette granularité analytique permet de préserver la validité interne du corpus de recherche, en discriminant formellement les omissions dues à la structure conditionnelle du questionnaire (structural missingness) des véritables refus de répondre ou défaillances de saisie.
7.3 Application sélective avec across() sur des ensembles de colonnes
L’un des apports les plus remarquables de dplyr pour le traitement des données massives réside dans l’utilisation de la fonction across(), qui a succédé aux variantes historiques désormais obsolètes mutate_if(), mutate_at() et mutate_all(). La fonction across() permet d’appliquer une transformation donnée à un sous-ensemble complet de colonnes sélectionnées au moyen de prédicats logiques ou d’aides à la sélection (tidyselect helpers).
Considérons une batterie psychométrique comportant un vaste volume de questions ouvertes et de champs de métadonnées qualitatives. Si l’objectif du chercheur est de remplacer les NA par la mention « Absence d’information » sur absolument toutes les colonnes de type textuel du tableau, sans toucher aux scores cliniques continus, la formulation avec across() offre une compacité et une sécurité inégalées :
donnees_traitees <- cohorte_psy |>
mutate(across(where(is.character), ~ replace_na(.x, "Absence d'information")))
Dans cette expression hautement expressive, where(is.character) opère comme un filtre d’introspection typologique qui scanne les métadonnées de chaque colonne du tableau. Seules les variables dont le type primitif est textuel sont transmises à la fonction de remplacement. La syntaxe compacte avec tilde et variable muette .x (ou la syntaxe équivalente avec une fonction anonyme (x) replace_na(x, "...") standardisée depuis R 4.1) applique la substitution chirurgicale à chaque colonne identifiée.
L’analyste peut également restreindre cette application par le nom des variables à l’aide des prédicats tidyselect standard, comme dans l’exemple suivant :
donnees_traitees <- cohorte_psy |>
mutate(across(starts_with("echelle_anxiete_"), ~ replace_na(.x, "Non évalué")))
Cette méthodologie réduit drastiquement les risques de bugs inhérents à l’écriture de boucles itératives manuelles for(), protège l’immuabilité des colonnes numériques adjacentes, et confère au code un niveau de concision et d’élégance conforme aux meilleurs standards du génie logiciel académique contemporain.
8. Le piège des variables catégorielles : Facteurs versus Chaînes de caractères
8.1 Mécanisme des niveaux (levels) dans les facteurs sous R
Dans l’architecture de R, les variables catégorielles sont très fréquemment stockées sous la forme de facteurs (classe factor). Cette structure de données, d’une importance capitale pour la modélisation statistique comme les analyses de variance (ANOVA) ou les régressions logistiques, ne stocke pas le texte sous forme de chaînes brutes en mémoire. Un facteur est en réalité un vecteur sous-jacent d’entiers pointant vers une table de référence interne contenant des étiquettes uniques appelées niveaux ou levels.
Cette dualité interne explique pourquoi les tentatives naïves d’imputation textuelle sur un facteur déclenchent immanquablement des erreurs de programmation sévères. Si une variable de groupe expérimental est codée sous forme de facteur avec les niveaux c("Contrôle", "Traitement_A", "Traitement_B") et qu’une observation présente un NA, tenter d’exécuter une assignation directe telle que :
groupe[is.na(groupe)] <- "Non assigné"
provoquera immédiatement l’affichage du message d’avertissement :
Warning message: In `[<-.factor`(`*tmp*`, is.na(groupe), value = « Non assigné ») : invalid factor level, NA generated
Ce dysfonctionnement s’explique simplement : la chaîne « Non assigné » n’a pas été déclarée au préalable dans le dictionnaire des niveaux autorisés du facteur. Refusant d’altérer arbitrairement la définition structurelle de la variable, l’interpréteur R rejette l’insertion et réinjecte à la place un nouveau NA. L’analyste se retrouve donc au point de départ, avec une valeur manquante persistante, tout en croyant avoir assaini ses données. Il est donc fondamental de diagnostiquer préalablement la nature de la colonne au moyen de la fonction is.factor() avant toute opération d’assainissement textuel.
8.2 Résolution du problème avec le package forcats
Pour surmonter rigoureusement cette contrainte propre aux facteurs, le métapackage Tidyverse intègre une bibliothèque entièrement dédiée à la manipulation des variables catégorielles : le package forcats. Au sein de cette boîte à outils, la fonction fct_na_value_to_level() représente la méthode contemporaine par excellence pour transformer une absence de donnée en une modalité formellement reconnue au sein des niveaux du facteur.
La syntaxe d’application s’avère particulièrement fluide :
library(forcats)
cohorte_psy <- cohorte_psy |>
mutate(statut_cat = fct_na_value_to_level(statut_cat, level = "Non spécifié"))
Cette fonction exécute une double opération atomique sécurisée : elle injecte d’abord le libellé « Non spécifié » à la fin de la table des levels de la variable catégorielle, puis elle assigne le code entier correspondant aux positions précédemment occupées par les NA. Il convient de souligner que cette commande a officiellement succédé à la fonction historique fct_explicit_na(), qui a été dépréciée dans les versions récentes de forcats pour offrir une terminologie plus cohérente avec l’ensemble du langage.
Si le chercheur opère dans un cadre restreint en R de base sans accès aux bibliothèques du Tidyverse, la même modification structurelle impose d’étendre manuellement le dictionnaire des niveaux avant d’effectuer l’assignation par indexation logique :
levels(cohorte_psy$statut_cat) <- c(levels(cohorte_psy$statut_cat), "Non spécifié")
cohorte_psy$statut_cat[is.na(cohorte_psy$statut_cat)] <- "Non spécifié"
Une alternative radicale, lorsque la dimension catégorielle n’a plus vocation à être introduite sous forme de contrastes au sein d’un modèle statistique paramétrique, consiste à démystifier la variable en la rétrogradant au statut de simple vecteur textuel. L’exécution préalable de cohorte_psy$statut_cat <- as.character(cohorte_psy$statut_cat) convertit l’objet en chaîne standard, libérant l’utilisateur des contraintes de conformité des levels et autorisant l’usage immédiat de tidyr::replace_na().
9. Traitements textuels avancés et nettoyage conditionnel avec stringr
9.1 Harmonisation des fausses chaînes de valeurs manquantes
Dans la pratique courante de la recherche empirique, les fichiers bruts issus de logiciels d’évaluation psychométrique ou d’enquêtes en ligne (tels que Qualtrics, Google Forms, REDCap ou LimeSurvey) arrivent rarement dans un état de propreté idéal. Lors de l’exportation et de l’importation par des fonctions comme read.csv(), de nombreuses pseudo-valeurs manquantes sont encodées sous forme de textes littéraux parasites : les mentions "NA", "N/A", "null", "aucun", ".", voire de simples espaces blancs non visibles à l’œil nu " ".
Pour l’interpréteur R, ces mentions ne constituent en aucune façon des données manquantes. Il s’agit de chaînes de caractères parfaitement valides, dotées d’une longueur non nulle, sur lesquelles le test is.na() renverra immanquablement FALSE. Dès lors, toute tentative d’application de replace_na() sur un tel corpus ignorera superbement ces cellules aberrantes. Une phase de normalisation et d’harmonisation amont s’avère indispensable pour convertir ces fausses chaînes textuelles en authentiques indicateurs logiques NA avant d’entreprendre l’imputation finale unifiée.
Cette harmonisation s’accomplit avec une efficacité remarquable en combinant les fonctions stringr::str_trim() et dplyr::na_if() au sein d’un pipeline de préparation :
library(stringr)
donnees_propres <- donnees_brutes |>
mutate(across(where(is.character), ~ str_trim(.x))) |>
mutate(across(where(is.character), ~ na_if(.x, "N/A"))) |>
mutate(across(where(is.character), ~ na_if(.x, "null"))) |>
mutate(across(where(is.character), ~ na_if(.x, ""))) |>
mutate(across(where(is.character), ~ replace_na(.x, "Non documenté")))
Dans ce protocole séquencé, str_trim() commence par éliminer tous les espaces résiduels d’amorce et de fin qui pourraient masquer la vacuité d’une réponse. Par la suite, les appels successifs à na_if() comparent les éléments du vecteur à des chaînes indicatrices de carence et forcent la conversion de ces concordances vers le statut logique formel NA_character_. Une fois la base assainie et standardisée sur le plan de ses représentations d’incomplétude, la fonction replace_na() intervient pour conférer un libellé textuel homogène et harmonisé à l’ensemble du jeu d’observations.
9.2 Remplacement contextuel basé sur des motifs d’expressions régulières
Dans certaines situations complexes, les motifs de non-réponse ne sont pas standardisés sous une forme littérale exacte, mais se déclinent à travers des variations textuelles rédigées librement par les enquêteurs lors de la collecte (par exemple : « pas de réponse », « absence de données », « non applicable selon participant »). La détection de ces motifs exige la puissance des expressions régulières (regex) au moyen de la bibliothèque stringr.
Il importe également d’éclairer la fonction spécialisée str_replace_na() offerte par stringr, dont l’action est souvent confondue avec celle de tidyr::replace_na(). La commande str_replace_na(string, replacement = "NA") a été explicitement conçue pour convertir un indicateur logique NA en une chaîne littérale exacte contenant par défaut les caractères alphabétiques "NA". Bien que cela semble anodin, cette distinction sémantique est capitale :
vecteur_test <- c("Patient 1", NA, "Patient 3")
str_replace_na(vecteur_test, replacement = "Inconnu")
La nuance épistémologique entre str_replace_na() et replace_na() réside principalement dans le domaine d’application visé. Alors que tidyr::replace_na() s’adresse prioritairement à l’ingénierie des données tabulaires et accepte des listes configuratrices pour des data frames entiers, stringr::str_replace_na() s’insère au sein de pipelines de traitement de texte pur, notamment lors de la préparation de chaînes destinées à la concaténation vectorisée via str_c() ou paste0(), où la présence d’un NA logique provoquerait la propagation indésirable de la valeur manquante sur toute la chaîne résultante.
Le choix de l’outil approprié dépend donc rigoureusement de la finalité de l’opération : si la transformation vise la complétion d’un champ documentaire au sein d’une matrice d’observations scientifiques, l’emploi de l’écosystème tidyr / dplyr demeure le standard recommandé par la communauté scientifique internationale.
10. Études de cas appliquées en psychologie quantitative et sciences sociales
10.1 Nettoyage d’une batterie de tests psychométriques (Exemple : BDI-II ou NEO-PI-R)
Pour ancrer ces développements théoriques au cœur de la pratique empirique de la recherche, développons un cas d’application concret articulé autour de l’assainissement d’un inventaire clinique d’évaluation de la symptomatologie dépressive, à l’image du Beck Depression Inventory (BDI-II). Dans un protocole d’administration numérique moderne, les réponses brutes collectées contiennent fréquemment une combinaison de métadonnées sociodémographiques qualitatives, d’indicateurs de consentement formel et de scores d’items psychométriques individuels quantifiés sur des échelles de Likert allant de 0 à 3.
Supposons que nous recevions la matrice d’évaluation suivante pour cinq participants consécutifs admis au sein d’un protocole hospitalier de suivi de l’humeur :
reponses_bdi <- data.frame(
sujet = c("S01", "S02", "S03", "S04", "S05"),
consentement = c("Oui", "Oui", NA, "Oui", "Oui"),
motif_consultation = c("Trouble du sommeil", NA, "Attaque de panique", NA, "Tristesse persistante"),
item1_tristesse = c(2, 3, NA, 0, 1),
item2_pessimisme = c(1, 2, NA, 0, NA),
item3_echec = c(1, NA, NA, 0, 2),
stringsAsFactors = FALSE
)
La règle méthodologique absolue en psychométrie impose une ségrégation étanche entre les variables descriptives qualitatives et les scores quantitatifs bruts des items. Dans les colonnes d’items (item1 à item3), les valeurs manquantes résultent soit d’une omission par fatigue, soit d’un refus délibéré de répondre face à des thématiques culpabilisantes. Remplacer ces NA par du texte dans les colonnes d’items briserait irrémédiablement la structure mathématique de l’échelle, interdisant tout calcul ultérieur du score total de dépression par sommation arithmétique.
Le traitement raisonné consiste donc à assainir textuellement les variables descriptives, tout en conservant l’intégrité numérique des items pour calculer un indice de complétude psychométrique par sujet :
bdi_prepare <- reponses_bdi |>
replace_na(list(
consentement = "Statut non documenté",
motif_consultation = "Motif non spécifié lors de l'admission"
)) |>
rowwise() |>
mutate(
items_repondus = sum(!is.na(c_across(starts_with("item")))),
taux_completude = items_repondus / 3,
score_sommaire = ifelse(taux_completude >= 0.8, sum(c_across(starts_with("item")), na.rm = TRUE), NA_real_)
) |>
ungroup()
Dans ce pipeline clinique hautement reproductible, les variables qualitatives sont documentées sans ambiguïté documentaire, tandis que les fonctions vectorisées calculent rigoureusement la proportion d’items complétés par chaque sujet. Conformément aux recommandations psychométriques internationales, si un sujet présente un taux de complétude inférieur à un seuil tolérable (ici 80 % des items de l’échelle), le score sommaire n’est pas estimé de façon hasardeuse par sommation incomplète, mais est consigné formellement comme une valeur manquante quantitative NA_real_ en vue d’imputations stochastiques ultérieures.
10.2 Traitement des questions ouvertes et commentaires libres en recherche clinique
Dans de nombreux protocoles mixtes alliant psychiatrie quantitative et psychopathologie clinique, les questionnaires d’évaluation sont complétés par des champs de saisie ouverts où les participants ou les praticiens peuvent formuler des observations narratives complémentaires (verbatim textuels, descriptions de ressentis somatiques, commentaires sur la tolérance médicamenteuse). L’absence de texte dans ces champs correspond le plus souvent au fait que le participant n’avait aucune remarque particulière à ajouter.
Lorsque ces bases de données qualitatives sont exportées pour faire l’objet de fouille de textes (text mining) ou de traitement automatique du langage naturel (NLP) via des bibliothèques spécialisées comme tidytext, la présence de valeurs NA au sein du corpus textuel introduit des biais algorithmiques sévères. Les fonctions de segmentation textuelle (tokenization) par mots ou n-grammes de tidytext::unnest_tokens() échouent ou éliminent silencieusement les participants ne présentant aucun commentaire, faussant le dénombrement global de la cohorte initiale.
La préparation méthodologique d’un tel corpus impose de convertir systématiquement les cellules vierges en une étiquette standardisée sans contenu sémantique parasitant les dictionnaires lexicaux :
corpus_clinique <- corpus_brut |>
mutate(observations_libres = replace_na(observations_libres, "Aucune observation formulée"))
L’intégration explicite de cette formule standardisée garantit que chaque unité statistique (chaque patient) est maintenue au sein de l’arborescence du corpus. Lors de la phase ultérieure de filtrage des mots vides (stop words), l’expression « Aucune observation formulée » peut être soit isolée pour quantifier la prévalence des consultations sans incident verbalisé, soit exclue des calculs de fréquence sans risquer de faire disparaître les métadonnées sociodémographiques associées à ces observations.
11. Bonnes pratiques méthodologiques, reproductibilité et pièges statistiques
11.1 Conséquences statistiques de la conversion des NA en chaînes
Bien que la substitution d’une étiquette textuelle à un indicateur NA constitue une pratique légitime pour l’affichage de tables de bord et la documentation qualitative, l’analyste doit impérativement mesurer les répercussions méthodologiques profondes de cette opération sur la validité de ses modélisations statistiques ultérieures.
Le premier écueil relève de la neutralisation irréversible des mécanismes de sécurité intégrés à l’interpréteur R. Les fonctions d’estimation paramétrique (telles que lm() pour la régression linéaire ou glm() pour la régression logistique) disposent d’un argument fondamental nommé na.action, configuré par défaut sur na.omit. Ce mécanisme détecte les valeurs manquantes authentiques et ajuste automatiquement les degrés de liberté du modèle en conséquence. Dès lors qu’un NA est converti en une chaîne textuelle explicite telle que « Inconnu », cette modalité est assimilée par le moteur de modélisation à un niveau catégoriel parfaitement légitime.
Cette transformation artificielle introduit des distorsions conceptuelles majeures :
- Le modèle statistique crée un paramètre de régression spécifique pour la modalité textuelle « Inconnu », surconsommant des degrés de liberté de manière injustifiée.
- Si la non-réponse est corrélée à une variable non mesurée (phénomène MNAR), l’estimation des coefficients de régression pour les autres modalités s’avère lourdement biaisée.
- Les tests de comparaisons multiples et d’hypothèses nulles perdent leur étalonnage probabiliste, augmentant drastiquement le risque d’erreur de type I (faux positifs) ou de type II (faux négatifs).
Les recommandations méthodologiques édictées par l’American Psychological Association (APA) préconisent la transparence absolue dans la documentation du traitement des valeurs manquantes. Dans une perspective inférentielle, la substitution textuelle ne doit jamais être utilisée pour masquer une incomplétude statistique. Lorsque des calculs mathématiques ou des tests d’hypothèses sont programmés, les méthodes d’imputation multiple par équations chaînées (implémentées sous R dans l’excellent package mice) ou par algorithmes espérance-maximisation (package Amelia) doivent systématiquement primer sur toute manipulation textuelle empirique.
11.2 Traçabilité du code et documentation du flux de travail
Dans un contexte scientifique international où la crise de la reproductibilité mobilise l’attention des comités de lecture, la traçabilité des transformations opérées sur les données brutes constitue un impératif éthique et déontologique. Chaque décision d’assainissement textuel doit être minutieusement documentée dans un cahier de laboratoire électronique et inscrite au sein d’un codebook (dictionnaire de variables) accessible aux auditeurs de la recherche.
La règle fondamentale d’ingénierie des données prescrit l’immutabilité des fichiers sources. L’analyste ne doit en aucun cas modifier directement ses fichiers de données brutes sur disque (fichiers CSV, XLSX ou bases SQL d’origine). Le pipeline analytique doit charger les données sources en mode de lecture seule, créer une copie d’étape en mémoire vive (nommée explicitement par exemple donnees_etape1_nettoyees), et appliquer l’ensemble des substitutions via des scripts commentés et versionnés avec l’outil Git.
Pour s’assurer qu’aucune valeur manquante non intentionnelle ne subsiste à la suite de la phase d’imputation textuelle, l’implémentation de tests unitaires automatisés à l’aide de la bibliothèque testthat représente une excellente pratique d’assurance qualité logicielle :
library(testthat)
test_that("Vérification de l'absence de NA résiduels dans la colonne statut", {
expect_equal(sum(is.na(cohorte_psy$statut_marital)), 0)
})
L’intégration de tels points de contrôle tout au long du script de prétraitement garantit l’intégrité de la chaîne d’analyse et certifie que toute altération ultérieure du pipeline sera immédiatement signalée en cas de régression ou d’introduction accidentelle de nouvelles valeurs manquantes non répertoriées.
12. Guide décisionnel récapitulatif pour les utilisateurs de R
12.1 Matrice de choix d’outils selon le contexte analytique
Face à la multiplicité des solutions techniques offertes par l’écosystème du langage R pour substituer des chaînes de caractères aux valeurs manquantes, le chercheur ou l’analyste de données doit opérer un choix éclairé en fonction des contraintes spécifiques de son environnement de travail. Le choix optimal découle d’un compromis rationnel entre lisibilité algorithmique, dépendances externes, complexité des règles logiques à mettre en œuvre et volume des structures de données traitées.
Pour formaliser cette démarche décisionnelle, le tableau descriptif ci-dessous synthétise les propriétés comparées des approches étudiées :
Méthode par indexation en R de base (Base R) :
Cette approche se caractérise par une dépendance logicielle nulle, ne requérant aucun chargement de package tiers. Sa vitesse d’exécution sur les vecteurs atomiques est remarquable. En revanche, sa lisibilité est modérée et le risque d’erreur syntaxique par duplication du nom des variables est élevé. Elle s’impose particulièrement lors du développement de packages R autonomes ou sur des serveurs de calcul haute performance hautement sécurisés où le déploiement du Tidyverse est restreint.
Méthode avec tidyr::replace_na() :
Recommandée pour son excellente lisibilité au sein de flux de préparation interactifs, cette méthode nécessite le chargement de l’extension tidyr. Sa signature simple permet de traiter indifféremment des vecteurs isolés ou des data frames entiers via une liste associative de remplacement. Elle constitue la norme contemporaine par excellence pour les rapports d’analyse reproductibles sous Quarto ou R Markdown.
Méthode conditionnelle avec dplyr::case_when() :
Bien que sa dépendance logicielle envers le package dplyr soit formelle, cette approche offre une expressivité logique inégalée. Elle est indispensable dès lors que l’attribution d’une étiquette textuelle de remplacement dépend de conditions logiques croisées impliquant d’autres variables du tableau de données (comme la discrimination des non-réponses structurelles face aux omissions réelles).
Méthode pour facteurs avec forcats::fct_na_value_to_level() :
Cette procédure est spécifiquement dédiée aux variables catégorielles de classe factor. Elle résout formellement le problème du rejet d’insertion textuelle en intégrant d’abord la modalité dans les levels avant d’imputer. Elle doit être systématiquement préférée pour sécuriser les colonnes qualitatives destinées à l’inférence sans altérer leur structure typologique sous-jacente.
12.2 Synthèse des instructions et aide-mémoire de programmation
Pour clore ce manuel de référence et offrir aux chercheurs un guide opérationnel d’application quotidienne, ce récapitulatif regroupe les commandes fondamentales indispensables à la manipulation et à la substitution des valeurs NA par du texte sous R :
1. Substitution dans un vecteur isolé (Tidyverse) :
x <- replace_na(x, "Non renseigné")
2. Substitution dans un vecteur isolé (R de base) :
x[is.na(x)] <- "Non renseigné"
3. Substitution simultanée dans un data frame complet :
donnees <- donnees |> replace_na(list(var1 = "Inconnu", var2 = "Non spécifié"))
4. Substitution dans un pipeline dplyr avec mutate :
donnees <- donnees |> mutate(var = replace_na(var, "Valeur"))
5. Substitution automatique sur toutes les colonnes textuelles :
donnees <- donnees |> mutate(across(where(is.character), ~ replace_na(.x, "Absent")))
6. Substitution sécurisée sur une variable catégorielle (Facteur) :
donnees <- donnees |> mutate(var_fact = forcats::fct_na_value_to_level(var_fact, level = "Non documenté"))
7. Conversion préalable des fausses chaînes en véritables NA :
donnees <- donnees |> mutate(var = dplyr::na_if(var, "N/A")) |> mutate(var = replace_na(var, "Non documenté"))
8. Vérification post-transformation de l’absence de NA résiduels :
table(donnees$var, useNA = "always")
En intégrant ces instructions standardisées au sein de leurs protocoles de recherche, les analystes de données s’assurent d’une manipulation rigoureuse, transparente et pérenne de leurs corpus empiriques. L’assainissement systématique des données manquantes, loin de se réduire à un simple artifice d’affichage, participe pleinement à l’élévation des standards de qualité et de reproductibilité de la science computationnelle contemporaine.
Références
- American Psychological Association. (2020). Publication manual of the American Psychological Association (7th ed.). American Psychological Association. https://apastyle.apa.org/
- Beck, A. T., Steer, R. A., & Brown, G. K. (1996). Manual for the Beck Depression Inventory-II. Psychological Corporation. https://doi.org/10.1037/t00742-000
- Little, R. J. A., & Rubin, D. B. (2019). Statistical analysis with missing data (3rd ed.). John Wiley & Sons. https://doi.org/10.1002/9781119482864
- R Core Team. (2023). R: A language and environment for statistical computing. R Foundation for Statistical Computing. https://www.r-project.org/
- Rubin, D. B. (1976). Inference and missing data. Biometrika, 63(3), 581-592. https://doi.org/10.1093/biomet/63.3.581
- Schafer, J. L. (1997). Analysis of incomplete multivariate data. Chapman & Hall/CRC. https://doi.org/10.1201/9781439821862
- van Buuren, S. (2018). Flexible imputation of missing data (2nd ed.). Chapman & Hall/CRC. https://stefvanbuuren.name/fimd/
- Wickham, H. (2014). Tidy data. Journal of Statistical Software, 59(10), 1-23. https://doi.org/10.18637/jss.v059.i10
- Wickham, H. (2019). Advanced R (2nd ed.). Chapman & Hall/CRC. https://adv-r.hadley.nz/
- Wickham, H., Çetinkaya-Rundel, M., & Grolemund, G. (2023). R for data science (2nd ed.). O’Reilly Media. https://r4ds.hadley.nz/
- Wickham, H., François, R., Henry, L., Müller, K., & Vaughan, D. (2023). dplyr: A grammar of data manipulation (R package version 1.1.4). https://CRAN.R-project.org/package=dplyr
- Wickham, H., Vaughan, D., & Girlich, M. (2024). tidyr: Tidy messy data (R package version 1.3.1). https://CRAN.R-project.org/package=tidyr