37  Conditions logiques

Dans ce chapitre, nous allons aborder les conditions et vecteurs logiques qui sont composés de trois valeurs possibles : TRUE (vrai), FALSE (faux) et NA (manquant). Les vecteurs logiques sont notamment utiliser pour sélectionner des observations, par exemple avec dplyr::filter().

Nous avons également déjà aborder les conditions pour combiner ensemble plusieurs variables (cf. Chapitre 10), notamment avec dplyr::if_else() ou dplyr::case_when().

37.1 Opérateurs de comparaison

Une manière commune de créer un vecteur logique consiste à utiliser l’un des opérateurs de comparaison suivants : < (strictement inférieur), <= (inférieur ou égal), > (strictement supérieur), >= (supérieur ou égal), == (est égal à), != (est différent de).

On peut comparer un vecteur de plusieurs valeurs avec une valeur unique.

x <- c(1, 5, 2, 8)
x < 3
[1]  TRUE FALSE  TRUE FALSE

Si l’on prend deux vecteurs de même longueur, la comparaison se fera ligne à ligne.

y <- c(3, 5, 1, 7)
y >= x
[1]  TRUE  TRUE FALSE FALSE
y == x
[1] FALSE  TRUE FALSE FALSE
y != x
[1]  TRUE FALSE  TRUE  TRUE

On peut ainsi facilement sélectionner des lignes d’un tableau de données à partir d’une condition sur certaines variables.

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
data("hdv2003", package = "questionr")
hdv2003 |> nrow()
[1] 2000
hdv2003 |>
  filter(sexe == "Femme") |> 
  nrow()
[1] 1101
hdv2003 |> 
  filter(age < 25) |> 
  nrow()
[1] 169
Tester l’égalité d’une valeur décimale

Lorsque l’on effectue un test d’égalité avec des valeurs décimales, le test échouera si les deux valeurs ne sont pas parfaitement identique.

Prenons un exemple :

x <- 1 / 49 * 49
x
[1] 1
x == 1
[1] FALSE

Pourquoi ce test échoue-t-il ? Le nombre de décimales stockées par l’ordinateur est limité et, de ce fait, il peut y avoir quelques écarts d’arrondis. Ainsi, x n’est pas tout à fait égal à 1, ce qui devient visible si on l’affiche avec un nombre élevé de décimales.

print(x, digits = 16)
[1] 0.9999999999999999

Dans ce cas là, on pourra avoir recours à dplyr::near() qui prendra en compte la précision de l’ordinateur dans la comparaison.

near(x, 1)
[1] TRUE

On peut aussi utiliser cette fonction en personnalisant le niveau de tolérance pour la comparaison.

near(c(2.1, 3.4), 2, tol = 1)
[1]  TRUE FALSE

37.2 Comparaison et valeurs manquantes

Les valeurs manquantes (NA) peuvent être parfois problématiques lors d’une comparaison car elles renvoient systématique une valeur manquante.

2 < NA
[1] NA
NA == 6
[1] NA

Lorsque l’on sélectionne des observations avec la syntaxe des crochets ([], voir Chapitre 4), cela va générer des lignes vides / manquantes.

d <- tibble(
  a = c(1, NA, 3, 4),
  b = c("x", "y", "x", "y")
)
d[d$a > 2, ]
# A tibble: 3 × 2
      a b    
  <dbl> <chr>
1    NA <NA> 
2     3 x    
3     4 y    

Le recours à dplyr::filter() est plus sûr car les lignes pour lesquelles la condition renvoie NA ne sont pas sélectionnées.

d |> filter(a > 2)
# A tibble: 2 × 2
      a b    
  <dbl> <chr>
1     3 x    
2     4 y    

L’opérateur == ne peut pas être utilisé pour tester si une valeur est manquante. On utilisera à la place la fonction is.na().

d$a == NA
[1] NA NA NA NA
is.na(d$a)
[1] FALSE  TRUE FALSE FALSE
Astuce

Voici deux petites fonctions permettant de tester si deux valeurs sont identiques ou différentes, en tenant compte des NA comme l’un des valeurs possibles (deux NA seront alors considérés comme égaux).

is_different <- function(x, y) {
  (x != y & !is.na(x) & !is.na(y)) | xor(is.na(x), is.na(y))
}

is_equal <- function(x, y) {
  (x == y & !is.na(x) & !is.na(y)) | (is.na(x) & is.na(y))
}

v <- c(1, NA, NA, 2)
w <- c(1, 2, NA, 3)

