Cómo hacer un gran ejemplo reproducible de R

2473

Cuando se discute el desempeño con colegas, se enseña, se envía un informe de error o se busca orientación en las listas de correo y aquí en Stack Overflow, a menudo se pide un ejemplo reproducible y siempre es útil.

¿Cuáles son tus consejos para crear un excelente ejemplo? ¿Cómo se pegan las estructuras de datos deen formato de texto? ¿Qué otra información debe incluir?

¿Hay otros trucos además de usar dput(), dump()o structure()? ¿Cuándo debe incluir library()o require()declaraciones? ¿Qué palabras reservadas debe uno evitar, además c, df, data, etc.?

¿Cómo se puede hacer un gran ejemplo reproducible?

Hack-R
fuente
34
Estoy confundido sobre el alcance de la pregunta. Las personas parecen haber saltado a la interpretación de ejemplos reproducibles al hacer preguntas sobre SO o R-help (cómo "reproducir el error"). ¿Qué pasa con los ejemplos reproducibles de R en las páginas de ayuda? ¿En demos de paquetes? En tutoriales / presentaciones?
Baptiste
15
@baptiste: lo mismo menos el error. Todas las técnicas que expliqué se usan en páginas de ayuda de paquetes, y en tutoriales y presentaciones que doy sobre R
Joris Meys el
33
Los datos son a veces el factor limitante, ya que la estructura puede ser demasiado compleja para simular. Para producir datos públicos a partir de datos privados: stackoverflow.com/a/10458688/742447 en stackoverflow.com/questions/10454973/…
Etienne Low-Décarie

Respuestas:

1727

Un ejemplo reproducible mínimo consta de los siguientes elementos:

  • Un conjunto de datos mínimo, necesario para demostrar el problema
  • El código ejecutable mínimo necesario para reproducir el error, que se puede ejecutar en el conjunto de datos dado
  • la información necesaria sobre los paquetes usados, la versión R y el sistema en el que se ejecuta.
  • en el caso de procesos aleatorios, una semilla (establecida por set.seed()) para la reproducibilidad 1

Para ver ejemplos de buenos ejemplos reproducibles mínimos , consulte los archivos de ayuda de la función que está utilizando. En general, todo el código proporcionado cumple los requisitos de un ejemplo reproducible mínimo: se proporcionan datos, se proporciona un código mínimo y todo es ejecutable. También mire las preguntas sobre Stack Overflow con muchos votos a favor.

Produciendo un conjunto de datos mínimo

Para la mayoría de los casos, esto se puede hacer fácilmente simplemente proporcionando un marco de vector / datos con algunos valores. O puede usar uno de los conjuntos de datos integrados, que se proporcionan con la mayoría de los paquetes.
Se puede ver una lista completa de los conjuntos de datos integrados library(help = "datasets"). Hay una breve descripción de cada conjunto de datos y se puede obtener más información, por ejemplo, ?mtcarsdonde 'mtcars' es uno de los conjuntos de datos en la lista. Otros paquetes pueden contener conjuntos de datos adicionales.

Hacer un vector es fácil. A veces es necesario agregarle algo de aleatoriedad, y hay una gran cantidad de funciones para hacerlo. sample()puede aleatorizar un vector o dar un vector aleatorio con solo unos pocos valores. letterses un vector útil que contiene el alfabeto. Esto se puede usar para hacer factores.

Algunos ejemplos :

  • valores aleatorios: x <- rnorm(10)para distribución normal, x <- runif(10)para distribución uniforme, ...
  • una permutación de algunos valores: x <- sample(1:10)para el vector 1:10 en orden aleatorio.
  • un factor aleatorio: x <- sample(letters[1:4], 20, replace = TRUE)

Para las matrices, uno puede usar matrix(), por ejemplo:

matrix(1:10, ncol = 2)

Hacer marcos de datos se puede hacer usando data.frame(). Se debe prestar atención a nombrar las entradas en el marco de datos y no hacerlo demasiado complicado.

Un ejemplo :

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

Para algunas preguntas, se pueden necesitar formatos específicos. Para estos, uno puede utilizar cualquiera de los previstos as.someTypefunciones: as.factor, as.Date, as.xts, ... Estos en combinación con los trucos de vectores y / o trama de datos.

Copia tus datos

