Programmation défensive en R : valider les données cliniques avec checkmate et assertthat

Affirmez la structure, les types, les plages, l’unicité et les valeurs autorisées d’un jeu de données ADaM en haut de votre script, pour qu’une mauvaise hypothèse échoue tôt et bruyamment — un jeu de données corrigé, pas un tableau à réexécuter

Un tutoriel complet et exécutable sur la programmation défensive en R — faire échouer un script d’analyse clinique tôt et bruyamment lorsque ses données d’entrée violent les attentes. Apprenez pourquoi les assertions « échouer vite » comptent (une mauvaise hypothèse détectée en haut d’un script est un jeu de données corrigé ; détectée dans un tableau, c’est une réexécution complète), puis validez un vrai jeu de données ADaM avant de calculer : partez du stopifnot de base, passez à checkmate pour des vérifications riches et expressives de la structure, des types, des plages, de l’unicité et des valeurs autorisées d’un data frame, utilisez assertthat pour des prédicats lisibles avec des messages personnalisés, et enveloppez le tout dans une seule fonction « barrière » réutilisable qui s’exécute en haut de chaque script. Sur des données pharmaverse publiques, pour que chaque ligne s’exécute.

Date de publication

1 juillet 2026

Modifié

7 juillet 2026

AstucePoints clés
  • La programmation défensive signifie qu’un script vérifie ses hypothèses avant d’agir dessus. Vous affirmez à quoi les données d’entrée doivent ressembler tout en haut ; si la réalité n’est pas d’accord, le script s’arrête là avec un message clair au lieu de calculer un résultat faux et de le transmettre en aval.
  • Une assertion est un fait énoncé qui doit être vrai, imposé à l’exécution. « AGE est numérique, n’a aucune valeur manquante et se situe entre 0 et 120. » Si c’est vérifié, l’exécution se poursuit silencieusement ; sinon, elle lève une erreur.
  • stopifnot() est le socle sans dépendance ; checkmate offre des vérifications riches et expressives sur un data frame (structure, type, plage, unicité, valeurs autorisées) avec des messages qui nomment le problème exact ; assertthat offre des prédicats lisibles et des messages d’échec personnalisés, formulés pour des humains.
  • Échouez tôt, et c’est peu coûteux. Une mauvaise hypothèse détectée en haut d’un script est un jeu de données corrigé ; la même mauvaise hypothèse détectée dans un tableau fini est une réexécution complète de tout ce qui se trouve en dessous.
  • Regroupez les vérifications dans une seule fonction-barrière. Un validate_adsl() qui exécute chaque assertion et renvoie les données de façon invisible transforme « j’espère que l’entrée est propre » en une seule ligne réutilisable et auditable.

Introduction

Le jeu de données avait l’air correct. Le nombre de sujets était bon, toutes les colonnes étaient là, le script s’est exécuté jusqu’au bout sans une seule erreur. Puis quelqu’un a remarqué que le tableau de sécurité comptait des sujets fantômes — une valeur "U" de SAFFL (l’indicateur de population de sécurité) s’était glissée lors d’une fusion en amont, et le code qui filtrait sur SAFFL == "Y" avait tranquillement fait exactement ce qu’on lui avait dit, sur des données qui n’ont jamais été valides. Rien n’a échoué. C’est là le problème.

La programmation défensive est la solution : écrire du code qui se méfie de son entrée et vérifie ses hypothèses avant d’agir dessus. Au lieu d’espérer que le jeu de données ADaM (Analysis Data Model) remis à votre analyse est propre, vous affirmez ce qu’il doit être — les bonnes colonnes, les bons types, des valeurs dans les plages, une clé sujet unique, seulement les codes autorisés — et si une assertion est fausse, le script s’arrête tout en haut, bruyamment, avec un message qui nomme le problème. Cette leçon construit cette discipline en R de trois façons : le stopifnot() de base, le package checkmate et le package assertthat, appliqués à un vrai ADSL — le jeu de données d’analyse au niveau sujet, une ligne par sujet — issu de données pharmaverse publiques, pour que chaque ligne s’exécute telle quelle.

Voici la forme de l’idée : une batterie d’assertions protège le jeu de données, et celle qui détecte le mauvais SAFFL se déclenche avant qu’aucune analyse ne s’exécute.

