Das Buch "Die Kunst des Programmierens in R. Eintauchen in Big Data"

Bild Hallo habrozhiteli! Viele Benutzer verwenden R für bestimmte Aufgaben - hier, um ein Histogramm zu erstellen, um eine Regressionsanalyse durchzuführen oder um andere separate Operationen im Zusammenhang mit der statistischen Datenverarbeitung durchzuführen. Dieses Buch ist jedoch für diejenigen gedacht, die Software in R entwickeln möchten. Die Programmierkenntnisse der beabsichtigten Leser dieses Buches können von einer beruflichen Qualifikation bis zu „Ich habe einen Programmierkurs am College belegt“ reichen. Der Schlüssel besteht jedoch darin, R-Code für bestimmte Zwecke zu schreiben . (Eine gründliche Kenntnis der Statistik ist in der Regel nicht erforderlich.)

Einige Beispiele für Leser, die von diesem Buch profitieren könnten:

  • Ein Analyst (der beispielsweise in einem Krankenhaus oder einer Regierungsbehörde arbeitet), der regelmäßig statistische Berichte erstellen und Programme für diesen Zweck entwickeln muss.
  • Ein Wissenschaftler, der an der Entwicklung einer statistischen Methodik beteiligt ist - neue oder bestehende Methoden in integrierte Verfahren integrieren. Die Methodik muss codiert werden, damit sie in der Forschungsgemeinschaft verwendet werden kann.
  • Spezialisten für Marketing, Rechtsberatung, Journalismus, Veröffentlichung usw., die an der Entwicklung von Code zum Erstellen komplexer grafischer Darstellungen von Daten beteiligt sind.
  • Professionelle Programmierer mit Erfahrung in der Softwareentwicklung für Projekte im Zusammenhang mit statistischen Analysen.
  • Studierende der Statistik und Datenverarbeitung.


Somit ist dieses Buch kein Hinweis auf die unzähligen statistischen Methoden des wunderbaren Pakets R. Tatsächlich widmet es sich der Programmierung und befasst sich mit Programmierproblemen, die in anderen Büchern über R selten zu finden sind. Selbst grundlegende Themen werden unter dem Gesichtspunkt der Programmierung betrachtet. Einige Beispiele für diesen Ansatz:

  • Dieses Buch enthält Abschnitte der "Erweiterten Beispiele". Normalerweise bieten sie vollständige Allzweckfunktionen anstelle von isolierten Codeteilen, die auf bestimmten Daten basieren. Darüber hinaus können einige dieser Funktionen bei Ihrer täglichen Arbeit mit R nützlich sein. Wenn Sie diese Beispiele studieren, lernen Sie nicht nur, wie bestimmte R-Konstrukte funktionieren, sondern auch, wie Sie sie zu nützlichen Programmen kombinieren. In vielen Fällen beschreibe ich alternative Lösungen und beantworte die Frage: „Warum wurde das so gemacht?“
  • Das Material wird unter Berücksichtigung der Wahrnehmung des Programmierers präsentiert. Zum Beispiel behaupte ich bei der Beschreibung von Datenrahmen nicht nur, dass der Datenrahmen in R eine Liste ist, sondern ich weise auch auf die Konsequenzen dieser Tatsache aus programmtechnischer Sicht hin. Auch im Text wird R mit anderen Sprachen verglichen, in denen dies nützlich sein kann (für Leser, die diese Sprachen sprechen).
  • Das Debuggen spielt eine entscheidende Rolle bei der Programmierung in jeder Sprache, aber die meisten Bücher über R erwähnen dieses Thema nicht. In diesem Buch widmete ich ein ganzes Kapitel den Debugging-Tools, verwendete das Prinzip der „fortgeschrittenen Beispiele“ und präsentierte vollständig entwickelte Demos, wie Programme in der Realität debuggt werden.
  • Heutzutage sind in allen Haushalten Multi-Core-Computer aufgetaucht, und die Programmierung von Grafikprozessoren (GPUs) führt zu einer unmerklichen Revolution auf dem Gebiet des wissenschaftlichen Rechnens. Immer mehr R-Anwendungen erfordern sehr viel Rechenaufwand, und die Parallelverarbeitung ist für R-Programmierer relevant geworden. Neben der Beschreibung der Mechanik wird diesem Thema ein ganzes Kapitel gewidmet. Außerdem werden fortgeschrittene Beispiele gegeben.
  • In einem separaten Kapitel wird erläutert, wie Informationen zur internen Implementierung und zu anderen Aspekten von R verwendet werden können, um die Arbeit von R-Code zu beschleunigen.
  • Eines der Kapitel befasst sich mit der Schnittstelle von R mit anderen Programmiersprachen wie C und Python. Auch hier wird besonderes Augenmerk auf fortgeschrittene Beispiele und Empfehlungen für das Debuggen gelegt.

