Vraag Hoe maak je een geweldig R-reproduceerbaar voorbeeld?


Bij het bespreken van prestaties met collega's, lesgeven, een bugrapport verzenden of zoeken naar begeleiding op mailinglijsten en hier bij SO, a reproduceerbaar voorbeeld wordt vaak gevraagd en altijd nuttig.

Wat zijn jouw tips voor het maken van een uitstekend voorbeeld? Hoe plak je datastructuren van  in een tekstformaat? Welke andere informatie moet je opnemen?

Zijn er andere trucs naast het gebruik dput(), dump() of structure()? Wanneer moet je opnemen library() of require() uitspraken? Welke gereserveerde woorden moet men naast vermijden c, df, data, enz?

Hoe maakt iemand een goed  reproduceerbaar voorbeeld?


2373


oorsprong


antwoorden:


Een minimaal reproduceerbaar voorbeeld bestaat uit de volgende items:

  • een minimale dataset, nodig om de fout te reproduceren
  • het minimale uitvoerbare code die nodig is om de fout te reproduceren, die kan worden uitgevoerd op de gegeven dataset.
  • de nodige informatie over de gebruikte pakketten, R-versie en het systeem waarop het wordt uitgevoerd.
  • in het geval van willekeurige processen, een seed (ingesteld door set.seed()) voor reproduceerbaarheid

Het bekijken van de voorbeelden in de helpbestanden van de gebruikte functies is vaak nuttig. Over het algemeen voldoet de hier gegeven code aan de vereisten van een minimaal reproduceerbaar voorbeeld: gegevens worden verstrekt, minimale code wordt verstrekt en alles is uitvoerbaar.

Het produceren van een minimale dataset

In de meeste gevallen kan dit eenvoudig worden gedaan door slechts een vector / gegevensframe met enkele waarden aan te leveren. Of u kunt een van de ingebouwde gegevenssets gebruiken die bij de meeste pakketten worden geleverd.
Een uitgebreide lijst van ingebouwde datasets is te zien met library(help = "datasets"). Er is een korte beschrijving van elke dataset en meer informatie kan bijvoorbeeld worden verkregen met ?mtcars waar 'mtcars' een van de datasets in de lijst is. Andere pakketten kunnen extra datasets bevatten.

Een vector maken is eenvoudig. Soms is het nodig om wat willekeur toe te voegen, en er zijn een heel aantal functies om dat te maken. sample() kan een vector randomiseren, of een willekeurige vector geven met slechts een paar waarden. letters is een nuttige vector die het alfabet bevat. Dit kan worden gebruikt voor het maken van factoren.

Een paar voorbeelden:

  • willekeurige waarden: x <- rnorm(10) voor normale distributie, x <- runif(10) voor uniforme distributie, ...
  • een permutatie van enkele waarden: x <- sample(1:10) voor vector 1:10 in willekeurige volgorde.
  • een willekeurige factor: x <- sample(letters[1:4], 20, replace = TRUE)

Voor matrices kan men gebruiken matrix(), bijv .:

matrix(1:10, ncol = 2)

Gegevensframes maken kan met behulp van data.frame(). Men moet opletten om de vermeldingen in het dataframe te benoemen en om het niet al te gecompliceerd te maken.

Een voorbeeld :

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

Voor sommige vragen kunnen specifieke indelingen nodig zijn. Voor deze kunt u een van de geboden gebruiken as.someType functies: as.factor, as.Date, as.xts, ... Deze in combinatie met de vector- en / of dataframe-tricks.

Kopieer uw gegevens

Als u gegevens heeft die te moeilijk zijn om met deze tips te construeren, dan kunt u altijd een subset van uw originele gegevens maken, bijvoorbeeld met behulp van head(), subset()of de indices. Gebruik dan bijvoorbeeld dput() om ons iets te geven dat onmiddellijk in R kan worden gezet:

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