A horizontal bar chart of five input-data assertions guarding an ADaM ADSL dataset before analysis. Four checks pass in azure — structure is a data frame, required columns present, types and ranges valid, subject key unique. The fifth check, allowed values (SAFFL must be Y or N), fails in orange, annotated 'stops the script'. This is fail-fast defensive programming: the bad value is caught at the top, not in a finished table.

Cette barrière orange, c’est tout l’intérêt de la programmation défensive : la mauvaise valeur est détectée ici, avant qu’elle ne puisse devenir un nombre faux dans un tableau signé par le QC (contrôle qualité).

Pourquoi échouer vite

Une erreur est la moins coûteuse à l’instant où elle survient et la plus coûteuse à l’instant où quelqu’un en aval croit le résultat. Un SAFFL valant "U" détecté par une assertion à la ligne 5 vous coûte un message clair et un fichier d’entrée corrigé. La même mauvaise valeur découverte dans un tableau d’efficacité fini vous coûte un diagnostic, une correction et une réexécution de chaque dérivation, tableau, listing et figure — les TLF (tableaux, listings et figures) — construits par-dessus. Le bug n’a pas grossi ; c’est son rayon d’impact qui a grossi.

L’objectif n’est donc pas d’éviter les erreurs — de mauvaises données arriveront toujours — mais de les faire remonter le plus tôt possible, le plus bruyamment possible. C’est exactement ce que fait la programmation défensive avec assertions : elle convertit une mauvaise hypothèse silencieuse et latente en un arrêt net et immédiat au point où l’hypothèse a été faite. On fait plus facilement confiance à un script qui échoue vite qu’à un script qui s’exécute toujours, car lorsqu’il va jusqu’au bout, vous savez que ses entrées ont réellement respecté leur contrat.

Le jeu de données que nous défendons

Nous validons l’ADSL pilote CDISC (Clinical Data Interchange Standards Consortium) livré dans pharmaverseadam. Un détail importe avant toute vérification : pharmaverseadam::adsl est un tibble, et certaines fonctions d’assertion sont strictes sur la classe, donc nous le convertissons d’abord en un data.frame ordinaire. Nous regardons les quatre colonnes que nous allons vérifier — la clé sujet USUBJID, l’AGE numérique, le SAFFL (l’indicateur de population de sécurité, "Y"/"N") et SEX.

adsl <- as.data.frame(pharmaverseadam::adsl)

# The columns our assertions will guard
head(adsl[, c("USUBJID", "AGE", "SAFFL", "SEX")], 5)
      USUBJID AGE SAFFL SEX
1 01-701-1015  63     Y   F
2 01-701-1023  64     Y   M
3 01-701-1028  71     Y   M
4 01-701-1033  74     Y   M
5 01-701-1034  77     Y   F

Notre contrat pour ce jeu de données, énoncé en langage clair, est : c’est un data frame non vide ; il contient USUBJID, AGE et SAFFL ; USUBJID est une clé de type caractère unique et non manquante ; AGE est numérique, n’a aucune valeur manquante et se situe entre 0 et 120 ; et SAFFL ne contient que "Y" ou "N". Tout ce qui suit transforme chacune de ces clauses en une assertion.

Le socle : le stopifnot() de base

Vous n’avez pas besoin d’un package pour commencer. Le stopifnot() de base R prend un nombre quelconque de conditions logiques et lève une erreur à l’instant où l’une n’est pas TRUE. C’est la référence sans dépendance que toute installation R possède déjà.

adsl <- as.data.frame(pharmaverseadam::adsl)

# Each condition must be TRUE, or execution stops here
stopifnot(
  is.data.frame(adsl),
  nrow(adsl) > 0,
  all(c("USUBJID", "AGE", "SAFFL") %in% names(adsl)),
  all(adsl$SAFFL %in% c("Y", "N"))
)

Rien ne s’affiche, et c’est le succès — chaque condition a tenu, donc le script continue. Observez maintenant ce qui se passe quand une condition est fausse. Pour que la page continue de se rendre (un vrai échec interromprait le script), nous interceptons l’erreur et affichons son message :

# Pretend an upstream merge introduced a bad flag
saffl_bad <- c("Y", "N", "U")

tryCatch(
  stopifnot(all(saffl_bad %in% c("Y", "N"))),
  error = function(e) cat("Error:", conditionMessage(e))
)
Error: all(saffl_bad %in% c("Y", "N")) is not TRUE

