wake-up-neo.net

Wie organisiere ich große R-Programme?

Wenn ich ein R-Projekt von beliebiger Komplexität durchführe, werden meine Skripte schnell lang und verwirrend.

Welche Methoden kann ich anwenden, damit die Arbeit mit meinem Code immer Spaß macht? Ich denke über Dinge wie nach

  • Platzierung von Funktionen in Quelldateien
  • Wann soll etwas in eine andere Quelldatei aufgeteilt werden?
  • Was soll in der Master-Datei sein
  • Verwendung von Funktionen als Organisationseinheiten (ob dies sinnvoll ist, da R den Zugriff auf den globalen Status erschwert)
  • Einzugs-/Zeilenumbruchpraktiken.
    • Behandeln (wie {?
    • Setzen Sie Dinge wie)} in 1 oder 2 Zeilen?

Was sind Ihre Faustregeln für die Organisation großer R-Skripte?

157
Dan Goldstein

Die Standardantwort ist die Verwendung von Paketen - siehe Writing R Extensions Handbuch sowie verschiedene Tutorials im Web.

Es gibt dir

  • eine quasi-automatische Möglichkeit, Ihren Code thematisch zu organisieren
  • empfiehlt Ihnen dringend, eine Hilfedatei zu schreiben, damit Sie über die Benutzeroberfläche nachdenken
  • viele Plausibilitätsprüfungen über R CMD check
  • eine Chance, Regressionstests hinzuzufügen
  • sowie ein Mittel für Namespaces.

Das einfache Ausführen von source() über Code funktioniert für wirklich kurze Snippets. Alles andere sollte in einem Paket enthalten sein - auch wenn Sie nicht vorhaben, es zu veröffentlichen, da Sie interne Pakete für interne Repositorys schreiben können.

In Bezug auf den Teil 'Bearbeiten' enthält das R Internals Handbuch ausgezeichnete R-Codierungsstandards in Abschnitt 6. Andernfalls Normalerweise verwende ich Standardeinstellungen in Emacs 'ESS-Modus .

Update 13.08.2008: David Smith hat gerade über das Google R Style Guide gebloggt.

69

Ich mag es, verschiedene Funktionen in ihre eigenen Dateien zu packen.

Aber ich mag das Paketsystem von R nicht. Es ist ziemlich schwer zu bedienen.

Ich bevorzuge eine einfache Alternative, um die Funktionen einer Datei in einer Umgebung (was jede andere Sprache als "Namespace" bezeichnet) zu platzieren und sie anzuhängen. Zum Beispiel habe ich eine 'util'-Gruppe von Funktionen wie folgt erstellt:

util = new.env()

util$bgrep = function [...]

util$timeit = function [...]

while("util" %in% search())
  detach("util")
attach(util)

Dies ist alles in einer Datei til.R . Wenn Sie es ausgeben, erhalten Sie die Umgebung 'util', so dass Sie util$bgrep() und so aufrufen können; aber außerdem macht der attach() -Aufruf es so einfach bgrep() und solche funktionieren direkt. Wenn Sie nicht alle diese Funktionen in ihre eigene Umgebung gestellt haben, verschmutzen sie den Namespace der obersten Ebene des Interpreters (den, den ls() anzeigt).

Ich habe versucht, Pythons System zu simulieren, bei dem jede Datei ein Modul ist. Das wäre besser, aber das scheint in Ordnung zu sein.

50
Brendan OConnor

Dies mag sich ein wenig offensichtlich anhören, insbesondere wenn Sie ein Programmierer sind, aber ich denke über logische und physikalische Codeeinheiten nach.

Ich weiß nicht, ob dies Ihr Fall ist, aber wenn ich in R arbeite, beginne ich selten mit einem großen komplexen Programm. Normalerweise beginne ich in einem Skript und trenne den Code in logisch trennbare Einheiten, wobei ich häufig Funktionen verwende. Datenmanipulations- und Visualisierungscode werden in eigenen Funktionen usw. abgelegt. Diese Funktionen sind in einem Abschnitt der Datei zusammengefasst (Datenmanipulation oben, dann Visualisierung usw.). Letztendlich möchten Sie darüber nachdenken, wie Sie die Wartung Ihres Skripts vereinfachen und die Fehlerrate senken können.

Wie fein/grobkörnig Sie Ihre Funktionen gestalten, variiert und es gibt verschiedene Faustregeln: z. 15 Codezeilen oder "eine Funktion sollte für die Ausführung einer Aufgabe verantwortlich sein, die durch ihren Namen gekennzeichnet ist" usw. Ihre Laufleistung variiert. Da R Call-by-Reference nicht unterstützt, kann ich meine Funktionen in der Regel zu feinkörnig gestalten, wenn Datenrahmen oder ähnliche Strukturen weitergegeben werden. Aber dies kann eine Überkompensation für einige alberne Performance-Fehler sein, als ich mit R anfing.

