Frage Wie man ein großartiges reproduzierbares Beispiel macht?


Wenn Sie mit Kollegen über die Leistung sprechen, unterrichten, einen Fehlerbericht senden oder in Mailinglisten und hier auf SO nach Anleitung suchen, a reproduzierbares Beispiel wird oft gefragt und immer hilfreich.

Was sind deine Tipps für ein hervorragendes Beispiel? Wie fügen Sie Datenstrukturen ein?  in einem Textformat? Welche anderen Informationen sollten Sie angeben?

Gibt es neben der Verwendung noch weitere Tricks? dput(), dump() oder structure()? Wann sollten Sie aufnehmen? library() oder require() Aussagen? Welche reservierten Wörter sollte man zusätzlich vermeiden? c, df, data, etc?

Wie macht man etwas Großartiges?  reproduzierbares Beispiel?


2373


Ursprung


Antworten:


Ein minimal reproduzierbares Beispiel besteht aus folgenden Elementen:

  • ein minimaler Datensatz, der notwendig ist, um den Fehler zu reproduzieren
  • das Minimale lauffähig Code erforderlich, um den Fehler zu reproduzieren, der auf dem angegebenen Dataset ausgeführt werden kann.
  • die notwendigen Informationen über die verwendeten Pakete, die R-Version und das System, auf dem es ausgeführt wird.
  • Bei zufälligen Prozessen wird ein Seed (gesetzt durch set.seed()) für die Reproduzierbarkeit

Es ist oft hilfreich, die Beispiele in den Hilfedateien der verwendeten Funktionen zu betrachten. Im Allgemeinen erfüllt der gesamte dort angegebene Code die Anforderungen eines minimal reproduzierbaren Beispiels: Daten werden bereitgestellt, minimaler Code wird bereitgestellt und alles ist ausführbar.

Erstellen eines minimalen Datasets

In den meisten Fällen kann dies einfach dadurch erreicht werden, dass ein Vektor / Datenrahmen mit einigen Werten bereitgestellt wird. Oder Sie können einen der integrierten Datensätze verwenden, die mit den meisten Paketen bereitgestellt werden.
Eine umfassende Liste von integrierten Datensätzen kann mit angezeigt werden library(help = "datasets"). Zu jedem Datensatz gibt es eine kurze Beschreibung und weitere Informationen erhalten Sie zum Beispiel mit ?mtcars wo 'mtcars' ist einer der Datensätze in der Liste. Andere Pakete können zusätzliche Datensätze enthalten.

Einen Vektor zu erstellen ist einfach. Manchmal ist es notwendig, etwas Zufall hinzuzufügen, und es gibt eine ganze Reihe von Funktionen, um das zu erreichen. sample() kann einen Vektor randomisieren oder einen Zufallsvektor mit nur wenigen Werten geben. letters ist ein nützlicher Vektor, der das Alphabet enthält. Dies kann zur Herstellung von Faktoren verwendet werden.

Ein paar Beispiele:

  • zufällige Werte: x <- rnorm(10) für die normale Verteilung, x <- runif(10) für eine gleichmäßige Verteilung, ...
  • eine Permutation einiger Werte: x <- sample(1:10) für den Vektor 1:10 in zufälliger Reihenfolge.
  • ein zufälliger Faktor: x <- sample(letters[1:4], 20, replace = TRUE)

Für Matrizen kann man verwenden matrix(), z.B :

matrix(1:10, ncol = 2)

Datenrahmen können mit gemacht werden data.frame(). Man sollte darauf achten, die Einträge im Datenrahmen zu benennen und nicht übermäßig kompliziert zu machen.

Ein Beispiel :

set.seed(1)
Data <- data.frame(
    X = sample(1:10),
    Y = sample(c("yes", "no"), 10, replace = TRUE)
)

Für einige Fragen können bestimmte Formate benötigt werden. Für diese kann man eines der bereitgestellten verwenden as.someType Funktionen: as.factor, as.Date, as.xts, ... Diese in Kombination mit den Vektor- und / oder Datenrahmentricks.