Le message est honnête mais laconique : il renvoie l’expression qui a échoué, pas le problème. Il vous dit que all(saffl_bad %in% c("Y", "N")) n’est pas vrai — vous devez encore comprendre que "U" est le coupable. Pour une vérification ponctuelle, c’est très bien. Pour valider un jeu de données clinique, vous voulez que le message nomme la mauvaise valeur. C’est ce qu’ajoute checkmate.

checkmate : des assertions riches et expressives

checkmate a été conçu exactement pour cela — des vérifications d’arguments rapides pour la programmation défensive en R. Chaque vérification se décline en trois variantes, et savoir laquelle choisir, c’est la moitié du savoir-faire :

  • assert_*() lève une erreur si la vérification échoue (renvoie l’objet de façon invisible si elle réussit) — la forme « échouer vite » que vous placez en haut d’un script.
  • test_*() renvoie TRUE/FALSE — à utiliser dans un if.
  • check_*() renvoie TRUE en cas de succès ou une chaîne de message décrivant l’échec — pour collecter plusieurs problèmes, ou (comme ici) pour afficher un message d’échec sans interrompre.

Commencez par la structure. assert_data_frame() confirme que l’objet est un data frame et, avec min.rows, qu’il n’est pas vide :

library(checkmate)
adsl <- as.data.frame(pharmaverseadam::adsl)

assert_data_frame(adsl, min.rows = 1)

Elle a réussi, donc elle a renvoyé le data frame de façon invisible et n’a rien affiché. Ensuite, confirmez que les colonnes dont l’analyse dépend sont présentes. assert_names() avec must.include vérifie que les noms requis existent — et son message d’échec est précis :

library(checkmate)
adsl <- as.data.frame(pharmaverseadam::adsl)

assert_names(names(adsl), must.include = c("USUBJID", "AGE", "SAFFL"))

Voici maintenant les vérifications qui portent le vrai poids — type et plage, unicité et valeurs autorisées — chacune sur sa colonne. assert_numeric() affirme qu’AGE est numérique, couvre une plage clinique plausible et n’a aucune valeur manquante ; assert_character() affirme que la clé sujet est de type caractère et unique ; assert_subset() affirme que SAFFL ne puise que dans l’ensemble autorisé :

library(checkmate)
adsl <- as.data.frame(pharmaverseadam::adsl)

assert_numeric(adsl$AGE, lower = 0, upper = 120, any.missing = FALSE)  # type + range + no NA
assert_character(adsl$USUBJID, any.missing = FALSE, unique = TRUE)     # key is present and unique
assert_subset(adsl$SAFFL, c("Y", "N"))                                 # only allowed codes

Tout est silencieux — le jeu de données respecte son contrat sur chaque clause. La valeur de checkmate apparaît quand une clause échoue. Comparez le message que produit ici le mauvais SAFFL avec le stopifnot() laconique ci-dessus :

library(checkmate)
saffl_bad <- c("Y", "N", "U")

tryCatch(
  assert_subset(saffl_bad, c("Y", "N")),
  error = function(e) cat(conditionMessage(e))
)
Assertion on 'saffl_bad' failed: Must be a subset of {'Y','N'}, but has additional elements {'U'}.

Voilà la différence. checkmate ne dit pas seulement que la vérification a échoué — il dit ce qui est autorisé et quel élément supplémentaire a enfreint la règle ({'U'}). La même richesse s’applique aux plages (Element 2 is not >= 0), à l’unicité (Contains duplicated values, position 2) et aux valeurs manquantes (Contains missing values (element 3)). Le message pointe un collègue — ou vous, six mois plus tard — droit vers la correction.

Vérifications non bloquantes : test_* et check_*

Un appel assert_*() est un arrêt net, ce que vous voulez en haut d’un script. Mais parfois vous voulez bifurquer selon le résultat, ou collecter plusieurs problèmes dans un seul rapport plutôt que de mourir au premier. C’est à cela que servent les deux autres variantes.

Utilisez test_*() quand vous avez besoin d’un simple TRUE/FALSE — par exemple, dans un if :

library(checkmate)
adsl <- as.data.frame(pharmaverseadam::adsl)

if (!test_character(adsl$USUBJID, unique = TRUE)) {
  message("USUBJID is not unique — dedupe before deriving")
} else {
  cat("Subject key is unique:", test_character(adsl$USUBJID, unique = TRUE))
}
Subject key is unique: TRUE

