Come fare un ottimo esempio riproducibile di R


2473

Quando si discute delle prestazioni con i colleghi, si insegna, si invia una segnalazione di bug o si cerca una guida sulle mailing list e qui su Stack Overflow, viene spesso richiesto un esempio riproducibile e sempre utile.

Quali sono i tuoi consigli per creare un esempio eccellente? Come incollare le strutture dati dain un formato di testo? Quali altre informazioni dovresti includere?

Ci sono altri trucchi oltre a utilizzare dput(), dump()o structure()? Quando dovresti includere library()o require()dichiarazioni? Che riservava parole dovrebbero evitare di uno, oltre a c, df, data, ecc?

Come si fa a diventare grandi esempio riproducibile?


34
Sono confuso riguardo alla portata della domanda. Le persone sembrano aver saltato sull'interpretazione dell'esempio riproducibile nel porre domande su SO o R-help (come "riprodurre l'errore"). Che dire degli esempi R riproducibili nelle pagine della guida? Nelle demo dei pacchetti? In tutorial / presentazioni?
battezzato il

15
@baptiste: lo stesso meno l'errore. Tutte le tecniche che ho spiegato sono utilizzate nelle pagine di aiuto del pacchetto e nei tutorial e presentazioni che do su R
Joris Meys,

33
I dati sono talvolta il fattore limitante, poiché la struttura potrebbe essere troppo complessa per essere simulata. Per produrre i dati pubblici di dati privati: stackoverflow.com/a/10458688/742447 in stackoverflow.com/questions/10454973/...
Etienne bassa Décarie

Risposte:


1727

Un esempio riproducibile minimo è costituito dai seguenti elementi:

  • un set di dati minimo, necessario per dimostrare il problema
  • il codice eseguibile minimo necessario per riprodurre l'errore, che può essere eseguito sul set di dati specificato
  • le informazioni necessarie sui pacchetti utilizzati, sulla versione R e sul sistema su cui vengono eseguite.
  • nel caso di processi casuali, un seme (impostato da set.seed()) per la riproducibilità 1

Per esempi di buoni esempi riproducibili minimi , consultare i file della guida della funzione che si sta utilizzando. In generale, tutto il codice fornito soddisfa i requisiti di un esempio riproducibile minimo: vengono forniti dati, viene fornito un codice minimo e tutto è eseguibile. Guarda anche le domande su Stack Overflow con molti voti positivi.

Produrre un set di dati minimo

Nella maggior parte dei casi, questo può essere fatto semplicemente fornendo un frame vettoriale / dati con alcuni valori. In alternativa, è possibile utilizzare uno dei set di dati integrati, forniti con la maggior parte dei pacchetti.
È possibile visualizzare un elenco completo di set di dati integrati con library(help = "datasets"). Esiste una breve descrizione per ogni set di dati e ulteriori informazioni possono essere ottenute, ad esempio, in ?mtcarscui "mtcars" è uno dei set di dati nell'elenco. Altri pacchetti potrebbero contenere set di dati aggiuntivi.

Fare un vettore è facile. A volte è necessario aggiungere un po 'di casualità ad esso, e ci sono un numero intero di funzioni per farlo. sample()può randomizzare un vettore o dare un vettore casuale con solo pochi valori. lettersè un vettore utile contenente l'alfabeto. Questo può essere usato per creare fattori.

Alcuni esempi:

  • valori casuali: x <- rnorm(10)per distribuzione normale, x <- runif(10)per distribuzione uniforme, ...
  • una permutazione di alcuni valori: x <- sample(1:10)per il vettore 1:10 in ordine casuale.
  • un fattore casuale: x <- sample(letters[1:4], 20, replace = TRUE)

Per le matrici, è possibile utilizzare matrix(), ad esempio:

matrix(1:10, ncol = 2)

La creazione di frame di dati può essere eseguita utilizzando data.frame(). Bisogna prestare attenzione a nominare le voci nel frame di dati e non renderle eccessivamente complicate.

Un esempio :

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

Per alcune domande, possono essere necessari formati specifici. Per questi, si può usare qualsiasi delle previste as.someTypefunzioni: as.factor, as.Date, as.xts, ... Questi in combinazione con il vettore e / o della struttura dati trucchi.

Copia i tuoi dati