Auszug. 7.8.4. Wann sollten globale Variablen verwendet werden?


Es besteht kein Konsens über die Verwendung globaler Variablen in der Programmierergemeinschaft. Offensichtlich gibt es keine richtige Antwort auf die im Titel dieses Abschnitts gestellte Frage, da dies eine Frage der persönlichen Vorlieben und des Stils ist. Dennoch glauben viele Programmierer, dass ein vollständiges Verbot globaler Variablen, das viele Programmierlehrer befürworten, unnötig hart wäre. In diesem Abschnitt untersuchen wir die möglichen Vorteile globaler Variablen im Kontext von R.-Strukturen. Der Begriff „globale Variable“ bezeichnet jede Variable, die sich in der Umgebungshierarchie über der Ebene des interessierenden Codes befindet.

Die Verwendung globaler Variablen in R ist häufiger als erwartet. Überraschenderweise verwendet R in seiner internen Implementierung sehr häufig globale Variablen (sowohl im C-Code als auch in R-Funktionen). Daher wird der Superzuweisungsoperator << - in vielen Bibliotheksfunktionen von R verwendet (obwohl er normalerweise zum Schreiben in eine Variable verwendet wird, die sich nur eine Ebene höher in der Hierarchie der Variablen befindet). Der Multithread-Code und der GPU-Code, die zum Schreiben schneller Programme verwendet werden (siehe Kapitel 16), verwenden normalerweise globale Variablen, die den Hauptmechanismus für die Interaktion zwischen parallelen Ausführenden darstellen.

Kehren wir der Vollständigkeit halber zu einem früheren Beispiel aus Abschnitt 7.7 zurück:

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 

Wie bereits erwähnt, kann dieser Code umständlich werden, insbesondere wenn x und y selbst Listen sind.

Schauen Sie sich andererseits ein alternatives Schema mit globalen Variablen an:

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

Vielleicht ist die zweite Version viel sauberer, weniger sperrig und erfordert keine Listenmanipulation. Klares Code verursacht normalerweise weniger Probleme beim Schreiben, Debuggen und Warten.

Aus diesen Gründen - um die Sperrigkeit des Codes zu vereinfachen und zu verringern - haben wir uns entschieden, globale Variablen zu verwenden, anstatt Listen im zuvor angegebenen DES-Code zurückzugeben. Betrachten Sie dieses Beispiel genauer.

Es wurden zwei globale Variablen verwendet (beide sind Listen mit unterschiedlichen Informationen): Die Sim-Variable ist dem Bibliothekscode zugeordnet, und die Variable mm1glbls ist dem spezifischen Anwendungscode M / M / 1 zugeordnet. Beginnen wir mit sim.