Als uw dataframe een factor heeft met veel niveaus, de dput uitvoer kan onpraktisch zijn omdat het nog steeds alle mogelijke factorniveaus zal weergeven, zelfs als ze niet aanwezig zijn in de de subset van uw gegevens. Om dit probleem op te lossen, kunt u de droplevels() functie. Zie hieronder hoe soort een factor is met slechts één niveau:

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

Een ander voorbehoud voor dput is dat het niet werkt voor ingetoetst data.table objecten of gegroepeerd tbl_df (klasse grouped_df) van dplyr. In deze gevallen kunt u terug converteren naar een normaal gegevensframe voordat u het deelt, dput(as.data.frame(my_data)).

In het slechtste geval kun je een tekstweergave geven die kan worden gelezen met behulp van de text parameter van 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)

Minimale code produceren

Dit zou het makkelijke gedeelte moeten zijn, maar dat is vaak niet het geval. Wat je niet moet doen, is:

  • voeg allerlei soorten gegevensconversies toe. Zorg ervoor dat de verstrekte gegevens al in de juiste indeling zijn (tenzij dat het probleem is natuurlijk)
  • kopieer-plak een hele functie / stuk code dat een fout oplevert. Probeer eerst te achterhalen welke regels precies in de fout resulteren. Meestal kom je erachter wat het probleem zelf is.

Wat je moet doen, is:

  • voeg toe welke pakketten moeten worden gebruikt als u een gebruikt (met library())
  • als u verbindingen of makefiles opent, voegt u een code toe om ze te sluiten of de bestanden te verwijderen (met unlink())
  • als je opties wijzigt, zorg er dan voor dat de code een statement bevat om ze terug te zetten naar de originele. (bv op <- par(mfrow=c(1,2)) ...some code... par(op) )
  • test voer uw code uit in een nieuwe, lege R-sessie om ervoor te zorgen dat de code kan worden uitgevoerd. Mensen moeten in staat zijn om uw gegevens en uw code gewoon in de console te plakken en exact hetzelfde te krijgen als u heeft.

Geef extra informatie

In de meeste gevallen is alleen de R-versie en het besturingssysteem voldoende. Wanneer conflicten ontstaan ​​met pakketten, geeft u de uitvoer van sessionInfo() kan echt helpen. Als het gaat om verbindingen met andere applicaties (via ODBC of iets anders), moet u daar ook versienummers voor opgeven, en indien mogelijk ook de nodige informatie over de installatie.

Als u R in runt R Studio gebruik makend van rstudioapi::versionInfo() kan handig zijn om uw RStudio-versie te melden.

Als u een probleem hebt met een specifiek pakket, wilt u misschien de versie van het pakket opgeven door de uitvoer van te geven packageVersion("name of the package").


1450



(Hier is mijn advies van Hoe een reproduceerbaar voorbeeld te schrijven . Ik heb geprobeerd het kort maar krachtig te maken)

Hoe een reproduceerbaar voorbeeld te schrijven.

U krijgt waarschijnlijk de meeste hulp bij uw R-probleem als u een reproduceerbaar voorbeeld geeft. Een reproduceerbaar voorbeeld stelt iemand anders in staat om uw probleem opnieuw te creëren door alleen R-code te kopiëren en te plakken.

Er zijn vier dingen die u moet opnemen om uw voorbeeld reproduceerbaar te maken: vereiste pakketten, gegevens, code en een beschrijving van uw R-omgeving.

  • Pakketjesmoet worden geladen aan de bovenkant van het script, dus het is gemakkelijk om zie welke het voorbeeld nodig heeft.

  • De gemakkelijkste manier om op te nemen gegevens in een e-mail of Stack Overflow-vraag is te gebruiken dput() genereren de R-code om het opnieuw te maken. Bijvoorbeeld om de. Opnieuw te maken mtcars dataset in R, Ik zou de volgende stappen uitvoeren:

    1. Rennen dput(mtcars) in R
    2. Kopieer de uitvoer
    3. Typ in mijn reproduceerbare script mtcars <- dan plakken.
  • Besteed een beetje tijd aan het waarborgen van uw code is gemakkelijk voor anderen lezen:

    • zorg ervoor dat je spaties hebt gebruikt en je variabelenamen zijn beknopt, maar leerzaam

    • gebruik opmerkingen om aan te geven waar uw probleem ligt

    • doe je best om alles te verwijderen dat niets met het probleem te maken heeft.
      Hoe korter je code is, hoe gemakkelijker het is om te begrijpen.

  • Vermeld de uitvoer van sessionInfo() in een opmerking in uw code. Dit vat uw samen R milieu en maakt het gemakkelijk om te controleren of u een verouderde gebruikt pakket.

