I. Introduction à R et RStudio

Auteur·rice

Guillaume Guex

1 Présentation de R et RStudio

1.1 R : qu’est-ce ?

R est un logiciel d’analyses statistiques libre multiplateforme, distribué via le :

Ce dernier s’appuie sur un langage de programmation, qui porte le même nom. Il possède de nombreux avantages :

  • Gratuit et open-source.
  • Multiplateforme.
  • Nombreuses fonctions pour le traitement de données, les statistiques et la visualisation.
  • Extensible et flexible.
  • Très utilisé dans la communauté scientifique et l’administration publique.

1.2 RStudio et Posit

RStudio est un environnement de développement intégré (IDE) pour R, développé par Posit (entreprise privée très active dans le développement de R, et aussi Python récemment) qui facilite son utilisation. Il est aussi gratuit et multiplateforme, et peut être téléchargé sur : https://posit.co/download/rstudio-desktop/. Il permet d’écrire des scripts R, de visualiser les résultats de manière plus agréable, et d’avoir accès à de nombreuses fonctionnalités supplémentaires.

Nous allons faire le tour de son interface un peu plus loin.

1.3 Les notebooks et Quarto

Ce fichier contient le même code que dans le fichier Introduction_a_R.R, mais dans un format plus agréable à lire (mais peut-être plus abstrait): il s’agit d’un Notebook, écrit avec Quarto.

Un Notebook intègre du texte formaté, mais aussi des bouts de code R, avec le résultat de la console R juste en dessous. Par exemple :

# Ceci est un commentaire. Ci dessous, on va afficher le résultat du calcul
4 + 3
[1] 7

Ces notebooks permettent de sauvegarder le code sous format PDF, HTML ou Word, avec les résultats de l’exécution du code. Ils sont idéaux pour l’analyse exploratoire et les rapports, bien que les scripts classiques restent préférables pour le développement de logiciels ou de fonctions complexes.

Libre à vous d’utiliser votre format de fichier préféré pour travailler.

2 Interface RStudio

Pour commencer, nous allons faire un rapide tour de l’interface de RStudio (les éléments que nous utiliserons seront notés comme importants, les autres comme secondaires).

  • Dans le coin en haut à gauche, on trouve les Sources. C’est ici que sont ouverts les différents scripts (comme ce dernier), qui sont sauvegardés dans un format texte avec l’extension “.r” ou “.R” (ou .Rmd / .qmd pour un Notebook). Les codes se trouvant ici sont généralement copiés et exécutés dans la Console (voir section Section 3). On peut aussi y afficher certains jeux de données en mémoire. Si aucun fichier n’est ouvert, ce panneau est caché et la Console occupe toute la partie gauche.

  • Dans le coin en bas à gauche, se trouvent les onglets Console, Terminal et Background Jobs :

    • IMPORTANT
      • Console : permet d’entrer les commandes et de voir la plupart des résultats de l’exécution d’un code.
    • SECONDAIRE
      • Terminal : permet d’accéder au système de commandes (terminal) du système d’exploitation.
      • Background Jobs : permet d’effectuer des tâches en arrière plan.
  • Dans le coin en haut à droite, se trouvent les onglets Environment, History, Connections et Tutorial :

    • IMPORTANT
      • Environment : permet de voir les variables qui sont en ce moment en mémoire.
    • SECONDAIRE
      • History : permet de voir l’historique des commandes entrées.
      • Connections : permet d’effectuer des connections avec des bases de données distantes.
      • Tutorial : offre des tutoriels sur certains sujets de R.
  • Finalement, dans le coin en bas à droite, on trouve divers onglets :

    • IMPORTANT
      • Files : un navigateur de fichiers intégré.
      • Plots : permet d’afficher les graphiques crées par R.
      • Help : permet de trouver de la documentation pour les diverses packages ou fonctions de R.
    • SECONDAIRE
      • Packages : permet de gérer les rapidement les packages de R.
      • Viewer : permet de visualiser du contenu local en pdf, html ou autre.
      • Presentation : permet de visualiser des “présentations” html R.

3 Méthode de travail

