Wie man ein großartiges reproduzierbares R-Beispiel macht


2473

Wenn Sie die Leistung mit Kollegen besprechen, unterrichten, einen Fehlerbericht senden oder nach Anleitungen zu Mailinglisten und hier zum Stapelüberlauf suchen, wird häufig ein reproduzierbares Beispiel gefragt und ist immer hilfreich.

Was sind Ihre Tipps, um ein hervorragendes Beispiel zu erstellen? Wie fügen Sie Datenstrukturen ein?in einem Textformat? Welche weiteren Informationen sollten Sie angeben?

Gibt es noch andere Tricks zusätzlich zur Verwendung dput(), dump()oder structure()? Wann sollten Sie library()oder require()Aussagen einschließen ? Welche reservierten Worte sollte man vermeiden, zusätzlich zu c, df, dataetc.?

Wie macht man einen tollen reproduzierbares Beispiel?


34
Ich bin verwirrt über den Umfang der Frage. Die Leute scheinen sich auf die Interpretation reproduzierbarer Beispiele gestürzt zu haben, indem sie Fragen zu SO oder R-Hilfe gestellt haben (wie man "den Fehler reproduziert"). Was ist mit reproduzierbaren R-Beispielen auf Hilfeseiten? In Paketdemos? In Tutorials / Präsentationen?
Taufe

15
@baptiste: Das gleiche abzüglich des Fehlers. Alle Techniken, die ich erklärt habe, werden in Pakethilfeseiten und in Tutorials und Präsentationen verwendet, die ich über R
Joris Meys

33
Die Daten sind manchmal der begrenzende Faktor, da die Struktur zu komplex sein kann, um sie zu simulieren. So erstellen Sie öffentliche Daten aus privaten Daten: stackoverflow.com/a/10458688/742447 in stackoverflow.com/questions/10454973/…
Etienne Low-Décarie

Antworten:


1727

Ein minimal reproduzierbares Beispiel besteht aus folgenden Elementen:

  • Ein minimaler Datensatz, der zur Demonstration des Problems erforderlich ist
  • Der minimale ausführbare Code, der zum Reproduzieren des Fehlers erforderlich ist und auf dem angegebenen Datensatz ausgeführt werden kann
  • die erforderlichen Informationen zu den verwendeten Paketen, der R-Version und dem System, auf dem es ausgeführt wird.
  • bei zufälligen Prozessen ein Keim (gesetzt von set.seed()) für die Reproduzierbarkeit 1

Beispiele für gute Beispiele mit minimaler Reproduzierbarkeit finden Sie in den Hilfedateien der von Ihnen verwendeten Funktion. Im Allgemeinen erfüllt der gesamte dort angegebene Code die Anforderungen eines minimal reproduzierbaren Beispiels: Daten werden bereitgestellt, minimaler Code wird bereitgestellt und alles kann ausgeführt werden. Schauen Sie sich auch Fragen zu Stack Overflow mit vielen positiven Stimmen an.

Erstellen eines minimalen Datensatzes

In den meisten Fällen kann dies einfach durch Bereitstellen eines Vektor- / Datenrahmens mit einigen Werten erfolgen. Oder Sie können eines der integrierten Datasets verwenden, die in den meisten Paketen enthalten sind.
Eine umfassende Liste der integrierten Datensätze finden Sie mit library(help = "datasets"). Zu jedem Datensatz gibt es eine kurze Beschreibung. Weitere Informationen erhalten Sie beispielsweise, ?mtcarswenn 'mtcars' einer der Datensätze in der Liste ist. Andere Pakete enthalten möglicherweise zusätzliche Datensätze.

Das Erstellen eines Vektors ist einfach. Manchmal ist es notwendig, etwas Zufälligkeit hinzuzufügen, und es gibt eine ganze Reihe von Funktionen, um dies zu erreichen. sample()kann einen Vektor randomisieren oder einen zufälligen Vektor mit nur wenigen Werten angeben. lettersist ein nützlicher Vektor, der das Alphabet enthält. Dies kann zur Herstellung von Faktoren verwendet werden.

Einige Beispiele:

  • Zufallswerte: x <- rnorm(10)für Normalverteilung, x <- runif(10)für Gleichverteilung, ...
  • eine Permutation einiger Werte: x <- sample(1:10)für Vektor 1:10 in zufälliger Reihenfolge.
  • ein zufälliger Faktor: x <- sample(letters[1:4], 20, replace = TRUE)

Für Matrizen kann man matrix()zB verwenden:

matrix(1:10, ncol = 2)

