Wie man ein großartiges reproduzierbares R-Beispiel macht
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üge ich Datenstrukturen aus r in ein Textformat ein? 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
, data
etc.?
Wie macht man ein gutes reproduzierbares Beispiel?
Antworten
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 für das angegebene Dataset 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, ?mtcars
wenn '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. letters
ist 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.someType
Funktionen: 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 dput
Ausgabe 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 dput
mö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 dput
ist, dass es nicht für verschlüsselte data.table
Objekte oder für gruppierte tbl_df
(Klassen 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 text
Parametern 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")
.
(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?
Es ist am wahrscheinlichsten, dass Sie bei Ihrem R-Problem gute Hilfe erhalten, 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 dasmtcars
Dataset in R neu zu erstellen , führen Sie die folgenden Schritte aus:- Laufen Sie
dput(mtcars)
in R. - Kopieren Sie die Ausgabe
- Geben Sie in mein reproduzierbares Skript ein und
mtcars <-
fügen Sie es ein.
- Laufen Sie
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.
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 raster
Paket 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")
Inspiriert von diesem Beitrag verwende ich jetzt eine praktische Funktion,
reproduce(<mydata>)
wenn ich in StackOverflow posten muss.
SCHNELLE ANWEISUNGEN
Wenn myData
der 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 dput
und 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
dput
Ausgabe - 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:
- Github - pubR / reproduce.R
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=n
Argument. 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==
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.
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 ist es hilfreich, 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
...
(auf die 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 .
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
")
Manchmal ist das Problem mit einem kleineren Datenelement 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 reproduziert haben, 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 weitergegeben 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 veröffentlichen 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:
- Wie erstelle ich einen Beispieldatensatz aus privaten Daten (Ersetzen von Variablennamen und -ebenen durch nicht informative Platzhalter)?
- Finden Sie die Verteilung anhand einer Reihe von Zufallszahlen, die aus einer kontinuierlichen univariaten Verteilung gezogen wurden
Die bisherigen Antworten sind offensichtlich großartig für den Teil der Reproduzierbarkeit. Dies dient lediglich der Klarstellung, dass ein reproduzierbares Beispiel nicht der alleinige 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 die 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?
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)
Dann können Sie Ihre Daten manuell eingeben. Dies ist eher für kleinere als für große Beispiele effizient.
Um schnell dput
Daten zu erstellen, können 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 die sep
in letzterem bei Bedarf ändern . Dies funktioniert natürlich nur, wenn sich Ihre Daten in der Zwischenablage befinden.
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:
- Geben Sie Eingabedaten ein
- Geben Sie die erwartete Ausgabe an
- 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)
Überprüfen Sie die integrierten Datensätze, um einen für Ihr Problem geeigneten zu finden.
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 dput
ein Datenobjekt in R-Code umwandeln (z dput(myData)
. B. ). Ich sage als "letzter Ausweg", weil die Ausgabe von dput
oft 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
Auf Ihre Frage hin ist es viel wahrscheinlicher, dass die Leute schnell verstehen, 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:
- mit eingebautem Datensatz
- mit benutzergenerierten Daten
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.
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 anonymize
Funktion aus dem Paket SciencesPo
ist sehr albern, aber für mich funktioniert es gut mit der dput
Funktion.
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
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 eingerichteten 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
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 .
Ich entwickle das Wakefield- Paket , um diesem Bedürfnis nach schnellem dput
Austausch reproduzierbarer Daten gerecht zu werden. Manchmal 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 dput
ist unpraktisch.
Über:
Mit wakefield kann der Benutzer nur minimalen Code zur Reproduktion von Daten freigeben . Der Benutzer legtn
(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
.. ... ... ... ... ... ... ... ...
Wenn Sie ein oder mehr factor
Variable (n) in Ihren Daten , die Sie mit reproduzierbarer machen wollen dput(head(mydata))
, sollten Sie erwägen , droplevels
um es, so dass das Niveau der Faktoren , die nicht in der minimierten Datensatz nicht in Ihrem enthalten ist , dput
ausgegeben, um zu Machen Sie das Beispiel minimal :
dput(droplevels(head(mydata)))
Ich frage mich, ob ein http://old.r-fiddle.org/Link könnte eine sehr nette Möglichkeit sein, ein Problem zu teilen. Es erhält eine eindeutige ID wie und man könnte sogar darüber nachdenken, es in SO einzubetten.
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")
Eine weitere Sache, wenn Sie eine Funktion aus einem bestimmten Paket verwendet haben, erwähnen Sie diese Bibliothek.
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.
Abgesehen von allen oben genannten Antworten, die ich sehr interessant fand, konnte es manchmal sehr einfach sein, wie hier diskutiert wird: - WIE MAN EIN MINDESTPRODUZIERBARES 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 der 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))
Hier sind einige meiner Vorschläge:
- Versuchen Sie, Standard-R-Datasets zu verwenden
- Wenn Sie über einen eigenen Datensatz verfügen, fügen Sie diesen hinzu
dput
, damit andere Ihnen leichter helfen können - Verwenden Sie
install.package()
nicht, es sei denn, es ist wirklich notwendig, die Leute werden verstehen, wenn Sie nurrequire
oder 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 welche haben
- Fügen Sie auch eventuelle Fehler hinzu
All dies ist Teil eines reproduzierbaren Beispiels.
Es ist eine gute Idee, Funktionen aus dem testthat
Paket 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. Durch testthat
die Verwendung kann sich Ihr Helfer auf den Code konzentrieren, was Zeit spart, und bietet ihm die Möglichkeit, zu erkennen, dass er Ihr Problem gelöst hat, bevor er es veröffentlicht