Généralement, on va travailler sur un fichier script R (.r ou .R) que l’on éditera au fur et à mesure de l’avancée du travail. Ce fichier contiendra une séquence de manipulations, d’analyses et de production de résultats. Ce script devrait pouvoir être utilisé de manière indépendante, sans avoir recours à des manipulations préalables. (sauf peut-être l’adaptation des chemins d’accès aux données).

Pour exécuter tout le script R, on le sélectionne intégralement (commande-A ou ctrl-A) puis on envoie cette sélection dans la console (commande-retour ou ctrl-retour). Il est aussi possible d’exécuter uniquement des sous-parties du script en sélectionnant uniquement ces dernières, ou d’exécuter la ligne en cours. Il est également possible d’écrire directement dans la console pour exécuter des commandes (souvent pour faire des tests).

Une session R commence à l’ouverture de RStudio, et s’arrête lorsque l’on quitte R ou que l’on crée une nouvelle session via “Session \(\to\) Restart R”. Lors d’une session R, toutes les variables et objets créés par un code exécuté dans la console (peu importe la manière) vont être sauvés dans l’environnement d’exécution. On peut alors réutiliser ces variables et objets dans des commandes ultérieures.

Pour être sûr que votre script R crée par lui-même toutes les variables et objets nécessaires à son fonctionnement, n’oubliez pas de vérifier son exécution en partant d’une session vierge.

Il est également important de bien commenter son script R, c’est-à-dire d’ajouter des lignes de texte explicatives qui ne sont pas exécutées par R. En R, les commentaires commencent par le symbole #, ce qui indique à l’interpréteur d’ignorer le reste de la ligne.

# Ceci est un commentaire avant du code
3 + 4
[1] 7
2 - 2 # Ceci est un commentaire à côté d'une ligne de code
[1] 0

Vous pouvez trouver un exemple de mini travail statistique dans le script Mini_analyse.R.

4 Les opérateurs

4.1 Les opérateurs de calcul

Comme Python ou Julia, R est un langage interprété muni d’une invite de commande. On peut aussi bien exécuter des scripts (la source) que faire des calculs lignes par lignes. On peut par exemple l’utiliser comme une calculatrice :

3 + 8 - 2 # addition, soustraction
[1] 9
63 / 3 * 2 # division, multiplication
[1] 42
63 / (3 * 2) # parenthèses
[1] 10.5
3^3 # puissance
[1] 27
81^(1/2) # racine
[1] 9
23 %/% 3 # division entière
[1] 7
23 %% 3 # modulo
[1] 2

4.2 Les opérateurs logiques

Il est également possible d’effectuer des tests logiques :

4 < 4 # plus petit que
[1] FALSE
3 >= 3 # plus grand ou égal
[1] TRUE
4 == 5 # égal
[1] FALSE
4 != 5 # différent de
[1] TRUE
(4 < 2) | (2 < 3) # opérateur "ou"
[1] TRUE
(4 < 2) & (2 < 3) # opérateur "et"
[1] FALSE
!(4 < 2) # négation
[1] TRUE

Les éléments <, +, -, etc. s’appellent des opérateurs.

5 Les variables et les fonctions

5.1 Les variables

Mais R est aussi un langage de programmation, c’est-à-dire qu’il dispose de plusieurs fonctionnalités permettant de construire des scripts complexes. L’élément de base d’un langage de programmation sont les variables. Une variable est un réceptacle, portant un nom, qui permet de stocker des données.

On affecte des données dans une variable avec l’opérateur = ou <- (il y a des nuances subtiles entre les deux) :

a = -4
ma_variable <- 13.2
Avertissement

En R, l’opérateur historique d’assignation est <- et reste le plus utilisé dans la communauté. Cependant, dans ce cours, nous allons utiliser l’opérateur = car c’est le standard dans les autres langages.

On peut ensuite utiliser le nom de la variable dans une opération ultérieure. La valeur stockée sera alors utilisée à la place de la variable :

a - 10
[1] -14
11 > ma_variable
[1] FALSE
ma_variable + a
[1] 9.2

5.2 Les fonctions