v == w
[1]  TRUE    NA    NA FALSE
is_equal(v, w)
[1]  TRUE FALSE  TRUE FALSE
v != w
[1] FALSE    NA    NA  TRUE
is_different(v, w)
[1] FALSE  TRUE FALSE  TRUE

37.3 Opérateurs logiques (algèbre booléenne)

Les opérateurs logiques permettent de combiner ensemble plusieurs vecteurs logiques :

  • & : opérateur et (x & y est vrai si à la fois x et y sont vrais) ;

  • | : opérateur ou (x | y est vrai si x ou y ou les deux sont vrais) ;

  • xor() : opérateur ou exclusif (xor(x, y) est vrai si seulement x ou seulement y est vrai, mais pas les deux) ;

  • ! : opérateur non (!x est vrai si x est faux).

Représentation graphique de l’ensemble des opérations logiques. Le cercle de gauche représente x et celui de droite y. La région colorée représente le résultat de l’opération.

Représentation graphique de l’ensemble des opérations logiques. Le cercle de gauche représente x et celui de droite y. La région colorée représente le résultat de l’opération.

Ils permettent de combiner plusieurs conditions entre elles.

hdv2003 |>
  filter(sexe == "Femme" & age < 25) |> 
  nrow()
[1] 93
hdv2003 |>
  filter(sexe == "Femme" | age < 25) |> 
  nrow()
[1] 1177

Pour des conditions complexes, on utilisera des parenthèses pour indiquer dans quel ordre effectuer les opérations.

# sélectionne les jeunes femmes et les hommes âgés
hdv2003 |>
  filter(
    (sexe == "Femme" & age < 25) | 
      (sexe == "Homme" & age > 60)) |> 
  nrow()
[1] 315

37.3.1 Opérations logiques et Valeurs manquantes

On sera vigilant·e avec les valeurs manquantes. Cela peut paraître un peu obscur au premier abord, mais est en fait parfaitement logique.

df <- tibble(x = c(TRUE, FALSE, NA))

df |> 
  mutate(
    et_na = x & NA,
    ou_na = x | NA
  )
# A tibble: 3 × 3
  x     et_na ou_na
  <lgl> <lgl> <lgl>
1 TRUE  NA    TRUE 
2 FALSE FALSE NA   
3 NA    NA    NA   

TRUE | NA vaut TRUE car la condition reste vrai quelle que soit la valeur du deuxième paramètre, tandis que FALSE | NA renvoie NA car le résultat est indéterminé (il dépend du deuxième paramètre).

37.3.2 L’opérateur %in%

Il est fréquent de vouloir tester simultanément plusieurs égalités. Par exemple :

x <- c("a", "b", "c", "d")
x == "a" | x == "b"
[1]  TRUE  TRUE FALSE FALSE

On aura alors avantageusement recours à l’opérateur %in% que l’on peut traduire par appartient à et qui teste si les éléments appartiennent à un certain ensemble.

x %in% c("a", "b")
[1]  TRUE  TRUE FALSE FALSE

37.4 Aggrégation

Pour résumer un ensemble de valeurs logiques en une seule, on utilisera les fonction all() et any() qui teste si toutes les valeurs / au moins une valeur est vrai. Ces deux fonctions acceptent un argument na.rm permettant de ne pas tenir compte des valeurs manquantes.

x <- c(TRUE, NA, FALSE, FALSE)
any(x)
[1] TRUE
all(x)
[1] FALSE

Un vecteur logique peut-être vu comme un vecteur de valeur binaire (0 si FALSE, 1 si TRUE). On peut dès lors effectuer des opérations comme la somme ou la moyenne.

sum(x, na.rm = TRUE)
[1] 1
mean(x, na.rm = TRUE)
[1] 0.3333333

37.5 Programmation

Lorsque l’on programme avec R, notamment avec des structures conditionnelles telles que if ... else ..., on a besoin d’écrire des conditions qui ne renvoient qu’une et une seule valeur logique.

Les opérateurs & et | s’appliquent sur des vecteurs et donc renvoient potentiellement plusieurs valeurs. On privilégiera alors les variantes && et || qui ne renvoient qu’une seule valeur et produise une erreur sinon.

De même, pour vérifier qu’un objet est bien égal à TRUE ou à FALSE, n’est pas nul, n’est pas manquant et est de longueur 1, on utilisera isTRUE() et isFALSE().

isTRUE(TRUE)
[1] TRUE
isTRUE(NA)
[1] FALSE
isTRUE(NULL)
[1] FALSE
isTRUE(c(TRUE, TRUE))
[1] FALSE