Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
💥 Les 22 & 23 novembre : Accès 100% GRATUIT
à la Bibliothèque Numérique ENI. Je m'inscris !
  1. Livres et vidéos
  2. Data Scientist et langage R
  3. Full Stack R
Extrait - Data Scientist et langage R Autoformation aux bases de l'intelligence artificielle dans l'univers de la data (3e édition)
Extraits du livre
Data Scientist et langage R Autoformation aux bases de l'intelligence artificielle dans l'univers de la data (3e édition)
1 avis
Revenir à la page d'achat du livre

Full Stack R

Pourquoi ce chapitre ?

Après la première édition de cet ouvrage, nous avons reçu de nombreuses questions auxquelles nous avons répondu avec enthousiasme. En prenant un peu de recul, il s’avère que ces questions portaient souvent sur des problèmes d’implémentation, parfois un peu techniques. Nous espérons que ce chapitre répondra à ces attentes très "pragmatiques" de nos lecteurs.

Le titre de ce chapitre, Full Stack R, évoque l’idée de parcourir les aspects de l’écosystème R qui vont vous permettre de faire un code solide et imbriqué dans son environnement en termes de résilience, de données, d’espace mémoire, de processeurs, d’interface avec les autres applications dans un contexte web. C’est aussi un clin d’œil au fait que certains développeurs se présentent comme des "full stack developer".

Programmation fonctionnelle et/ou défensive

Sans être un langage conçu pour les aficionados de la programmation fonctionnelle, R permet de limiter certains effets de bord en mettant en œuvre des notions issues de ce type de programmation avec un style proche de celui-ci (mais en moins compact que des langages spécialisés sur cette approche). Cela se caractérise en partie par le fait de coder en utilisant des fonctions qui s’évaluent comme sont évaluées des fonctions en mathématiques classiques. On encapsule donc les structures de contrôle dans ces fonctions. 

Ce style d’écriture est très utile dans la partie transformation des données (qui devient reproductible sur d’autres agrégats de données que celui sur lequel on a mis au point le programme) et sur la mise en œuvre d’algorithmes décrits par ailleurs sous une formalisation mathématique classique (c’est-à-dire sans affectation  !).

Nous avons vu par exemple plus haut dans l’ouvrage qu’il est possible en R de créer facilement des fonctions qui prennent d’autres fonctions comme arguments et produisent de nouvelles fonctions.

Les différentes possibilités suivantes offertes par R sont typiques de ce style d’écriture, qui facilite la mise en œuvre de programmes conformes à leurs exigences (c’est-à-dire dont on peut prouver la conformité algorithmique, limiter les effets de bord, comparer à des expressions mathématiques) :

  • Le langage est vectorisé, il permet de manipuler des entités comme les vector, matrixarray avec une formulation proche de la manipulation des vecteurs, matrices et tenseurs en mathématiques.

  • On peut limiter les opérations d’affectation (a <- b) et diminuer ainsi les effets de bord (en programmation fonctionnelle réelle, on ne tolère aucune opération d’affectation). 

  • Il est possible d’utiliser des fonctions anonymes, qui s’appliquent localement sans risque de déborder de leur contexte. Dans certains langages, les fonctions anonymes sont appelées fonctions λ (lambda).

  • Il est facile de créer des fermetures, plus connues sous le vocable closure en anglais, fonctions produites...

Persistance, bases de données et R

L’objectif de cette section n’est pas d’établir un exposé exhaustif de ce sujet, mais de vous mettre le pied à l’étrier en posant quelques savoir-faire reproductibles dans divers environnements.

R offre la possibilité de sauvegarder et relire des objets sans aucune transformation, c’est-à-dire dans un état correspondant à leur représentation mémoire. Ce peut être une variable, un objet RC, un modèle, un dataframe, une fonction… Ce mode de sauvegarde est très utile pour préserver la mémoire de votre programme ou pour gérer un processus de construction de modèles : ces fonctions sont très rapides en écriture comme en lecture, ce qui permet de supprimer de son environnement les objets les plus consommateurs de mémoire après les avoir sauvegardés, puis de les restaurer au moment où ils sont de nouveau nécessaires.