Les autres éléments fondamentaux d’un langage de programmation sont les fonctions. Elles fonctionnent comme une “boîte noire”, qui prend une ou plusieurs valeurs en entrée, appelés arguments, effectue une suite d’opérations, et rend (presque toujours) un résultat. Leur structure suit toujours le schéma :

<nom_de_la_fonction>(<arg1>, <arg2>, ...)
sqrt(81) # racine carrée de 81
[1] 9
b = abs(-4) + 2 # valeur absolue de -4, plus 2, que l'on sauve dans b
print(b) # affichage de la valeur de b dans la console
[1] 6
log(256, 2) # logarithme de base 2 de 256
[1] 8

Certains arguments ne sont pas obligatoires, ils prennent alors une valeur par défaut :

log(256) # en base "e"
[1] 5.545177

On peut aussi nommer explicitement les arguments, et ainsi les mettre dans l’ordre désiré :

log(base=2, x=256) # équivalent à log(256, 2) 
[1] 8
runif(1, max=100) # min, qui vient avant max, prend sa valeur par défaut.
[1] 96.42741

On peut voir l’aide sur les différentes fonctions avec l’opérateur ? (ou chercher sur internet) :

?log
?runif

6 Les types et structures de données

6.1 Les types de données

R n’utilise pas que des nombres comme type de données, on trouve aussi :

chaine_de_car = "Bonjour Monde" # Les chaînes de caractères
booleen = TRUE # Les booléens (VRAI ou FAUX).
booleen_2 = F # booléen aussi (FALSE en abrégé)
T == 1 # VRAI. R convertit (coerce) le booléen en nombre pour le calcul
[1] TRUE
F == 0 # VRAI.
[1] TRUE

Pour voir quel type de données il s’agit, on utilise la fonction typeof() :

typeof(chaine_de_car)
[1] "character"
typeof(booleen)
[1] "logical"

Il existe également quatre “données” très particulières, qui nécessitent généralement un traitement particulier :

NULL # Rien, ensemble vide
NULL
1/0 # Infini (-Inf existe aussi)
[1] Inf
0/0 # "Not a number" (indéfini)
[1] NaN
NA # Valeur manquante (dans des jeux de données, valeurs souvent omises)
[1] NA

6.2 Les structures de données

De plus, dans R, les données sont souvent structurées de manière particulière.

Les vecteurs sont des collections unidimensionnelles d’éléments du même type appelés composantes. En mathématique, on les note de la façon suivante :

\[ \begin{align*} &\text{un vecteur colonne avec 3 éléments :} \qquad \begin{pmatrix} 4\\5\\6\end{pmatrix} \\ &\text{un vecteur ligne avec 4 éléments :} \qquad \begin{pmatrix} 2 & 7.3 & -2 & 0 \end{pmatrix} \end{align*} \]

En R, on les crée avec la fonction c() (pour “combine”). Bien qu’ils s’affichent horizontalement, R les adapte automatiquement (comme colonne ou ligne) selon les besoins du calcul matriciel :

vecteur_de_nombres = c(4, 5, 6) 
vecteur_de_nombres
[1] 4 5 6
vecteur_de_car = c("oui", "non", "peut-etre")
vecteur_de_car
[1] "oui"       "non"       "peut-etre"
sequence = 13:40 # une séquence de 13 à 40 avec incrément de 1 
sequence 
 [1] 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
[26] 38 39 40

Les listes qui, contrairement aux vecteurs, peuvent contenir des types de données différentes :

liste_variee = list("a", 4, TRUE)
liste_variee
[[1]]
[1] "a"

[[2]]
[1] 4

[[3]]
[1] TRUE

Les dataframes, formés à partir de plusieurs vecteurs de types différents, sont utiles pour stocker un jeu de données :

numero = seq(25, 45, 5) # séquence de 25 à 45 de 5 en 5.
reponse = c(rep("oui", 2), rep("non", 3)) # deux "oui", suivis de trois "non".
mes_donnees = data.frame(numero, reponse)
mes_donnees
  numero reponse
1     25     oui
2     30     oui
3     35     non
4     40     non
5     45     non

Afin de voir quelle est la structure de données d’une variable, on utilise la fonction class() :

