REPL

Dans RStudio, vous trouverez la console. Elle se matérialise par une ligne commançant par un chevron > et qui attend que vous frappiez quelque chose au clavier. C’est une boucle qui fonctionne ainsi :

  • Read
  • Eval
  • Print

On l’appelle donc la Read-Eval-Print Loop (REPL).

Elle lit ce que vous tapez, l’évalue et affiche le résultat.

Vous pouvez utiliser la console comme une calculatrice :

> 3 + 4
[1] 7

Le [1] signifie que le résultat est un vecteur et que l’élément qui débute la ligne (ici 7) est le premier.

Nous reviendrons sur cette notion de vecteur. C’est un objet qui contient plusieurs éléments du même type.

Pour comprendre la notation [1] examinez la variable letters qui est un vecteur de 26 éléments.

> letters
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y"
[26] "z"

Une variable est un symbole qui associe un nom à une valeur. La variable letters est associée aux lettres de l’alphabet en minuscules.

Pour définir une variable dans R on utilise l’opérateur d’assignement <-. Attention il est constitué de 2 caractères < et -.

> total <- 3 + 4 + 8 + 9

L’assignememt est une opération silencieuse. La REPL n’affiche donc rien. Cependant, vous pouvez afficher la valeur de total en lui demandant d’évaluer la variable :

> total
[1] 24

Vous pouvez également parenthèser l’assignement. Cela peut être utile pour vérifier la bonne marche de vos programmes.

> (total <- 3 + 4 + 8 + 9)
[1] 24

Dans R on peut utiliser (et définir) des fonctions. Pour cela on utilise des parenthèses. Utilisez la fonction sin et la variable prédéfinie pi pour calculer \(\sin\dfrac{\pi}{4}\).

Vous pouvez obtenir de l’aide sur une fonction en utlisant la fonction help.

> help(sin)

Parcourez cette aide pour trouver quelle fonction s’apparente à la fonction mathématque \(x\to\arctan(x)\).

Consultez l’aide de la fonction sqrt puis vérifiez que \(\sin\dfrac{\pi}{4}=\dfrac{\sqrt{2}}{2}\).

Fichiers de scripts

Un fichier de script R un fichier de texte dont le nom est de la forme un_script.R. C’est-à-dire que l’extension est de la forme .R.

Créez un nouveau fichier de script et sauvegardez le (<CTRL>+<SHIFT>+N puis <CTRL>+S).

Dedans, insérez les instructions suivantes.

x <- sin(pi/4)
y <- sqrt(2)/2
x - y

En plaçant le curseur au niveau de chacune des lignes vous les envoyer se faire evaluer vers la console en utilisant <CTRL>+<ENTER>.

Les algorithmes associés aux fonctions sin et sqrt ainsi que la précision limitée de la partie décimale des nombres en machine fait que les variables x et y ont des valeurs légerement différentes et R.

Une remarque le test logique pour vérifier si \(x\) et \(y\) sont égaux s’écrit ainsi ==.

> x == y
[1] FALSE

Un seul signe égal = équivaut à l’assignement <- ! Tentez de comprendre ce qui se passe ici :

> x = y
> x == y
[1] TRUE

Pour executer l’ensemble d’un script vous pouvez le source. Placez vous quelque part dans le script créé précedemment puis faîtes <CTRL>+<SHIFT>+S.

Vous pouvez retester l’égalité entre \(x\) et \(y\) pour vous convaincre que le script a bien été executé. Vous pouvez faire <CTRL>+<SHIFT>+<ENTER> pour voir une exécution avec affichage de toutes les évaluations sur la console.

Packages

Un ensemble de fonctions utiles autour d’un même sujet sont regroupées en package.