Dans l’exemple suivant, nous allons sauvegarder et restaurer de deux façons différentes un dataframe comportant un calendrier. La fonction génératrice de ce calendrier n’a rien à voir avec l’objet du chapitre, mais nous vous proposons de la conserver, car un tel calendrier est très utile pour associer des évènements ou des contextes à différentes dates. C’est d’ailleurs en partie grâce à l’utilisation de cette petite technique d’agrégation que les auteurs de cet ouvrage ont été primés lors d’un hackaton où il fallait créer des modèles mixtes comportant des séries temporelles et diverses features à transformer et analyser au regard de la nature des jours (par exemple, la nature de l’activité d’une grande part de la population n’est pas la même les dimanches et les lundis). 

## création d'un calendrier facile à manipuler                 ## 
fabrique_sequence_jours <- function(debut = "2000-01-01", 
                                    fin   = "2020-12-31" ){ 
  require(dplyr) ...

Parallélisme

Il existe plusieurs solutions pour répartir les traitements, sans même citer les caractéristiques d’Hadoop (ou de Spark) qui permettent d’encapsuler les mécanismes de parallélisation destinés aux données massives. Pour répartir la charge de calcul, on peut facilement imaginer que sur une machine unique, il est possible de lancer plusieurs processus R ou de travailler sur plusieurs corps de processeur, et que sur un ensemble de machines, il est possible de travailler par clusters reliés par des mécanismes de bas niveau ou par une répartition triviale des traitements (en invoquant des machines distantes en mode remote).

Il existe plusieurs frameworks pour gérer la répartition sur plusieurs machines : dans l’environnement R, on utilise souvent snow ou nws. Un package de plus haut niveau encapsule plusieurs mécanismes et ne nécessite pas de compétences spécifiques (si tant est que les clusters soient installés !). L’exemple suivant introduit ce package nommé future. Le terme future fait allusion au fait que la programmation parallèle consiste à instancier des opérations parallèles dont on attend les résultats plus tard, quand ils seront disponibles. Remarquez que l’usage des boucles foreach du package éponyme permet par ailleurs de gérer...

Collecter des données externes

Quand vous disposez d’un accès à un site qui abrite un fichier qui vous intéresse, la collecte de données en est triviale, car il vous suffit d’utiliser l’instruction download.file(url, « mon_fichier ») pour y accéder. Par ailleurs, les différents fournisseurs de données, en particulier de séries temporelles économiques, vous informent presque systématiquement des syntaxes à utiliser en R. Par exemple, les World Bank Data sont particulièrement faciles d’accès et font l’objet d’un package R dédié (WDI).

Le site d’Open Data (http://www.quandl.com) propose plusieurs modes d’accès. Afin de vous faire découvrir la façon d’accéder aux informations fournies par une API JSON (cas très courant !), nous allons utiliser un package très pratique au travers d’un petit exemple simple pointant sur une des nombreuses séries temporelles du site (les cours du pétrole brut).

require(jsonlite) 
url_api <- "https://www.quandl.com/api/v3/datasets/OPEC/ORB.json" 
retour_api <- fromJSON(url_api) 
 
s_OPEC <- data.frame(retour_api$data$data)    
names(s_OPEC) <- c("seq_jours","cours") 
s_OPEC <- s_OPEC[order(s_OPEC$seq_jours),]...

Créer une API avec R

Jusqu’alors, nous avons utilisé les API (fonctions et/ou données) présentées par d’autres sur Internet. À l’inverse, nous allons maintenant exposer une API avec une fonction simple (ici, multiplier un entier par 2).

Pour cela, il faut d’abord créer la fonction à exposer. Pour que celle-ci soit comprise comme étant une API, il faudra ajouter un décorateur dans le code. Typiquement, un décorateur prend la forme d’un tag intégré dans un commentaire, qui donne une information d’environnement à un programme à même de l’utiliser.

# mul2.R 
 
# le commentaire suivant décore la fonction 
# avec le type d'action et le nom de l'API 
#* @get /mul2 
mul2 <-   function(x=1){as.integer(x)*2}  

Ce code sera appelé par la fonction qui va l’interpréter dans le package plumber, puis exposer l’API sur un port donné.

library(plumber) 
r <- plumb("mul2.R")  
r$run(port=8000) 

Ici, l’API est exposée sur votre serveur local localhost. Il est alors possible d’accéder à l’API, par exemple en utilisant l’instruction curl en ligne de commande de votre système d’exploitation.

curl "http://localhost:8000/mul2?x=100"  

[200] 

Le résultat...