U kunt controleren of u een reproduceerbaar voorbeeld hebt gemaakt door een nieuwe R-sessie op te starten en uw script in te plakken.

Voordat u al uw code in een e-mail plaatst, kunt u overwegen deze aan te zetten Gst github . Het geeft je code een mooie syntaxisaccentuering, en je hoeft je geen zorgen te maken over iets dat wordt gemangeld door het e-mailsysteem.


514



Persoonlijk geef ik de voorkeur aan "één" voeringen. Iets in de trant van:

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)

De datastructuur moet het idee van het probleem van de schrijver nabootsen en niet de exacte woordelijke structuur. Ik waardeer het echt als variabelen mijn eigen variabelen niet overschrijven of god verhoede, functies (zoals df).

Je kunt ook een paar hoeken knippen en wijzen op een bestaande gegevensset, zoiets als:

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

Vergeet niet om speciale pakketten te vermelden die u misschien gebruikt.

Als u iets probeert te demonstreren op grotere objecten, kunt u het proberen

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

Als u met ruimtelijke gegevens werkt via de raster pakket, kunt u enkele willekeurige gegevens genereren. Veel voorbeelden zijn te vinden in het pakketvignet, maar hier is een kleine goudklomp.

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)

Als u behoefte heeft aan een ruimtelijk object zoals geïmplementeerd sp, kunt u sommige datasets via externe bestanden (zoals ESRI-shapefile) in "ruimtelijke" pakketten krijgen (zie de Spatial-weergave in taakaanzichten).

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

258



Geïnspireerd door ditzelfde bericht, gebruik ik nu een handige functie
reproduce(<mydata>) wanneer ik moet posten naar StackOverflow.


SNELLE INSTRUCTIES

Als myData is de naam van je object om te reproduceren, voer het volgende uit in R:

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

reproduce(myData)

Details:

Deze functie is een intelligente wrapper voor dput en doet het volgende:

  • bemonstert automatisch een grote gegevensset (op basis van grootte en klasse.) Grootte van steekproef kan worden aangepast)
  • creëert een dput uitgang
  • kunt u specificeren welke kolommen om te exporteren
  • hangt aan de voorkant ervan objName <- ... zodat het gemakkelijk kan worden gekopieerd + geplakt, maar ...
  • Als u aan een mac werkt, wordt de uitvoer automatisch naar het klembord gekopieerd, zodat u hem gewoon kunt uitvoeren en vervolgens op uw vraag kunt plakken.

De bron is hier beschikbaar:


Voorbeeld:

# 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 is ongeveer 100 x 102. Ik wil 10 rijen en enkele specifieke kolommen samplen

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

Geeft de volgende uitvoer:

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

Merk ook op dat het geheel van de uitvoer zich bevindt in een mooie enkele, lange regel, geen lange alinea van gehakte lijnen. Dit maakt het gemakkelijker om te lezen op SO vragen berichten en ook gemakkelijker te kopiëren + plakken.


Update okt 2013:

U kunt nu opgeven hoeveel regels tekstuitvoer u wilt gebruiken (dat wil zeggen, wat u in StackOverflow gaat plakken). Gebruik de lines.out=n argument hiervoor. Voorbeeld:

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

    ==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 is een goede gids:

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

Maar het belangrijkste is: zorg ervoor dat je een klein stukje code maakt dat we kunnen gebruiken om te zien wat het probleem is. Een nuttige functie hiervoor is dput(), maar als u zeer grote gegevens hebt, wilt u misschien een kleine voorbeeldgegevensset maken of alleen de eerste tien regels gebruiken.