Kopieren Sie Ihre Daten

Wenn Sie Daten haben, die mit diesen Tipps zu schwierig zu konstruieren sind, können Sie immer eine Teilmenge Ihrer Originaldaten erstellen, z head(), subset()oder die Indizes. Dann benutze zB. dput() um uns etwas zu geben, das sofort in R gesetzt werden kann:

> dput(head(iris,4))
structure(list(Sepal.Length = c(5.1, 4.9, 4.7, 4.6), Sepal.Width = c(3.5, 
3, 3.2, 3.1), Petal.Length = c(1.4, 1.4, 1.3, 1.5), Petal.Width = c(0.2, 
0.2, 0.2, 0.2), Species = structure(c(1L, 1L, 1L, 1L), .Label = c("setosa", 
"versicolor", "virginica"), class = "factor")), .Names = c("Sepal.Length", 
"Sepal.Width", "Petal.Length", "Petal.Width", "Species"), row.names = c(NA, 
4L), class = "data.frame")

Wenn Ihr Datenrahmen einen Faktor mit vielen Ebenen hat, dput Die Ausgabe kann unhandlich sein, da sie immer noch alle möglichen Faktorstufen auflistet, auch wenn sie nicht in der Teilmenge Ihrer Daten enthalten sind. Um dieses Problem zu lösen, können Sie die droplevels() Funktion. Beachten Sie unten, wie Spezies ein Faktor mit nur einer Ebene ist:

> dput(droplevels(head(iris, 4)))
structure(list(Sepal.Length = c(5.1, 4.9, 4.7, 4.6), Sepal.Width = c(3.5, 
3, 3.2, 3.1), Petal.Length = c(1.4, 1.4, 1.3, 1.5), Petal.Width = c(0.2, 
0.2, 0.2, 0.2), Species = structure(c(1L, 1L, 1L, 1L), .Label = "setosa",
class = "factor")), .Names = c("Sepal.Length", "Sepal.Width", 
"Petal.Length", "Petal.Width", "Species"), row.names = c(NA, 
4L), class = "data.frame")

Eine andere Einschränkung für dput ist, dass es für keyed nicht funktioniert data.table Objekte oder für Gruppen tbl_df (Klasse grouped_df) von dplyr. In diesen Fällen können Sie vor dem Teilen in einen normalen Datenrahmen zurück konvertieren. dput(as.data.frame(my_data)).

Worst case Szenario, können Sie eine Textdarstellung geben, die mit dem eingelesen werden kann text Parameter von read.table :

zz <- "Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa"

Data <- read.table(text=zz, header = TRUE)

Minimaler Code erzeugen

Dies sollte der einfache Teil sein, ist es aber oft nicht. Was Sie nicht tun sollten, ist:

  • Fügen Sie alle Arten von Datenkonvertierungen hinzu. Stellen Sie sicher, dass die bereitgestellten Daten bereits im richtigen Format vorliegen (sofern das nicht das Problem ist)
  • Kopieren Sie eine ganze Funktion / einen Teil des Codes, der einen Fehler verursacht. Versuchen Sie zuerst herauszufinden, welche Zeilen genau zu dem Fehler führen. In den meisten Fällen werden Sie herausfinden, was das Problem ist.

Was Sie tun sollten, ist:

  • Fügen Sie hinzu, welche Pakete verwendet werden sollen, wenn Sie eines verwenden (mit library())
  • Wenn Sie Verbindungen oder Makefiles öffnen, fügen Sie Code hinzu, um sie zu schließen oder die Dateien zu löschen (mit unlink())
  • Wenn Sie die Optionen ändern, stellen Sie sicher, dass der Code eine Anweisung enthält, um sie auf die ursprünglichen zurückzusetzen. (z.B op <- par(mfrow=c(1,2)) ...some code... par(op) )
  • Testen Sie Ihren Code in einer neuen, leeren R-Sitzung, um sicherzustellen, dass der Code ausführbar ist. Die Leute sollten in der Lage sein, einfach Ihre Daten und Ihren Code in die Konsole zu kopieren und genau so zu erhalten, wie Sie es haben.