Selbst Programmierer, die sich mit globalen Variablen zurückhalten, sind sich einig, dass die Verwendung solcher Variablen gerechtfertigt sein kann, wenn sie wirklich global sind - in dem Sinne, dass sie im Programm weit verbreitet sind. All dies bezieht sich auf die Sim-Variable aus dem DES-Beispiel: Sie wird sowohl im Bibliothekscode (in schedevnt (), getnextevnt () und dosim ()) als auch im M / M / 1-Code (in mm1reactevnt ()) verwendet. In diesem speziellen Beispiel beschränken sich nachfolgende Aufrufe von sim auf das Lesen, in einigen Situationen ist jedoch eine Aufzeichnung möglich. Ein typisches Beispiel dieser Art ist eine mögliche Implementierung der Ereignisstornierung. Eine solche Situation kann beispielsweise auftreten, wenn das Prinzip „früher von zwei“ modelliert wird: Zwei Ereignisse sind geplant, und wenn eines davon auftritt, sollte das andere abgebrochen werden.

Die Verwendung von sim als globale Variable erscheint daher gerechtfertigt. Wenn wir uns jedoch entschieden weigerten, globale Variablen zu verwenden, könnte sim in eine lokale Variable in dosim () eingefügt werden. Diese Funktion übergibt sim im Argument aller im vorherigen Absatz genannten Funktionen (schedevnt (), getnextevnt () usw.), und jede dieser Funktionen gibt eine modifizierte sim-Variable zurück.
Zum Beispiel Zeile 94:

 reactevnt(head) 

in die folgende Form konvertiert:

 sim <- reactevnt(head) 

Danach sollte der Funktion mm1reactevnt (), die einer bestimmten Anwendung zugeordnet ist, die folgende Zeile hinzugefügt werden:

 return(sim) 

Sie können mit mm1glbls etwas Ähnliches tun, indem Sie in dosim () eine lokale Variable mit dem Namen (zum Beispiel) appvars einfügen. Wenn dies jedoch mit zwei Variablen erfolgt, müssen diese in die Liste aufgenommen werden, damit beide Variablen von der Funktion zurückgegeben werden können, wie im obigen Beispiel der Funktion f (). Und dann entsteht die sperrige Struktur von Listen innerhalb von Listen, die oben erwähnt wurde, oder vielmehr Listen innerhalb von Listen innerhalb von Listen.

Auf der anderen Seite bemerken Gegner der Verwendung globaler Variablen, dass die Einfachheit des Codes nicht umsonst ist. Sie befürchten, dass es während des Debugging-Prozesses schwierig ist, Stellen zu finden, an denen die globale Variable den Wert ändert, da die Änderung an einer beliebigen Stelle im Programm auftreten kann. Es scheint, dass in der Welt der modernen Texteditoren und integrierten Entwicklungstools, mit deren Hilfe alle Vorkommen einer Variablen gefunden werden können, das Problem auf der Strecke bleibt (der ursprüngliche Artikel, in dem die Verwendung globaler Variablen aufgegeben wurde, wurde 1970 veröffentlicht!). Dieser Faktor muss jedoch berücksichtigt werden.

Ein weiteres Problem, das Kritiker erwähnen, tritt auf, wenn eine Funktion aus mehreren nicht verwandten Teilen eines Programms mit unterschiedlichen Werten aufgerufen wird. Stellen Sie sich zum Beispiel vor, dass die Funktion f () aus verschiedenen Teilen des Programms aufgerufen wird, wobei jeder Aufruf seine eigenen x- und y-Werte anstelle eines Werts für jeden erhält. Das Problem kann gelöst werden, indem Vektoren mit x- und y-Werten erstellt werden, in denen jede Instanz von f () in Ihrem Programm ein separates Element enthält. Dies verliert jedoch die Einfachheit der Verwendung globaler Variablen.

Diese Probleme treten nicht nur in R auf, sondern auch in einem allgemeineren Kontext. In R verursacht die Verwendung globaler Variablen auf der oberen Ebene jedoch ein zusätzliches Problem, da der Benutzer auf dieser Ebene normalerweise viele Variablen hat. Es besteht die Gefahr, dass Code, der globale Variablen verwendet, versehentlich eine völlig fremde Variable mit demselben Namen ersetzt.

