Introduction à la programmation avec R
gilles.hunault "at" univ-angers.fr
Cours 3 - Conditions logiques et tests
Table des matières cliquable
1. Valeurs logiques et tests en R
Exercices : énoncés solutions [Retour à la page principale du cours]1. Valeurs logiques et tests en R
R dispose de deux valeurs logiques nommées FALSE et TRUE qui valent numériquement 0 et 1. Les opérateurs qui renvoient des valeurs logiques sont très classiquement < et >. Il faut leur adjoindre <= et >= et aussi == (bien noter qu'il y a deux signes "égal") pour tester l'égalité. Pour des variables simples, les connecteurs usuels nommés NON, ET, OU s'écrivent respectivement !, &, |. Il est très fortement conseillé d'utiliser des parenthèses pour séparer ces comparaisons logiques. Voici quelques exemples d'utilisation :
x <- 5 x==2 # FALSE x==5 # TRUE !(x>4) # FALSE x<2 # FALSE x>=5 # TRUE x>3 # TRUE (x>3) & (x<10) # TRUE (x>0) | (x==2) # TRUE TRUE + 1 # 2 FALSE*FALSE # 0 !7 # FALSEPour réaliser un test logique, on utilise une structure algorithmique dite d'alternative qui, en fonction d'une condition (opération à résultat logique), effectue une bloc d'instructions. Voici les deux formes algorithmiques, nommées respectivement SI_ALORS et SI_ALORS_SINON :
############################################## # # # structure "si_alors" # # # ############################################## SI (condition) ALORS [...] # bloc d'instructions exécutées # si la condition est vraie FINSI ############################################## # # # structure "si_alors_sinon" # # # ############################################## SI (condition) ALORS [...] # bloc d'instructions exécutées # si la condition est vraie SINON [...] # bloc d'instructions exécutées # si la condition est fausse FINSILa traduction en R se fait à l'aide des mots if, else et des accolades { et } pour délimiter les blocs d'instruction. De telles structures permettent donc de modifier l'exécution en séquence des instructions.
############################################## # # # structure "si_alors" en R # # # ############################################## if (condition) { [...] # bloc d'instructions exécutées # si la condition est vraie } ############################################## # # # structure "si_alors_sinon" en R # # # ############################################## if (condition) { [...] # bloc d'instructions exécutées # si la condition est vraie } else { [...] # bloc d'instructions exécutées # si la condition est fausse }Voici deux exemples en R. On remarquera que nous avons (ce qui est très fortement conseillé) commenté les fins de SI.
# STRUCTURE si Exemple 1 # ---------------------- # on indique quand on a commencé à traiter le fichier numéro 100 # (nbf est le numéro de fichier en cours de traitement) if (nbf==100) { cat(" fichier numéro ",nbf," atteint. \n") cat(" date et heure : ",date(),"\n\n") } # fin si nbf=100 # STRUCTURE si Exemple 2 # ---------------------- if (valCour>maxLoc) { # on met à jour le maximum local via la valeur courante maxLoc <- valCour nbInf <- 0 } else { nbInf <- nbInf + 1 } # fin siL'instruction stop en R permet de quitter le script en cours. Cela peut se révéler utile, par exemple si le fichier que l'on veut traiter n'est pas trouvé (noter le mot si dans cette phrase). Voici comment s'en servir :
## série de calculs sur un fichier nommé FN if (!file.exists(FN)) { cat("ATTENTION ! Le fichier ",FN," n'est pas présent.\n\n") ; stop(" donc arrêt du programme.\n") ; } # fin de si # si on arrive ici, c'est que le fichier existe ## suite des calculs...La valeur NA de R qui signifie Not Available, soit Non Accessible en français est aussi une valeur logique ce qui complique les cas à tester :
# NA est aussi une valeur logique x <- NA print(class(x)) # renvoie "logical" # un test raté avec x : # R répond # Error in if (x < 0) { : # valeur manquante là où TRUE / FALSE est requis if (x<=0) { cat(" x est négatif ou nul\n") } else { cat(" x n'est pas négatif ou nul...\n") cat(" mais il n'est peut-être pas positif") cat(" ni même numérique.\n") } # fin si # un test réussi avec x # R répond x est indéterminé (NA). if (is.na(x)) { cat(" x est indéterminé (NA).\n") } else { if (x<0) { cat(" x est strictement négatif\n") } else { cat(" x est positif ou nul.\n") } # fin si x<0 } # fin si is.na(x)2. Filtrage vectoriel
R est beaucoup plus puissant avec les tests logiques qu'on ne l'imagine car on peut coupler l'indexation avec ces tests et donc réaliser des affectations conditionnelles.
Pour accéder à un élément dans un vecteur, R fournit la notation "crochets" : ainsi x[k] correspond à l'élément numéro k sachant que, contrairement à la plupart des autres langages de programmation, R commence la numérotation à 1. Donc x[0] -- qui existe par ailleurs en R -- ne correspond pas au premier élément de x.
La notation crochets ou indexation permet de spécifier plusieurs éléments. Ainsi [1:n] correspond aux n premiers éléments. Un indice entier mais négatif signifie tout sauf cet indice donc (1:5)[ - c(3,4) ] renvoie 1 2 5.
Lorsqu'on fournit entre crochets pour une variable de type vecteur un vecteur de même longueur que cette variable composé de 0 et de 1, R extrait les valeurs correspondant à 1. Par exemple (1:3)[ c(0,1,0) ] renvoie 2. Comme nous avons expliqué que les valeurs logiques FALSE et TRUE valent numériquement 0 et 1, il est facile de comprendre que R fournit un mécanisme de filtrage très puissant avec les expressions logiques vectorielles.
Voici des exemples :
# le vecteur v # pos 1 2 3 4 5 6 7 8 9 v <- c( 1, 8, 2, -5, 15, 6, 9, -1, 4) # filtre des négatifs # pos 1 2 3 4 5 6 7 8 9 v<0 # renvoie FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE FALSE # num 0 0 0 1 0 0 0 1 0 # extraction des négatifs v[ v<0 ] # renvoie -5 -1 # comptage du nombre de positifs sum( v>0 ) # renvoie 7 # les éléments de v entre 3 et 10 flt1 <- (v>3) flt2 <- (v<10) v[ flt1 & flt2 ] # renvoie 8 6 9 43. Applications aux matrices et dataframes
En plus des vecteurs, R dispose de deux structures de données nommées matrix et data frame qui définissent des tableaux rectangulaires avec des lignes et des colonnes. Il y a toutefois des grandes différences entre ces deux structures. Les matrices sont homogènes en type et les lignes et les colonnes y sont équivalentes en fonctionnement alors que les data frames sont des listes particulières : les éléments de la liste sont les colonnes du tableau, avec la contrainte que toutes les colonnes doivent avoir le même nombre d'éléments. Un data frame est donc particulièrement adapté à contenir un fichier de données pour des études statistiques, avec des colonnes de types éventuellement différents.
Toutes les fonctions de R qui lisent des fichiers comme la fonction read.table(), la fonction read.xls() (du package gdata) renvoient des data frames.
Les conditions logiques et le filtrage vectoriel permettent d'extraire facilement des sous-ensembles de ces tableaux de données comme le montrent les exemples ci-dessous pour le fichier de données elf.txt :
# lecture du fichier elf.txt # elf <- read.table("http://forge.info.univ-angers.fr/~gh/wstat/Programmation_R/Programmation_introduction/elf.txt",head=TRUE) # extrait des données > head(elf) NUM SEXE AGE PROF ETUD REGI USAG 1 M001 1 62 1 2 2 3 2 M002 0 60 9 3 4 1 3 M003 1 31 9 4 4 1 4 M004 1 27 8 4 1 1 5 M005 0 22 8 4 1 2 6 M006 1 70 4 1 1 1 > tail(elf) NUM SEXE AGE PROF ETUD REGI USAG 94 M094 0 12 12 2 1 0 95 M095 1 31 6 4 0 0 96 M096 1 17 12 3 1 0 97 M097 1 39 1 2 1 0 98 M098 0 62 6 3 1 0 99 M100 1 48 9 4 2 0 # filtre "femme" flt1 <- elf$SEXE==1 # filtre "jeune" flt2 <- elf$AGE <20 # nombre de femmes sum( flt1 ) # nombre de jeunes sum( flt2 ) # nombre de femmes jeunes sum( flt1 & flt2 ) # extraction du tableau des femmes jeunes fj1 <- elf[ (flt1 & flt2) , ] # autre solution fj2 <- subset(elf, flt1 & flt2) # attention à ne pas écrire fj3 <- subset(elf, SEXE=1, AGE<20)4. Spécificités du langage R
En R, les indices commencent à 1. C'est une révolution par rapport aux autres langages de programmation qui ne comptent pas en position mais en décalage (offset) à partir du début du tableau. Cela facilite beaucoup les calculs.
A cause de la valeur NA -- qui est spécifique à R, la comparaison entre valeurs en R est compliquée :
## un vecteur x logique x <- c( NA, FALSE, TRUE ) ## table AND obtenue par outer(x, x, "&") <NA> FALSE TRUE <NA> NA FALSE NA FALSE FALSE FALSE FALSE TRUE NA FALSE TRUE ## table OR obtenue par outer(x, x, "|") <NA> FALSE TRUE <NA> NA NA TRUE FALSE NA FALSE TRUE TRUE TRUE TRUE TRUEHeureusement il y a de nombreuses fonctions comme is.na() et na.omit() qui permettent de détecter et d'enlever ces valeurs NA. On trouve aussi, comme pour la fonction sum(), une option na.rm=TRUE qui enlève (localement) ces valeurs NA.
R dispose de nombreuses fonctions logiques :
> apropos("^is.") [1] "is.array" "is.atomic" "isatty" "isBaseNamespace" "is.call" "is.character" "isClass" [8] "isClassDef" "isClassUnion" "is.complex" "is.data.frame" "isdebugged" "is.double" "is.element" [15] "is.empty.model" "is.environment" "is.expression" "is.factor" "is.finite" "is.function" "isGeneric" [22] "isGrammarSymbol" "isGroup" "isIncomplete" "is.infinite" "is.integer" "islands" "is.language" [29] "is.leaf" "is.list" "is.loaded" "is.logical" "is.matrix" "is.mts" "is.na" [36] "is.na<-" "is.na.data.frame" "is.na<-.default" "is.na<-.factor" "is.name" "isNamespace" "is.nan" [43] "is.na.numeric_version" "is.na.POSIXlt" "is.null" "is.numeric" "is.numeric.Date" "is.numeric.difftime" "is.numeric.POSIXt" [50] "is.numeric_version" "is.object" "ISOdate" "ISOdatetime" "isOpen" "is.ordered" "isoreg" [57] "is.package_version" "is.pairlist" "is.primitive" "is.qr" "is.R" "is.raster" "is.raw" [64] "is.recursive" "is.relistable" "isRestart" "isS4" "isSealedClass" "isSealedMethod" "isSeekable" [71] "is.single" "is.stepfun" "is.symbol" "isSymmetric" "isSymmetric.matrix" "is.table" "isTRUE" [78] "is.ts" "is.tskernel" "is.unsorted" "is.vector" "isVirtualClass" "isXS3Class"Comme nous l'avons dit précédemment, les structures de données de base sont les vecteurs et les listes. Il faut néanmoins bien connaitre les data frames et les matrices (et leurs "pièges") pour écrire de "beaux" programmes concis :
# quelques pièges des data frames # ------------------------------- > v1 <- 1:2 # nos données > v2 <- c("oui","non") # class(v2) renvoie "character" > d <- data.frame(v1,v2) # attention, voici le data frame > class(d) # sa classe [1] "data.frame" > is.list(d) # est-ce une liste ? [1] TRUE # oui ! > is.data.frame(d) # est-ce aussi un data frame ? [1] TRUE # oui, bien sûr > is.vector(d$v1) # quelle classe pour d$v1 ? [1] TRUE # facile > is.vector(d$v2) # et pour d$v2 ? [1] FALSE # perdu, data.frame() convertit en facteur par défaut > d$v1 # la preuve : [1] 1 2 > d$v2 [1] oui non Levels: non oui > is.factor(d$v2) # vérfication [1] TRUE # quelques pièges des matrices # ------------------------------- > m <- matrix(c(v1,v1*2,v1**2),nrow=3) > is.matrix(m) # facile [1] TRUE > is.vector((m[,2])) # aussi simple [1] TRUE > is.integer((m[,2])) # plus difficile [1] FALSE # car 1 et 2 sont des réels (ce n'est pas 1L et 2L) > m[1,2] <- "0" > is.factor((m[,2])) # pas de transformation en facteur [1] FALSE > is.character((m[,2])) # mais conversion de TOUTE la matrice en caractères [1] TRUEIl faut noter que R permet d'affecter des parties de vecteur ou de matrice grâce à l'indexation et au filtrage logique. Ainsi l'instruction :
v[ (v<5) ] <- 0vient remplacer dans v toutes les valeurs strictements inférieures à 5 par 0.
Enfin, il existe sous R plusieurs fonctions qui évitent de faire des tests logiques pour trouver des valeurs, les remplacer... comme les fonctions ifelse(), which(), grep(), sub()...
Exercices : énoncés solutions [Retour à la page principale du cours]
Retour à la page principale de (gH)