Si usted tiene algunos datos que serían demasiado difíciles de construir uso de estos consejos, entonces siempre se puede hacer un subconjunto de los datos originales, utilizando head(), subset()o los índices. Luego use dput()para darnos algo que se pueda poner en R inmediatamente:

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

Si su marco de datos tiene un factor con muchos niveles, la dputsalida puede ser difícil de manejar porque aún enumerará todos los niveles de factor posibles, incluso si no están presentes en el subconjunto de sus datos. Para resolver este problema, puede usar la droplevels()función. Observe a continuación cómo las especies son un factor con un solo nivel:

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

Al usar dput, es posible que también desee incluir solo columnas relevantes:

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

Otra advertencia para dputes que no funcionará para data.tableobjetos con clave o para agrupados tbl_df(clase grouped_df) de dplyr. En estos casos se puede convertir de nuevo a una trama de datos regular antes de compartir dput(as.data.frame(my_data)).

En el peor de los casos, puede proporcionar una representación de texto que se puede leer utilizando el textparámetro de 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)

Produciendo código mínimo

Esta debería ser la parte fácil, pero a menudo no lo es. Lo que no debes hacer es:

  • agregue todo tipo de conversiones de datos. Asegúrese de que los datos proporcionados ya estén en el formato correcto (a menos que ese sea el problema, por supuesto)
  • copie y pegue una función completa / fragmento de código que genera un error. Primero, intente localizar qué líneas dan como resultado exactamente el error. La mayoría de las veces descubrirá cuál es el problema usted mismo.

Lo que debes hacer es:

  • agregue qué paquetes deben usarse si usa alguno (usando library())
  • si abre conexiones o crea archivos, agregue algún código para cerrarlos o elimine los archivos (usando unlink())
  • Si cambia las opciones, asegúrese de que el código contenga una declaración para revertirlas a las originales. (por ejemplo op <- par(mfrow=c(1,2)) ...some code... par(op))
  • pruebe ejecutar su código en una nueva sesión R vacía para asegurarse de que el código sea ejecutable. Las personas deberían poder copiar y pegar sus datos y su código en la consola y obtener exactamente lo mismo que usted.

Dar información extra

En la mayoría de los casos, solo la versión R y el sistema operativo serán suficientes. Cuando surgen conflictos con los paquetes, dar la salida de sessionInfo()realmente puede ayudar. Cuando se habla de conexiones a otras aplicaciones (ya sea a través de ODBC o cualquier otra cosa), también se deben proporcionar números de versión para esas y, si es posible, también la información necesaria sobre la configuración.

Si está ejecutando R en R Studio usando rstudioapi::versionInfo()puede ser útil para reportar su versión rstudio.

Si tiene un problema con un paquete específico, puede proporcionar la versión del paquete dando la salida de packageVersion("name of the package").


1 Nota: La salida de set.seed()difiere entre R> 3.6.0 y versiones anteriores. Especifique qué versión de R utilizó para el proceso aleatorio, y no se sorprenda si obtiene resultados ligeramente diferentes al seguir preguntas anteriores. Para obtener el mismo resultado en tales casos, puede usar la RNGversion()función -antes set.seed()(por ejemplo :) RNGversion("3.5.2").

Joris Meys
fuente
66
¿Cómo se usa dputsi el marco de datos es muy grande y el problema se genera en el medio del marco de datos? ¿Hay alguna manera de usar dputpara reproducir la sección media de datos, digamos las filas 60 a 70?
BgnR
27
@BgnR Puede extraer parte del marco de datos utilizando índices, por ejemplo: tmp <- mydf[50:70,]seguido de dput(mydf). Si el marco de datos es realmente grande, intente aislar el problema y solo envíe las pocas líneas que causan el problema.
Joris Meys
44
@JorisMeys: ¿Hay alguna manera de decir heado dputlimitar los datos al nivel N de forma recursiva? Estoy tratando de encontrar un ejemplo reproducible y mis datos son una lista de marcos de datos. Entonces, dput(head(myDataObj))parece que no es suficiente, ya que genera un archivo de salida de 14 MB de tamaño.
Aleksandr Blekh
55
@JorisMeys: Solo para su información: la pregunta publicada en el comentario anterior es una pregunta separada: stackoverflow.com/questions/25127026/… .
Aleksandr Blekh
44
@Konrad Lo mejor que puede hacer es vincular el archivo y dar el comando mínimo para leer en ese archivo. Eso será menos complicado que copiar y pegar la salida de dput () :)
Joris Meys
590

