Informations techniques

Question 1 :

Imprécision numérique

1.1

Lorsque je demande à R de stocker en mémoire le nombre 1.14, quel nombre garde-t-il en mémoire réellement ?

Solution

print(1.14, digits = 20)
## [1] 1.1399999999999999

1.2

Lorsque je demande à R de stocker en mémoire le nombre 10000000000000001, quel nombre garde-t-il en mémoire réellement ?

Solution

# Sur mon ordinateur, j'obtiens

# Afin d'éviter l'affichage en notation scientifique
options_default <- options()  # copie des options par défaut conservée 
options(scipen = 12)

# Affichage du nombre
print(10000000000000001)
## [1] 10000000000000000

# Pour remettre les options par défaut
options(options_default)

Question 2:

Évaluation d’expressions en R et environnements

Supposons que nous chargeons en R le package jsonlite avec la commande :

library(jsonlite)

Ensuite, nous chargeons le package rjson avec la commande :

library(rjson)

Maintenant, nous voulons utiliser la fonction fromJSON du package jsonlite. Comment faire ?

Solution

library(jsonlite)
library(rjson)

# Comment utiliser la fonction fromJSON du package jsonlite ?
fromJSON # pas la bonne fonction car rjson a été chargé après jsonlite

# Solution : utiliser l'opérateur ::
jsonlite::fromJSON
# ou détacher le package rjson du chemin de recherche
detach(package:rjson)
fromJSON

Structures de contrôle

Question 3 :

Alternatives

Écrivez un bout de code R qui dépend d’un objet nommé message.

  • Si message contient uniquement la chaîne de caractères "Bonjour", le code doit provoquer l’impression de message.
  • Si message contient plutôt la chaîne de caractères "Au revoir", le code doit modifier message afin qu’il prenne plutôt la valeur "À la prochaine".
  • Si message contient n’importe quoi d’autre, le code doit créer un nouvel objet nommé reponse, contenant la valeur "Quoi?" et imprimer cet objet.

Trouvez deux solutions :

  • une faisant intervenir au moins une structure de contrôle if … else,
  • l’autre utilisant la fonction switch.

Solution

message <- "Bonjour"  # pour expérimenter

# Solution avec if ... else

if (isTRUE(message == "Bonjour")) {
  print(message)
} else if (isTRUE(message == "Au revoir")) {
  message <- "À la prochaine"
} else {
  reponse <- "Quoi?"
  print(reponse)
}

# Solution avec switch

switch(
  message,
  "Bonjour" = {
    print(message)
  },
  "Au revoir" = {
    message <- "À la prochaine"
  },
  {
    reponse <- "Quoi?"
    print(reponse)    
  }
)

Question 4 :

Boucles

Cet exercice utilise le jeu de données airquality du package datasets.

Écrivez une boucle qui produit un résultat équivalent à celui de la commande suivante :

apply(airquality, MARGIN = 2, FUN = range, na.rm = TRUE)
##      Ozone Solar.R Wind Temp Month Day
## [1,]     1       7  1.7   56     5   1
## [2,]   168     334 20.7   97     9  31

NOTE : Cet exercice permet de pratiquer l’écriture d’une boucle for, mais l’utilisation de la fonction apply est recommandée pour faire ce calcul.

Solution

# À reproduire : 
out_1_ref <- apply(airquality, MARGIN = 2, FUN = range, na.rm = TRUE)
out_1_ref
##      Ozone Solar.R Wind Temp Month Day
## [1,]     1       7  1.7   56     5   1
## [2,]   168     334 20.7   97     9  31
str(out_1_ref)
##  num [1:2, 1:6] 1 168 7 334 1.7 20.7 56 97 5 9 ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr [1:6] "Ozone" "Solar.R" "Wind" "Temp" ...

# Initialisation d'un objet pour contenir les résultats :
out_1 <- matrix(NA, nrow = 2, ncol = ncol(airquality))
colnames(out_1) <- names(airquality)
# Boucle :
for (col in names(airquality)) {
  out_1[, col] <- range(airquality[, col], na.rm = TRUE)
}
# J'aurais aussi pu itérer sur les positions 1 à ncol(airquality).
out_1
##      Ozone Solar.R Wind Temp Month Day
## [1,]     1       7  1.7   56     5   1
## [2,]   168     334 20.7   97     9  31

all.equal(out_1, out_1_ref)
## TRUE

Question 5 :