Utilisez check_*() quand vous voulez le message d’échec comme valeur — il renvoie TRUE en cas de succès ou la chaîne de message en cas d’échec, de sorte que vous pouvez en rassembler plusieurs et les rapporter ensemble au lieu d’interrompre à la première mauvaise colonne :

library(checkmate)

# Simulate a contaminated extract to see the messages, without stopping the render
checks <- c(
  SAFFL = check_subset(c("Y", "N", "U"), c("Y", "N")),
  AGE   = check_numeric(c(54, -2, 71), lower = 0),
  KEY   = check_character(c("id1", "id1", "id2"), unique = TRUE)
)
checks[checks != "TRUE"]   # keep only the clauses that failed
                                                             SAFFL 
"Must be a subset of {'Y','N'}, but has additional elements {'U'}" 
                                                               AGE 
                                           "Element 2 is not >= 0" 
                                                               KEY 
                          "Contains duplicated values, position 2" 

Chaque clause en échec se signale en langage clair — le "U" illégal, l’âge négatif, la clé dupliquée — le tout en une seule passe. C’est le schéma pour un rapport de QC qui liste chaque problème d’un jeu de données, pas seulement le premier sur lequel le script a trébuché.

assertthat : des prédicats lisibles et des messages personnalisés

assertthat aborde les choses sous un autre angle. Son assert_that() se lit presque comme de l’anglais et se spécialise dans la production de messages d’échec conviviaux — y compris ceux que vous écrivez vous-même. D’abord des prédicats simples :

library(assertthat)
adsl <- as.data.frame(pharmaverseadam::adsl)

assert_that(nrow(adsl) > 0, noNA(adsl$USUBJID), is.character(adsl$USUBJID))
[1] TRUE

noNA() et is.character() sont des prédicats intégrés d’assertthat ; l’appel renvoie TRUE parce que les trois tiennent — et il arrêterait le script à l’instant où l’un ne tiendrait pas. Sa variante see_if() renvoie plutôt un TRUE/FALSE avec le message d’échec attaché au lieu de lever une erreur (à l’image du test_*() de checkmate), de sorte que vous pouvez bifurquer selon le résultat. Glissez un mauvais indicateur et regardez-la le signaler sans interrompre :

library(assertthat)
adsl <- as.data.frame(pharmaverseadam::adsl)

saffl_bad <- adsl$SAFFL
saffl_bad[1] <- "U"                        # contaminate one flag
see_if(all(saffl_bad %in% c("Y", "N")))    # FALSE, with the reason attached
[1] FALSE
attr(,"msg")
[1] "Elements 1 of saffl_bad %in% c(\"Y\", \"N\") are not true"

La fonctionnalité qui distingue assertthat, c’est on_failure() : attachez un message personnalisé à votre propre prédicat, pour que l’assertion parle le langage de votre domaine plutôt que de renvoyer du code R. Définissez une fois un prédicat « est un indicateur valide », donnez-lui un message, et chaque échec se lit comme un relecteur le formulerait :

library(assertthat)

# A domain predicate with a human message
is_yn_flag <- function(x) all(x %in% c("Y", "N"))
on_failure(is_yn_flag) <- function(call, env) {
  paste0(deparse(call$x), " must contain only 'Y' or 'N' (found an out-of-range flag)")
}

tryCatch(
  assert_that(is_yn_flag(c("Y", "N", "U"))),
  error = function(e) cat(conditionMessage(e))
)
c("Y", "N", "U") must contain only 'Y' or 'N' (found an out-of-range flag)

Au lieu d’Elements 3 ... are not true, le relecteur voit une phrase dans le vocabulaire de l’étude. Pour un script de validation que d’autres personnes lisent, cette lisibilité vaut beaucoup.

Lequel, et quand

Les trois font le même travail — arrêter le script quand une hypothèse est fausse — mais ils font des compromis différents entre dépendances, qualité des messages et lisibilité. Choisissez le bon :

Outil Dépendance Idéal pour Message d’échec
stopifnot() R de base (aucune) une garde rapide sans dépendance ; quelques conditions laconique — renvoie l’expression en échec
checkmate un package léger valider la structure, les types, les plages, l’unicité, les valeurs autorisées d’un data frame ; rapports de QC (check_*) riche — nomme l’ensemble autorisé et la valeur fautive exacte
assertthat un package léger des prédicats lisibles ; des assertions dont les messages seront lus par des non-programmeurs humain — personnalisable par prédicat via on_failure()