(Aquí está mi consejo de Cómo escribir un ejemplo reproducible . He tratado de hacerlo breve pero dulce)

Cómo escribir un ejemplo reproducible.

Es más probable que obtenga una buena ayuda con su problema de R si proporciona un ejemplo reproducible. Un ejemplo reproducible le permite a otra persona recrear su problema simplemente copiando y pegando el código R.

Hay cuatro cosas que debe incluir para que su ejemplo sea reproducible: paquetes necesarios, datos, código y una descripción de su entorno R.

  • Los paquetes deben cargarse en la parte superior del script, por lo que es fácil ver cuáles necesita el ejemplo.

  • La forma más fácil de incluir datos en un correo electrónico o pregunta de desbordamiento de pila es utilizar dput()para generar el código R para recrearlo. Por ejemplo, para recrear el mtcarsconjunto de datos en R, realizaría los siguientes pasos:

    1. Correr dput(mtcars)en r
    2. Copia la salida
    3. En mi script reproducible, escriba y mtcars <-luego pegue.
  • Dedique un poco de tiempo a asegurarse de que su código sea ​​fácil de leer para otros:

    • asegúrese de haber usado espacios y sus nombres de variables sean concisos, pero informativos

    • use comentarios para indicar dónde radica su problema

    • haga todo lo posible para eliminar todo lo que no esté relacionado con el problema.
      Cuanto más corto sea su código, más fácil será de entender.

  • Incluya la salida de sessionInfo()en un comentario en su código. Esto resume su entorno R y facilita la comprobación de si está utilizando un paquete desactualizado.

Puede verificar que realmente haya hecho un ejemplo reproducible iniciando una nueva sesión R y pegando su script.

Antes de poner todo su código en un correo electrónico, considere ponerlo en Gistub de Gist . Le dará a su código un buen resaltado de sintaxis, y no tiene que preocuparse de que el sistema de correo electrónico estropee nada.

hadley
fuente
24
reprexin tidyversees un buen paquete para producir un ejemplo mínimo y reproducible: github.com/tidyverse/reprex
mt1022
19
De manera rutinaria recibo correos electrónicos con código en ellos. Incluso recibo correos electrónicos con documentos de Word adjuntos que contienen código. A veces incluso recibo correos electrónicos con documentos de Word adjuntos que contienen SCREENSHOTS de código.
Hadley
304

Personalmente, prefiero los revestimientos "uno". Algo en la línea:

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 estructura de datos debe imitar la idea del problema del escritor y no la estructura textual exacta. Realmente aprecio que las variables no sobrescriban mis propias variables o que Dios no lo permita, funciones (como df).

Alternativamente, uno podría cortar algunas esquinas y señalar un conjunto de datos preexistente, algo como:

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

No olvide mencionar cualquier paquete especial que pueda estar usando.

Si está intentando demostrar algo en objetos más grandes, puede intentar

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

Si está trabajando con datos espaciales a través del rasterpaquete, puede generar algunos datos aleatorios. Se pueden encontrar muchos ejemplos en la viñeta del paquete, pero aquí hay una pequeña 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)

Si necesita algún objeto espacial tal como está implementado sp, puede obtener algunos conjuntos de datos a través de archivos externos (como el archivo de forma ESRI) en paquetes "espaciales" (consulte la Vista espacial en Vistas de tareas).

library(rgdal)
ogrDrivers()
dsn <- system.file("vectors", package = "rgdal")[1]
ogrListLayers(dsn)
ogrInfo(dsn=dsn, layer="cities")
cities <- readOGR(dsn=dsn, layer="cities")
Roman Luštrik
fuente
1
En mi humilde opinión, cuando se usa sampleo runifes prudente set.seed. Al menos, esta es la sugerencia que he recibido al producir ejemplos que se transmiten en muestreo o generación de números aleatorios.
Konrad
1
@ Konrad Estoy de acuerdo, pero esto puede depender. Si solo está tratando de generar algunos números, es posible que no se necesite una semilla, pero si está tratando de comprender algo específico donde se necesitan números fijos, una semilla sería obligatoria.
Roman Luštrik
1
Siempre es mejor con una semilla imo, hace que sea más fácil comparar su propia solución con la salida esperada, comparar soluciones entre ellos, y de esta manera los usuarios que no saben (y no necesitan saber) funcionan como runifo sampleno están confundidos que no pueden obtener los mismos datos.
Moody_Mudskipper
2
@mikey ¿has mirado el paquete usmap ?
Roman Luštrik
2
@mikey el paquete tigris descargas archivos de forma de la oficina de censo en una variedad de formatos
Camille
277

