Introduction conceptuelle à R

De EduTech Wiki
Aller à la navigation Aller à la recherche

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.

Fonctionnement général de R

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 :

  1. 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.
  2. 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).
  3. 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

Dans la plupart des cas, la génération de l'Input en R se fait à travers l'écriture de code. Les instructions dans le contexte de la programmation sont appelées algorithmes et nous utiliserons les deux termes de manière interchangeable par la suite.

# Instruction 1
# Instruction 2
# Instruction 3
# ...

Comme dans la plupart des langages de programmation, les instructions en R se composent de deux types d'éléments fondamentaux :

  1. Les éléments littéraux
  2. 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, également appelés éléments atomiques, parmi lesquels les plus fréquents sont :

  • 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 (tout en majuscules et sans guillemets)
  • L'absence d'un élément
    NULL, NA (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 :

  1. Les données ou structures des données
  2. 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 :

# Affectation/Déclaration d'une référence symbolique avec le symbole d'affectation "<-"
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 :

  1. 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
    
  2. 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"
      
  3. 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 :

  1. Une phase de définition de la fonction, dans laquelle on définit ce que la fonction est censée faire
  2. 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)

Combiner éléments littéraux et symboliques

L'intérêt principale du codage réside dans la possibilité de combiner les éléments littéraux et symboliques. Par exemple, dans une analyse standard dans le cadre des sciences sociales, on peut identifier les étapes suivantes :

1 # 1. Créer une référence symbolique à des données et utiliser une procédure pour récupérer les données depuis un fichier
2 my_data <- read.csv(file="my_file_with_data.csv", header=TRUE, sep=",")
3 
4 # 2. Manipuler les données à l'aide d'autres procedures pour atteindre les finalités souhaitées
5 print(my_data)
6 summary(my_data)

Même sans connaître les détails du codage de la ligne 2, on peut néanmoins décrire son fonctionnement à l'aide des notions que nous avons vu plus haut :

  1. Nous créons une référence symbolique nommée my_data
  2. Nous utilisons une fonction appelée read.csv() qui acceptent certains arguments, comme par exemple le chemin/nom du fichier
  3. Ce fichier contient très probablement une structure de données, c'est-à-dire des éléments littéraux organisés d'une certaine manière, par exemple en lignes et colonnes
  4. Par conséquent, la référence symbolique my_data est maintenant liée à une structure de données littéraux que nous pouvons par la suite manipuler à travers cette même référence symbolique (e.g. lignes 5-6)

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.

Computation et environnement

La phase de computation est étroitement liée à l'un des concepts clés de l'utilisation de R : l'environnement. Si on se réfère souvent aux algorithmes (i.e. le code) comme les étapes dans une recette de cuisine, dans la même similitude l'environnement correspond à l'état de la cuisine, et de ses éléments, à un moment donné dans le temps. En d'autres termes, lorsque les instructions se réfèrent à des éléments, ces éléments doivent :

  1. Exister dans l'environnement pour que l'interprète puisse les identifier, le récupérer et les intégrer dans la computation
  2. Refléter leur état au moment précis dans lequel ils sont utilisés, c'est-à-dire refléter les éventuelles manipulations qui ont été apportées à l'élément par des instructions précédentes (e.g. même si on se réfère toujours à la viande dans une recette de cuisine, elle ne sera pas dans le même état au début de la recette, quand elle sort du frigo, ou à la fin, après qu'elle a été cuite).

L'utilisation de RStudio est très utile pour l'explication de ce concept, car le logiciel consacre justement une partie de l'interface à l'environnement. Cette partie de l'interface, qui se trouve habituellement sur le côté droit, affiche les éléments qui font partie de l'environnement après l'exécution des instructions.

Instructions et environnement

Paquets et environnement

Computation et session

Un autre concept très important dans l'utilisation de R est le principe de session.

Computation et debug

La phase de computation est également celle dans laquelle les problèmes apparaissent, car l'interprète n'arrive pas à exécuter les instructions données à cause d'erreurs (ou bugs) dans le code. Malheureusement, les messages d'erreurs fournis par R ne sont souvent pas très clairs, ce qui n'aide pas surtout les néophytes à comprendre les raisons des interruptions de la computation.

Output : la mise à disposition du résultat souhaité