Se si dispone di alcuni dati che sarebbe troppo difficile da costruire con questi suggerimenti, allora si può sempre fare un sottoinsieme dei dati originali, utilizzando head(), subset()o gli indici. Quindi usa dput()per darci qualcosa che può essere messo immediatamente in R:

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

Se il tuo frame di dati ha un fattore con molti livelli, l' dputoutput può essere ingombrante perché elencherà comunque tutti i possibili livelli di fattore anche se non sono presenti nel sottoinsieme dei tuoi dati. Per risolvere questo problema, è possibile utilizzare la droplevels()funzione. Si noti di seguito come la specie è un fattore con un solo livello:

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

Durante l'utilizzo dput, potresti anche voler includere solo le colonne pertinenti:

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

Un altro avvertimento dputè che non funzionerà per data.tableoggetti con chiave o per raggruppati tbl_df(classe grouped_df) da dplyr. In questi casi è possibile convertire di nuovo ad un frame di dati regolare prima condivisione, dput(as.data.frame(my_data)).

Nel peggiore dei casi, puoi dare una rappresentazione testuale che può essere letta usando il textparametro di 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)

Produrre codice minimo

Questa dovrebbe essere la parte facile ma spesso non lo è. Quello che non dovresti fare è:

  • aggiungi tutti i tipi di conversioni di dati. Assicurati che i dati forniti siano già nel formato corretto (a meno che questo non sia il problema ovviamente)
  • copia-incolla un'intera funzione / pezzo di codice che dà un errore. Innanzitutto, prova a individuare quali righe causano esattamente l'errore. Più spesso scoprirai qual è il problema.

Quello che dovresti fare è:

  • aggiungi quali pacchetti dovrebbero essere usati se ne usi uno (usando library())
  • se apri connessioni o crei file, aggiungi del codice per chiuderle o elimina i file (usando unlink())
  • se si modificano le opzioni, assicurarsi che il codice contenga un'istruzione per ripristinarle a quelle originali. (ad es. op <- par(mfrow=c(1,2)) ...some code... par(op))
  • test eseguire il codice in una nuova sessione R vuota per assicurarsi che il codice sia eseguibile. Le persone dovrebbero essere in grado di copiare e incollare i tuoi dati e il tuo codice nella console e ottenere esattamente lo stesso che hai.

Fornisci ulteriori informazioni

Nella maggior parte dei casi, basterà solo la versione R e il sistema operativo. Quando sorgono conflitti con i pacchetti, dare l'output di sessionInfo()può davvero aiutare. Quando si parla di connessioni ad altre applicazioni (tramite ODBC o qualsiasi altra cosa), si dovrebbero anche fornire i numeri di versione per questi e, se possibile, anche le informazioni necessarie sull'installazione.

Se si esegue R in R Studio utilizzando rstudioapi::versionInfo()può essere utile a segnalare la versione RStudio.

In caso di problemi con un pacchetto specifico, è possibile che si desideri fornire la versione del pacchetto fornendo l'output di packageVersion("name of the package").


1 Nota: l'output di set.seed()differisce tra R> 3.6.0 e versioni precedenti. Specifica quale versione R hai usato per il processo casuale e non stupirti se ottieni risultati leggermente diversi seguendo le vecchie domande. Per ottenere lo stesso risultato in questi casi, è possibile utilizzare la funzione RNGversion()prima set.seed()(ad es RNGversion("3.5.2"). :) .


6
Come si usa dputse il frame di dati è molto grande e il problema è generato dalla metà del frame di dati? Esiste un modo per dputriprodurre la sezione centrale dei dati, ad esempio le righe da 60 a 70?
BgnR

27
@BgnR È possibile estrarre parte del frame di dati utilizzando gli indici, ad esempio: tmp <- mydf[50:70,]seguito da dput(mydf). Se il frame di dati è davvero grande, prova a isolare il problema e invia solo le poche righe che causano il problema.
Joris Meys,

4
@JorisMeys: esiste un modo per dire heado dputlimitare i dati al livello N in modo ricorsivo? Sto cercando di fornire un esempio riproducibile e i miei dati sono un elenco di frame di dati. Quindi, dput(head(myDataObj))sembra non essere abbastanza, in quanto genera un file di output di 14 MB di dimensione.
Aleksandr Blekh,

5
@JorisMeys: Just FYI - domanda postata nel commento sopra come domanda separata: stackoverflow.com/questions/25127026/… .
Aleksandr Blekh,