Boucles

Cet exercice utilise aussi le jeu de données airquality du package datasets.

Écrivez une boucle qui produit un résultat équivalent à celui de la commande suivante :

aggregate(Temp ~ Month, data = airquality, FUN = mean)

NOTE : Cet exercice permet de pratiquer l’écriture d’une boucle for, mais l’utilisation de la fonction aggregate, ou d’une autre outil qui permet d’éviter la boucle, est recommandée pour faire ce calcul.

Solution

# À reproduire : 
out_2_ref <- aggregate(Temp ~ Month, data = airquality, FUN = mean)
out_2_ref
##   Month     Temp
## 1     5 65.54839
## 2     6 79.10000
## 3     7 83.90323
## 4     8 83.96774
## 5     9 76.90000
str(out_2_ref)
## 'data.frame':    5 obs. of  2 variables:
##  $ Month: int  5 6 7 8 9
##  $ Temp : num  65.5 79.1 83.9 84 76.9

# Initialisation d'un objet pour contenir les résultats :
out_2 <- data.frame(Month = unique(airquality$Month), Temp = NA)
# Boucle :
for (i in seq_len(nrow(out_2))) {
  out_2$Temp[i] <- mean(subset(
    airquality,
    subset = Month == out_2$Month[i],
    select = Temp,
    drop = TRUE
  ))
}
out_2
##   Month     Temp
## 1     5 65.54839
## 2     6 79.10000
## 3     7 83.90323
## 4     8 83.96774
## 5     9 76.90000

all.equal(out_2, out_2_ref)
## TRUE

Question 6 :

Boucles

Cet exercice utilise le facteur state.division du package datasets.

Soumettez les commandes suivantes dans la console :

ma_liste <- strsplit(levels(state.division), split = " ")
names(ma_liste) <- paste0("division", seq_along(ma_liste))

L’objet ma_liste est une liste dont les 9 éléments sont des vecteurs de différentes longueurs. Écrivez une boucle qui extrait le dernier élément de chaque vecteur et qui retourne un resultat formaté comme celui de la commande suivante.

sapply(ma_liste, FUN = tail, n = 1)
## division1  division2  division3  division4  division5  division6  division7  division8  division9 
## "England" "Atlantic" "Atlantic"  "Central"  "Central"  "Central"  "Central" "Mountain"  "Pacific" 

CONTRAINTE : La boucle doit itérer sur les entiers 1 à 9 en référence aux positions des éléments de ma_liste.

NOTE : Encore une fois, cet exercice permet de pratiquer l’écriture d’une boucle for, mais l’utilisation de la fonction sapply (ou une autre fonction de la famille des apply) est recommandée pour faire ce calcul.

Solution

# À reproduire : 
ma_liste <- strsplit(levels(state.division), split = " ")
names(ma_liste) <- paste0("division", seq_along(ma_liste))
sapply(ma_liste, FUN = tail, n = 1)
## division1  division2  division3  division4  division5  division6  division7  division8  division9 
## "England" "Atlantic" "Atlantic"  "Central"  "Central"  "Central"  "Central" "Mountain"  "Pacific" 

# Initialisation d'un objet pour contenir les résultats :
boucle_1 <- vector(mode = "character", length = length(ma_liste))
names(boucle_1) <- names(ma_liste)
# Boucle :
for (i in seq_along(ma_liste)) {
  boucle_1[i] <- tail(ma_liste[[i]], n = 1)  # ou ma_liste[[i]][length(ma_liste[[i]])]
}
boucle_1
## division1  division2  division3  division4  division5  division6  division7  division8  division9 
## "England" "Atlantic" "Atlantic"  "Central"  "Central"  "Central"  "Central" "Mountain"  "Pacific" 

Question 7 :

Boucles

Réécrivez la boucle précédente, cette fois en itérant sur les noms des éléments de ma_liste.

Solution

# Initialisation d'un objet pour contenir les résultats :
boucle_2 <- vector(mode = "character", length = length(ma_liste))
names(boucle_2) <- names(ma_liste)
# Boucle :
for (nom in names(ma_liste)) {
  boucle_2[nom] <- tail(ma_liste[[nom]], n = 1)
}
boucle_2
## division1  division2  division3  division4  division5  division6  division7  division8  division9 
## "England" "Atlantic" "Atlantic"  "Central"  "Central"  "Central"  "Central" "Mountain"  "Pacific" 