Inspirado en esta misma publicación, ahora uso una función práctica
reproduce(<mydata>)cuando necesito publicar en StackOverflow.


INSTRUCCIONES RÁPIDAS

Si myDataes el nombre de su objeto para reproducir, ejecute lo siguiente en R:

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

reproduce(myData)

Detalles:

Esta función es un contenedor inteligente dputy hace lo siguiente:

  • muestrea automáticamente un gran conjunto de datos (según el tamaño y la clase. El tamaño de la muestra se puede ajustar)
  • crea una dputsalida
  • le permite especificar qué columnas exportar
  • se agrega al frente objName <- ...para que se pueda copiar y pegar fácilmente, pero ...
  • Si trabaja en una Mac, la salida se copia automáticamente al portapapeles, de modo que simplemente puede ejecutarla y luego pegarla a su pregunta.

La fuente está disponible aquí:


Ejemplo:

# 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 mide aproximadamente 100 x 102. Quiero muestrear 10 filas y algunas columnas específicas

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

Da el siguiente resultado:

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

Observe también que la totalidad de la salida está en una bonita línea larga y no un párrafo alto de líneas cortadas. Esto facilita la lectura en publicaciones de preguntas SO y también es más fácil copiar y pegar.


Actualización de octubre de 2013:

Ahora puede especificar cuántas líneas de salida de texto ocuparán (es decir, qué pegará en StackOverflow). Usa el lines.out=nargumento para esto. Ejemplo:

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

    ==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==
Ricardo Saporta
fuente
196

Aquí hay una buena guía .

El punto más importante es: solo asegúrese de hacer un pequeño fragmento de código que podamos ejecutar para ver cuál es el problema . Una función útil para esto es dput(), pero si tiene datos muy grandes, es posible que desee hacer un pequeño conjunto de datos de muestra o solo usar las primeras 10 líneas más o menos.

EDITAR:

También asegúrese de identificar dónde está el problema usted mismo. El ejemplo no debe ser un script R completo con "En la línea 200 hay un error". Si usa las herramientas de depuración en R (Me encanta browser()) y Google, debería poder identificar realmente dónde está el problema y reproducir un ejemplo trivial en el que lo mismo sale mal.

Sacha Epskamp
fuente
165

La lista de correo de R-help tiene una guía de publicación que cubre preguntas y respuestas, incluido un ejemplo de generación de datos:

Ejemplos: a veces es útil proporcionar un pequeño ejemplo que alguien realmente puede ejecutar. Por ejemplo:

Si tengo una matriz x de la siguiente manera:

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

¿Cómo puedo convertirlo en un marco de datos con 8 filas y tres columnas llamadas 'fila', 'col' y 'valor', que tienen los nombres de dimensión como los valores de 'fila' y 'col', como este:

  > x.df
     row col value
  1    A   x      1

...
(A lo que la respuesta podría ser:

  > 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 palabra pequeña es especialmente importante. Debería apuntar a un ejemplo reproducible mínimo , lo que significa que los datos y el código deberían ser lo más simples posible para explicar el problema.

EDITAR: El código bonito es más fácil de leer que el código feo. Usa una guía de estilo .

algodón Richie
fuente
164

Desde R.2.14 (supongo) puede alimentar su representación de texto de datos directamente 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
") 
Paolo
fuente
3
@ sebastian-c ¿cómo es eso bueno para hacer un ejemplo reproducible? :)
TMS
@TMS Considerándolo seriamente, si el autor de la pregunta ha proporcionado los datos y el problema es pequeño (pero podría tener algunas soluciones), entonces podría ser más rápido y aún puede seguir todos los pasos.
sebastian-c
146

A veces, el problema realmente no es reproducible con un dato más pequeño, no importa cuánto lo intente, y no sucede con datos sintéticos (aunque es útil para mostrar cómo produjo conjuntos de datos sintéticos que no reprodujeron el problema, porque descarta algunas hipótesis).

  • Puede ser necesario publicar los datos en la web en algún lugar y proporcionar una URL.
  • Si los datos no pueden divulgarse al público en general, pero podrían compartirse en absoluto, entonces puede ofrecer enviarlos por correo electrónico a las partes interesadas (aunque esto reducirá la cantidad de personas que se molestarán en trabajar en eso).
  • Realmente no he visto esto hecho, porque las personas que no pueden divulgar sus datos son sensibles a la divulgación de cualquier forma, pero parece plausible que en algunos casos aún se puedan publicar datos si estuvieran lo suficientemente anonimizados / codificados / corrompidos ligeramente de alguna manera.