BEWERK:

Zorg er ook voor dat je zelf hebt geïdentificeerd waar het probleem ligt. Het voorbeeld mag geen volledig R-script zijn met "On line 200 is er een fout". Als u de hulpprogramma's voor foutopsporing in R gebruikt (I love browser()) en google zou je in staat moeten zijn om echt te identificeren waar het probleem zit en een triviaal voorbeeld te reproduceren waarin hetzelfde fout gaat.


168



De R-help mailinglijst heeft een boekingsgids die zowel vragen stellen als vragen beantwoorden, inclusief een voorbeeld van het genereren van gegevens:

Voorbeelden: soms helpt het   geef een klein voorbeeld dat iemand   kan echt rennen. Bijvoorbeeld:

Als ik een matrix x als volgt heb:

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

hoe kan ik het in een dataframe veranderen?   met 8 rijen en drie kolommen met de naam   'row', 'col' en 'value', die hebben   de dimensienamen als de waarden van 'row' en 'col', zoals deze:

  > x.df
     row col value
  1    A   x      1

...
  (Waarop het antwoord kan zijn:

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

)

Het woord klein is vooral belangrijk. Je zou moeten streven naar een minimaal reproduceerbaar voorbeeld, wat betekent dat de gegevens en de code zo eenvoudig mogelijk moeten zijn om het probleem te verklaren.

EDIT: Mooie code is gemakkelijker te lezen dan lelijke code. Gebruik een stijlgids.


142



Sinds R.2.14 (denk ik) kun je je datatekst-weergave direct naar read.table sturen:

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



Soms is het probleem echt niet reproduceerbaar met een kleiner gegevensbestand, hoe hard je het ook probeert, en gebeurt het niet met synthetische gegevens (hoewel het handig is om te laten zien hoe je synthetische gegevensverzamelingen hebt gemaakt die wel niet reproduceren het probleem, omdat het enkele hypothesen uitsluit).

  • Het kan nodig zijn om de gegevens ergens op internet te plaatsen en een URL op te geven.
  • Als de gegevens niet voor het grote publiek kunnen worden vrijgegeven maar wel kunnen worden gedeeld, kunt u deze mogelijk aanbieden aan geïnteresseerde partijen (hoewel dit het aantal mensen dat de moeite neemt om te werken zal verminderen). ben ermee bezig).
  • Ik heb dit niet echt gedaan, omdat mensen die hun gegevens niet kunnen vrijgeven, gevoelig zijn om het elke vorm vrij te geven, maar het lijkt aannemelijk dat in sommige gevallen men nog steeds gegevens kan posten als het voldoende geanonimiseerd / gecorrumpeerd / beschadigd is in zekere zin.

Als u geen van beide kunt doen, moet u waarschijnlijk een consultant inhuren om uw probleem op te lossen ...

Bewerk: Twee nuttige SO-vragen voor anonimiseren / verhaspelen:


126



De antwoorden tot dusverre zijn duidelijk geweldig voor het reproduceerbaarheidsgedeelte. Dit is alleen maar om te verduidelijken dat een reproduceerbaar voorbeeld niet de enige component van een vraag kan en mag zijn. Vergeet niet uit te leggen hoe je het wilt zien en de contouren van je probleem, niet alleen hoe je hebt geprobeerd om er tot nu toe te komen. Code is niet genoeg; je hebt ook woorden nodig.

Hier is een reproduceerbaar voorbeeld van wat je moet vermijden (getrokken uit een echt voorbeeld, namen veranderd om onschuldigen te beschermen):


Het volgende is voorbeeldgegevens en een deel van de functie waar ik problemen mee heb.

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

Hoe kan ik dit bereiken?



115



Snel een maken dput van uw gegevens kunt u (een deel van) de gegevens kopiëren naar uw klembord en het volgende uitvoeren in R:

voor gegevens in Excel:

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

voor gegevens in een txt-bestand:

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

U kunt de sep in het laatste geval indien nodig. Dit werkt alleen als uw gegevens natuurlijk op het klembord staan.


100