Geben Sie zusätzliche Informationen an

In den meisten Fällen reichen nur die R-Version und das Betriebssystem aus. Wenn Konflikte mit Paketen auftreten, geben Sie die Ausgabe von sessionInfo() kann wirklich helfen. Wenn man über Verbindungen zu anderen Anwendungen spricht (sei es über ODBC oder irgendetwas anderes), sollte man auch Versionsnummern für diese und, wenn möglich, auch die notwendigen Informationen zum Setup angeben.

Wenn Sie R in ausführen R Studio verwenden rstudioapi::versionInfo() kann hilfreich sein, um Ihre RStudio-Version zu melden.

Wenn Sie ein Problem mit einem bestimmten Paket haben, können Sie die Version des Pakets bereitstellen, indem Sie die Ausgabe von packageVersion("name of the package").


1450



(Hier ist mein Rat von Wie schreibe ich ein reproduzierbares Beispiel? . Ich habe versucht, es kurz zu machen, aber süß)

Wie schreibe ich ein reproduzierbares Beispiel?

Wenn Sie ein reproduzierbares Beispiel angeben, erhalten Sie am ehesten eine gute Hilfe für Ihr R-Problem. Ein reproduzierbares Beispiel ermöglicht es einem anderen Benutzer, Ihr Problem durch Kopieren und Einfügen von R-Code neu zu erstellen.

Es gibt vier Dinge, die Sie berücksichtigen müssen, um Ihr Beispiel reproduzierbar zu machen: benötigte Pakete, Daten, Code und eine Beschreibung Ihrer R-Umgebung.

  • Paketesollte am oberen Rand des Skripts geladen werden, so ist es einfach zu Schau, welche das Beispiel braucht.

  • Der einfachste Weg zum Einbinden Daten in einer E-Mail oder Stack Overflow Frage zu verwenden ist dput() generieren der R-Code, um es neu zu erstellen. Zum Beispiel, um das neu zu erstellen mtcars Datensatz in R, Ich würde die folgenden Schritte durchführen:

    1. Lauf dput(mtcars) in R
    2. Kopiere die Ausgabe
    3. In meinem reproduzierbaren Skript tippe mtcars <- dann einfügen.
  • Verbringen Sie ein wenig Zeit, um sicherzustellen, dass Ihre Code ist leicht für andere zu lesen:

    • Stellen Sie sicher, dass Sie Leerzeichen verwendet haben und Ihre Variablennamen knapp sind informativ

    • Verwenden Sie Kommentare, um anzugeben, wo Ihr Problem liegt

    • tun Sie Ihr Bestes, um alles zu entfernen, was nicht mit dem Problem zusammenhängt.
      Je kürzer der Code ist, desto einfacher ist es zu verstehen.

  • Fügen Sie die Ausgabe von ein sessionInfo() in einem Kommentar in Ihrem Code. Dies fasst Ihre zusammen R Umgebung und macht es einfach zu überprüfen, ob Sie ein veraltetes verwenden Paket.

Sie können überprüfen, ob Sie tatsächlich ein reproduzierbares Beispiel erstellt haben, indem Sie eine neue R-Sitzung starten und Ihr Skript einfügen.

Bevor Sie Ihren gesamten Code in eine E-Mail schreiben, ziehen Sie ihn in Betracht Gist Github . Es wird Ihrem Code eine schöne Syntax-Hervorhebung geben und Sie müssen sich keine Sorgen machen, dass irgendetwas vom E-Mail-System gestört wird.


514



Persönlich bevorzuge ich "One" Liner. Etwas in der Art:

my.df <- data.frame(col1 = sample(c(1,2), 10, replace = TRUE),
        col2 = as.factor(sample(10)), col3 = letters[1:10],
        col4 = sample(c(TRUE, FALSE), 10, replace = TRUE))
my.list <- list(list1 = my.df, list2 = my.df[3], list3 = letters)

Die Datenstruktur sollte die Idee des Problems des Schreibers und nicht die genaue wörtliche Struktur nachahmen. Ich schätze es sehr, wenn Variablen meine eigenen Variablen nicht überschreiben oder Gott verbieten, Funktionen (wie df).