Natürlich ist das Problem leicht zu lösen - es reicht aus, lange Namen für globale Variablen zu wählen, die an eine bestimmte Anwendung gebunden sind. Umgebungen bieten jedoch auch einen vernünftigen Kompromiss, wie in der folgenden Situation für das DES-Beispiel.

Innerhalb der Funktion dosim () befindet sich die Zeile

 sim <<- list() 

kann durch eine Zeichenfolge ersetzt werden

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

Es wird eine neue Umgebung erstellt, auf die die Variable simenv auf der obersten Ebene verweist. Diese Umgebung dient als Container zum Einkapseln globaler Variablen, auf die durch Aufrufe von get () und assign () zugegriffen werden kann. Zum Beispiel Strings

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

in schedevnt () nehmen Sie die Form an

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

Ja, diese Lösung ist auch umständlich, aber zumindest nicht so kompliziert wie Listen in Listen in Listen. Und es schützt vor versehentlichem Schreiben in eine fremde Variable auf der obersten Ebene. Die Verwendung des Superzuweisungsoperators ergibt immer noch weniger umständlichen Code, aber dieser Kompromiss sollte berücksichtigt werden.

Wie üblich gibt es keinen einzigen Programmierstil, der in allen Situationen die besten Ergebnisse liefert. Eine Lösung mit globalen Variablen ist eine weitere Option, die in Ihr Arsenal an Programmierwerkzeugen aufgenommen werden sollte.

7.8.5. Kurzschlüsse


Ich möchte Sie daran erinnern, dass Abschlüsse von R aus Argumenten und dem Funktionskörper in Verbindung mit der Umgebung zum Zeitpunkt des Aufrufs bestehen. Die Tatsache, dass die Umgebung enthalten ist, ist Teil des Programmierparadigmas, das das Konzept verwendet, das auch als Closure bezeichnet wird (hier gibt es eine gewisse Überlastung der Terminologie).

Ein Abschluss ist eine Funktion, die eine lokale Variable erstellt und dann eine andere Funktion erstellt, die auf diese Variable zugreift. Die Beschreibung ist zu abstrakt, daher sollte ich ein Beispiel geben.

 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 } 

Lassen Sie uns überprüfen, wie dieser Code funktioniert, bevor wir uns mit den Implementierungsdetails befassen:

 > 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 

Hier wird die Funktion counter () zweimal aufgerufen und die Ergebnisse werden mit c1 und c2 belegt. Wie erwartet bestehen diese beiden Variablen aus Funktionen, nämlich Kopien von f (). F () greift jedoch über den Superzuweisungsoperator auf die Variable ctr zu, und diese Variable ist eine Variable mit dem angegebenen Namen local to counter (), da sie die erste auf dem Pfad in der Umgebungshierarchie ist. Es ist Teil der Umgebung f () und wird als solches in das gepackt, was zur Aufrufseite von counter () zurückkehrt. Der entscheidende Punkt ist, dass sich die Variable ctr bei verschiedenen Aufrufen von counter () in verschiedenen Umgebungen befindet (in der Beispielumgebung wurde sie unter den Adressen 0x8d445c0 und 0x8d447d4 im Speicher gespeichert). Mit anderen Worten, unterschiedliche Aufrufe von counter () erzeugen physikalisch unterschiedliche Instanzen von ctr.

Infolgedessen arbeiten die Funktionen c1 () und c2 () als völlig unabhängige Zähler. Dies ist aus dem Beispiel ersichtlich, in dem jede Funktion mehrmals aufgerufen wird.

»Weitere Informationen zum Buch finden Sie auf der Website des Herausgebers
» Inhalt
» Auszug

25% Rabatt auf Gutschein für Straßenhändler - R.

Nach Zahlung der Papierversion des Buches wird eine elektronische Version des Buches per E-Mail verschickt.

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


All Articles