4
@Konrad La cosa migliore che potresti fare è collegarti al file e dare il comando minimo per leggere quel file. Sarà meno seccante che incollare l'output di dput () :)
Joris Meys,

590

(Ecco il mio consiglio da Come scrivere un esempio riproducibile . Ho provato a renderlo breve ma dolce)

Come scrivere un esempio riproducibile.

È molto probabile che tu ottenga un buon aiuto con il tuo problema R se fornisci un esempio riproducibile. Un esempio riproducibile consente a qualcun altro di ricreare il problema semplicemente copiando e incollando il codice R.

Ci sono quattro cose che devi includere per rendere riproducibile il tuo esempio: pacchetti richiesti, dati, codice e una descrizione del tuo ambiente R.

  • I pacchetti devono essere caricati nella parte superiore dello script, quindi è facile vedere quali sono necessari nell'esempio.

  • Il modo più semplice per includere dati in una domanda di posta elettronica o StackTranslate.it è utilizzare dput()per generare il codice R per ricrearlo. Ad esempio, per ricreare il mtcarsset di dati in R, eseguire i seguenti passaggi:

    1. Esegui dput(mtcars)in R
    2. Copia l'output
    3. Nel mio script riproducibile, digita mtcars <-quindi incolla.
  • Dedica un po 'di tempo a garantire che il tuo codice sia facile da leggere per gli altri:

    • assicurati di aver utilizzato gli spazi e che i nomi delle variabili siano concisi, ma informativi

    • usa i commenti per indicare dove si trova il tuo problema

    • fai del tuo meglio per rimuovere tutto ciò che non è correlato al problema.
      Più breve è il tuo codice, più facile è capire.

  • Includi l'output di sessionInfo()in un commento nel tuo codice. Questo riepiloga il tuo ambiente R e semplifica il controllo se stai utilizzando un pacchetto obsoleto.

Puoi verificare di aver effettivamente fatto un esempio riproducibile avviando una nuova sessione R e incollando lo script.

Prima di inserire tutto il codice in un'e-mail, considera di metterlo su Gist github . Fornirà al tuo codice una buona evidenziazione della sintassi e non dovrai preoccuparti di nulla che venga rovinato dal sistema di posta elettronica.


24
reprexin tidyverseè un buon pacchetto per produrre esempi minimali e riproducibili: github.com/tidyverse/reprex
mt1022

19
Ricevo regolarmente e-mail con codice in esse. Ricevo persino e-mail con documenti Word allegati che contengono codice. A volte ricevo persino e-mail con documenti Word allegati che contengono SCREENSHOT di codice.
Hadley,

304

Personalmente, preferisco le fodere "one". Qualcosa del genere:

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)

La struttura dei dati dovrebbe imitare l'idea del problema dello scrittore e non l'esatta struttura testuale. Lo apprezzo molto quando le variabili non sovrascrivono le mie stesse variabili o Dio non voglia funzioni (come df).

In alternativa, si potrebbero tagliare alcuni angoli e puntare a un set di dati preesistente, qualcosa del tipo:

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

Non dimenticare di menzionare eventuali pacchetti speciali che potresti utilizzare.

Se stai cercando di dimostrare qualcosa su oggetti più grandi, puoi provare

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

Se stai lavorando con dati spaziali tramite il rasterpacchetto, puoi generare alcuni dati casuali. Molti esempi possono essere trovati nella vignetta del pacchetto, ma ecco una piccola pepita.

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)

Se hai bisogno di alcuni oggetti spaziali come implementato in sp, puoi ottenere alcuni set di dati tramite file esterni (come lo shapefile ESRI) in pacchetti "spaziali" (vedi la vista Spaziale in Viste delle attività).

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, quando si utilizza sampleo runifè prudente set.seed. Almeno, questo è il suggerimento che ho ricevuto durante la produzione di esempi relativi al campionamento o alla generazione di numeri casuali.
Konrad,

1
@Konrad Sono d'accordo, ma questo può dipendere. Se stai solo cercando di generare alcuni numeri, un seme potrebbe non essere necessario, ma se stai cercando di capire qualcosa di specifico in cui sono necessari numeri fissi, un seme sarebbe obbligatorio.
Roman Luštrik,

1
È sempre meglio con un seed imo, rende più semplice confrontare la propria soluzione con l'output previsto, confrontare le soluzioni tra loro e in questo modo gli utenti che non conoscono (e non hanno bisogno di sapere) funzioni come runifo samplenon sono confuse che non possono ottenere gli stessi dati.
Moody_Mudskipper il