Alternativ könnte man ein paar Ecken abschneiden und auf einen bereits vorhandenen Datensatz zeigen, etwa so:

library(vegan)
data(varespec)
ord <- metaMDS(varespec)

Vergessen Sie nicht, spezielle Pakete zu erwähnen, die Sie möglicherweise verwenden.

Wenn Sie etwas auf größeren Objekten demonstrieren möchten, können Sie es versuchen

my.df2 <- data.frame(a = sample(10e6), b = sample(letters, 10e6, replace = TRUE))

Wenn Sie mit räumlichen Daten über die raster Paket können Sie einige zufällige Daten generieren. Eine Menge Beispiele finden Sie in der Packungsvignette, aber hier ist ein kleines Nugget.

library(raster)
r1 <- r2 <- r3 <- raster(nrow=10, ncol=10)
values(r1) <- runif(ncell(r1))
values(r2) <- runif(ncell(r2))
values(r3) <- runif(ncell(r3))
s <- stack(r1, r2, r3)

Wenn Sie ein räumliches Objekt benötigen, wie es in implementiert ist sp, können Sie einige Datensätze über externe Dateien (wie ESRI-Shapefile) in "räumlichen" Paketen erhalten (siehe Räumliche Ansicht in Task-Ansichten).

library(rgdal)
ogrDrivers()
dsn <- system.file("vectors", package = "rgdal")[1]
ogrListLayers(dsn)
ogrInfo(dsn=dsn, layer="cities")
cities <- readOGR(dsn=dsn, layer="cities")

258



Inspiriert von diesem Post, benutze ich jetzt eine praktische Funktion
reproduce(<mydata>) wenn ich nach StackOverflow schreiben muss.


Kurze Anweisungen

Ob myData ist der Name Ihres Objekts, das reproduziert werden soll, führen Sie Folgendes in R aus:

install.packages("devtools")
library(devtools)
source_url("https://raw.github.com/rsaporta/pubR/gitbranch/reproduce.R")

reproduce(myData)

Einzelheiten:

Diese Funktion ist ein intelligenter Wrapper für dput und macht folgendes:

  • Automatisches Sampling eines großen Datensatzes (basierend auf Größe und Klasse. Die Stichprobengröße kann angepasst werden)
  • erstellt ein dput Ausgabe
  • können Sie angeben welche Spalten zum Exportieren
  • hängt an der Vorderseite davon objName <- ... so dass es leicht kopiert und eingefügt werden kann, aber ...
  • Wenn Sie auf einem Mac arbeiten, wird die Ausgabe automatisch in die Zwischenablage kopiert, sodass Sie sie einfach ausführen und dann in Ihre Frage einfügen können.

Die Quelle ist hier verfügbar:


Beispiel:

# sample data
DF <- data.frame(id=rep(LETTERS, each=4)[1:100], replicate(100, sample(1001, 100)), Class=sample(c("Yes", "No"), 100, TRUE))

DF ist ungefähr 100 x 102. Ich möchte 10 Reihen und einige spezifische Spalten probieren

reproduce(DF, cols=c("id", "X1", "X73", "Class"))  # I could also specify the column number. 

Gibt folgende Ausgabe:

This is what the sample looks like: 

    id  X1 X73 Class
1    A 266 960   Yes
2    A 373 315    No            Notice the selection split 
3    A 573 208    No           (which can be turned off)
4    A 907 850   Yes
5    B 202  46   Yes         
6    B 895 969   Yes   <~~~ 70 % of selection is from the top rows
7    B 940 928    No
98   Y 371 171   Yes          
99   Y 733 364   Yes   <~~~ 30 % of selection is from the bottom rows.  
100  Y 546 641    No        


    ==X==============================================================X==
         Copy+Paste this part. (If on a Mac, it is already copied!)
    ==X==============================================================X==

 DF <- structure(list(id = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 25L, 25L, 25L), .Label = c("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"), class = "factor"), X1 = c(266L, 373L, 573L, 907L, 202L, 895L, 940L, 371L, 733L, 546L), X73 = c(960L, 315L, 208L, 850L, 46L, 969L, 928L, 171L, 364L, 641L), Class = structure(c(2L, 1L, 1L, 2L, 2L, 2L, 1L, 2L, 2L, 1L), .Label = c("No", "Yes"), class = "factor")), .Names = c("id", "X1", "X73", "Class"), class = "data.frame", row.names = c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 98L, 99L, 100L)) 

    ==X==============================================================X==