Das Erstellen von Datenrahmen kann mit erfolgen data.frame(). Man sollte darauf achten, die Einträge im Datenrahmen zu benennen und nicht zu 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 eine der vorgesehenen Verwendung as.someTypeFunktionen: as.factor, as.Date, as.xts, ... Diese in Kombination mit dem Vektor und / oder Datenrahmen Tricks.

Kopieren Sie Ihre Daten

Wenn Sie einige Daten, die zu schwierig sein würde , mit diesen Tipps zu konstruieren, dann können Sie immer einen Teil Ihrer ursprünglichen Daten machen, verwenden head(), subset()oder die Indizes. Verwenden Sie dann dput(), um uns etwas zu geben, das sofort in R eingefügt werden kann:

> dput(iris[1:4, ]) # first four rows of the iris data set
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 enthält, kann die dputAusgabe unhandlich sein, da weiterhin alle möglichen Faktorstufen aufgelistet werden, auch wenn sie nicht in der Teilmenge Ihrer Daten vorhanden sind. Um dieses Problem zu lösen, können Sie die droplevels()Funktion verwenden. Beachten Sie unten, wie Arten ein Faktor mit nur einer Ebene sind:

> dput(droplevels(iris[1: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")

Bei der Verwendung dputmöchten Sie möglicherweise auch nur relevante Spalten einschließen:

> dput(mtcars[1:3, c(2, 5, 6)]) # first three rows of columns 2, 5, and 6
structure(list(cyl = c(6, 6, 4), drat = c(3.9, 3.9, 3.85), wt = c(2.62, 
2.875, 2.32)), row.names = c("Mazda RX4", "Mazda RX4 Wag", "Datsun 710"
), class = "data.frame")

Eine weitere Einschränkung dputist, dass es nicht für verschlüsselte data.tableObjekte oder für gruppierte tbl_df(Klasse grouped_df) von funktioniert dplyr. In diesen Fällen können Sie vor der Freigabe wieder in einen regulären Datenrahmen konvertieren dput(as.data.frame(my_data)).

Im schlimmsten Fall können Sie eine Textdarstellung angeben, die mit den folgenden textParametern eingelesen werden kann 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)

Minimalen Code produzieren

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 (es sei denn, dies ist natürlich das Problem).
  • Kopieren und Einfügen einer ganzen Funktion / eines Codeabschnitts, der einen Fehler ausgibt. Versuchen Sie zunächst herauszufinden, welche Zeilen genau zum Fehler führen. Meistens werden Sie selbst herausfinden, wo das Problem liegt.

Was Sie tun sollten, ist:

  • fügen Sie hinzu, welche Pakete verwendet werden sollen, wenn Sie welche verwenden (using library())
  • Wenn Sie Verbindungen öffnen oder Dateien erstellen, fügen Sie Code hinzu, um sie zu schließen, oder löschen Sie die Dateien (mit unlink())
  • Wenn Sie Optionen ändern, stellen Sie sicher, dass der Code eine Anweisung enthält, um sie auf die ursprünglichen zurückzusetzen. (zB 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 Benutzer sollten in der Lage sein, Ihre Daten und Ihren Code einfach in die Konsole zu kopieren und einzufügen und genau das zu erhalten, was Sie haben.

Geben Sie zusätzliche Informationen

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

Wenn Sie R in R Studio mit rstudioapi::versionInfo()ausführen , kann es hilfreich sein, 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 angeben packageVersion("name of the package").


1 Hinweis: Die Ausgabe von set.seed()unterscheidet sich zwischen R> 3.6.0 und früheren Versionen. Geben Sie an, welche R-Version Sie für den Zufallsprozess verwendet haben, und wundern Sie sich nicht, wenn Sie bei der Beantwortung alter Fragen leicht unterschiedliche Ergebnisse erhalten. Um in solchen Fällen das gleiche Ergebnis zu erzielen, können Sie die RNGversion()-funktion vorher verwenden set.seed()(zB :) RNGversion("3.5.2").


6
Wie verwenden Sie, dputwenn der Datenrahmen sehr groß ist und das Problem in der Mitte des Datenrahmens generiert wird? Gibt es eine Möglichkeit, dputden mittleren Datenabschnitt zu reproduzieren, z. B. die Zeilen 60 bis 70?
BgnR

27
@BgnR Sie können einen Teil des Datenrahmens mithilfe von Indizes extrahieren, z . B.: tmp <- mydf[50:70,]Gefolgt von dput(mydf). Wenn der Datenrahmen wirklich groß ist, versuchen Sie, das Problem zu isolieren, und senden Sie einfach die wenigen Zeilen, die das Problem verursachen.
Joris Meys

4
@JorisMeys: Gibt es eine Möglichkeit, Daten rekursiv zu erkennen headoder dputauf Stufe N zu beschränken? Ich versuche, ein reproduzierbares Beispiel zu finden, und meine Daten sind eine Liste von Datenrahmen. Dies dput(head(myDataObj))scheint also nicht ausreichend zu sein, da eine Ausgabedatei mit einer Größe von 14 MB generiert wird.
Aleksandr Blekh

5
@JorisMeys: Nur zu Ihrer Information - Frage im Kommentar oben als separate Frage gepostet : stackoverflow.com/questions/25127026/… .
Aleksandr Blekh

4
@Konrad Das Beste, was Sie tun können, ist, eine Verknüpfung zur Datei herzustellen und den minimalen Befehl zum Einlesen dieser Datei anzugeben. Das ist weniger mühsam als das Einfügen der Ausgabe von dput () :)
Joris Meys

590

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

Wie schreibe ich ein reproduzierbares Beispiel?

Sie erhalten höchstwahrscheinlich gute Hilfe bei Ihrem R-Problem, wenn Sie ein reproduzierbares Beispiel angeben. Ein reproduzierbares Beispiel ermöglicht es einer anderen Person, Ihr Problem durch einfaches Kopieren und Einfügen von R-Code neu zu erstellen.

Es gibt vier Dinge, die Sie einschließen müssen, um Ihr Beispiel reproduzierbar zu machen: erforderliche Pakete, Daten, Code und eine Beschreibung Ihrer R-Umgebung.

  • Pakete sollten oben im Skript geladen werden, damit Sie leicht erkennen können, welche im Beispiel benötigt werden.

  • Der einfachste Weg, Daten in eine E-Mail- oder Stapelüberlauffrage aufzunehmen, besteht darin dput(), den R-Code zu generieren, um ihn neu zu erstellen. Um beispielsweise das mtcarsDataset in R neu zu erstellen , führen Sie die folgenden Schritte aus:

    1. Laufen Sie dput(mtcars)in R.
    2. Kopieren Sie die Ausgabe
    3. Geben Sie in mein reproduzierbares Skript ein und mtcars <-fügen Sie es ein.
  • Nehmen Sie sich etwas Zeit, um sicherzustellen, dass Ihr Code für andere leicht lesbar ist:

    • Stellen Sie sicher, dass Sie Leerzeichen verwendet haben und Ihre Variablennamen präzise, ​​aber informativ sind

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

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

  • Fügen Sie die Ausgabe von sessionInfo()in einen Kommentar in Ihren Code ein. Dies fasst Ihre R-Umgebung zusammen und erleichtert die Überprüfung, ob Sie ein veraltetes Paket verwenden.

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 einfügen , sollten Sie ihn auf Gist Github ablegen . Dadurch wird Ihrem Code eine schöne Syntaxhervorhebung verliehen, und Sie müssen sich keine Sorgen machen, dass das E-Mail-System etwas beschädigt.


24
reprexin tidyverseist ein gutes Paket für die Herstellung von minimalen, reproduzierbaren Beispiel: github.com/tidyverse/reprex
mt1022

19
Ich erhalte regelmäßig E-Mails mit Code. Ich erhalte sogar E-Mails mit angehängten Word-Dokumenten, die Code enthalten. Manchmal bekomme ich sogar E-Mails mit angehängten Word-Dokumenten, die SCREENSHOTS-Code enthalten.
Hadley

304

Persönlich bevorzuge ich "Ein" -Liner. Etwas in der Richtung:

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 Autorenproblems imitieren und nicht die genaue wörtliche Struktur. Ich weiß es wirklich zu schätzen, wenn Variablen meine eigenen Variablen nicht überschreiben oder Gott verbietet, Funktionen (wie df).

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

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

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

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

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

Wenn Sie über das rasterPaket mit räumlichen Daten arbeiten , können Sie zufällige Daten generieren. Viele Beispiele finden Sie in der Paketvignette, 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 abrufen (siehe Raumansicht in Aufgabenansichten).

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

1
IMHO, wenn mit sampleoder runifes ist umsichtig set.seed. Zumindest ist dies der Vorschlag, den ich erhalten habe, als ich Beispiele für Stichproben oder Zufallszahlengenerierung erstellt habe.
Konrad

1
@Konrad Ich stimme zu, aber das kann davon abhängen. Wenn Sie nur versuchen, einige Zahlen zu generieren, wird möglicherweise kein Startwert benötigt. Wenn Sie jedoch versuchen, etwas Bestimmtes zu verstehen, bei dem feste Zahlen benötigt werden, ist ein Startwert obligatorisch.
Roman Luštrik

1
Mit einem Seed-Imo ist es immer besser, es macht es einfacher, die eigene Lösung mit der erwarteten Ausgabe zu vergleichen, Lösungen untereinander zu vergleichen, und auf diese Weise können Benutzer, die Funktionen nicht kennen (und nicht kennen müssen), Funktionen mögen runifoder samplenicht verwirrt sein dass sie nicht die gleichen Daten bekommen können.
Moody_Mudskipper

2
@mikey hast du dir das usmap paket angesehen ?
Roman Luštrik

2
@ Mikey das Paket Tigris lädt Shapefiles vom Census Bureau in einer Vielzahl von Formaten herunter
Camille

277

Inspiriert von diesem Beitrag verwende ich jetzt eine praktische Funktion,
reproduce(<mydata>)wenn ich in StackOverflow posten muss.


SCHNELLE ANWEISUNGEN

Wenn myDatader Name Ihres zu reproduzierenden Objekts lautet, führen Sie in R Folgendes 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 dputund führt Folgendes aus:

  • tastet automatisch einen großen Datensatz ab (basierend auf Größe und Klasse. Die Stichprobengröße kann angepasst werden).
  • erstellt eine dputAusgabe
  • Hier können Sie angeben, welche Spalten exportiert werden sollen
  • wird an die Vorderseite angehängt, objName <- ...damit 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 finden Sie hier:


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 Zeilen und einige spezifische Spalten abtasten

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

Gibt die 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 sich die gesamte Ausgabe in einer schönen, langen Zeile befindet, nicht in einem hohen Absatz aus zerhackten Zeilen. Dies erleichtert das Lesen von SO-Fragen und das Kopieren und Einfügen.


Update Okt 2013:

Sie können jetzt angeben, wie viele Zeilen Textausgabe benötigt werden (dh was Sie in StackOverflow einfügen). Verwenden Sie dazu das lines.out=nArgument. Beispiel:

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

    ==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==

196

Hier ist eine gute Anleitung .

Der wichtigste Punkt ist: Stellen Sie einfach sicher, dass Sie einen kleinen Code erstellen, den wir ausführen können, um das Problem zu ermitteln . Eine nützliche Funktion hierfür ist dput(), aber wenn Sie sehr große Datenmengen haben, möchten Sie möglicherweise einen kleinen Beispieldatensatz erstellen oder nur die ersten 10 Zeilen oder so verwenden.

BEARBEITEN:

Stellen Sie außerdem sicher, dass Sie selbst festgestellt haben, wo das Problem liegt. Das Beispiel sollte kein vollständiges R-Skript mit "In Zeile 200 liegt ein Fehler vor" sein. Wenn Sie die Debugging-Tools in R (ich liebe browser()) und Google verwenden, sollten Sie in der Lage sein, das Problem wirklich zu identifizieren und ein triviales Beispiel zu reproduzieren, in dem dasselbe schief geht.


165

Die R-Help-Mailingliste enthält einen Posting-Leitfaden, der sowohl das Stellen als auch das Beantworten von Fragen umfasst, einschließlich eines Beispiels zum Generieren von Daten:

Beispiele: Manchmal hilft es, ein kleines Beispiel anzugeben, das jemand tatsächlich ausführen kann. Zum Beispiel:

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 mit 8 Zeilen und drei Spalten mit den Namen 'row', 'col' und 'value' machen, deren Dimensionsnamen die Werte von 'row' und 'col' haben?

  > x.df
     row col value
  1    A   x      1

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

  > 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. Sie sollten ein minimal reproduzierbares Beispiel anstreben , was bedeutet, dass die Daten und der Code so einfach wie möglich sein sollten, um das Problem zu erklären.

BEARBEITEN: Hübscher Code ist leichter zu lesen als hässlicher Code. Verwenden Sie einen Styleguide .


164

Seit R.2.14 (ich denke) können Sie Ihre Datentextdarstellung direkt an Folgendes weitergeben read.table:

 df <- read.table(header=TRUE, 
  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
") 

3
@ sebastian-c wie ist das gut um reproduzierbare beispiele zu machen ?? :)
TMS

@TMS Wenn der Fragesteller die Daten angegeben hat und das Problem klein ist (aber möglicherweise einige Lösungen hat), ist es möglicherweise schneller und Sie können trotzdem alle Schritte ausführen.
Sebastian-c

146

Manchmal ist das Problem mit kleineren Daten wirklich nicht reproduzierbar, egal wie sehr Sie es versuchen, und es tritt nicht mit synthetischen Daten auf (obwohl es nützlich ist zu zeigen, wie Sie synthetische Datensätze erstellt haben, die das Problem nicht reproduzierten, weil es schließt einige Hypothesen aus).

  • Möglicherweise müssen Sie die Daten irgendwo im Internet veröffentlichen und eine URL angeben.
  • Wenn die Daten nicht öffentlich zugänglich gemacht werden können, sondern überhaupt geteilt werden können, können Sie möglicherweise anbieten, sie per E-Mail an interessierte Parteien zu senden (obwohl dies die Anzahl der Personen verringert, die sich die Mühe machen, zu arbeiten darauf).
  • Ich habe dies nicht wirklich gesehen, weil Leute, die ihre Daten nicht freigeben können, sensibel dafür sind, sie in irgendeiner Form freizugeben, aber es erscheint plausibel, dass man in einigen Fällen immer noch Daten veröffentlichen könnte, wenn sie ausreichend anonymisiert / verschlüsselt / leicht beschädigt wären irgendwie.

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

edit : Zwei nützliche SO-Fragen zur Anonymisierung / Verschlüsselung:


1
Für die Erstellung synthetischer Datensätze geben die Antworten auf diese Frage nützliche Beispiele, einschließlich Anwendungen von fitdistrund fitdistrplus.
Iterator

137

Die bisherigen Antworten sind offensichtlich gut für den Teil der Reproduzierbarkeit. Dies dient lediglich der Klarstellung, dass ein reproduzierbares Beispiel nicht der einzige Bestandteil einer Frage sein kann und sollte. Vergessen Sie nicht zu erklären, wie es aussehen soll und welche Konturen Ihr Problem aufweist, und nicht nur, wie Sie bisher versucht haben, dorthin zu gelangen. Code ist nicht genug; Du brauchst auch Worte.

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


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

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

Wie kann ich das erreichen?



124

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

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

>fix(mydata)

Wenn Sie den Befehl 'fix' ausführen, wird dieses Popup-Fenster angezeigt

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


18
... danndput(mydata)
GSee

Was ist dein Frontend? Es wäre schön, eine vollständige Antwort zu haben. Usw. machen Sie Daten, die Sie direkt schleifen können for (d in data) {...}.
Léo Léopold Hertz 준영

119

So erstellen Sie schnell eine dput Daten Sie die Daten einfach in Ihre Zwischenablage kopieren und in R Folgendes 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 sepdiese bei Bedarf ändern . Dies funktioniert natürlich nur, wenn sich Ihre Daten in der Zwischenablage befinden.


116

Richtlinien:


Ihr Hauptziel bei der Erstellung Ihrer Fragen sollte es sein, den Lesern das Verständnis und die Reproduktion Ihres Problems auf ihren Systemen so einfach wie möglich zu machen. Um dies zu tun:

  1. Geben Sie Eingabedaten ein
  2. Stellen Sie die erwartete Ausgabe bereit
  3. Erklären Sie Ihr Problem kurz und bündig
    • Wenn Sie mehr als 20 Zeilen Text + Code haben, können Sie wahrscheinlich zurückgehen und vereinfachen
    • Vereinfachen Sie Ihren Code so weit wie möglich, während Sie das Problem / den Fehler beibehalten

Dies erfordert zwar einige Arbeit, scheint jedoch ein fairer Kompromiss zu sein, da Sie andere bitten, für Sie zu arbeiten.

Daten bereitstellen:


Eingebaute Datensätze

Die mit Abstand beste Option besteht darin, sich auf integrierte Datensätze zu verlassen. Dies macht es für andere sehr einfach, an Ihrem Problem zu arbeiten. Geben Sie data()an der Eingabeaufforderung R ein, um zu sehen, welche Daten für Sie verfügbar sind. Einige klassische Beispiele:

  • iris
  • mtcars
  • ggplot2::diamonds (externes Paket, aber fast jeder hat es)

In dieser SO-Qualitätssicherung erfahren Sie, wie Sie Datensätze finden, die für Ihr Problem geeignet sind.

Wenn Sie Ihr Problem umformulieren können, um die integrierten Datensätze zu verwenden, erhalten Sie mit größerer Wahrscheinlichkeit gute Antworten (und positive Stimmen).

Selbst generierte Daten

Wenn Ihr Problem sehr spezifisch für einen Datentyp ist, der nicht in den vorhandenen Datensätzen enthalten ist, geben Sie den R-Code an, der den kleinstmöglichen Datensatz generiert, in dem sich Ihr Problem manifestiert. Zum Beispiel

set.seed(1)  # important to make random data reproducible
myData <- data.frame(a=sample(letters[1:5], 20, rep=T), b=runif(20))

Jetzt kann jemand, der versucht, meine Frage zu beantworten, diese beiden Zeilen kopieren / einfügen und sofort mit der Arbeit an dem Problem beginnen.

dput

Als letzten Ausweg können Sie dputein Datenobjekt in R-Code umwandeln (z dput(myData). B. ). Ich sage als "letzter Ausweg", weil die Ausgabe von dputoft ziemlich unhandlich ist, beim Kopieren und Einfügen ärgerlich ist und den Rest Ihrer Frage verdeckt.

Erwartete Ausgabe bereitstellen:


Jemand hat einmal gesagt:

Ein Bild der erwarteten Ausgabe sagt mehr als 1000 Worte

- eine sehr weise Person

Wenn Sie etwas wie "Ich habe erwartet, dass dieses Ergebnis erzielt wird" hinzufügen können:

   cyl   mean.hp
1:   6 122.28571
2:   4  82.63636
3:   8 209.21429

Bei Ihrer Frage verstehen die Leute viel eher schnell, was Sie versuchen zu tun. Wenn Ihr erwartetes Ergebnis groß und unhandlich ist, haben Sie wahrscheinlich nicht genug darüber nachgedacht, wie Sie Ihr Problem vereinfachen können (siehe weiter unten).

Erklären Sie Ihr Problem kurz und bündig


Die Hauptsache ist, Ihr Problem so weit wie möglich zu vereinfachen, bevor Sie Ihre Frage stellen. Die Neuformulierung des Problems, um mit den integrierten Datensätzen zu arbeiten, wird in dieser Hinsicht sehr hilfreich sein. Sie werden auch oft feststellen, dass Sie Ihr eigenes Problem beantworten, indem Sie den Vereinfachungsprozess durchlaufen.

Hier einige Beispiele für gute Fragen:

In beiden Fällen liegen die Probleme des Benutzers mit ziemlicher Sicherheit nicht in den einfachen Beispielen, die sie liefern. Vielmehr abstrahierten sie die Natur ihres Problems und wandten es auf einen einfachen Datensatz an, um ihre Frage zu stellen.

Warum noch eine Antwort auf diese Frage?


Diese Antwort konzentriert sich auf das, was ich für die beste Vorgehensweise halte: Verwenden Sie integrierte Datensätze und geben Sie das, was Sie als Ergebnis erwarten, in minimaler Form an. Die wichtigsten Antworten konzentrieren sich auf andere Aspekte. Ich erwarte nicht, dass diese Antwort an Bedeutung gewinnt. Dies ist nur hier, damit ich in Kommentaren zu Fragen von Neulingen darauf verlinken kann.


113

Reproduzierbarer Code ist der Schlüssel, um Hilfe zu erhalten. Es gibt jedoch viele Benutzer, die skeptisch sind, auch nur einen Teil ihrer Daten einzufügen. Zum Beispiel könnten sie mit sensiblen Daten oder mit Originaldaten arbeiten, die zur Verwendung in einem Forschungsbericht gesammelt wurden. Aus irgendeinem Grund dachte ich, es wäre schön, eine praktische Funktion zum "Verformen" meiner Daten zu haben, bevor sie öffentlich eingefügt werden. Die anonymizeFunktion aus dem Paket SciencesPoist sehr albern, aber für mich funktioniert es gut mit der dputFunktion.

install.packages("SciencesPo")

dt <- data.frame(
    Z = sample(LETTERS,10),
    X = sample(1:10),
    Y = sample(c("yes", "no"), 10, replace = TRUE)
)

> dt
   Z  X   Y
1  D  8  no
2  T  1 yes
3  J  7  no
4  K  6  no
5  U  2  no
6  A 10 yes
7  Y  5  no
8  M  9 yes
9  X  4 yes
10 Z  3  no

Dann anonymisiere ich es:

> anonymize(dt)
     Z    X  Y
1   b2  2.5 c1
2   b6 -4.5 c2
3   b3  1.5 c1
4   b4  0.5 c1
5   b7 -3.5 c1
6   b1  4.5 c2
7   b9 -0.5 c1
8   b5  3.5 c2
9   b8 -1.5 c2
10 b10 -2.5 c1

Möglicherweise möchten Sie auch einige Variablen anstelle der gesamten Daten abtasten, bevor Sie den Befehl Anonymisierung und dput anwenden.

    # sample two variables without replacement
> anonymize(sample.df(dt,5,vars=c("Y","X")))
   Y    X
1 a1 -0.4
2 a1  0.6
3 a2 -2.4
4 a1 -1.4
5 a2  3.6

102

Oft benötigen Sie einige Daten für ein Beispiel, möchten jedoch nicht Ihre genauen Daten veröffentlichen. Um einen vorhandenen data.frame in einer etablierten Bibliothek zu verwenden, importieren Sie ihn mit dem Befehl data.

z.B,

data(mtcars)

und dann das Problem machen

names(mtcars)
your problem demostrated on the mtcars data set

13
Viele integrierte Datensätze (wie beliebte mtcarsund irisDatensätze) benötigen den dataAufruf nicht, um verwendet zu werden.
Gregor Thomas

92

Wenn Sie einen großen Datensatz haben, der nicht einfach mit dem Skript erstellt werden kann dput(), senden Sie Ihre Daten an den Pastebin und laden Sie sie mit read.table:

d <- read.table("http://pastebin.com/raw.php?i=m1ZJuKLH")

Inspiriert von @Henrik .


90

Ich entwickle das Wakefield- Paket , um dieses Bedürfnis zu befriedigen , manchmal schnell reproduzierbare Daten auszutauschendput Austausch funktioniert es gut für kleinere Datensätze, aber viele der Probleme, mit denen wir uns befassen, sind viel größer. Das Teilen eines so großen Datensatzes über dputist unpraktisch.

Über:

Wakefield kann der Benutzer nur minimalen Code zur Reproduktion von Daten . Der Benutzer legt n(Anzahl der Zeilen) fest und gibt eine beliebige Anzahl von voreingestellten Variablenfunktionen an (derzeit sind es 70), die reale Daten nachahmen (z. B. Geschlecht, Alter, Einkommen usw.).

Installation:

Derzeit (2015.06.11), wakefield ist ein Paket GitHub aber zu CRAN geht schließlich nach Unit - Tests geschrieben werden. Verwenden Sie zur schnellen Installation Folgendes:

if (!require("pacman")) install.packages("pacman")
pacman::p_load_gh("trinker/wakefield")

Beispiel:

Hier ist ein Beispiel:

r_data_frame(
    n = 500,
    id,
    race,
    age,
    sex,
    hour,
    iq,
    height,
    died
)

Dies erzeugt:

    ID  Race Age    Sex     Hour  IQ Height  Died
1  001 White  33   Male 00:00:00 104     74  TRUE
2  002 White  24   Male 00:00:00  78     69 FALSE
3  003 Asian  34 Female 00:00:00 113     66  TRUE
4  004 White  22   Male 00:00:00 124     73  TRUE
5  005 White  25 Female 00:00:00  95     72  TRUE
6  006 White  26 Female 00:00:00 104     69  TRUE
7  007 Black  30 Female 00:00:00 111     71 FALSE
8  008 Black  29 Female 00:00:00 100     64  TRUE
9  009 Asian  25   Male 00:30:00 106     70 FALSE
10 010 White  27   Male 00:30:00 121     68 FALSE
.. ...   ... ...    ...      ... ...    ...   ...

72

Wenn Sie ein oder mehr factorVariable (n) in Ihren Daten , die Sie mit reproduzierbarer machen wollen dput(head(mydata)), sollten Sie erwägen , droplevelsum es, so dass das Niveau der Faktoren , die nicht in der minimierten Datensatz nicht in Ihrem enthalten ist , dputausgegeben, um zu Machen Sie das Beispiel minimal :

dput(droplevels(head(mydata)))

65

Ich frage mich, ob ein http://old.r-fiddle.org/-Link eine sehr gute Möglichkeit sein könnte, ein Problem zu teilen. Es erhält eine eindeutige ID wie und man könnte sogar darüber nachdenken, es in SO einzubetten.


47

Bitte fügen Sie Ihre Konsolenausgaben nicht wie folgt ein:

If I have a matrix x as follows:
> 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
>

How can I turn it into a dataframe with 8 rows, and three
columns named `row`, `col`, and `value`, which have the
dimension names as the values of `row` and `col`, like this:
> x.df
    row col value
1    A   x      1
...
(To which the answer might be:
> 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")
)

Wir können es nicht direkt kopieren und einfügen.

Um Fragen und Antworten richtig reproduzierbar zu machen, versuchen Sie, +& zu entfernen, >bevor Sie sie veröffentlichen, und geben Sie folgende #Ausgaben und Kommentare ein:

#If I have a matrix x as follows:
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

# How can I turn it into a dataframe with 8 rows, and three
# columns named `row`, `col`, and `value`, which have the
# dimension names as the values of `row` and `col`, like this:

#x.df
#    row col value
#1    A   x      1
#...
#To which the answer might be:

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")

Wenn Sie eine Funktion aus einem bestimmten Paket verwendet haben, erwähnen Sie diese Bibliothek.


2
Entfernen Sie das >und fügen Sie es #manuell hinzu oder gibt es eine automatische Möglichkeit, dies zu tun?
BCArg

3
@BCArg Ich entferne >manuell. Aber zusätzlich #benutze ich die Ctrl+Shift+CVerknüpfung im RStudioEditor.
user2100721

33

Sie können dies mit reprex tun .

Wie mt1022 feststellte , ist "... ein gutes Paket zur Herstellung eines minimalen, reproduzierbaren Beispiels " reprex " von tidyverse ".

Laut Tidyverse :

Das Ziel von "reprex" ist es, Ihren problematischen Code so zu verpacken, dass andere ihn ausführen und Ihren Schmerz spüren können.

Ein Beispiel finden Sie auf der tidyverse -Website.

library(reprex)
y <- 1:4
mean(y)
reprex() 

Ich denke, dies ist der einfachste Weg , um ein reproduzierbares Beispiel zu erstellen.


33

Abgesehen von allen oben genannten Antworten, die ich sehr interessant fand, könnte es manchmal sehr einfach sein, wie hier diskutiert wird: WIE MAN EIN MINDESTREPRODUZIERBARES BEISPIEL MACHT, UM HILFE BEI ​​R ZU ERHALTEN

Es gibt viele Möglichkeiten, einen Zufallsvektor zu erstellen. Erstellen Sie einen 100-Zahlen-Vektor mit Zufallswerten in R, gerundet auf 2 Dezimalstellen oder einer Zufallsmatrix in R.

mydf1<- matrix(rnorm(20),nrow=20,ncol=5)

Beachten Sie, dass es manchmal aus verschiedenen Gründen wie Dimension usw. sehr schwierig ist, bestimmte Daten gemeinsam zu nutzen. Alle oben genannten Antworten sind jedoch großartig und sehr wichtig zu denken und zu verwenden, wenn ein Beispiel für reproduzierbare Daten erstellt werden soll. Beachten Sie jedoch, dass es gut ist, dem Datenbeispiel einige Informationen hinzuzufügen, um Daten so repräsentativ wie das Original zu machen (falls das OP die Originaldaten nicht gemeinsam nutzen kann) (wenn wir die Daten mydf1 nennen).

class(mydf1)
# this shows the type of the data you have 
dim(mydf1)
# this shows the dimension of your data

Darüber hinaus sollte man den Typ, die Länge und die Attribute von Daten kennen, bei denen es sich um Datenstrukturen handeln kann

#found based on the following 
typeof(mydf1), what it is.
length(mydf1), how many elements it contains.
attributes(mydf1), additional arbitrary metadata.

#If you cannot share your original data, you can str it and give an idea about the structure of your data
head(str(mydf1))

28

Hier sind einige meiner Vorschläge:

  • Versuchen Sie, Standard-R-Datasets zu verwenden
  • Wenn Sie einen eigenen Datensatz haben, fügen Sie diese hinzu dput , damit andere Ihnen leichter helfen können
  • Nicht verwenden, es install.package()sei denn, es ist wirklich notwendig, die Leute werden verstehen, wenn Sie nur requireoder verwendenlibrary
  • Versuche prägnant zu sein,

    • Haben Sie einen Datensatz
    • Versuchen Sie, die Ausgabe, die Sie benötigen, so einfach wie möglich zu beschreiben
    • Mach es selbst, bevor du die Frage stellst
  • Es ist einfach, ein Bild hochzuladen. Laden Sie also Diagramme hoch, wenn Sie haben
  • Fügen Sie auch eventuelle Fehler hinzu

All dies ist Teil eines reproduzierbaren Beispiels.


1
Sie haben hier nichts Substanzielles hinzugefügt. dput()wurde bereits erwähnt, und vieles davon wiederholt nur die Standard-SO-Richtlinien.
Rich Scriven

1
Ich hatte ein Problem mit der install.packageFunktion im Beispiel, das (meiner Meinung nach) nicht wirklich notwendig ist. Darüber hinaus würde die Verwendung des Standard-R-Datensatzes die Reproduzierbarkeit erleichtern. Die SO-Richtlinien haben zu diesen Themen nichts spezielles gesagt. Außerdem sollte es meine Meinung abgeben, und diese sind die, denen ich am meisten begegnet bin.
TheRimalaya

18

Es ist eine gute Idee, Funktionen aus dem testthatPaket zu verwenden, um zu zeigen, was Sie erwarten. Auf diese Weise können andere Personen Ihren Code ändern, bis er fehlerfrei ausgeführt wird. Dies entlastet diejenigen, die Ihnen helfen möchten, da sie Ihre Textbeschreibung nicht entschlüsseln müssen. Zum Beispiel

library(testthat)
# code defining x and y
if (y >= 10) {
    expect_equal(x, 1.23)
} else {
    expect_equal(x, 3.21)
}

ist klarer als "Ich denke, x würde 1,23 für y gleich oder größer als 10 und 3,21 sonst ergeben, aber ich habe kein Ergebnis erhalten". Selbst in diesem albernen Beispiel denke ich, dass der Code klarer ist als die Wörter. Mithilfe dieser Funktion kann sich testthatIhr Helfer auf den Code konzentrieren, was Zeit spart. Auf diese Weise können sie erkennen, dass sie Ihr Problem gelöst haben, bevor sie es veröffentlichen

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.