2
@mikey hai visto il pacchetto usmap ?
Roman Luštrik,

2
@mikey il pacchetto tigris scarica gli shapefile dal Census Bureau in una varietà di formati
camille

277

Ispirato proprio da questo post, ora uso una comoda funzione
reproduce(<mydata>)quando devo pubblicare su StackOverflow.


ISTRUZIONI RAPIDE

Se myDataè il nome dell'oggetto da riprodurre, eseguire quanto segue in R:

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

reproduce(myData)

Dettagli:

Questa funzione è un wrapper intelligente per dput:

  • campiona automaticamente un set di dati di grandi dimensioni (basato su dimensioni e classe. Le dimensioni del campione possono essere regolate)
  • crea un dputoutput
  • ti permette di specificare quali colonne esportare
  • si aggiunge in primo piano in objName <- ...modo che possa essere facilmente copiato + incollato, ma ...
  • Se si lavora su un Mac, l'output viene copiato automaticamente negli Appunti, in modo da poterlo semplicemente eseguire e incollare nella domanda.

La fonte è disponibile qui:


Esempio:

# 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 è di circa 100 x 102. Voglio campionare 10 righe e alcune colonne specifiche

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

Fornisce il seguente output:

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

Si noti inoltre che l'intero output è in una bella riga singola, lunga, non in un paragrafo alto di righe tratteggiate. Ciò semplifica la lettura dei post delle domande SO e anche la copia e incolla.


Aggiornamento ottobre 2013:

Ora puoi specificare quante righe di output di testo occuperanno (cioè, cosa incollerai in StackOverflow). Usa l' lines.out=nargomento per questo. Esempio:

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

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

Ecco una buona guida .

Il punto più importante è: assicurati solo di creare un piccolo pezzo di codice che possiamo eseguire per vedere qual è il problema . Una funzione utile per questo è dput(), ma se disponi di dati molto grandi, potresti voler creare un piccolo set di dati di esempio o utilizzare solo le prime 10 righe o giù di lì.

MODIFICARE:

Assicurati anche di aver identificato il problema da solo. L'esempio non dovrebbe essere un intero script R con "Nella riga 200 c'è un errore". Se usi gli strumenti di debug in R (I love browser()) e Google dovresti essere in grado di identificare davvero dove si trova il problema e riprodurre un esempio banale in cui la stessa cosa va storta.


165

La mailing list di R-help ha una guida alla pubblicazione che copre sia le domande che le domande, incluso un esempio di generazione di dati:

Esempi: a volte aiuta a fornire un piccolo esempio che qualcuno può effettivamente eseguire. Per esempio:

Se ho una matrice x come segue:

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

come posso trasformarlo in un frame di dati con 8 righe e tre colonne denominate 'row', 'col' e 'value', che hanno i nomi delle dimensioni come i valori di 'row' e 'col', in questo modo:

  > x.df
     row col value
  1    A   x      1

...
(A cui potrebbe essere la risposta:

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

)

La parola piccola è particolarmente importante. Dovresti puntare a un esempio riproducibile minimo , il che significa che i dati e il codice dovrebbero essere il più semplice possibile per spiegare il problema.

EDIT: il codice grazioso è più facile da leggere rispetto al codice brutto. Usa una guida di stile .


164

Da R.2.14 (suppongo) puoi alimentare la tua rappresentazione del testo dei dati direttamente a 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 come va bene per fare un esempio riproducibile ?? :)
TMS

@TMS Pensandoci seriamente, se il richiedente ha fornito i dati e il problema è piccolo (ma potrebbe avere alcune soluzioni), allora potrebbe essere più veloce e puoi ancora seguire tutti i passaggi.
sebastian-c,

146

A volte il problema in realtà non è riproducibile con un pezzo di dati più piccolo, non importa quanto ci si provi, e non si verifica con i dati sintetici (anche se è utile mostrare come hai prodotto set di dati sintetici che non riproducono il problema, perché esclude alcune ipotesi).

  • Potrebbe essere necessario pubblicare i dati sul Web da qualche parte e fornire un URL.
  • Se i dati non possono essere divulgati al pubblico in generale ma potrebbero essere condivisi del tutto, allora potresti essere in grado di offrire di inviarli via e-mail alle parti interessate (anche se questo ridurrà il numero di persone che si preoccuperanno di lavorare su di esso).
  • In realtà non l'ho visto fatto, perché le persone che non sono in grado di rilasciare i propri dati sono sensibili al rilascio di qualsiasi modulo, ma sembrerebbe plausibile che in alcuni casi si possano ancora pubblicare dati se fossero sufficientemente anonimizzati / strapazzati / corrotti leggermente in qualche modo.