class(vecteur_de_nombres)
[1] "numeric"
class(vecteur_de_car)
[1] "character"
class(liste_variee)
[1] "list"
class(mes_donnees)
[1] "data.frame"

La longueur et les dimensions des structures peuvent s’obtenir avec :

length(vecteur_de_car)
[1] 3
dim(mes_donnees)
[1] 5 2

6.3 Opérations sur les structures de données

Les opérateurs que nous avons vu au préalable peuvent aussi s’utiliser avec les vecteurs. Dans ce cas, l’opération se fera composante par composante. Si les vecteurs n’ont pas la même longueur, le plus petit sera “dupliqué” en boucle pour atteindre la taille du plus grand : c’est ce qu’on appelle le recyclage.

c(5, 6, 2) - c(1, 2, 3)
[1]  4  4 -1
c(3, 2, 4) + 2
[1] 5 4 6
c(2, 4, 8, 16) / c(1, 2)
[1] 2 2 8 8
2^c(1, 2, 3)
[1] 2 4 8
c(1, 2, 3, 4, 5, 6) %% 2 == 0 # T pour les valeurs paires
[1] FALSE  TRUE FALSE  TRUE FALSE  TRUE

Pour accéder à un élément d’une structure de données, on utilise les crochets :

vecteur_de_nombres[1] # premier élément du vecteur de nombres
[1] 4
vecteur_de_car[c(1, 3)] # éléments 1 et 3 du vecteur de caractères
[1] "oui"       "peut-etre"
vecteur_de_car[c(T, F, T)] # éléments 1 et 3 du vecteur de caractères
[1] "oui"       "peut-etre"
sequence[sequence %% 2 == 0] # Les nombres pairs de la séquence
 [1] 14 16 18 20 22 24 26 28 30 32 34 36 38 40

Dans une liste, il y a une différence fondamentale entre le simple et le double crochet :

  • [] renvoie une sous-liste (le contenant).
  • [[]] renvoie le contenu de l’élément (les données elles-mêmes).
liste_variee[2]
[[1]]
[1] 4
liste_variee[[2]]
[1] 4

Dans un dataframe, deux indices sont nécessaires. Les indices avant la virgule portent sur le lignes, celle après la virgule sur les colonnes :

mes_donnees[c(1, 2), 1]
[1] 25 30

En omettant un indice avant ou après la virgule, on sélectionne tous les éléments correspondants :

mes_donnees[, 2] # le vecteur qui constitue la deuxième colonne
[1] "oui" "oui" "non" "non" "non"

On utilise généralement une condition logique pour sélectionner les lignes (ou colonnes) voulues. Par exemple, pour n’avoir que les individus qui ont répondu “oui”, on fait:

mes_donnees[mes_donnees[, 2] == "oui", ]
  numero reponse
1     25     oui
2     30     oui

Bien entendu, toutes ces accès peuvent aussi servir pour modifier nos structures de données. P.ex, modification de la première valeur avec 101 :

vecteur_de_nombres
[1] 4 5 6
vecteur_de_nombres[1] = 101
vecteur_de_nombres
[1] 101   5   6

Remplacement du premier élément par “POSITIF” et du troisième par “INCONNU” :

vecteur_de_car
[1] "oui"       "non"       "peut-etre"
vecteur_de_car[c(1, 3)] = c("POSITIF", "INCONNU") 
vecteur_de_car
[1] "POSITIF" "non"     "INCONNU"

Mise à zéro des termes paires :

sequence
 [1] 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
[26] 38 39 40
sequence[sequence %% 2 == 0] = 0 
sequence
 [1] 13  0 15  0 17  0 19  0 21  0 23  0 25  0 27  0 29  0 31  0 33  0 35  0 37
[26]  0 39  0

6.4 L’accès par nom

Dans un dataframe les colonnes (les variables) portent des noms, qui sont donnés avec la fonction names() :

names(mes_donnees)
[1] "numero"  "reponse"

L’opérateur $ peut être utilisé pour accéder à l’une des variables :

mes_donnees$numero # la variable "numéro".
[1] 25 30 35 40 45
mes_donnees$reponse # la variable "réponse".
[1] "oui" "oui" "non" "non" "non"

On peut aussi modifier ces noms avec la fonction names() :

