NA signifie « inconnu » - et il se propage
Les vrais jeux de données ont des trous : la question d'enquête laissée blanche, le capteur qui a raté un relevé. R représente chaque trou par NA - not available. Le modèle mental crucial : NA n'est pas zéro, ni une chaîne vide, ni un nombre spécial. Il signifie « il y a une valeur ici, mais nous ne savons pas laquelle ».
Prends ça au sérieux et le comportement de R devient logique. Que vaut un nombre inconnu plus un ? Inconnu. Que vaut la moyenne de 4, 8 et quelque chose d'inconnu ? Inconnue - la valeur manquante pourrait être n'importe quoi, donc la moyenne pourrait être n'importe quoi :
Les trois affichent NA. Cette contagion est une fonctionnalité, pas un bug : R refuse de prétendre discrètement connaître une réponse qu'il ignore. Un tableur qui saute silencieusement les cases vides peut masquer le fait que la moitié d'une colonne manque ; R te force à le remarquer et à décider ce que les valeurs manquantes doivent signifier pour ton analyse. Le reste de cet article porte sur la façon de prendre cette décision délibérément.
Tester la présence de NA : is.na(), jamais ==
Voici le premier piège dans lequel tout le monde tombe. Tu veux trouver les valeurs manquantes, alors tu écris une comparaison - et ça ne marche pas :
x == NA renvoie NA NA NA - pas un seul TRUE. Pourquoi ? Suis la logique de l'« inconnu » : 4 est-il égal à une valeur inconnue ? Impossible à dire - l'inconnue pourrait être 4. Même la case NA se compare en NA : un inconnu est-il égal à un autre ? Inconnu. Une comparaison avec NA ne peut jamais renvoyer TRUE ou FALSE, donc comme test c'est inutile - et pire, un masque entièrement NA dans [ ] ne sélectionne pas ce que tu attends.
Le bon outil est is.na(), conçu pour répondre exactement à cette question et qui renvoie des logiques honnêtes : FALSE TRUE FALSE. À partir de là, deux idiomes que tu utiliseras constamment :
sum(is.na(x)) compte les valeurs manquantes (TRUE compte pour 1) - lance-le sur chaque colonne d'un nouveau jeu de données avant toute autre chose. which(is.na(x)) les localise. Et x[!is.na(x)] ne garde que les valeurs observées - suppression manuelle via un masque logique, le même motif de filtrage de vecteur que d'habitude.
Ignorer les NA dans les résumés : na.rm = TRUE
Tu n'as généralement pas besoin de retirer les NA à la main, parce que les fonctions de résumé de R ont une soupape intégrée - l'argument na.rm (NA remove) :
Avec na.rm = TRUE, la fonction écarte les valeurs manquantes et calcule sur ce qui reste : la moyenne de 4 et 8 est 6. sum(), sd(), median(), min(), max(), var() - toute la famille des statistiques descriptives l'accepte.
Remarque que la valeur par défaut est FALSE. C'est R qui prend parti en ta faveur : il veut qu'ignorer des données manquantes soit un choix explicite que tu écris, pas un comportement silencieux par défaut. Quand tu tapes na.rm = TRUE, tu affirmes « les valeurs manquantes peuvent être ignorées ici » - ce qui est vrai quand un capteur a raté quelques relevés au hasard, et dangereusement faux quand, disons, les plus bas revenus ont sauté la question sur le salaire. L'argument te fait assumer ce jugement.
Supprimer les lignes incomplètes : na.omit() et complete.cases()
Dans un data frame, le manque vit dans les cellules, mais l'analyse travaille souvent ligne par ligne - l'opération courante est donc de supprimer les lignes contenant un NA :
na.omit(df) renvoie le data frame moins chaque ligne contenant au moins un NA - ici, seule Rosa survit. complete.cases(df) renvoie le masque logique de lignes (TRUE FALSE FALSE) derrière la même idée, et l'indexation avec lui donne des résultats identiques avec deux avantages : tu peux d'abord compter ce que tu t'apprêtes à perdre (sum(!complete.cases(df))), et tu peux restreindre les colonnes qui comptent - df[complete.cases(df[, "age"]), ] ne supprime que les lignes où age manque, gardant Mia même si son score est inconnu.
Cette forme restreinte aux colonnes compte plus qu'il n'y paraît : na.omit() sur un data frame large peut jeter silencieusement la plupart de tes lignes à cause de NA dans des colonnes que tu n'avais jamais prévu d'analyser. Sache combien de lignes tu supprimes, et pourquoi, avant de les supprimer.
Remplacer les NA
Parfois le bon geste est de combler les trous plutôt que de supprimer des lignes. L'idiome combine is.na() avec une affectation :
Lis-le ainsi : « dans les cases où visits est manquant, mets 0 ». C'est légitime exactement quand NA encode une valeur connue - un client sans historique de visites a réellement eu zéro visite.
Mais sois honnête sur les cas où c'est vrai. Si NA signifie « nous n'avons pas réussi à mesurer », substituer 0 fabrique des données : remplacer des notes manquantes par 0 tire la moyenne vers le bas comme si ces élèves avaient eu zéro, alors qu'en vérité tu ne sais pas ce qu'ils ont obtenu. Compare la moyenne ci-dessus (2,4) avec la moyenne na.rm de l'original (4) : mêmes données, affirmations différentes. Remplacer par la moyenne ou la médiane déforme moins mais sous-estime encore la variabilité. La règle : n'impute une valeur que si tu peux dire, en mots simples, pourquoi cette valeur est bien ce qu'était réellement l'entrée manquante. Sinon garde le NA et utilise na.rm - « inconnu » est souvent la valeur la plus véridique du jeu de données.
NA, NULL, NaN et Inf
R a quatre valeurs spéciales qui se ressemblent mais signifient des choses réellement différentes :
| Valeur | Signification | Longueur | Source typique |
|---|---|---|---|
NA | Une valeur existe mais est inconnue | 1 (occupe une case) | Donnée manquante |
NULL | Aucun objet du tout | 0 (aucune case) | Élément de liste supprimé, résultat vide |
NaN | Calcul à réponse indéfinie | 1 | 0 / 0, log(-1) |
Inf | Un nombre au-delà du représentable | 1 | 1 / 0, dépassement |
Les distinctions qui comptent en pratique : NULL disparaît dans les vecteurs (c(1, NULL, 3) a deux éléments - il ne peut pas représenter une observation manquante), alors que NA garde sa place. NaN compte comme manquant (is.na(NaN) vaut TRUE, donc na.rm le retire aussi), mais l'inverse est faux - is.nan(NA) vaut FALSE. Et Inf n'est pas manquant - c'est un nombre réel et comparable (Inf > 1e300 vaut TRUE), donc na.rm ne le retirera pas ; utilise is.finite() pour filtrer les nombres ordinaires.
Pour être complet : NA existe discrètement en versions typées (NA_integer_, NA_real_, NA_character_) pour pouvoir s'installer dans n'importe quel vecteur sans casser la règle du type unique. Tu les taperas rarement, mais tu les verras dans le code des packages et les messages d'erreur de dplyr.
Ce que tu retiens
NAsignifie « inconnu », et les inconnus sont contagieux : tout calcul touchant un NA renvoie NA - à dessein.- Teste avec
is.na(), jamais== NA; compte les trous avecsum(is.na(x)). na.rm = TRUEfait ignorer les NA aux fonctions de résumé - une décision explicite, appel par appel, que les valeurs manquantes sont négligeables.na.omit()supprime en bloc les lignes incomplètes ;complete.cases()te donne le masque, dénombrable et restreignable aux colonnes qui comptent.- Ne remplace un NA (
x[is.na(x)] <- valeur) que si tu peux justifier ce qu'était réellement la valeur manquante. - NA ≠ NULL ≠ NaN ≠ Inf : valeur manquante, objet absent, calcul indéfini, nombre non borné.
Prochaine étape : les fonctions - emballer ta logique en morceaux nommés et réutilisables.
Questions fréquentes
Pourquoi mean() renvoie-t-il NA en R ?
Parce qu'au moins une valeur du vecteur vaut NA, et NA est contagieux : si une entrée est inconnue, R dit que la réponse est inconnue aussi. Passe na.rm = TRUE - mean(x, na.rm = TRUE) - pour calculer la moyenne des valeurs présentes. La plupart des fonctions de résumé (sum, sd, median, min, max) acceptent le même argument.
Comment vérifier la présence de NA en R ?
Avec is.na(x), qui renvoie TRUE partout où une valeur est manquante. Ne teste jamais avec x == NA - comparer quoi que ce soit à un inconnu donne NA, pas TRUE ni FALSE, donc le test échoue silencieusement. sum(is.na(x)) compte les valeurs manquantes ; which(is.na(x)) trouve leurs positions.
Comment supprimer les lignes contenant des NA d'un data frame en R ?
na.omit(df) supprime chaque ligne contenant au moins un NA. Pour plus de contrôle, complete.cases(df) renvoie un vecteur logique marquant les lignes entièrement observées, donc df[complete.cases(df), ] fait la même chose explicitement - et tu peux l'appliquer aux seules colonnes qui comptent, par exemple df[complete.cases(df[, c("age", "score")]), ].
Quelle est la différence entre NA et NULL en R ?
NA est une valeur manquante - elle occupe une case dans un vecteur et a une longueur de 1. NULL est l'absence d'objet - il a une longueur de 0 et disparaît quand on le met dans un vecteur : c(1, NULL, 3) n'a que deux éléments. Utilise NA pour une donnée manquante ; NULL apparaît quand on supprime des éléments de liste ou comme retour vide.