Un choix par défaut pratique pour le travail clinique : checkmate pour la validation des données (ses vérifications de data frame et de vecteur sont conçues exactement pour cela, avec les messages les plus utiles), assertthat là où un message personnalisé en langage clair importe, et stopifnot() pour une garde jetable où tirer un package n’en vaut pas la peine. Ils coexistent sans problème — utilisez chacun là où il est le plus fort.

Regroupez-le dans une barrière

Le gain de tout cela, c’est une seule fonction-barrière que vous appelez une fois en haut de chaque script qui consomme un ADSL. Elle exécute chaque assertion et, si toutes réussissent, renvoie les données de façon invisible pour qu’elles se glissent directement dans un pipeline ; si l’une échoue, elle s’arrête avec le message spécifique de checkmate. Le contrat vit à un seul endroit, est réutilisable d’une étude à l’autre, et est auditable.

library(checkmate)

validate_adsl <- function(data) {
  assert_data_frame(data, min.rows = 1)
  assert_names(names(data), must.include = c("USUBJID", "AGE", "SAFFL"))
  assert_character(data$USUBJID, any.missing = FALSE, unique = TRUE)
  assert_numeric(data$AGE, lower = 0, upper = 120, any.missing = FALSE)
  assert_subset(data$SAFFL, c("Y", "N"))
  invisible(data)   # passes the data straight through when valid
}

Sur le vrai ADSL propre, la barrière est silencieuse et rend les données, de sorte que vous pouvez ouvrir votre analyse avec adsl <- validate_adsl(as.data.frame(pharmaverseadam::adsl)) et savoir que tout ce qui suit cette ligne s’exécute sur des données qui ont respecté leur contrat :

library(checkmate)

adsl <- validate_adsl(as.data.frame(pharmaverseadam::adsl))
cat("Validated. Subjects:", nrow(adsl))
Validated. Subjects: 306

Et quand un extrait contaminé arrive — ici, un sujet estampillé avec le "U" fantôme — la barrière se déclenche avant qu’une seule dérivation ne s’exécute, en nommant la violation exacte :

library(checkmate)

adsl_bad <- as.data.frame(pharmaverseadam::adsl)
adsl_bad$SAFFL[1] <- "U"   # the bad value from the introduction

tryCatch(
  validate_adsl(adsl_bad),
  error = function(e) cat("Validation failed:", conditionMessage(e))
)
Validation failed: Assertion on 'data$SAFFL' failed: Must be a subset of {'Y','N'}, but has additional elements {'U'}.

Voilà la programmation défensive en une ligne en haut d’un script : le "U" qui s’était autrefois glissé silencieusement dans un tableau de sécurité est maintenant détecté à l’instant où les données sont chargées — une entrée corrigée, pas un TLF à réexécuter.

🟢 Avec un agent IA

Demandez à Prova « écris une fonction-barrière checkmate qui valide mon ADSL — affirme que USUBJID est une clé unique et non manquante, que AGE est numérique entre 0 et 120, et que SAFFL vaut seulement Y ou N — et s’arrête avec un message clair si l’une échoue » — elle répond en s’appuyant sur les leçons de ce pilier, avec du code checkmate et assertthat exécutable que vous pouvez essayer sur les données pharmaverse d’exemple. The runtime is the judge. Demandez à Prova →

Problèmes fréquents

Une assertion interrompt tout le script alors que je voulais seulement tester une condition. assert_*() est un arrêt net par conception — c’est le comportement « échouer vite ». Quand vous devez plutôt bifurquer selon le résultat, utilisez la variante test_*() (renvoie TRUE/FALSE, idéale dans un if) ; quand vous voulez collecter plusieurs messages d’échec sans mourir au premier, utilisez check_*() (renvoie TRUE ou la chaîne de message). Réservez assert_*() à la barrière en haut du script, où s’arrêter est le but.

assert_data_frame() échoue sur un jeu de données qui en est clairement un. Beaucoup de jeux de données pharmaverse sont des tibbles, et certaines fonctions sont strictes sur la classe. Convertissez d’abord avec as.data.frame() (comme le fait chaque bloc ici), ou faites passer le tibble par des vérifications qui l’acceptent. C’est la classe, pas les données, qui pose problème.