Beachten Sie auch, dass die gesamte Ausgabe in einer schönen einzelnen, langen Zeile und nicht in einem hohen Absatz von Zeilen mit Zeilenumbrüchen vorliegt. Dies erleichtert das Lesen von Posts in SO-Fragen und das Kopieren und Einfügen.


Update Oktober 2013:

Sie können jetzt angeben, wie viele Textzeilen ausgegeben werden (dh was Sie in StackOverflow einfügen). Benutze die lines.out=n Argument dafür. Beispiel:

reproduce(DF, cols=c(1:3, 17, 23), lines.out=7) Erträge:

    ==X==============================================================X==
         Copy+Paste this part. (If on a Mac, it is already copied!)
    ==X==============================================================X==

 DF <- structure(list(id = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 25L,25L, 25L), .Label
      = c("A", "B", "C", "D", "E", "F", "G", "H","I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U","V", "W", "X", "Y"), class = "factor"),
      X1 = c(809L, 81L, 862L,747L, 224L, 721L, 310L, 53L, 853L, 642L),
      X2 = c(926L, 409L,825L, 702L, 803L, 63L, 319L, 941L, 598L, 830L),
      X16 = c(447L,164L, 8L, 775L, 471L, 196L, 30L, 420L, 47L, 327L),
      X22 = c(335L,164L, 503L, 407L, 662L, 139L, 111L, 721L, 340L, 178L)), .Names = c("id","X1",
      "X2", "X16", "X22"), class = "data.frame", row.names = c(1L,2L, 3L, 4L, 5L, 6L, 7L, 98L, 99L, 100L))

    ==X==============================================================X==

241



Hier ist eine gute Anleitung:

http://www.r-bloggers.com/three-tips-for-posting-good-questions-to-r-help-and-stack-overflow/

Aber das Wichtigste ist: Stellen Sie sicher, dass Sie einen kleinen Code machen, den wir ausführen können, um das Problem zu erkennen. Eine nützliche Funktion dafür ist dput(), aber wenn Sie sehr große Datenmengen haben, möchten Sie vielleicht einen kleinen Beispieldatensatz erstellen oder nur die ersten 10 Zeilen verwenden.

BEARBEITEN:

Stellen Sie außerdem sicher, dass Sie das Problem selbst identifiziert haben. Das Beispiel sollte kein komplettes R-Skript mit "Online 200 ist ein Fehler" sein. Wenn Sie die Debugging-Tools in R verwenden (ich liebe browser()) und Google sollten Sie in der Lage sein, wirklich zu identifizieren, wo das Problem ist und ein triviales Beispiel reproduzieren, in dem das gleiche Ding schief geht.


168



Die R-Hilfe-Mailingliste hat a Buchungsführer Dies umfasst sowohl das Stellen von Fragen als auch das Beantworten von Fragen, einschließlich eines Beispiels zum Generieren von Daten:

Beispiele: Manchmal hilft es   ein kleines Beispiel, dass jemand   kann tatsächlich laufen. Beispielsweise:

