Introduction conceptuelle à R
Cet article est en construction: un auteur est en train de le modifier.
En principe, le ou les auteurs en question devraient bientôt présenter une meilleure version.
Introduction
On se réfère à R souvent comme s'il s'agissait d'une entité unique, notamment un logiciel à utiliser pour des analyses statistiques, mais la réalité est un peu plus complexe. Cette introduction conceptuelle à R vise identifier différentes composantes de R et la manière dont elles s'articulent entre elles pour fournir un écosystème puissant et flexible. Contrairement à d'autres introductions à R qui sont basées principalement sur la découverte des particularités du langage, nous proposons ici plutôt un survol conceptuel qui puisse s'intégrer avec des approches plus pragmatiques.
Prérequis
Dans cette introduction conceptuelle, nous utiliserons l'environnement de travail typique de R, notamment avec l'utilisation de RStudio qui nous permettra de mieux cerner certains aspects comme par exemple la notion de variable, fonction, paquet, session, environnement et d'autres encore. Pour maximiser la compréhension de ces aspects, sur le plan théorique, il peut être utile d'avoir lu au préalable l'Introduction à la programmation.
Fonctionnement général de R
L'utilisation de R se fait à travers des instructions qui sont passées à un interprète en tant que Input. Ces instructions déclenchent de la computation, c'est-à-dire qu'elles sont interprétées, afin de produire un Output correspondant. Cet Ouput peut ensuite servir, éventuellement, en tant que Input pour une nouvelle computation, en créant ainsi un mécanisme cyclique représenté dans la figure suivante.
Ces trois phases sont généralement effectuées au sein de la même interface graphique, par exemple à travers RStudio ou la ligne de commande qui est disponible dans la version R de base. Néanmoins, le fait que ces trois phases puissent être décomposées permet une plus grande flexibilité et des opportunités intéressantes pour chaque phase :
- Input
- Les instructions peuvent être produites de différentes manières, par exemple à travers du code ou même à travers une interface graphique créé ponctuellement pour mener à bien un type d'analyse bien spécifique (voir par exemple les ShinyApps). La production des instructions peut être faite à travers un éditeur de texte avec aide à la syntaxe, étalée sur plusieurs fichiers pour gérer la complexité d'une analyse articulée, ou encore organisée en procédures réutilisables à différents endroits et pour des projets différents.
- Computation
- La computation, c'est-à-dire l'interprétation des instructions de Input, peut se faire sur la même machine où les instructions ont été écrites ou sur une ou plusieurs machines différentes. Ceci permet par exemple de lancer des analyses qui nécessitent d'une grande puissance computationnelle sur des serveurs distants, qui tournent pendant plusieurs heures, voire jours, avant d'atteindre le(s) résultat(s) correspondant(s).
- Output
- Le résultat des instructions peut être récupéré et affiché de différentes manières, de la plus simple (e.g. résultat textuel dans la ligne de commande) à la plus complexe (e.g. la génération d'un report scientifique mélangeant du texte, des figures, références, tableaux, etc.).
L'enjeu principal dans l'utilisation de R réside donc dans la création des instructions instrumentales aux résultats souhaités, un mécanisme qui peut s'avérer complexe car il présuppose la synergie des plusieurs composantes, ainsi que le respect de contraintes spécifiques au niveau de ce qui est considérée une série d'instructions interprétables par R. Nous proposons de suite une description plus approfondie des trois composantes Input-Computation-Output. Ceci nous permettra de préparer le terrain pour des concepts plus spécifiques et récurrents dans l'utilisation de R.
Input : la génération d'instructions valables
Comme dans la plupart des langages de programmation, les instructions en R se composent de deux types d'éléments fondamentaux :
- Les éléments littéraux
- Les éléments symboliques
Éléments littéraux
Les éléments littéraux sont tout simplement des éléments dans les instructions qui représentent exactement la valeur qu'ils affichent. Il s'agit d'une notion plus simple à comprendre que à expliquer, mais elle est néanmoins importante à saisir avant d'aborder les éléments symboliques.
En R il existe plusieurs types d'éléments littéraux, notamment :
- Les nombres entiers, par exemple :
3
,97
,123456789
, etc.
- Les nombres décimaux, par exemple :
1.34
,29.781
, etc.
- Les suites de caractères, délimitées habituellement par des guillemets, par exemple :
"EduTechWiki"
,"Pas du tout d'accord"
,"ID-12345"
, etc.
- Les valeurs logiques :
TRUE
,FALSE
,NA
(tout en majuscules et sans guillemets)
- L'absence d'un élément
NULL
(tout en majuscules et sans guillemets)
Éléments symboliques
Les éléments symboliques, au contraire, sont des éléments qui représentent quelque chose d'autre ; on parle souvent à ce propos de référence symbolique justement pour mettre en évidence cette fonction de représentation. On peut diviser les éléments symboliques en R en deux grandes catégories :
- Les données ou structures des données
- Les procédures ou fonctions
Références à des données ou structures de données
La manière la plus utilisée pour créer une référence symbolique à des données ou structures des données consiste à utiliser le symbole d'affectation <-
, comme par exemple dans le code suivant :
country <- "Switzerland"
Avec cette instruction nous avons créé une liaison (i.e. binding en anglais) entre la référence symbolique country
et la valeur littérale, constituée par une suite de caractères, "Switzerland"
. Métaphoriquement, c'est comme si on avait lié un fil entre le mot country
et la suite de caractères "Switzerland"
. Chaque fois qu'on tire le mot country
, nous pouvons récupérer ce qui est de l'autre côté du fil, dans ce cas "Switzerland"
. Cet exemple nous permet de proposer déjà quelques considérations préliminaire sur ce mécanisme :
- L'association entre référence symbolique et la valeur est tout à fait arbitraire.
- D'un point de vue formel, les associations suivantes sont tout à fait valables :
country <- "Geneva" country <- FALSE country <- 3.14
- L'interprète R doit pouvoir discriminer entre une référence symbolique est une valeur littérale
- Pour cette raison, les références symboliques doivent respecter certaines caractéristiques, comme par exemple :
- On ne peut pas utiliser des nombres entiers ou décimaux comme noms des références symboliques, car cela créerait un mis-match de ce type :
# Code NON valable 13 <- 17
- Les références symboliques ne peuvent contenir des espaces, autrement elles seraient traitées comme deux références symboliques différentes :
# Code NON valable my country <- "Switzerland"
- Elles ne peuvent pas contenir le tiret haut
-
, car ceci correspond à l'opération de la soustraction, et l'interprète essayerait ainsi de soustraire le deuxième mot du premier :# Code NON valable my-country <- "Switzerland"
- Les références symboliques peuvent se composer de tous les caractères alphanumériques, à condition de ne pas commencer avec un chiffre, et peuvent utiliser également le tiret bas. Il est de bonne pratique d'utiliser une stratégie cohérente pour les références symboliques composées de plusieurs mots, par exemple :
# "Snake case" : séparation par tiret bas (ou underscore) en utilisant que des minuscules my_beloved_country <- "Switzerland" # "Camel case" : utilisation d'une lettre majuscule à partir du deuxième mot composé myBelovedCountry <- "Switzerland"
- Les références symboliques peuvent se référer à d'autres références symboliques.
- Dans ce cas, c'est comme si vous attachez un fil à un autre fil et ainsi de suite. Le risque, très élevé d'ailleurs dans des analyses complexes, est de finir par emmêler les fils et se désorienter parmi les différentes références symboliques :
first_country <- "Switzerland" second_country <- "France" my_beloved_country <- first_country
Pour éviter le risque de se retrouver avec trop de références symboliques à des données, R met à disposition plusieurs types de structures de données. Parmi se structures on retrouve :
- Les vecteurs
- Les matrices
- Les data frames
- Les listes
Les différences techniques entre ces structures dépassent les objectifs introductifs de cet article, mais il est néanmoins important de sensibiliser au fait que selon le type de structure utilisée, il sera possible (ou impossible) d'effectuer certaines procédures ou fonctions, et que certaines structures n'acceptent que des éléments du même type (e.g. que des suites de caractères ou que des chiffres).
Le code suivant associe à la référence symbolique my_beloved_countries
un vecteur composé par 3 suite de caractères :
my_beloved_countries <- c("Switzerland", "France", "Italy")
La notation c()
est un exemple de procédure ou de fonction que nous allons aborder dans le point suivant.
Références à des procedures ou fonctions
L'autre catégorie d'éléments symboliques est représentée par les procedures, également appelées routines, ou plus simplement identifiées en tant que fonctions. Si les données et structures de données jouent un rôle principalement statique, de stockage de l'information, les fonctions servent à manipuler l'information, afin de passer progressivement à des stades qui s'approchent au résultat souhaité.
En raison de leur caractère dynamique, les fonctions ont un cycle de vie qui se compose de deux stades :
- Une phase de définition de la fonction, dans laquelle on définit ce que la fonction est censée faire
- Une phase d'invocation de la fonction, dans laquelle la fonction est intégrée dans la suite d'instructions
La phase de définition est unique, c'est-à-dire qu'elle est faite une fois seulement, en amont de l'invocation ; l'invocation, au contraire, peut se faire à plusieurs reprises.
# Définition d'une référence symbolique à une procedure
my_procedure <- function () {
# Do something
}
# Invocation de la fonction à plusieurs reprises
my_procedure()
my_procedure()
my_procedure()
Comme on le verra plus bas dans la page, R possède déjà plusieurs fonctions qui ont été définies en amont par les créateurs du langage, et d'autres encore sont disponibles dans des paquets externes. Il est tout à fait possible de mener des analyses sans faire recours à la phase de définition des fonctions, mais en se limitant à invoquer des fonctions déjà définies.
Même dans ce cas, il est important de bien saisir le caractère symbolique des fonctions qui suit le même principe des références symboliques aux données : l'association entre le nom de la fonction et son comportement est arbitraire, mais le nom de la fonction doit respecter les mêmes contraintes, afin que l'interprète de R puisse l'identifier en tant que telle.
L'un des intérêts principaux des fonctions et la manipulation des éléments littéraux, directement ou indirectement à travers les références symboliques. Le code suivant illustre ce principe à travers la fonction mean()
qui accepte des données de type numérique en tant que Input et affiche la moyenne arithmétique en tant que Output :
# Fonction invoquée avec de nombres littéraux
mean(c(1, 2, 3, 4, 5, 6))
# Fonction invoquée avec une référence symbolique
my_numbers <- c(1, 2, 3, 4, 5, 6)
mean(my_numbers)
Computation : l'interprétation d'une série d'instructions
La phase de computation est celle qui ne nécessite pas d'intervention humaine, car elle est automatisée et déterminée par les inputs qui ont été données à travers les instructions. La computation se fait à travers la lecture des instructions et leur évaluation. Bien que la lecture des instructions se fasse de manière linéaire, de gauche à droite et du haut vers le bas, l'évaluation du code suit des règles de précédence qui sont établi par le langage de programmation. Par exemple dans le mécanisme d'affectation de la référence symbolique sum_the_numbers
, l'addition des deux chiffres est faite en amont de l'affectation :
sum_the_numbers <- 10 + 20
De cette manière, sum_the_numbers
équivaut à 30. S'il n'y avait pas de mécanisme de précédence dans l'évaluation du code, on aurait eu d'abord l'affectation à 10, et ensuite l'addition de 20 en dehors de l'affectation :
sum_the_numbers <- 10
sum_the_numbers + 20
Dans ce cas, la valeur de sum_the_numbers
serait restée à 10, et l'addition suivante de 20 n'aurait pas été retenue par la référence symbolique.
L'un des enjeux majeurs dans la programmation à travers du code est celui de définir l'évaluation des étapes intermédiaires dans le bon ordre, afin que les procedures suivantes puissent s'appuyer sur les étapes précédentes. Si ceci peut sembler élémentaire dans le contexte d'un nombre limité d'instructions, cet aspect devient primordiale lors que l'analyse est étalée dans un grand nombre d'instructions. D'ailleurs, cet aspect est encore plus déterminant lorsque parmi les instructions figurent des procedures dont le temps d'exécution ne peut pas être prévu à l'avance, comme par exemple la récupération d'informations depuis le web.