names(mes_donnees) = c("id", "answer")
mes_donnees
  id answer
1 25    oui
2 30    oui
3 35    non
4 40    non
5 45    non

Ceci est aussi valable pour les listes :

liste_variee
[[1]]
[1] "a"

[[2]]
[1] 4

[[3]]
[1] TRUE
names(liste_variee) = c("caractere", "chiffre", "booleen")
liste_variee
$caractere
[1] "a"

$chiffre
[1] 4

$booleen
[1] TRUE

On peut alors y accéder avec l’opérateur $ :

liste_variee$chiffre
[1] 4

Dans une liste et un dataframe, il est également possible d’attribuer des noms dès sa création :

liste_avec_noms = list(vecteur=c(2, 3, 4), caractere="oui", booleen=F)
liste_avec_noms
$vecteur
[1] 2 3 4

$caractere
[1] "oui"

$booleen
[1] FALSE
dataframe_avec_noms = data.frame(age=c(15, 63, 42), sexe=c("F", "H", "F"))
dataframe_avec_noms
  age sexe
1  15    F
2  63    H
3  42    F

L’énorme avantage de l’opérateur $ dans RStudio, au delà d’être plus explicite, est qu’il active l’autocomplétion. Dès que vous tapez le $, une liste déroulante vous propose les colonnes disponibles.

7 Chargement et sauvegarde de données

Pour charger des données, le format de prédilection dans R est le format CSV (comma-separated values) ou TSV (tab-separated values), c’est-à-dire un fichier texte où les données sont séparées par des virgules ou des tabulations. Par abus de langage, on parle de format CSV pour tout fichier dont les valeurs sont séparées par un caractère particulier. Le CSV “français” utilise d’ailleurs le ; comme séparateur (on peut alors utiliser read.csv2()) :

df = read.csv("data/notes.csv")
df
     nom annee inscription note
1 Yutaro  1999         Oui  4.5
2 Kadija  2002         Oui  5.5
3  Aline  2001         Non  4.8
4    Sam  2003         Oui  5.3

Notez que le chemin utilisé est un chemin relatif, depuis le répertoire de travail. On peut changer ce dernier dans les options “More” sous l’onglet “Files”. On peut également utiliser un chemin absolu. Pour en savoir plus sur les chemins d’accès : https://en.wikipedia.org/wiki/Path_(computing).

On peut ensuite travailler sur le jeu de données, puis le sauvegarder à nouveau. P.ex., on modifie une “erreur”, qui était l’entrée “Non” pour la variable “inscription” de la troisième ligne.

df$inscription[3] = "Oui" # ou df[3, 3] = "Oui" ou df[3, "inscription"] = "Oui"
df
     nom annee inscription note
1 Yutaro  1999         Oui  4.5
2 Kadija  2002         Oui  5.5
3  Aline  2001         Oui  4.8
4    Sam  2003         Oui  5.3

Et on sauve notre fichier dans le dossier data (avec les options sans noms des lignes)

write.csv(df, "data/notes_modifiees.csv", row.names=F)

8 Les packages

R est extensible grâce à l’installation de packages. Pour l’installation, on peut passer par l’interface RStudio (“Tools \(\to\) Install Packages…”), ou utiliser la commande suivante :

# install.packages("readxl") # DÉCOMMENTEZ POUR INSTALLER

Un package ne doit être installé qu’une seule fois dans R. Cependant, on utilise rarement tous les packages installés dans le même script. Pour charger un package, et rendre ainsi disponible ses fonctions et données, on utilise la commande suivante :

library(readxl)

On peut alors utiliser les nouvelles fonctions ou données disponibles dans le package, par exemple le chargement de fichiers excel :

df2 = read_excel("data/notes.xlsx")
df2 
# A tibble: 4 × 4
  nom    annee inscription note 
  <chr>  <dbl> <chr>       <chr>
1 Yutaro  1999 Oui         4.5  
2 Kadija  2002 Oui         5.5  
3 Aline   2001 Non         4.8  
4 Sam     2003 Oui         5.3  

Notez que c’est un nouveau format : un tibble. Ce format est une sorte de dataframe mais fait partie du Tidyverse, un ensemble de packages créé par RStudio, dont nous verrons l’utilisation la semaine prochaine :