Si no puede hacer ninguno de estos, entonces probablemente necesite contratar a un consultor para resolver su problema ...

editar : Dos preguntas SO útiles para la anonimización / codificación:

Ben Bolker
fuente
1
Para producir conjuntos de datos sintéticos, las respuestas a esta pregunta dan ejemplos útiles, incluidas las aplicaciones de fitdistry fitdistrplus.
Iterator
137

Las respuestas hasta ahora son obviamente excelentes para la parte de reproducibilidad. Esto es simplemente para aclarar que un ejemplo reproducible no puede y no debe ser el único componente de una pregunta. No olvides explicar cómo quieres que se vea y los contornos de tu problema, no solo cómo has intentado llegar hasta ahora. El código no es suficiente; Necesitas palabras también.

Aquí hay un ejemplo reproducible de qué evitar hacer (extraído de un ejemplo real, nombres cambiados para proteger a los inocentes):


Lo siguiente son datos de muestra y parte de la función con la que tengo problemas.

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

Cómo puedo conseguir esto ?


Ari B. Friedman
fuente
124

Tengo una manera muy fácil y eficiente de hacer un ejemplo de R que no se ha mencionado anteriormente. Puede definir su estructura en primer lugar. Por ejemplo,

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

>fix(mydata)

Cuando ejecute el comando 'arreglar', obtendrá este cuadro emergente

Luego puede ingresar sus datos manualmente. Esto es eficiente para ejemplos más pequeños en lugar de grandes.

jasmine_007
fuente
18
... entoncesdput(mydata)
GSee
¿Cuál es tu interfaz? Sería bueno tener una respuesta completa. Etc crea una información que puede hacer un bucle directamente for (d in data) {...}.
Léo Léopold Hertz 준영
119

Para crear rápidamente un dput de sus datos, puede simplemente copiar (un pedazo de) los datos en su portapapeles y ejecutar lo siguiente en R:

para datos en Excel:

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

para datos en un archivo txt:

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

Puede cambiar el sepen este último si es necesario. Esto solo funcionará si sus datos están en el portapapeles, por supuesto.

JT85
fuente
116

Pautas:


Su objetivo principal en la elaboración de sus preguntas debe ser hacer que sea lo más fácil posible para los lectores comprender y reproducir su problema en sus sistemas. Para hacerlo:

  1. Proporcionar datos de entrada
  2. Proporcionar salida esperada
  3. Explica tu problema sucintamente
    • Si tiene más de 20 líneas de texto + código, probablemente pueda regresar y simplificar
    • simplifique su código tanto como sea posible mientras preserva el problema / error

Esto requiere algo de trabajo, pero parece una compensación justa ya que le está pidiendo a otros que hagan el trabajo por usted.

Proporcionar datos:


Conjuntos de datos incorporados

La mejor opción , con mucho, es confiar en los conjuntos de datos incorporada. Esto hace que sea muy fácil para otros trabajar en su problema. Escriba data()en el indicador R para ver qué datos están disponibles para usted. Algunos ejemplos clásicos:

  • iris
  • mtcars
  • ggplot2::diamonds (paquete externo, pero casi todos lo tienen)

Consulte este SO QA para saber cómo encontrar conjuntos de datos adecuados para su problema.

Si puede reformular su problema para utilizar los conjuntos de datos integrados, es mucho más probable que obtenga buenas respuestas (y votos a favor).

Datos autogenerados

Si su problema es muy específico para un tipo de datos que no está representado en los conjuntos de datos existentes, proporcione el código R que genera el conjunto de datos más pequeño posible en el que se manifiesta su problema. Por ejemplo

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

Ahora, alguien que intenta responder mi pregunta puede copiar / pegar esas dos líneas y comenzar a trabajar en el problema de inmediato.

dput

Como último recurso , puede usar dputpara transformar un objeto de datos en código R (por ejemplo dput(myData)). Digo como "último recurso" porque la salida de dputes a menudo bastante difícil de manejar, molesta para copiar y pegar, y oscurece el resto de su pregunta.