Question 8 :

Boucles

Réécrivez encore une fois la boucle précédente, cette fois en itérant directement sur les éléments de la liste ma_liste.

Solution

# Initialisation d'un objet pour contenir les résultats :
boucle_3 <- vector(mode = "character", length = length(ma_liste))
names(boucle_3) <- names(ma_liste)
# Boucle :
i <- 0
for (element in ma_liste) {
  i <- i + 1
  boucle_3[i] <- tail(element, n = 1)
}
boucle_3
## division1  division2  division3  division4  division5  division6  division7  division8  division9 
## "England" "Atlantic" "Atlantic"  "Central"  "Central"  "Central"  "Central" "Mountain"  "Pacific" 

Fonctions

Question 9 :

Fonctions anonymes

Résolvez maintenant le problème d’extraire le dernier élément de chaque vecteur dans la liste ma_liste par un appel à la fonction sapply, qui fait lui appel à une fonction anonyme que vous devez écrire.

Solution

sapply(ma_liste, FUN = function(x) tail(x, n = 1))
# ou
sapply(ma_liste, FUN = function(x) x[length(x)])
## division1  division2  division3  division4  division5  division6  division7  division8  division9 
## "England" "Atlantic" "Atlantic"  "Central"  "Central"  "Central"  "Central" "Mountain"  "Pacific" 

# Remarque :
# Une autre solution possible (qui n'utilise cependant pas de fonction anonyme) :
mapply(FUN = "[", ma_liste, lapply(ma_liste, FUN = length))

Question 10 :

Boucles et fonctions

Écrivez une fonction R qui prend un entrée un vecteur numérique et qui retourne en sortie la position du premier nombre entier impair dans ce vecteur. La fonction doit retourner NA si le vecteur ne contient aucun nombre impair.

EXEMPLE :

x <- c(6, 3, 5.5, 1, 0, -5)
pos_1er_impair(x)
## [1] 2

CONTRAINTE : Votre fonction doit parcourir le vecteur donné en entrée jusqu’au premier nombre impair seulement. Ainsi, elle parcourt tout le vecteur uniquement si le premier nombre impair se trouve à la dernière position ou si le vecteur ne comporte aucun nombre impair.

INDICE : Une boucle while ou repeat vous sera utile.

Solution

# Fonction qui retourne la position du premier nombre 
#   entier impair dans un vecteur numérique
# Argument en entrée : x = vecteur numérique
# Sortie : la position du 1er entier impair dans x
pos_1er_impair <- function(x) {
  pos <- NA
  i <- 0
  while (is.na(pos) && i < length(x)) {
    i <- i + 1
    # cat("Vérification de la position", i, "\n")
    if (!is.na(x[i]) && x[i] %% 2 == 1) {
      pos <- i
    }
  }
  return(pos)
}

# Pour tester un peu la fonction
pos_1er_impair(c(6, 3, 5.5, 1, 0, -5))
## [1] 2
pos_1er_impair(c(6, 4, 5.5, 2, 0, -5))
## [1] 6
pos_1er_impair(c(6, 2, 5.5))
## [1] NA

Question 11 :

Arguments des fonctions

Reprenez la fonction compte_impair_vectoriel des notes de cours et ajoutez-lui un argument na.rm, prenant comme valeur par défaut FALSE, qui traite les valeurs manquantes de la même façon que l’argument na.rm de la fonction range (et de plusieurs autres fonctions).

compte_impair_vectoriel <- function(x) {
  sum(x %% 2 == 1)
}

Solution

compte_impair_vectoriel <- function(x, na.rm = FALSE) {
  sum(x %% 2 == 1, na.rm = na.rm)
}

compte_impair_vectoriel(c(6, 3, 5.5, NA, 0, -5))
## [1] NA
compte_impair_vectoriel(c(6, 3, 5.5, NA, 0, -5), na.rm = TRUE)
## [1] 2

Question 12 :

Arguments des fonctions

Reprenez la fonction compte_impair_boucle des notes de cours et ajoutez-lui un argument na.rm, prenant comme valeur par défaut FALSE, comme vous venez de le faire pour la fonction compte_impair_vectoriel.

compte_impair_boucle <- function(x) {
  k <- 0
  for (n in x) {
    if (n %% 2 == 1) k <- k + 1
  }
  k
}

Solution