Se non riesci a eseguire nessuna di queste operazioni, probabilmente dovrai assumere un consulente per risolvere il tuo problema ...

modifica : due utili domande SO per l'anonimizzazione / rimescolamento:


1
Per la produzione di set di dati sintetici, le risposte a questa domanda forniscono esempi utili, comprese le applicazioni di fitdistre fitdistrplus.
Iteratore,

137

Le risposte finora sono ovviamente ottime per la parte della riproducibilità. Questo serve solo a chiarire che un esempio riproducibile non può e non dovrebbe essere l'unico componente di una domanda. Non dimenticare di spiegare come vuoi che appaia e i contorni del tuo problema, non solo come hai tentato di arrivarci finora. Il codice non è abbastanza; hai bisogno anche di parole.

Ecco un esempio riproducibile di cosa evitare di fare (tratto da un esempio reale, nomi cambiati per proteggere gli innocenti):


Di seguito sono riportati i dati di esempio e parte della funzione con cui ho problemi.

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

Come posso raggiungere questo obiettivo ?



124

Ho un modo molto semplice ed efficace per fare un esempio R che non è stato menzionato sopra. Puoi definire la tua struttura in primo luogo. Per esempio,

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

>fix(mydata)

Quando esegui il comando 'fix', otterrai questa finestra pop-up

Quindi puoi inserire i tuoi dati manualmente. Questo è efficace per esempi più piccoli anziché per quelli più grandi.


18
... poidput(mydata)
GSee

Qual è il tuo frontend? Sarebbe bello avere una risposta completa. Etc crea un dato che puoi eseguire direttamente in loop come for (d in data) {...}.
Léo Léopold Hertz

119

Per creare rapidamente uno dputdei tuoi dati puoi semplicemente copiare (un pezzo di) i dati negli Appunti ed eseguire quanto segue in R:

per dati in Excel:

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

per i dati in un file txt:

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

È possibile modificare sepil secondo, se necessario. Funzionerà solo se i tuoi dati sono negli appunti ovviamente.


116

Linee guida:


Il tuo obiettivo principale nell'elaborare le tue domande dovrebbe essere quello di rendere il più semplice possibile per i lettori comprendere e riprodurre il tuo problema sui loro sistemi. Fare così:

  1. Fornire dati di input
  2. Fornire l'output previsto
  3. Spiega il tuo problema in modo succinto
    • se hai più di 20 righe di testo + codice probabilmente puoi tornare indietro e semplificare
    • semplifica il codice il più possibile preservando il problema / errore

Questo richiede un po 'di lavoro ma sembra un giusto compromesso poiché stai chiedendo ad altri di fare un lavoro per te.

Fornitura dei dati:


Set di dati integrati