Proporcionar salida esperada:


Alguien dijo una vez:

Una imagen de salida esperada vale 1000 palabras

- una persona muy sabia

Si puede agregar algo como "esperaba obtener este resultado":

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

a su pregunta, es mucho más probable que las personas entiendan rápidamente lo que está tratando de hacer. Si su resultado esperado es grande y difícil de manejar, entonces probablemente no haya pensado lo suficiente sobre cómo simplificar su problema (ver a continuación).

Explique su problema sucintamente


Lo principal que debe hacer es simplificar su problema tanto como sea posible antes de hacer su pregunta. Reenmarcar el problema para que funcione con los conjuntos de datos integrados ayudará mucho a este respecto. También encontrará a menudo que con solo pasar por el proceso de simplificación responderá su propio problema.

Aquí hay algunos ejemplos de buenas preguntas:

En ambos casos, los problemas del usuario casi con certeza no se deben a los simples ejemplos que proporcionan. Más bien, abstrajeron la naturaleza de su problema y lo aplicaron a un conjunto de datos simple para hacer su pregunta.

¿Por qué otra respuesta a esta pregunta?


Esta respuesta se centra en lo que creo que es la mejor práctica: usar conjuntos de datos integrados y proporcionar lo que espera como resultado en una forma mínima. Las respuestas más destacadas se centran en otros aspectos. No espero que esta respuesta se destaque; esto está aquí únicamente para que pueda vincularlo en comentarios a preguntas para novatos.

BrodieG
fuente
113

El código reproducible es clave para obtener ayuda. Sin embargo, hay muchos usuarios que pueden ser escépticos de pegar incluso una parte de sus datos. Por ejemplo, podrían estar trabajando con datos confidenciales o con datos originales recopilados para usar en un trabajo de investigación. Por cualquier motivo, pensé que sería bueno tener una función útil para "deformar" mis datos antes de pegarlos públicamente. La anonymizefunción del paquete SciencesPoes muy tonta, pero para mí funciona muy bien con la dputfunción.

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

Luego lo anonimizo:

> 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

También es posible que desee muestrear algunas variables en lugar de todos los datos antes de aplicar el comando de anonimización y 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
daniel
fuente
102

A menudo necesita algunos datos como ejemplo, sin embargo, no desea publicar sus datos exactos. Para usar algunos data.frame existentes en una biblioteca establecida, use el comando de datos para importarlo.

p.ej,

data(mtcars)

y luego hacer el problema

names(mtcars)
your problem demostrated on the mtcars data set
userJT
fuente
13
Muchos conjuntos de datos integrados (como populares mtcarsy irisconjuntos de datos) en realidad no necesitan dataque se use la llamada.
Gregor Thomas
92

Si tiene un conjunto de datos grande que no se puede usar fácilmente en el script dput(), publique sus datos en pastebin y cárguelos usando read.table:

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

Inspirado por @Henrik .

TMS
fuente
90

Estoy desarrollando el paquete wakefield para abordar esta necesidad de compartir rápidamente datos reproducibles, a veces dputfunciona bien para conjuntos de datos más pequeños, pero muchos de los problemas con los que nos enfrentamos son mucho mayores, compartir un conjunto de datos tan grande no dputes práctico.

Acerca de:

wakefield permite al usuario compartir código mínimo para reproducir datos. El usuario establecen(número de filas) y especifica cualquier número de funciones variables preestablecidas (actualmente hay 70) que imitan datos reales si (cosas como género, edad, ingresos, etc.)

Instalación:

Actualmente (2015-06-11), wakefield es un paquete de GitHub pero eventualmente irá a CRAN después de que se escriban las pruebas unitarias. Para instalar rápidamente, use:

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

Ejemplo:

Aquí hay un ejemplo:

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

Esto 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
.. ...   ... ...    ...      ... ...    ...   ...
Tyler Rinker
fuente
72

Si tiene una o más factorvariables en sus datos con las que desea hacer reproducibles dput(head(mydata)), considere agregarlas droplevels, de modo que los niveles de factores que no están presentes en el conjunto de datos minimizado no se incluyan en su dputsalida, para Haz que el ejemplo sea mínimo :

dput(droplevels(head(mydata)))
revs docendo discimus
fuente
65

