Come creare un ottimo esempio riproducibile con R.
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 ottimo esempio? Come incollare le strutture dati da r in un formato di testo? Quali altre informazioni dovresti includere?
Ci sono altri trucchi oltre all'uso 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 fare un grande r esempio riproducibile?
Risposte
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, la versione R e il sistema su cui viene eseguito.
- nel caso di processi casuali, un seme (impostato da
set.seed()
) per la riproducibilità 1
Per esempi di buoni esempi riproducibili minimi , vedere 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 i dati, viene fornito il codice minimo e tutto è eseguibile. Guarda anche le domande su Stack Overflow con molti voti positivi.
Produzione di un set di dati minimo
Nella maggior parte dei casi, ciò può essere fatto facilmente fornendo solo un vettore / frame di dati con alcuni valori. Oppure puoi utilizzare uno dei set di dati incorporati, forniti con la maggior parte dei pacchetti.
È possibile visualizzare un elenco completo di set di dati incorporati con library(help = "datasets")
. C'è una breve descrizione per ogni set di dati e possono essere ottenute più informazioni, ad esempio, ?mtcars
dove "mtcars" è uno dei set di dati nell'elenco. Altri pacchetti potrebbero contenere set di dati aggiuntivi.
Creare un vettore è facile. A volte è necessario aggiungere un po 'di casualità 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 utile vettore 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, si può usare matrix()
, ad esempio:
matrix(1:10, ncol = 2)
La creazione di frame di dati può essere eseguita utilizzando data.frame()
. Si dovrebbe prestare attenzione a nominare le voci nel frame di dati e a non renderlo eccessivamente complicato.
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.someType
funzioni: 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 data frame ha un fattore con molti livelli, l' dput
output può essere ingombrante perché elencherà comunque tutti i possibili livelli di fattore anche se non sono presenti nel sottoinsieme dei dati. Per risolvere questo problema, puoi utilizzare la droplevels()
funzione. Nota 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.table
oggetti 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 fornire una rappresentazione testuale che può essere letta utilizzando il text
parametro 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)
Produzione di codice minimo
Questa dovrebbe essere la parte facile ma spesso non lo è. Quello che non dovresti fare è:
- aggiungere 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 e incolla un'intera funzione / blocco di codice che restituisce un errore. Innanzitutto, prova a individuare esattamente le righe che provocano l'errore. Il più delle volte scoprirai qual è il problema da solo.
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 eliminare i file (usando
unlink()
) - se modifichi le opzioni, assicurati che il codice contenga un'istruzione per riportarle a quelle originali. (ad esempio
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 informazioni aggiuntive
Nella maggior parte dei casi, saranno sufficienti solo la versione R e il sistema operativo. Quando sorgono conflitti con i pacchetti, fornire l'output di sessionInfo()
può davvero aiutare. Quando si parla di connessioni ad altre applicazioni (sia tramite ODBC o qualsiasi altra cosa), è necessario fornire anche i numeri di versione per quelle e, se possibile, anche le informazioni necessarie sulla configurazione.
Se si esegue R in R Studio utilizzando rstudioapi::versionInfo()
può essere utile a segnalare la versione RStudio.
Se hai un problema con un pacchetto specifico potresti voler fornire la versione del pacchetto dando l'output di packageVersion("name of the package")
.
1 Nota: l'output di è set.seed()
diverso tra R> 3.6.0 e versioni precedenti. Specifica quale versione R hai utilizzato per il processo casuale e non sorprenderti se ottieni risultati leggermente diversi quando segui vecchie domande. Per ottenere lo stesso risultato in questi casi, puoi usare la funzione RNGversion()
-prima set.seed()
(es :) RNGversion("3.5.2")
.
(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 riceva un buon aiuto con il tuo problema R se fornisci un esempio riproducibile. Un esempio riproducibile consente a qualcun altro di ricreare il tuo 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 dovrebbero essere caricati all'inizio dello script, quindi è facile vedere di quali ha bisogno l'esempio.
Il modo più semplice per includere i dati in un'e-mail o in una domanda di overflow dello stack è utilizzare
dput()
per generare il codice R per ricrearlo. Ad esempio, per ricreare ilmtcars
set di dati in R, eseguire i seguenti passaggi:- Corri
dput(mtcars)
in R - Copia l'output
- Nel mio script riproducibile, digita
mtcars <-
quindi incolla.
- Corri
Dedica un po 'di tempo ad assicurarti 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 codice, più facile sarà da capire.
Includere l'output di
sessionInfo()
in un commento nel codice. Questo riepiloga il tuo ambiente R e semplifica il controllo se stai utilizzando un pacchetto obsoleto.
Puoi verificare di aver effettivamente creato un esempio riproducibile avviando una nuova sessione R e incollando lo script in.
Prima di inserire tutto il tuo codice in un'e-mail, considera di metterlo su Gist GitHub . Fornirà al tuo codice una buona evidenziazione della sintassi e non dovrai preoccuparti che nulla venga alterato dal sistema di posta elettronica.
Personalmente, preferisco le battute "una". Qualcosa sulla falsariga:
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. Apprezzo molto quando le variabili non sovrascrivono le mie variabili o Dio non voglia, funzioni (come df
).
In alternativa, è possibile tagliare alcuni angoli e indicare un set di dati preesistente, qualcosa come:
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 i dati spaziali tramite il raster
pacchetto, 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 un oggetto spaziale come implementato in sp
, puoi ottenere alcuni set di dati tramite file esterni (come shapefile ESRI) in pacchetti "spaziali" (vedi la vista spaziale in Viste attività).
library(rgdal)
ogrDrivers()
dsn <- system.file("vectors", package = "rgdal")[1]
ogrListLayers(dsn)
ogrInfo(dsn=dsn, layer="cities")
cities <- readOGR(dsn=dsn, layer="cities")
Ispirato proprio da questo post, ora utilizzo una comoda funzione
reproduce(<mydata>)
quando devo pubblicare su StackOverflow.
ISTRUZIONI RAPIDE
Se myData
è il nome del tuo oggetto da riprodurre, esegui 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 dput
e fa quanto segue:
- campiona automaticamente un set di dati di grandi dimensioni (in base alle dimensioni e alla classe. La dimensione del campione può essere regolata)
- crea un
dput
output - consente di specificare quali colonne esportare
- si aggiunge alla parte anteriore in
objName <- ...
modo che possa essere facilmente copiato + incollato, ma ... - Se lavori su un Mac, l'output viene copiato automaticamente negli appunti, in modo che tu possa semplicemente eseguirlo e quindi incollarlo alla tua domanda.
La fonte è disponibile qui:
- Github - pubR / riprodurre.R
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 un paragrafo alto di righe spezzettate. Ciò rende più facile leggere sui post di domande SO e anche più facile copiare + incollare.
Aggiornamento ottobre 2013:
È ora possibile specificare quante righe di testo verranno occupate (cioè cosa incollare in StackOverflow). Usa l' lines.out=n
argomento per questo. Esempio:
reproduce(DF, cols=c(1:3, 17, 23), lines.out=7)
rende:
==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==
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 hai dati molto grandi, potresti voler creare un piccolo set di dati di esempio o usare solo le prime 10 righe o giù di lì.
MODIFICARE:
Assicurati anche di aver identificato tu stesso dove si trova il problema. L'esempio non dovrebbe essere un intero script R con "Sulla 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.
La mailing list di R-help ha una guida alla pubblicazione che copre sia le domande che le risposte, incluso un esempio di generazione di dati:
Esempi: a volte è utile 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 dataframe con 8 righe e tre colonne denominate "row", "col" e "value", che hanno i nomi delle dimensioni come valori di "row" e "col", in questo modo:
> x.df
row col value
1 A x 1
...
(A cui la risposta potrebbe essere:
> 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 piccolo è particolarmente importante. Dovresti mirare a un esempio riproducibile minimo , il che significa che i dati e il codice dovrebbero essere il più semplici possibile per spiegare il problema.
EDIT: il codice grazioso è più facile da leggere del codice brutto. Usa una guida di stile .
Dalla R.2.14 (immagino) puoi fornire la rappresentazione del testo dei tuoi 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
")
A volte il problema non è davvero riproducibile con un dato più piccolo, non importa quanto ci provi, e non si verifica con dati sintetici (sebbene sia utile mostrare come hai prodotto set di dati sintetici che non hanno riprodotto 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 rilasciati al pubblico in generale, ma potrebbero essere condivisi del tutto, potresti essere in grado di offrirti di inviarli tramite posta elettronica alle parti interessate (sebbene ciò ridurrà il numero di persone che si preoccuperanno di lavorare su di essa).
- In realtà non l'ho visto fare, perché le persone che non possono rilasciare i propri dati sono sensibili al rilascio di qualsiasi forma, ma sembrerebbe plausibile che in alcuni casi si possano ancora pubblicare dati se fossero sufficientemente anonimizzati / codificati / corrotti leggermente in qualche modo.
Se non puoi fare nessuno di questi, probabilmente devi assumere un consulente per risolvere il tuo problema ...
modifica : due utili domande SO per l'anonimizzazione / rimescolamento:
- Come creare un set di dati di esempio da dati privati (sostituendo nomi e livelli di variabili con segnaposto non informativi)?
- Dato un insieme di numeri casuali tratti da una distribuzione univariata continua, trova la distribuzione
Le risposte finora sono ovviamente ottime per la parte della riproducibilità. Questo è solo per chiarire che un esempio riproducibile non può e non deve essere l'unico componente di una domanda. Non dimenticare di spiegare come vuoi che sia e i contorni del tuo problema, non solo come hai tentato di arrivarci finora. Il codice non è sufficiente; hai bisogno anche di parole.
Ecco un esempio riproducibile di cosa evitare di fare (tratto da un esempio reale, nomi modificati 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 ?
Ho un modo molto semplice ed efficiente per creare un esempio R che non è stato menzionato sopra. In primo luogo puoi definire la tua struttura. Per esempio,
mydata <- data.frame(a=character(0), b=numeric(0), c=numeric(0), d=numeric(0))
>fix(mydata)
Quindi puoi inserire i tuoi dati manualmente. Questo è efficiente per esempi più piccoli piuttosto che per quelli grandi.
Per creare rapidamente uno dput
dei tuoi dati puoi semplicemente copiare (una parte di) i dati negli appunti ed eseguire quanto segue in R:
per i 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 sep
in quest'ultimo se necessario. Questo funzionerà solo se i tuoi dati sono negli appunti, ovviamente.
Linee guida:
Il tuo obiettivo principale nell'elaborare le tue domande dovrebbe essere quello di rendere il più semplice possibile per i lettori capire e riprodurre il tuo problema sui loro sistemi. Fare così:
- Fornisci dati di input
- Fornire l'output previsto
- Spiega brevemente il tuo problema
- se hai più di 20 righe di testo + codice puoi probabilmente tornare indietro e semplificare
- semplificare il più possibile il codice preservando il problema / errore
Questo richiede un po 'di lavoro, ma sembra un giusto compromesso dal momento che stai chiedendo ad altri di lavorare per te.
Fornitura di dati:
Set di dati incorporati
L'opzione migliore è di gran lunga affidarsi a set di dati integrati. Questo rende molto facile per gli altri lavorare sul tuo problema. Digita 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)
Esamina i set di dati integrati per trovarne uno adatto al tuo problema.
Se sei in grado di riformulare il tuo problema per utilizzare i set di dati incorporati, è molto più probabile che tu ottenga buone risposte (e voti positivi).
Dati autogenerati
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 dput
per trasformare un oggetto dati in codice R (ad esempio dput(myData)
). Dico come "ultima risorsa" perché l'output di dput
è spesso piuttosto ingombrante, fastidioso da copiare e incollare e oscura il resto della domanda.
Fornire l'output previsto:
Qualcuno una volta ha detto:
Un'immagine dell'output previsto vale 1000 parole
- una persona molto saggia
Se puoi aggiungere qualcosa come "Mi aspettavo di ottenere questo risultato":
cyl mean.hp
1: 6 122.28571
2: 4 82.63636
3: 8 209.21429
alla tua domanda, è molto più probabile che le persone capiscano rapidamente cosa stai cercando di fare. Se il risultato atteso è ampio e poco maneggevole, probabilmente non hai pensato abbastanza a 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 incorporati aiuterà molto a questo proposito. Scoprirai anche spesso che solo attraversando il processo di semplificazione risponderai al tuo problema.
Ecco alcuni esempi di buone domande:
- con set di dati integrato
- con i dati generati dagli utenti
In entrambi i casi, i problemi dell'utente quasi certamente non sono legati ai semplici esempi che forniscono. Piuttosto, hanno astratto la natura del loro problema e l'hanno applicata a un semplice set di dati per porre la loro domanda.
Perché ancora un'altra risposta a questa domanda?
Questa risposta si concentra su quella che penso sia la migliore pratica: usa set di dati incorporati e fornisci ciò che ti aspetti come risultato in una forma minima. Le risposte più importanti si concentrano su altri aspetti. Non mi aspetto che questa risposta raggiunga alcun rilievo; questo è qui solo in modo che possa collegarmi ad esso nei commenti alle domande dei principianti.
Il codice riproducibile è la chiave per ottenere aiuto. Tuttavia, ci sono molti utenti che potrebbero essere scettici sull'incollare anche solo 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 bello avere una comoda funzione per "deformare" i miei dati prima di incollarli pubblicamente. La anonymize
funzione del pacchetto SciencesPo
è molto sciocca, ma per me funziona bene con la dput
funzione.
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 anonimo:
> 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 l'anonimizzazione e il comando 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
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 poi risolvi il problema
names(mtcars)
your problem demostrated on the mtcars data set
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 .
Sto sviluppando il pacchetto Wakefield per rispondere a questa esigenza di condividere rapidamente dati riproducibili, a volte dput
funziona bene per set di dati più piccoli, ma molti dei problemi che affrontiamo sono molto più grandi, condividere un set di dati così grande tramite dput
è impraticabile.
Di:
Wakefield consente all'utente di condividere un codice minimo per riprodurre i dati. L'utente impostan
(numero di righe) e specifica un numero qualsiasi di funzioni variabili preimpostate (attualmente ce ne sono 70) che imitano i dati reali (cose come sesso, età, reddito ecc.)
Installazione:
Attualmente (2015-06-11), wakefield è un pacchetto GitHub ma andrà a CRAN alla fine dopo la scrittura degli unit test. Per installare rapidamente, usa:
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
.. ... ... ... ... ... ... ... ...
Se si dispone di una o più factor
variabili nei dati con cui si desidera renderli riproducibili dput(head(mydata))
, considerare di aggiungerle droplevels
, in modo che i livelli di fattori che non sono presenti nel set di dati ridotto a icona non siano inclusi dput
nell'output, al fine di rendi l'esempio minimo :
dput(droplevels(head(mydata)))
Mi chiedo se un file http://old.r-fiddle.org/link potrebbe essere un modo molto semplice per condividere un problema. Riceve un ID univoco simile e si potrebbe persino pensare di incorporarlo in SO.
Si prega di non incollare gli output 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 le domande e le risposte correttamente riproducibili, prova a rimuovere +
& >
prima di pubblicarlo e a inserire #
output e commenti 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")
Un'altra cosa, se hai usato una funzione da un certo pacchetto, menziona quella libreria.
Puoi farlo usando reprex .
Come ha notato mt1022 , "... un buon pacchetto per produrre esempi minimi e riproducibili è " reprex " di tidyverse ".
Secondo Tidyverse :
L'obiettivo di "reprex" è impacchettare il codice problematico in modo tale che altre persone possano eseguirlo e sentire il tuo dolore.
Un esempio è fornito sul sito web di tidyverse .
library(reprex)
y <- 1:4
mean(y)
reprex()
Penso che questo sia il modo più semplice per creare un esempio riproducibile.
A parte tutte le risposte sopra che ho trovato molto interessanti, a volte potrebbe essere molto facile come discusso qui: - COME FARE UN ESEMPIO MINIMO RIPRODUCIBILE PER OTTENERE AIUTO CON R
Esistono molti modi per creare un vettore casuale Crea un vettore di 100 numeri con valori casuali in R arrotondati a 2 decimali o una 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 ottime e molto importanti da pensare e utilizzare quando si vuole fare un esempio di dati riproducibile. Ma si noti che per rendere un dato rappresentativo dell'originale (nel caso in cui l'OP non possa condividere i dati originali), è bene aggiungere alcune informazioni con l'esempio di 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 di 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))
Ecco alcuni dei miei suggerimenti:
- Prova a utilizzare i set di dati R predefiniti
- Se hai il tuo set di dati, includili con
dput
, in modo che altri possano aiutarti più facilmente - Non usare a
install.package()
meno che non sia veramente necessario, le persone capiranno se usi solorequire
olibrary
Cerca di essere conciso,
- Avere un set di dati
- Prova a descrivere l'output di cui hai bisogno nel modo più semplice possibile
- Fallo da solo prima di porre la domanda
- È facile caricare un'immagine, quindi carica i grafici se lo hai
- Includere anche eventuali errori che potresti avere
Tutti questi fanno parte di un esempio riproducibile.
È una buona idea usare le funzioni del testthat
pacchetto per mostrare cosa ci si aspetta che accada. Pertanto, altre persone possono modificare il codice finché non viene eseguito senza errori. Questo allevia il peso 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 risulterebbe essere 1,23 per y uguale o superiore a 10 e 3,21 altrimenti, ma non ho ottenuto nessuno dei due risultati". Anche in questo stupido esempio, penso che il codice sia più chiaro delle parole. L'utilizzo testthat
consente al tuo aiutante di concentrarsi sul codice, il che fa risparmiare tempo e fornisce loro un modo per sapere che hanno risolto il tuo problema, prima di pubblicarlo