Wenn ich eine Matrix x wie folgt habe:

  > x <- matrix(1:8, nrow=4, ncol=2,
                dimnames=list(c("A","B","C","D"), c("x","y"))
  > x
    x y
  A 1 5
  B 2 6
  C 3 7
  D 4 8
  >

Wie kann ich daraus einen Datenrahmen machen?   mit 8 Zeilen und drei Spalten mit dem Namen   'row', 'col' und 'value', die haben   die Dimensionsnamen als die Werte von 'row' und 'col', wie folgt:

  > x.df
     row col value
  1    A   x      1

...
  (Auf die Antwort könnte lauten:

  > x.df <- reshape(data.frame(row=rownames(x), x), direction="long",
                    varying=list(colnames(x)), times=colnames(x),
                    v.names="value", timevar="col", idvar="row")

)

Das Wort klein ist besonders wichtig. Du solltest nach einem Ziel streben minimal reproduzierbares Beispiel, was bedeutet, dass die Daten und der Code so einfach wie möglich sein sollten, um das Problem zu erklären.

EDIT: Hübscher Code ist einfacher zu lesen als hässlicher Code. Benutze einen Gestaltungsrichtlinie.


142



Seit R.2.14 (ich denke) können Sie Ihre Datentextdarstellung direkt in read.table einspeisen:

df <- read.table(header=T, text="Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa
") 

136



Manchmal ist das Problem mit einem kleineren Teil der Daten nicht reproduzierbar, egal wie sehr Sie es versuchen und nicht mit synthetischen Daten (obwohl es nützlich ist, zu zeigen, wie Sie synthetische Datensätze erzeugt haben, die dies taten nicht reproduzieren das Problem, weil es einige Hypothesen ausschließt).

  • Es kann erforderlich sein, die Daten irgendwo im Web zu veröffentlichen und eine URL anzugeben.
  • Wenn die Daten nicht an die Öffentlichkeit freigegeben werden können, aber geteilt werden können, können Sie diese möglicherweise an interessierte Personen per E-Mail senden (obwohl dadurch die Anzahl der Personen verringert wird, die sich die Mühe machen, zu arbeiten) darauf).
  • Ich habe das nicht wirklich gesehen, weil Leute, die ihre Daten nicht veröffentlichen können, sensitiv sind, irgendeine Form freizugeben, aber es erscheint plausibel, dass man in manchen Fällen noch Daten posten könnte, wenn sie ausreichend anonymisiert / verschlüsselt sind irgendwie.

Wenn Sie beides nicht tun können, müssen Sie wahrscheinlich einen Berater engagieren, um Ihr Problem zu lösen ...

bearbeiten: Zwei nützliche SO Fragen zur Anonymisierung / Scrambling:


126



Die bisherigen Antworten sind offensichtlich für den Teil der Reproduzierbarkeit sehr gut. Dies soll lediglich verdeutlichen, dass ein reproduzierbares Beispiel nicht der einzige Bestandteil einer Frage sein kann und soll. Vergiss nicht zu erklären, wie es aussehen soll, und die Konturen deines Problems, nicht nur, wie du bisher versucht hast, dorthin zu gelangen. Code ist nicht genug; Du brauchst auch Worte.

Hier ist ein reproduzierbares Beispiel dafür, was zu vermeiden ist (aus einem realen Beispiel, Namen geändert, um die Unschuldigen zu schützen):


Das Folgende sind Beispieldaten und ein Teil der Funktion, mit der ich Probleme habe.

code
code
code
code
code (40 or so lines of it)

Wie kann ich das erreichen?



115



Um schnell ein dput Ihrer Daten können Sie einfach die Daten in die Zwischenablage kopieren und in R ausführen:

für Daten in Excel:

dput(read.table("clipboard",sep="\t",header=TRUE))

für Daten in einer TXT-Datei:

dput(read.table("clipboard",sep="",header=TRUE))

Sie können das ändern sep im letzteren Fall wenn nötig. Dies funktioniert nur, wenn sich Ihre Daten natürlich in der Zwischenablage befinden.


100



Ich habe eine sehr einfache und effiziente Möglichkeit, ein R-Beispiel zu erstellen, das oben nicht erwähnt wurde. Sie können Ihre Struktur zuerst definieren. Zum Beispiel

mydata <- data.frame(a=character(0), b=numeric(0),  c=numeric(0), d=numeric(0))

>fix(mydata)

when you execute 'fix' command,you will get this pop-up box 

Dann können Sie Ihre Daten manuell eingeben. Dies ist effizient für kleinere Beispiele und nicht für große.


95