Le message est inutilement laconique. Vous êtes sur le stopifnot() de base, qui ne fait que renvoyer l’expression en échec. Basculez cette vérification vers checkmate pour un message qui nomme l’ensemble autorisé et la valeur fautive, ou vers assertthat avec un message on_failure() écrit dans le langage de votre étude. La vérification est la même ; le diagnostic est bien meilleur.

Questions fréquentes

La programmation défensive consiste à écrire du code qui ne fait pas confiance à ses entrées : avant de calculer quoi que ce soit, il affirme ce que les données et les arguments doivent être — types corrects, valeurs dans les plages, colonnes requises présentes, une clé unique — et s’arrête immédiatement avec un message clair si une hypothèse est fausse. En pratique, cela signifie placer un bloc d’assertions (stopifnot(), checkmate ou assertthat) en haut d’un script ou d’une fonction, pour qu’une mauvaise entrée échoue tôt et bruyamment plutôt que de produire un résultat faux qui se propage en aval.

stopifnot() est du R de base : il prend des conditions logiques et échoue à la première qui n’est pas TRUE, avec un message laconique qui renvoie l’expression en échec. Les fonctions assert_*() de checkmate sont des vérifications typées et spécialisées (assert_numeric(), assert_data_frame(), assert_subset(), …) qui valident structure, type, plage, unicité et valeurs autorisées, et échouent avec un message riche nommant le problème exact. Les deux arrêtent le script ; assert_*() vous donne des vérifications bien plus expressives et de meilleurs diagnostics, tandis que stopifnot() ne nécessite aucun package.

Utilisez checkmate pour valider des données — ses fonctions assert_*()/test_*()/check_*() sont conçues pour la structure, les types, les plages, l’unicité et les valeurs autorisées, avec les messages d’échec les plus informatifs, ce qui en fait le choix par défaut solide pour la validation de données cliniques. Utilisez assertthat quand un message d’échec lisible et personnalisé importe le plus — son on_failure() vous permet d’attacher un message en langage clair à votre propre prédicat. Ils sont complémentaires ; beaucoup de scripts utilisent checkmate pour les vérifications de data frame et assertthat là où un message formulé dans le langage du domaine aide un relecteur.

Affirmez sa structure et le contrat de chaque colonne. Avec checkmate : assert_data_frame(df, min.rows = 1) pour la structure, assert_names(names(df), must.include = c(...)) pour les colonnes requises, puis des vérifications par colonne — assert_numeric(df$AGE, lower = 0, upper = 120, any.missing = FALSE) pour le type et la plage, assert_character(df$USUBJID, unique = TRUE) pour une clé unique, et assert_subset(df$SAFFL, c("Y","N")) pour les valeurs autorisées. Enveloppez toute la batterie dans une seule fonction qui renvoie les données de façon invisible, et appelez-la en haut de chaque script qui lit le jeu de données.

Ils partagent les mêmes vérifications mais diffèrent par ce qu’ils font d’un échec. assert_*() lève une erreur (et renvoie l’objet de façon invisible en cas de succès) — la forme « échouer vite » pour une barrière en haut de script. test_*() renvoie TRUE/FALSE — pour bifurquer dans un if. check_*() renvoie TRUE en cas de succès ou une chaîne de message en cas d’échec — pour collecter plusieurs problèmes dans un seul rapport sans interrompre au premier.

Testez vos connaissances

Écrivez une fonction validate_vs() pour un extrait de signes vitaux qui doit satisfaire deux clauses : la colonne SYSBP (pression artérielle systolique) est numérique, n’a aucune valeur manquante et se situe entre 0 et 300 ; et la colonne ANL01FL (un indicateur de population d’analyse) ne contient que "Y" ou "N". Utilisez checkmate, faites en sorte que la fonction renvoie les données de façon invisible en cas de succès, et confirmez qu’elle s’arrête sur un data frame dont le SYSBP contient un -1.

Il vous faut deux assertions : assert_numeric(data$SYSBP, lower = 0, upper = 300, any.missing = FALSE) et assert_subset(data$ANL01FL, c("Y", "N")). Terminez la fonction par invisible(data) pour qu’un data frame valide passe directement. Pour voir l’échec sans interrompre la page, appelez-la dans tryCatch(..., error = function(e) conditionMessage(e)).

library(checkmate)