compte_impair_boucle <- function(x, na.rm = FALSE) {
  k <- 0
  for (n in x) {
    if (is.na(n)) {
      if (na.rm) {
        next
      } else {
        k <- NA
        break
      }
    } else {
      if (n %% 2 == 1) k <- k + 1
    }
  }
  k
}

# Autre approche :
# retirer les NA si na.rm prend la valeur TRUE

compte_impair_boucle <- function(x, na.rm = FALSE) {
  if (na.rm) x <- na.omit(x)
  if (any(is.na(x))) {
    k <- NA
  } else {
    k <- 0
    for (n in x) {
      if (n %% 2 == 1) k <- k + 1
    }
  }
  k
}

compte_impair_boucle(c(6, 3, 5.5, NA, 0, -5))
## [1] NA
compte_impair_boucle(c(6, 3, 5.5, NA, 0, -5), na.rm = TRUE)
## [1] 2

Question 13 :

Arguments des fonctions

Créez une nouvelle fonction, nommée compte_impair, qui a la même utilité que les fonctions compte_impair_vectoriel et compte_impair_boucle.

Cette fonction doit posséder, en plus des arguments x et na.rm comme ceux de compte_impair_vectoriel et compte_impair_boucle, un nouvel argument servant à contrôler la façon dont la fonction effectue le calcul. Ce nouvel argument doit être nommé methode et doit accepter seulement deux valeurs : "calcul_vectoriel" (la valeur par défaut) ou "boucle".

Si methode prend la valeur "calcul_vectoriel", compte_impair doit appeler la fonction compte_impair_vectoriel pour faire le calcul. Si methode prend plutôt la valeur "boucle", compte_impair doit appeler la fonction compte_impair_boucle.

La nouvelle fonction doit seulement retourner ce que l’appel à la fonction compte_impair_vectoriel ou compte_impair_boucle retourne.

Solution

compte_impair <- function(x, na.rm = FALSE, methode = c("calcul_vectoriel", "boucle")) {
  methode <- match.arg(methode)
  if (methode == "calcul_vectoriel") {
    print("Calcul vectoriel effectué")
    compte_impair_vectoriel(x = x, na.rm = na.rm)
  } else if (methode == "boucle") {
    print("Boucle effectuée")
    compte_impair_boucle(x = x, na.rm = na.rm)
  }
}

compte_impair(c(6, 3, 5.5, NA, 0, -5), na.rm = TRUE)
## [1] "Calcul vectoriel effectué"
## [1] 2
compte_impair(c(6, 3, 5.5, NA, 0, -5), na.rm = TRUE, methode = c("calcul_vectoriel"))
## [1] "Calcul vectoriel effectué"
## [1] 2
compte_impair(c(6, 3, 5.5, NA, 0, -5), na.rm = TRUE, methode = c("boucle"))
## [1] "Boucle effectuée"
## [1] 2
compte_impair(c(6, 3, 5.5, NA, 0, -5), na.rm = TRUE, methode = c("autre"))
## Error in match.arg(methode) : 
##   'arg' should be one of “calcul vectoriel”, “boucle” 

Question 14 :

Fonctions

Créez une fonction qui peut prendre en entrée n’importe quels arguments, en n’importe quelle quantité, et qui ne fait que retourner les noms des arguments qui lui ont été fournis en entrée.

Solution

nom_args <- function(...) {
  names(list(...))
}

nom_args(x = Orange, FUN = mean)
## [1] "x"   "FUN"
nom_args(x = Orange, mean)
## [1] "x" "" 
nom_args(Orange, mean)
## NULL

Question 15 :

Programmation fonctionnelle

Utilisez la fonction lapply pour remplacer la boucle suivante, qui produit un histogramme pour chacune des variables du jeu de données quakes.

for (nom_variable in names(quakes)) {
  hist(
    x = quakes[, nom_variable],
    xlab = nom_variable,
    main = paste("Variable", nom_variable, "du jeu de données quakes")
  )
}

Solution

# À remplacer
for (nom_variable in names(quakes)) {
  hist(
    x = quakes[, nom_variable],
    xlab = nom_variable,
    main = paste("Variable", nom_variable, "du jeu de données quakes")
  )
}

# Appel à la fonction lapply équivalent à la boucle précédente
lapply(names(quakes), FUN = function(nom_variable) {
  hist(
    x = quakes[, nom_variable],
    xlab = nom_variable,
    main = paste("Variable", nom_variable, "du jeu de données quakes")
  ) 
})