Wann werden logische Einheiten in ihre eigenen physischen Einheiten extrahiert (wie Quelldateien und größere Gruppierungen wie Pakete)? Ich habe zwei Fälle. Erstens ist es ärgerlich, wenn die Datei zu groß wird und sich zwischen logisch nicht zusammenhängenden Einheiten bewegt. Zweitens, wenn ich Funktionen habe, die von anderen Programmen wiederverwendet werden können. Normalerweise beginne ich damit, eine gruppierte Einheit, beispielsweise Datenbearbeitungsfunktionen, in einer separaten Datei abzulegen. Ich kann diese Datei dann aus jedem anderen Skript beziehen.

Wenn Sie Ihre Funktionen bereitstellen möchten, müssen Sie über Pakete nachdenken. Ich setze R-Code aus verschiedenen Gründen nicht in der Produktion oder zur Wiederverwendung durch andere ein (kurz: Die Organisationskultur bevorzugt andere Sprachen, Bedenken hinsichtlich der Leistung, der GPL usw.). Außerdem neige ich dazu, meine Sammlungen von Quelldateien ständig zu verfeinern und zu erweitern, und ich würde mich lieber nicht mit Paketen befassen, wenn ich eine Änderung vornehme. Sie sollten sich also die anderen paketbezogenen Antworten wie die von Dirk ansehen, um weitere Informationen zu diesem Thema zu erhalten.

Schließlich denke ich, dass Ihre Frage nicht unbedingt speziell für R. ist. Ich würde wirklich empfehlen, Code Complete von Steve McConnell zu lesen, der viel Wissen über solche Probleme und Codierungspraktiken im Allgemeinen enthält.

33
ars

Ich stimme dem Rat von Dirk zu! IMHO, das Organisieren Ihrer Programme von einfachen Skripten zu dokumentierten Paketen ist für das Programmieren in R wie das Umschalten von Word zu TeX/LaTeX zum Schreiben. Ich empfehle einen Blick auf das sehr nützliche Erstellen von R-Paketen: Ein Tutorial von Friedrich Leisch.

19
Paolo

Meine prägnante Antwort:

  1. Schreiben Sie Ihre Funktionen sorgfältig, und identifizieren Sie allgemein genügend Ausgänge und Eingänge.
  2. Beschränken Sie die Verwendung globaler Variablen.
  3. Verwenden Sie S3-Objekte und gegebenenfalls S4-Objekte.
  4. Platzieren Sie die Funktionen in Paketen, insbesondere wenn Ihre Funktionen C/Fortran aufrufen.

Ich glaube, R wird in der Produktion immer häufiger verwendet, daher ist der Bedarf an wiederverwendbarem Code größer als zuvor. Ich finde den Dolmetscher viel robuster als zuvor. Es besteht kein Zweifel, dass R 100-300x langsamer ist als C, aber normalerweise konzentriert sich der Engpass auf einige Codezeilen, die an C/C++ delegiert werden können. Ich denke, es wäre ein Fehler, die Stärken von R bei der Datenmanipulation und statistischen Analyse auf eine andere Sprache zu übertragen. In diesen Fällen ist der Performance-Nachteil gering und in jedem Fall die Einsparung an Entwicklungsaufwand wert. Wenn nur die Ausführungszeit in Frage käme, würden wir alle Assembler schreiben.

15
gappy

Ich wollte herausfinden, wie man Pakete schreibt, habe aber nicht die Zeit investiert. Für jedes meiner Mini-Projekte speichere ich alle meine Low-Level-Funktionen in einem Ordner mit dem Namen 'functions /' und speichere sie in einem separaten Namespace, den ich explizit erstelle.

Mit den folgenden Codezeilen wird eine Umgebung mit dem Namen "myfuncs" im Suchpfad erstellt, sofern diese noch nicht vorhanden ist (mithilfe von attach), und mit den Funktionen in den .r-Dateien in meinem Verzeichnis 'functions /' aufgefüllt (mithilfe von .r) sys.source). Normalerweise setze ich diese Zeilen oben in mein Hauptskript, das für die "Benutzeroberfläche" bestimmt ist, von der aus High-Level-Funktionen (Aufrufen der Low-Level-Funktionen) aufgerufen werden.

if( length(grep("^myfuncs$",search()))==0 )
  attach("myfuncs",pos=2)
for( f in list.files("functions","\\.r$",full=TRUE) )
  sys.source(f,pos.to.env(grep("^myfuncs$",search())))