L'opzione migliore è di gran lunga fare affidamento su set di dati integrati. Questo rende molto facile per gli altri lavorare sul tuo problema. Digitare data()al prompt R per vedere quali dati sono disponibili. Alcuni esempi classici:

  • iris
  • mtcars
  • ggplot2::diamonds (pacchetto esterno, ma quasi tutti ce l'hanno)

Vedi questo SO QA per come trovare i set di dati adatti al tuo problema.

Se riesci a riformulare il tuo problema per utilizzare i set di dati integrati, avrai molte più probabilità di ottenere risposte valide (e voti positivi).

Dati generati da sé

Se il problema è molto specifico per un tipo di dati che non è rappresentato nei set di dati esistenti, fornire il codice R che genera il set di dati più piccolo possibile su cui si manifesta il problema. Per esempio

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

Ora qualcuno che cerca di rispondere alla mia domanda può copiare / incollare quelle due righe e iniziare a lavorare immediatamente sul problema.

dput

Come ultima risorsa , è possibile utilizzare dputper trasformare un oggetto dati in codice R (ad es dput(myData).). Dico come "ultima risorsa" perché l'output di dputè spesso piuttosto ingombrante, fastidioso da copiare e incollare e oscura il resto della tua domanda.

Fornire output previsto:


Qualcuno una volta disse:

Un'immagine dell'output previsto vale 1000 parole

- una persona molto saggia

Se puoi aggiungere qualcosa del tipo "Mi aspettavo di ottenere questo risultato":

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

alla tua domanda, le persone hanno molte più probabilità di capire rapidamente cosa stai cercando di fare. Se il risultato atteso è grande e ingombrante, probabilmente non hai pensato abbastanza su come semplificare il tuo problema (vedi dopo).

Spiega il tuo problema in modo succinto


La cosa principale da fare è semplificare il più possibile il problema prima di porre la domanda. Ri-inquadrare il problema per lavorare con i set di dati integrati aiuterà molto in questo senso. Scoprirai spesso che, semplicemente attraverso il processo di semplificazione, risponderai al tuo problema.

Ecco alcuni esempi di buone domande:

In entrambi i casi, i problemi dell'utente non sono quasi certamente con i semplici esempi che forniscono. Piuttosto hanno estratto la natura del loro problema e lo hanno applicato a un semplice set di dati per porre la loro domanda.

Perché ancora un'altra risposta a questa domanda?


Questa risposta si concentra su ciò che penso sia la migliore pratica: utilizzare set di dati integrati e fornire ciò che ti aspetti di conseguenza in una forma minima. Le risposte più importanti si concentrano su altri aspetti. Non mi aspetto che questa risposta raggiunga un certo rilievo; questo è qui solo in modo che io possa collegarmi ad esso nei commenti alle domande dei principianti.


113

Il codice riproducibile è la chiave per ottenere aiuto. Tuttavia, ci sono molti utenti che potrebbero essere scettici di incollare anche una parte dei loro dati. Ad esempio, potrebbero lavorare con dati sensibili o su dati originali raccolti da utilizzare in un documento di ricerca. Per qualsiasi motivo, ho pensato che sarebbe stato utile avere una comoda funzione per "deformare" i miei dati prima di incollarli pubblicamente. La anonymizefunzione dal pacchetto SciencesPoè molto sciocca, ma per me funziona bene con la dputfunzione.

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

Quindi lo anonimizzo:

> 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

Si potrebbe anche voler campionare poche variabili invece di tutti i dati prima di applicare il comando anonimizzazione e dput.

    # 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

Spesso hai bisogno di alcuni dati per un esempio, tuttavia, non vuoi pubblicare i tuoi dati esatti. Per utilizzare alcuni data.frame esistenti nella libreria stabilita, utilizzare il comando data per importarlo.

per esempio,

data(mtcars)

e quindi fare il problema

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

13
Molti set di dati integrati (come i set di dati mtcarse popolari iris) in realtà non hanno bisogno della datachiamata per essere utilizzati.
Gregor Thomas,

92

Se disponi di un set di dati di grandi dimensioni che non può essere facilmente inserito nello script utilizzando dput(), pubblica i tuoi dati su pastebin e caricali utilizzando read.table:

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

Ispirato da @Henrik .


90

Sto sviluppando il pacchetto wakefield per rispondere a questa esigenza di condividere rapidamente dati riproducibili, a volte dputfunziona bene per set di dati più piccoli, ma molti dei problemi che affrontiamo sono molto più grandi, la condivisione di un set di dati così grande tramite dputnon è pratica.

Di:

wakefield consente all'utente di condividere un codice minimo per riprodurre i dati. L'utente impostan(numero di righe) e specifica qualsiasi numero di funzioni variabili preimpostate (attualmente ce ne sono 70) che imitano dati reali se (cose come sesso, età, reddito ecc.)

Installazione:

Attualmente (11/06/2015), wakefield è un pacchetto GitHub ma andrà a CRAN alla fine dopo la scrittura dei test unitari. Per installare rapidamente, utilizzare:

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

Esempio:

Ecco un esempio:

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

Questo produce:

    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

Se hai una o più factorvariabili nei tuoi dati con cui desideri rendere riproducibile dput(head(mydata)), considera di aggiungervi droplevels, in modo che i livelli di fattori che non sono presenti nel set di dati ridotto a icona non siano inclusi dputnell'output, al fine di rendere l'esempio minimo :

dput(droplevels(head(mydata)))

65

Mi chiedo se un link http://old.r-fiddle.org/ potrebbe essere un modo molto semplice di condividere un problema. Riceve un ID univoco come e si potrebbe persino pensare di incorporarlo in SO.


47

Non incollare le uscite della console in questo modo:

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

Non possiamo copiarlo e incollarlo direttamente.

Per rendere correttamente riproducibili domande e risposte, prova a rimuoverlo +e >prima di pubblicarlo e inserisci #output e commenti come questo:

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

Un'altra cosa, se hai usato una funzione da un determinato pacchetto, menziona quella libreria.


2
rimuovi >e aggiungi #manualmente o c'è un modo automatico per farlo?
BCArg

3
@BCArg rimuovo >manualmente. Ma, per l'aggiunta di #, utilizzo il Ctrl+Shift+Ccollegamento RStudionell'editor.
user2100721

33

Puoi farlo usando reprex .

Come notato da mt1022 , "... un buon pacchetto per produrre esempi minimali e riproducibili è " reprex " di tidyverse ".

Secondo Tidyverse :

L'obiettivo di "reprex" è impacchettare il tuo codice problematico in modo tale che altre persone possano eseguirlo e sentire il tuo dolore.

Un esempio è fornito sul sito web tidyverse .

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

Penso che questo sia il modo più semplice per creare un esempio riproducibile.


33

A parte tutte le risposte di cui sopra che ho trovato molto interessante, a volte potrebbe essere molto semplice come è discusso qui: - COME REALIZZARE UN ESEMPIO RIPRODUCIBILE MINIMO PER OTTENERE AIUTO CON R

Esistono molti modi per creare un vettore casuale Creare un vettore di 100 numeri con valori casuali in R arrotondato a 2 decimali o matrice casuale in R

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

Si noti che a volte è molto difficile condividere un dato dato a causa di vari motivi come la dimensione ecc. Tuttavia, tutte le risposte di cui sopra sono grandi e molto importanti da pensare e usare quando si vuole fare un esempio di dati riproducibili. Tuttavia, per rendere i dati più rappresentativi dell'originale (nel caso in cui l'OP non possa condividere i dati originali), è bene aggiungere alcune informazioni con l'esempio dei dati come (se chiamiamo i dati mydf1)

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

