Le livre "L'art de la programmation dans R. Immerger dans le Big Data"

image Salut, habrozhiteli! De nombreux utilisateurs utilisent R pour des tâches spécifiques - ici pour construire un histogramme, là pour effectuer une analyse de régression ou effectuer d'autres opérations distinctes liées au traitement des données statistiques. Mais ce livre est écrit pour ceux qui veulent développer des logiciels en R. Les compétences en programmation des lecteurs prévus de ce livre peuvent aller d'une qualification professionnelle à «J'ai suivi un cours de programmation au collège», mais la clé est d'écrire du code R à des fins spécifiques . (Une connaissance approfondie des statistiques n'est généralement pas nécessaire.)

Quelques exemples de lecteurs qui pourraient bénéficier de ce livre:

  • Un analyste (par exemple, travaillant dans un hôpital ou une agence gouvernementale) qui doit régulièrement publier des rapports statistiques et développer des programmes à cet effet.
  • Un scientifique impliqué dans le développement d'une méthodologie statistique - nouvelles ou intégrant des méthodes existantes dans des procédures intégrées. La méthodologie doit être codée afin de pouvoir être utilisée dans la communauté des chercheurs.
  • Spécialistes du marketing, du support juridique, du journalisme, de l'édition, etc., engagés dans le développement de code pour la construction de représentations graphiques complexes de données.
  • Programmeurs professionnels ayant une expérience en développement de logiciels affectés à des projets liés à l'analyse statistique.
  • Les étudiants étudient les statistiques et le traitement des données.


Ainsi, ce livre n'est pas une référence aux innombrables méthodes statistiques du merveilleux paquet R. En fait, il est consacré à la programmation et il traite des problèmes de programmation qui sont rarement trouvés dans d'autres livres sur R. Même les sujets fondamentaux sont considérés sous l'angle de la programmation. Quelques exemples de cette approche:

  • Ce livre contient des sections des "Exemples avancés". Habituellement, ils fournissent des fonctions polyvalentes complètes au lieu de morceaux de code isolés basés sur des données spécifiques. De plus, certaines de ces fonctions peuvent être utiles dans votre travail quotidien avec R. En étudiant ces exemples, vous apprendrez non seulement comment fonctionnent les constructions R spécifiques, mais aussi comment les combiner dans des programmes utiles. Dans de nombreux cas, je décris des solutions alternatives et réponds à la question: "Pourquoi cela a-t-il été fait de cette façon?"
  • Le matériel est présenté en tenant compte de la perception du programmeur. Par exemple, lorsque je décris des trames de données, je prétends non seulement que la trame de données dans R est une liste, mais je souligne également les conséquences de ce fait du point de vue de la programmation. Toujours dans le texte, R est comparé à d'autres langues où cela peut être utile (pour les lecteurs qui parlent ces langues).
  • Le débogage joue un rôle crucial dans la programmation dans n'importe quel langage, mais la plupart des livres sur R ne mentionnent pas ce sujet. Dans ce livre, j'ai consacré un chapitre entier aux outils de débogage, utilisé le principe des «exemples avancés» et présenté des démos entièrement développées de la façon dont les programmes sont débogués dans la réalité.
  • De nos jours, les ordinateurs multicœurs sont apparus dans tous les foyers et la programmation des processeurs graphiques (GPU) produit une révolution imperceptible dans le domaine du calcul scientifique. De plus en plus d'applications R nécessitent de très grandes quantités de calcul, et le traitement parallèle est devenu pertinent pour les programmeurs R. Un chapitre entier est consacré à ce sujet dans le livre, en plus de décrire la mécanique, des exemples avancés sont également donnés.
  • Un chapitre séparé explique comment utiliser les informations sur l'implémentation interne et d'autres aspects de R pour accélérer le travail du code R.
  • L'un des chapitres se concentre sur l'interface de R avec d'autres langages de programmation tels que C et Python. Encore une fois, une attention particulière est accordée aux exemples avancés et aux recommandations de débogage.

Extrait. 7.8.4. Quand faut-il utiliser des variables globales?


Il n'y a pas de consensus sur l'utilisation des variables globales dans la communauté des programmeurs. De toute évidence, il n'y a pas de bonne réponse à la question posée dans le titre de cette section, car il s'agit de préférences et de style personnels. Néanmoins, de nombreux programmeurs pensent qu'une interdiction complète des variables globales, que de nombreux enseignants en programmation préconisent, serait inutilement difficile. Dans cette section, nous examinons les avantages possibles des variables globales dans le contexte des structures R. Le terme «variable globale» signifiera toute variable qui se trouve dans la hiérarchie de l'environnement au-dessus du niveau de code d'intérêt.

L'utilisation de variables globales dans R est plus courante que vous ne le pensez. Étonnamment, R utilise très largement les variables globales dans son implémentation interne (à la fois dans le code C et dans les fonctions R). Ainsi, l'opérateur de super-affectation << - est utilisé dans de nombreuses fonctions de bibliothèque de R (bien qu'il soit généralement utilisé pour écrire dans une variable située seulement un niveau plus haut dans la hiérarchie des variables). Le code multithread et le code GPU utilisés pour écrire des programmes rapides (voir chapitre 16) utilisent généralement des variables globales qui fournissent le principal mécanisme d'interaction entre les exécuteurs parallèles.

Maintenant, pour être concret, revenons à un exemple précédent de la section 7.7:

f <- function(lxxyy) { # lxxyy — ,  x  y ... lxxyy$x <- ... lxxyy$y <- ... return(lxxyy) } #  x  y lxy$x <- ... lxy$y <- ... lxy <- f(lxy) #   x  y ... <- lxy$x ... <- lxy$y 

Comme mentionné précédemment, ce code peut devenir lourd, surtout si x et y sont eux-mêmes des listes.

D'un autre côté, jetez un œil à un schéma alternatif utilisant des variables globales:

 f <- function() { ... x <<- ... y <<- ... } #  x  y x <-... y <-... f() #  x  y  #   x  y ... <- x ... <- y 

Peut-être que la deuxième version est beaucoup plus propre, moins volumineuse et ne nécessite pas de manipulation de liste. Un code clair crée généralement moins de problèmes d'écriture, de débogage et de maintenance.

Pour ces raisons - pour simplifier et réduire l'encombrement du code - nous avons décidé d'utiliser des variables globales au lieu de renvoyer des listes dans le code DES donné précédemment. Considérez cet exemple plus en détail.

Deux variables globales ont été utilisées (les deux sont des listes contenant des informations différentes): la variable sim est associée au code de bibliothèque et la variable mm1glbls est associée au code d'application spécifique M / M / 1. Commençons par sim.

Même les programmeurs qui sont limités sur les variables globales conviennent que l'utilisation de telles variables peut être justifiée si elles sont vraiment globales - dans le sens où elles sont largement utilisées dans le programme. Tout cela concerne la variable sim de l'exemple DES: elle est utilisée à la fois dans le code de la bibliothèque (dans schedevnt (), getnextevnt () et dosim ()) et dans le code M / M / 1 (dans mm1reactevnt ()). Dans cet exemple particulier, les appels ultérieurs à sim se limitent à la lecture, mais l'enregistrement est possible dans certaines situations. Un exemple typique de ce type est une implémentation possible de l'annulation d'événement. Par exemple, une telle situation peut se produire lors de la modélisation du principe «le plus ancien des deux»: deux événements sont prévus et lorsque l'un d'eux se produit, l'autre doit être annulé.

Ainsi, l'utilisation de sim comme variable globale semble justifiée. Cependant, si nous refusions résolument d'utiliser des variables globales, sim pourrait être placé dans une variable locale à l'intérieur de dosim (). Cette fonction passera sim dans l'argument de toutes les fonctions mentionnées dans le paragraphe précédent (schedevnt (), getnextevnt (), etc.), et chacune de ces fonctions renverra une variable sim modifiée.
Par exemple, ligne 94:

 reactevnt(head) 

converti sous la forme suivante:

 sim <- reactevnt(head) 

Après cela, la ligne suivante doit être ajoutée à la fonction mm1reactevnt () associée à une application spécifique:

 return(sim) 

Vous pouvez faire quelque chose de similaire avec mm1glbls en incluant dans dosim () une variable locale avec le nom (par exemple) appvars. Mais si cela se fait avec deux variables, elles doivent être placées dans la liste afin que les deux variables puissent être renvoyées par la fonction, comme dans l'exemple ci-dessus de la fonction f (). Et puis surgit la structure volumineuse des listes à l'intérieur des listes, qui a été mentionnée ci-dessus, ou plutôt des listes à l'intérieur des listes à l'intérieur des listes.

D'un autre côté, les opposants à l'utilisation de variables globales remarquent que la simplicité du code n'est pas vaine. Ils craignent qu'au cours du processus de débogage, il soit difficile de trouver des endroits où la variable globale modifie la valeur, car le changement peut se produire n'importe où dans le programme. Il semblerait que dans le monde des éditeurs de texte modernes et des outils de développement intégrés qui aideront à trouver toutes les occurrences d'une variable, le problème passe à côté (l'article original exhortant à abandonner l'utilisation des variables globales a été publié en 1970!). Néanmoins, ce facteur doit être pris en compte.

Un autre problème mentionné par les critiques est rencontré lors de l'appel d'une fonction à partir de plusieurs parties non liées d'un programme avec des valeurs différentes. Par exemple, imaginez que la fonction f () est appelée à partir de différentes parties du programme, chaque appel recevant ses propres valeurs x et y au lieu d'une valeur pour chacune. Le problème peut être résolu en créant des vecteurs de valeurs x et y dans lesquels chaque instance de f () dans votre programme a un élément distinct. Cependant, cela perdra la simplicité d'utilisation des variables globales.

Ces problèmes se rencontrent non seulement dans R, mais aussi dans un contexte plus général. Cependant, dans R, l'utilisation de variables globales au niveau supérieur crée un problème supplémentaire, car l'utilisateur à ce niveau possède généralement de nombreuses variables. Il existe un risque que le code utilisant des variables globales remplace accidentellement une variable complètement étrangère portant le même nom.

Bien sûr, le problème est facilement résolu - il suffit de choisir des noms longs pour les variables globales liées à une application spécifique. Cependant, les environnements fournissent également un compromis raisonnable, comme dans la situation suivante pour l'exemple DES.

Dans la fonction dosim (), la ligne

 sim <<- list() 

peut être remplacé par une chaîne

 assign("simenv",new.env(),envir=.GlobalEnv) 

Il crée un nouvel environnement référencé par la variable simenv au niveau supérieur. Cet environnement sert de conteneur pour encapsuler les variables globales accessibles par des appels à get () et assign (). Par exemple, les chaînes

 if (is.null(sim$evnts)) { sim$evnts <<- newevnt 

dans schedevnt () prendre la forme

 if (is.null(get("evnts",envir=simenv))) { assign("evnts",newevnt,envir=simenv) 

Oui, cette solution est également lourde, mais au moins elle n'est pas aussi compliquée que des listes à l'intérieur de listes à l'intérieur de listes. Et il protège contre l'écriture accidentelle dans une variable étrangère au niveau supérieur. L'utilisation de l'opérateur de super-affectation donne toujours un code moins lourd, mais ce compromis doit être pris en compte.

Comme d'habitude, il n'y a pas de style de programmation unique qui donne les meilleurs résultats dans toutes les situations. Une solution avec des variables globales est une autre option qui devrait être incluse dans votre arsenal d'outils de programmation.

7.8.5. Court-circuits


Permettez-moi de vous rappeler que les fermetures de R se composent d'arguments et du corps de la fonction en conjonction avec l'environnement au moment de l'appel. Le fait de permettre l'environnement est impliqué dans le paradigme de programmation, qui utilise le concept, également appelé fermeture (il y a une surcharge de terminologie ici).

Une fermeture est une fonction qui crée une variable locale, puis crée une autre fonction qui accède à cette variable. La description est trop abstraite, donc je ferais mieux de donner un exemple.

 1 > counter 2 function () { 3 ctr <- 0 4 f <- function() { 5 ctr <<- ctr + 1 6 cat("this count currently has value",ctr,"\n") 7 } 8 return(f) 9 } 

Vérifions le fonctionnement de ce code avant de plonger dans les détails de l'implémentation:

 > c1 <- counter() > c2 <- counter() > c1 function() { ctr <<- ctr + 1 cat("this count currently has value",ctr,"\n") } <environment: 0x8d445c0> > c2 function() { ctr <<- ctr + 1 cat("this count currently has value",ctr,"\n") } <environment: 0x8d447d4> > c1() this count currently has value 1 > c1() this count currently has value 2 > c2() this count currently has value 1 > c2() this count currently has value 2 > c2() this count currently has value 3 > c1() this count currently has value 3 

Ici, la fonction counter () est appelée deux fois et les résultats sont affectés c1 et c2. Comme prévu, ces deux variables sont constituées de fonctions, à savoir des copies de f (). Cependant, f () accède à la variable ctr via l'opérateur de super-affectation, et cette variable sera une variable avec le nom spécifié local à counter (), car elle sera la première sur le chemin dans la hiérarchie d'environnement. Il fait partie de l'environnement f () et, en tant que tel, est empaqueté dans ce qui retourne du côté appel de counter (). Le point clé est qu'avec différents appels à counter (), la variable ctr sera dans différents environnements (dans l'exemple d'environnement, elle a été stockée en mémoire aux adresses 0x8d445c0 et 0x8d447d4). En d'autres termes, différents appels à counter () créeront des instances physiquement différentes de ctr.

Par conséquent, les fonctions c1 () et c2 () fonctionnent comme des compteurs complètement indépendants. Cela peut être vu dans l'exemple où chaque fonction est appelée plusieurs fois.

»Plus d'informations sur le livre sont disponibles sur le site Web de l'éditeur
» Contenu
» Extrait

25% de réduction sur les colporteurs - R

Lors du paiement de la version papier du livre, une version électronique du livre est envoyée par e-mail.

Source: https://habr.com/ru/post/fr452746/


All Articles