Wenn Sie Änderungen vornehmen, können Sie diese jederzeit mit denselben Zeilen erneut eingeben oder so etwas wie verwenden

evalq(f <- function(x) x * 2, pos.to.env(grep("^myfuncs$",search())))

um Ergänzungen/Änderungen in der von Ihnen erstellten Umgebung zu bewerten.

Ich weiß, dass es kludge ist, aber ich muss nicht zu formal sein (aber wenn Sie die Chance haben, unterstütze ich das Paketsystem - hoffentlich werde ich in Zukunft so migrieren).

Was die Kodierungskonventionen betrifft, ist dies das einzige, was ich in Bezug auf die Ästhetik gesehen habe (ich mag sie und folge ihnen locker, aber ich verwende nicht zu viele geschweifte Klammern in R):

http://www1.maths.lth.se/help/R/RCC/

Es gibt andere "Konventionen" bezüglich der Verwendung von [ drop = FALSE] und <- wie der Zuweisungsoperator in verschiedenen Präsentationen (normalerweise Keynote) bei useR! Konferenzen, aber ich halte keine davon für streng (obwohl das [ drop = FALSE] für Programme nützlich ist, bei denen Sie sich nicht sicher sind, welche Eingabe Sie erwarten).

11
hatmatrix

Zähle mich als eine andere Person für Pakete. Ich gebe zu, dass ich ziemlich schlecht darin bin, Manpages und Vignetten zu schreiben, bis ich es muss (dh freigelassen wird), aber es ist eine sehr praktische Möglichkeit, Source Doe zu bündeln. Wenn Sie es ernst meinen, Ihren Code zu pflegen, werden die Punkte, die Dirk anspricht, zu Plya.

6
geoffjentry

Ich stimme auch zu. Verwenden Sie zum Starten die Funktion package.skeleton (). Selbst wenn Sie der Meinung sind, dass Ihr Code möglicherweise nie wieder ausgeführt wird, kann dies Sie dazu motivieren, allgemeineren Code zu erstellen, der Ihnen später Zeit spart.

Der Zugriff auf die globale Umgebung ist mit dem Operator << - einfach, obwohl davon abgeraten wird.

4
cameron.bracken

Da ich noch nicht gelernt habe, Pakete zu schreiben, habe ich mich immer nach der Beschaffung von Unterskripten organisiert. Es ähnelt dem Schreiben von Klassen, ist aber nicht so involviert. Es ist nicht programmatisch elegant, aber ich stelle fest, dass ich im Laufe der Zeit Analysen aufbaue. Sobald ich einen großen Abschnitt habe, der funktioniert, verschiebe ich ihn oft in ein anderes Skript und gebe ihn einfach als Quelle aus, da die Arbeitsbereichsobjekte verwendet werden. Vielleicht muss ich Daten aus mehreren Quellen importieren, alle sortieren und die Schnittpunkte finden. Ich könnte diesen Abschnitt in ein zusätzliches Skript einfügen. Wenn Sie Ihre "Anwendung" jedoch für andere Personen verteilen möchten oder wenn eine interaktive Eingabe verwendet wird, ist ein Paket wahrscheinlich eine gute Route. Als Forscher muss ich selten meinen Analysecode verteilen, aber ich muss ihn oft erweitern oder optimieren.

3
kpierce8

Ich habe auch nach dem heiligen Gral des richtigen Workflows gesucht, um ein R-Großprojekt zusammenzustellen. Ich habe letztes Jahr dieses Paket mit dem Namen rsuite gefunden, und sicherlich war es das, wonach ich gesucht habe. Dieses R-Paket wurde explizit für die Bereitstellung großer R-Projekte entwickelt, aber ich fand heraus, dass es für kleinere, mittlere und große R-Projekte verwendet werden kann. Ich werde in einer Minute Links zu Beispielen aus der Praxis geben (siehe unten), aber zuerst möchte ich das neue Paradigma zum Erstellen von R-Projekten mit rsuite erläutern.

Hinweis. Ich bin nicht der Ersteller oder Entwickler von rsuite.

  1. Wir haben Projekte mit RStudio falsch gemacht. Das Ziel sollte nicht die Erstellung eines Projekts oder Pakets sein, sondern ein größerer Umfang. In rsuite erstellen Sie ein Super- oder Masterprojekt, das die Standard-R-Projekte und R-Pakete in allen möglichen Kombinationen enthält.

  2. Wenn Sie ein R-Superprojekt haben, brauchen Sie kein Unix make mehr, um die unteren Ebenen der darunterliegenden R-Projekte zu verwalten. Sie verwenden oben R-Skripte. Lass mich dir zeigen. Wenn Sie ein rsuite-Masterprojekt erstellen, erhalten Sie folgende Ordnerstruktur:

enter image description here

  1. In dem Ordner R legen Sie Ihre Projektmanagement-Skripte ab, die die make ersetzen.

  2. Der Ordner packages ist der Ordner, in dem rsuite alle Pakete enthält, aus denen das Superprojekt besteht. Sie können auch kopieren und ein Paket einfügen, auf das nicht über das Internet zugegriffen werden kann, und rsuite erstellt es ebenfalls.

  3. im Ordner deployment schreibt rsuite alle Paket-Binärdateien, die in den Paketdateien DESCRIPTION angegeben wurden. Sie projizieren also für sich genommen eine absolut reproduzierbare Zeit.

  4. rsuite wird mit einem Client für alle Betriebssysteme geliefert. Ich habe sie alle getestet. Sie können es aber auch als addin für RStudio installieren.

  5. Mit rsuite können Sie auch eine isolierte conda - Installation in einem eigenen Ordner conda erstellen. Dies ist keine Umgebung, sondern eine physische Python von Anaconda abgeleitete Installation auf Ihrem Computer. Dies funktioniert zusammen mit Rs SystemRequirements, von dem aus Sie alle Python Pakete, die Sie möchten, von jedem Conda-Kanal, den Sie möchten.

  6. Sie können auch lokale Repositorys erstellen, um R-Pakete abzurufen, wenn Sie offline sind, oder das Ganze schneller erstellen möchten.

  7. Wenn Sie möchten, können Sie das R-Projekt auch als Zip-Datei erstellen und für Kollegen freigeben. Es wird ausgeführt, sofern Ihre Kollegen dieselbe R-Version installiert haben.

  8. Eine andere Möglichkeit besteht darin, einen Container für das gesamte Projekt in Ubuntu, Debian oder CentOS zu erstellen. Anstatt also eine Zip-Datei für Ihren Projektbuild freizugeben, geben Sie den gesamten Container Docker für Ihr Projekt frei, damit es ausgeführt werden kann.

Ich habe viel mit rsuite experimentiert, um vollständige Reproduzierbarkeit zu erreichen, und es zu vermeiden, abhängig von den Paketen zu sein, die man in der globalen Umgebung installiert. Dies ist falsch, da das Projekt nach der Installation eines Paketupdates häufig nicht mehr funktioniert, insbesondere bei Paketen mit sehr spezifischen Aufrufen einer Funktion mit bestimmten Parametern.

Das erste, was ich zu experimentieren begann, war mit bookdown eBooks. Ich hatte noch nie das Glück, ein Buch zu haben, um den Test der Zeit länger als sechs Monate zu überstehen. Also habe ich das ursprüngliche Bookdown-Projekt konvertiert, um dem rsuite Framework zu folgen. Jetzt muss ich mich nicht mehr um die Aktualisierung meiner globalen R-Umgebung kümmern, da das Projekt einen eigenen Satz von Paketen im Ordner deployment hat.

Das nächste, was ich tat, war das Erstellen von maschinellen Lernprojekten, aber auf die rsuite Weise. Ein Master, Orchestrierungsprojekt an der Spitze und alle Unterprojekte und Pakete, die unter der Kontrolle des Masters stehen sollen. Es ändert wirklich die Art und Weise, wie Sie mit R codieren, und macht Sie produktiver.

Danach habe ich angefangen, in einem neuen Paket mit dem Namen rTorch zu arbeiten. Dies war größtenteils möglich, weil rsuite; es lässt dich denken und groß werden.

Ein Ratschlag. Das Erlernen von rsuite ist nicht einfach. Da es eine neue Art der Erstellung von R-Projekten darstellt, fühlt es sich schwierig an. Erschrecken Sie nicht bei den ersten Versuchen, steigen Sie weiter den Hang hinauf, bis Sie es geschafft haben. Es erfordert fortgeschrittene Kenntnisse Ihres Betriebssystems und Ihres Dateisystems.

Ich gehe davon aus, dass wir eines Tages mit RStudio Orchestrierungsprojekte wie mit rsuite über das Menü erstellen können. Es wäre großartig.

Links:

RSuite GitHUb repo

r4ds bookdown

Keras und glänzendes Tutorial

moderndive-book-rsuite

interpretable_ml-rsuite

IntroMachineLearningWithR-rsuite

clark-intro_ml-rsuite

hyndman-bookdown-rsuite

statistics_rethinking-rsuite

fread-benchmarks-rsuite

dataviz-rsuite

Retail-Segmentierung-H2O-Tutorial

telco-customer-churn-tutorial

sclerotinia_rsuite

1
f0nzie