Un package doit être tout d’abord installé sur votre ordinateur. R utilise un dépot de packages officiels abrités par le (https://cran.r-project.org/web/packages/index.html). Depuis la console, vous pouvez directement installer un package.

> install.packages("lobstr")

Le package lobstr sert à examiner le détail des structures de données.

Installez les packages suivants :

  • Le package rlang donne des outils de métaprogrammation.
  • Le package purrr donne des outils de programmation fonctionnelle.

Si l’installation d’un package n’est à faire qu’une seule fois par machine, pour utiliser un package, il faudra le charger en mémoire au début de chaque session R. Au début d’un script, vous devez ajouter :

library(purrr)
library(rlang)
library(lobstr)

pour utiliser ces packages dans votre code.

La fonction help permet d’accèder à la documentation de ces packages.

Vecteurs

Les vecteurs sont une suite d’objets de même type.

Les 4 types de bases sont :

  • logical
  • integer
  • numeric
  • character

Pour créer un vecteur on utilise la fonction c :

(booleens <- c(TRUE, FALSE, TRUE))
(entiers <- c(5L, 7L, 9L))
(decimaux <- c(1, pi, sqrt(2)))
(chaines <- c("A", "asdf", "Elle a dit \"incroyable\""))

Pour accèder aux éléments d’un vecteur on utilise les crochets. On peut donner une position ou un vecteur de positions :

> booleens[1]
[1] TRUE
> entiers[c(3, 1)]
[1] 9 5
> decimaux[-3]
[1] 1.000000 3.141593
> cat(chaines[-c(1, 2)])
Elle a dit "incroyable"

On peut même utiliser un vecteur de booléens :

> decimaux[booleens]
[1] 1.000000 1.414214

Si vous modifiez un élément d’un vecteur avec un élément d’un type différent, il y aura une conversion de type de l’élément si c’est possible et sinon du vecteur. Examinez les exemples suivants :

> booleens[3] <- 7L
> booleens
[1] 1 0 7
> booleens[1] <- "RADS"
> booleens
[1] "RADS" "0"    "7"   

Copy on modify (vector)

Lorsque vous modifiez un vecteur il est copié à une nouvelle adresse mémoire. Ce procédé hérité d’une logique de programmation fonctionnelle est à connaitre si vous voulez écrire des programmes R dont les performances restent acceptables.

Nous allons utiliser la fonction ref du package lobstr. Elle permet d’examiner l’adresse mémoire à laquelle est sotckée la valeur associée à une variable.

> (x <- 5:10)
[1]  5  6  7  8  9 10
> lobstr::ref(x)
[1:0x18b448c6378] <int> 
> x[1] <- 3L
> lobstr::ref(x)
[1:0x18b46655d30] <int> 

Dans cet exemple, vous voyez que l’adresse de x a été modifée. En fait, une copie a été réalisée.

Examinons ce qu’il se passe quand vous copiez le contenu d’une variable dans une autre variable :

> library(lobstr)
> (x <- 5:10)
[1]  5  6  7  8  9 10
> (y <- x)
[1]  5  6  7  8  9 10
> ref(x)
[1:0x18b461e2518] <int> 
> ref(y)
[1:0x18b461e2518] <int> 
> y[1] <- 3L
> ref(x)
[1:0x18b461e2518] <int> 
> ref(y)
[1:0x18b45ac4f98] <int>

C’est seulement quand vous modifiez y que la copie est réalisée.

Matrices

Pour créer une matrice nous allons utiliser une fonction dont les paramètres sont nommés. Examinez l’aide de la fonction matrix.

> (m <- matrix(data = 1:12, ncol = 3))
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

Pour accèder aux éléments d’une matrice, on utilise encore les crochets :

> m[4, 2]
[1] 8

On peut aussi utiliser des vecteurs d’entiers et de booléens :

> m[c(TRUE, FALSE, TRUE, FALSE), -2]
     [,1] [,2]
[1,]    1    9
[2,]    3   11

En fait une matrice est un vecteur possédant un attribut dim.

> attributes(m)
$dim
[1] 4 3

Vous pouvez modifier un attribut de la manière suivante :

> attr(m, "dim") <- c(3, 4)
> m
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12

Listes

Les listes ont l’avantage que ses éléments ne sont pas forcément du même type, contrairement aux vecteurs. Pour créer un vecteur on utilise la fonction list :

> maliste <- list(c("A","B","C","A"),matrix(1:4),1,2L, "D")
> maliste
[[1]]
[1] "A" "B" "C" "A"

[[2]]
     [,1]
[1,]    1
[2,]    2
[3,]    3
[4,]    4

[[3]]
[1] 1

[[4]]
[1] 2

[[5]]
[1] "D"

On peut connaître sa longueur à l’aide de length :

> length(maliste)
[1] 5

On peut vérifier si un objet est une liste à l’aide de is.list :

> is.list(maliste)
[1] TRUE

On peut concatener deux listes ainsi:

> c(list(1,4),list(2))
[[1]]
[1] 1

[[2]]
[1] 4

[[3]]
[1] 2

Data frame

Liste nommée de vecteurs de même longueur.

Pour mieux appréhender les “data frame”, on peut considérer cet exemple:

df <- mtcars
df$cyl[1] <-12

Considérons:

df$cyl

qui permet “d’extraire” la colonne cyl de df. L’opération suivante:

df$cyl[1] <-12

permet de modifier le premier élément de df$cyl.

Notez que cela modifie également df.

Call by value

Metaprogramming

library(purrr)
library(rlang)

X_1 = 3
X_2 = 4
X_3 = 5

my_sum <- function(prefix, min, max) {
  prefix <- enexpr(prefix)
  var_names <- paste0(prefix, "_", min:max)
  values <- map_dbl(var_names, function(name) get(name))
  sum(values)
}

my_sum(X, min = 1, max = 3)

Copy on modify (data.frame)

> ref(mtcars)
> mtcars$cyl[1] <- 12
> ref(mtcars)

À l’éxecution de ces lignes on peut voir que seule la colonne mpg a son adresse modifiée. Cela pose un problème puisque si on veut modifier une ligne du “data frame”: il y a aura autant de copie que de ligne. À la section suivante, on se propose de contourner ce problème.

Le package data.table

Dans cette partie nous allons utiliser:

library(lobstr)
library(data.table)
library(microbenchmark)

Cette instruction permet de réinitialiser mtcars:

data(mtcars)

Comme vu précédement il y a modification de l’adresse de mtcars$cyl:

data(mtcars)
ref(mtcars)
mtcars$cyl[1] <- 12
ref(mtcars)

Ici il n’y a pas modification de l’adresse: on utilise le paquet data.table:

data(mtcars)
df <-  as.data.table(mtcars)
ref(df)
df[1, cyl := 12]
ref(df)
data(mtcars)

X <- mtcars
Y <- as.data.table(mtcars)

# on effectue une centaine d'opérations pour comparer
foo_trad <- function() {
  for(i in 1:100) X[1,1] <- i
}

foo_data.table <- function() {
  for(i in 1:100)  Y[1, mpg := i]
}

microbenchmark(foo_trad(),foo_data.table())

En testant cela, et en faisant changeant 100 pour 1000, on voit que l’utilisation du package data.table a un coût de base, mais pour un grand nombre d’opération, on a un gain de temps.

Il faut noter que l’utilisation de data.table n’est pas standard, elle n’est pas présente par défaut dans R.

Microbenchmark

Nous allons utiliser le package microbenchmark.

Il vous faut l’installer, puis le charger en début de session.

On peut voir un exemple d’utilisation dans la partie précédente.