validate_vs <- function(data) {
  assert_data_frame(data, min.rows = 1)
  assert_numeric(data$SYSBP, lower = 0, upper = 300, any.missing = FALSE)
  assert_subset(data$ANL01FL, c("Y", "N"))
  invisible(data)
}

# A clean extract passes silently and returns the data
vs_ok <- data.frame(SYSBP = c(120, 138, 95), ANL01FL = c("Y", "Y", "N"))
validate_vs(vs_ok)

# A bad reading (-1) trips the range assertion
vs_bad <- data.frame(SYSBP = c(120, -1, 95), ANL01FL = c("Y", "Y", "N"))
tryCatch(validate_vs(vs_bad),
         error = function(e) cat(conditionMessage(e)))
#> Assertion on 'data$SYSBP' failed: Element 2 is not >= 0.

La barrière est silencieuse sur le data frame propre et s’arrête sur le mauvais, en nommant l’élément fautif — le même schéma que validate_adsl(), réorienté vers un contrat différent. Cette réutilisabilité est tout l’intérêt : une fonction-barrière par forme de jeu de données, appelée en haut de chaque script qui la lit.

A. Elle lève une erreur et arrête le script. B. Elle renvoie FALSE. C. Elle renvoie une chaîne de message décrivant l’échec (par ex. « Must be a subset of {‘Y’,‘N’}, but has additional elements {‘U’} »).

C. Les variantes check_*() renvoient TRUE en cas de succès ou une chaîne de message en cas d’échec — elles ne lèvent jamais d’erreur. C’est ce qui les rend idéales pour collecter plusieurs problèmes dans un seul rapport de QC. A décrit assert_subset() (la forme qui lève une erreur), et B décrit test_subset() (la forme TRUE/FALSE). Même vérification sous-jacente, trois comportements de retour différents.

Conclusion

La programmation défensive remplace « j’espère que l’entrée est propre » par « l’entrée est propre, et voici le code qui le prouve ». Vous énoncez chaque hypothèse sous forme d’assertion — structure, colonnes requises, type, plage, unicité, valeurs autorisées — et vous les placez en haut du script, pour qu’une violation arrête l’exécution immédiatement avec un message qui nomme la correction. stopifnot() est le socle sans dépendance ; checkmate offre des vérifications riches et expressives avec les meilleurs messages pour valider un data frame ; assertthat offre des prédicats lisibles et des échecs personnalisés, formulés pour des humains. Regroupez la batterie dans une seule barrière façon validate_adsl() et toute la discipline se réduit à une seule ligne réutilisable. Le "U" qui s’était autrefois glissé silencieusement dans un tableau de sécurité arrête maintenant le script à l’instant où les données se chargent — ce qui est toute la différence entre un fichier d’entrée corrigé et une soumission à réexécuter.

Note

Cette leçon est reproductible : chaque résultat de cette page a été produit par le code montré — copiez n’importe quel bloc et exécutez-le pour les reproduire. The runtime is the judge.

Leçons connexes

  • Double programmation en R avec diffdf — le schéma de QC complémentaire : là où les assertions défensives protègent les entrées d’un script, la double programmation valide ses sorties par re-dérivation indépendante. · Créer ADSL en R avec admiral — construire le jeu de données au niveau sujet dont cette leçon impose le contrat. · Les standards CDISC : CDASH, SDTM, ADaM, Define-XML — où les jeux de données ADaM et leurs métadonnées de variables se situent dans le pipeline de soumission. · Le passage de SAS à R pour la programmation clinique — les idiomes R, y compris les assertions, qui remplacent les macros de validation SAS.
  • Où cela s’inscrit : construire l’ADaM avec admiral → valider ses sorties (double programmation avec diffdf) → défendre ses entrées (assertions avec checkmate et assertthat — vous êtes ici) → reproductibilité et qualification des packages. Les assertions sont le QC le moins coûteux que vous possédez : elles détectent une mauvaise hypothèse en haut du script, avant qu’elle ne puisse devenir un nombre faux en aval.
Cette page vous a-t-elle été utile ?

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Programmation défensive en R : valider les données cliniques
    avec checkmate et assertthat},
  date = {2026-07-01},
  url = {https://www.datanovia.com/learn/pharma-clinical/05-validation-qc/defensive-programming-checkmate},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Programmation défensive en R : valider les données cliniques avec checkmate et assertthat.” 2026. July 1. https://www.datanovia.com/learn/pharma-clinical/05-validation-qc/defensive-programming-checkmate.