Inoltre, si dovrebbe conoscere il tipo, la lunghezza e gli attributi di un dato che può essere una struttura dati

#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

Ecco alcuni dei miei suggerimenti:

  • Prova a utilizzare i set di dati R predefiniti
  • Se hai il tuo set di dati, includili insieme dput, in modo che altri possano aiutarti più facilmente
  • Non usare a install.package()meno che non sia veramente necessario, le persone capiranno se si usa requireo semplicementelibrary
  • Cerca di essere conciso,

    • Avere un set di dati
    • Prova a descrivere l'output che ti serve nel modo più semplice possibile
    • Fallo da solo prima di porre la domanda
  • È facile caricare un'immagine, quindi carica grafici se ne hai
  • Includi anche eventuali errori che potresti avere

Tutti questi fanno parte di un esempio riproducibile.


1
Non hai davvero aggiunto nulla di sostanziale qui. dput()è stato menzionato in precedenza e gran parte di questo è solo una reiterazione delle linee guida SO standard.
Rich Scriven,

1
Ho avuto problemi con la install.packagefunzione inclusa nell'esempio che non è davvero necessario (secondo me). Inoltre, l'utilizzo del set di dati R predefinito faciliterebbe la riproduzione. Le linee guida SO non hanno parlato specificamente di questi argomenti. Inoltre, doveva dare la mia opinione e questi sono quelli che ho incontrato di più.
TheRimalaya,

18

È una buona idea usare le funzioni del testthatpacchetto per mostrare cosa ci si aspetta. Pertanto, altre persone possono modificare il codice fino a quando non viene eseguito senza errori. Questo facilita l'onere di coloro che vorrebbero aiutarti, perché significa che non devono decodificare la tua descrizione testuale. Per esempio

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

è più chiaro di "Penso che x sarebbe risultato essere 1,23 per y uguale o superiore a 10 e 3,21 altrimenti, ma non ho ottenuto alcun risultato". Anche in questo stupido esempio, penso che il codice sia più chiaro delle parole. L'utilizzo testthatconsente al tuo aiutante di concentrarsi sul codice, risparmiando tempo e fornendo loro un modo per sapere di aver risolto il problema, prima di pubblicarlo

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.