Me pregunto si un enlace http://old.r-fiddle.org/ podría ser una forma muy clara de compartir un problema. Recibe una identificación única como e incluso se podría pensar en incrustarlo en SO.

CMichael
fuente
47

No pegue las salidas de su consola de esta manera:

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

No podemos copiarlo y pegarlo directamente.

Para hacer que las preguntas y respuestas se reproduzcan correctamente, intente eliminar +y >antes de publicarlo y #obtenga resultados y comentarios como este:

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

Una cosa más, si ha utilizado alguna función de cierto paquete, mencione esa biblioteca.

usuario2100721
fuente
2
¿quita >y agrega el #manual o hay una forma automática de hacerlo?
BCArg
3
@BCArg elimino >manualmente. Pero, además de #, uso el Ctrl+Shift+Cacceso directo en el RStudioeditor.
user2100721
33

Puedes hacer esto usando reprex .

Como señaló mt1022 , "... un buen paquete para producir un ejemplo mínimo y reproducible es " reprex " de tidyverse ".

De acuerdo con Tidyverse :

El objetivo de "reprex" es empaquetar su código problemático de tal manera que otras personas puedan ejecutarlo y sentir su dolor.

Se da un ejemplo en el sitio web tidyverse .

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

Creo que esta es la forma más sencilla de crear un ejemplo reproducible.

andrii
fuente
33

Además de todas las respuestas anteriores que encontré muy interesantes, a veces podría ser muy fácil como se discute aquí: - CÓMO HACER UN EJEMPLO REPRODUCIBLE MÍNIMO PARA OBTENER AYUDA CON R

Hay muchas maneras de hacer un vector aleatorio Cree un vector de 100 números con valores aleatorios en R redondeados a 2 decimales o matriz aleatoria en R

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

Tenga en cuenta que a veces es muy difícil compartir datos dados debido a varias razones, como la dimensión, etc. Sin embargo, todas las respuestas anteriores son geniales y muy importantes para pensar y usar cuando se quiere hacer un ejemplo de datos reproducibles. Pero tenga en cuenta que para que los datos sean tan representativos como el original (en caso de que el OP no pueda compartir los datos originales), es bueno agregar cierta información con el ejemplo de datos como (si llamamos a los datos mydf1)

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

Además, se debe conocer el tipo, la longitud y los atributos de los datos que pueden ser estructuras de datos.

#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))
usuario5947301
fuente
28

Estas son algunas de mis sugerencias:

  • Intenta usar los conjuntos de datos R predeterminados
  • Si tiene su propio conjunto de datos, inclúyalos con dput para que otros puedan ayudarlo más fácilmente
  • No lo use a install.package()menos que sea realmente necesario, la gente entenderá si solo usa requireolibrary
  • Intenta ser conciso

    • Tener un conjunto de datos
    • Intente describir la salida que necesita de la manera más simple posible
    • Hágalo usted mismo antes de hacer la pregunta.
  • Es fácil cargar una imagen, así que cargue parcelas si tiene
  • Incluya también cualquier error que pueda tener

Todos estos son parte de un ejemplo reproducible.

TheRimalaya
fuente
1
Realmente no has agregado nada de sustancia aquí. dput()se ha mencionado anteriormente, y gran parte de esto es solo reiterar las pautas estándar de SO.
Rich Scriven
1
Estaba teniendo problemas con la install.packagefunción incluida en el ejemplo que no es realmente necesaria (en mi opinión). Además, el uso del conjunto de datos R predeterminado facilitaría la reproducción. Las pautas de SO no han mencionado nada sobre estos temas específicamente. Además, estaba destinado a dar mi opinión y estos son los que más me he encontrado.
TheRimalaya
18

Es una buena idea usar funciones del testthatpaquete para mostrar lo que espera que ocurra. Por lo tanto, otras personas pueden alterar su código hasta que se ejecute sin error. Esto alivia la carga de aquellos que desean ayudarlo, porque significa que no tienen que decodificar su descripción textual. Por ejemplo

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

es más claro que "Creo que x resultaría ser 1.23 para y igual o superior a 10, y 3.21 de lo contrario, pero no obtuve ninguno de los resultados". Incluso en este ejemplo tonto, creo que el código es más claro que las palabras. El uso testthatpermite que su asistente se concentre en el código, lo que les ahorra tiempo, y les proporciona una forma de saber que han resuelto su problema, antes de publicarlo.

húmedo
fuente