class(df2)
[1] "tbl_df"     "tbl"        "data.frame"

Généralement, dans un script R, on place toutes commandes de chargement des packages nécessaires au début du script, afin qu’un utilisateur externe sache quel(s) package(s) il doit installer pour le faire tourner.

9 Éléments de programmation

9.1 Les conditions

Comme tout langage de programmation, R permet d’utiliser des conditions. Si la condition if n’est pas vérifiée, R va vérifier les conditions else if les unes après les autres. Si aucune n’est vérifiée, le code sous else s’exécute :

a = 150 # A changer

if (a < 100) {
  print("la variable a est petite")
} else if(a < 1000) {
  print("la variable a est moyennement grande")
} else if(a < 10000){
  print("la variable a est très grande")
} else {
  print("la variable a est gigantesque")
}
[1] "la variable a est moyennement grande"

9.2 Les boucles

R utilise également des boucles, comme la boucle for. A chaque itération, la variable i va prendre une des valeurs dans le vecteur donné (ici, c(3, 4, 5, 6, 7, 8, 9)). La boucle s’arrête lorsque toutes les valeurs ont été passées en revue :

for(i in 3:9){
  i_carre = i^2
  print(i_carre)
}
[1] 9
[1] 16
[1] 25
[1] 36
[1] 49
[1] 64
[1] 81

La boucle while. Elle ne s’arrête pas tant que la condition donnée est valide. Attention aux boucles infinies !

compte_a_rebours = 10
while(compte_a_rebours != 0){
  print(compte_a_rebours)
  compte_a_rebours = compte_a_rebours - 1
}
[1] 10
[1] 9
[1] 8
[1] 7
[1] 6
[1] 5
[1] 4
[1] 3
[1] 2
[1] 1

Ou la boucle repeat, qui ne s’arrête jamais, sauf avec le mot clé break (ce mot clé peut s’utiliser dans les autres boucles) :

a = 1
vec = 1
repeat{
  a = a + 1
  vec = c(vec, a)
  if(length(vec) >= 50) break
}
vec
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
[26] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
Avertissement

Bien que cette boucle fonctionne, en R professionnel, on évite généralement de “faire grandir” un vecteur vec = c(vec, a). On préfère utiliser la vectorisation, qui permet de faire le calcul sur tous les éléments d’un coup sans écrire de boucle for.

9.3 Création de fonctions

Finalement, il est aussi possible de créer ses propres fonctions, afin d’isoler et de réutiliser des bouts de code. Voici un exemple de syntaxe :

trouver_les_diviseurs = function(nombre, liste_de_diviseur){
  diviseurs_du_nombre = c()
  for (i in liste_de_diviseur){
    if(nombre %% i == 0) diviseurs_du_nombre = c(diviseurs_du_nombre, i)
  }
  return(diviseurs_du_nombre)
}

Une fois la fonction déclarée et en mémoire, on peut l’utiliser à n’importe quel moment dans notre code :

trouver_les_diviseurs(49, 3)
NULL
trouver_les_diviseurs(50, c(3,5,10,11))
[1]  5 10
trouver_les_diviseurs(36, 1:12)
[1]  1  2  3  4  6  9 12

On peut aussi spécifier le nom des arguments, pour, p.ex. changer l’ordre dans lequel on entre les arguments :

trouver_les_diviseurs(liste_de_diviseur=1:47, nombre=47) 
[1]  1 47

On peut définir des arguments facultatifs en spécifiant une valeur par défaut dans la définition de la fonction :

trouver_les_diviseurs_2 = function(nombre, liste_de_diviseur = 1:nombre){
  diviseurs_du_nombre = c()
  for (i in liste_de_diviseur){
    if(nombre %% i == 0) diviseurs_du_nombre = c(diviseurs_du_nombre, i)
  }
  return(diviseurs_du_nombre)
}
trouver_les_diviseurs_2(321) # Utilise la liste 1:321 par défaut
[1]   1   3 107 321
trouver_les_diviseurs_2(321, 1:3) # On force une liste plus courte
[1] 1 3