When discussing performance with colleagues, teaching, sending a bug report or searching for guidance on mailing lists and here on Stack Overflow, a reproducible example is often asked and always helpful.
What are your tips for creating an excellent example? How do you paste data structures from r in a text format? What other information should you include?
Are there other tricks in addition to using
structure()? When should you include
require() statements? Which reserved words should one avoid, in addition to
How does one make a great r reproducible example?
Basically, a minimal reproducible example (MRE) should enable others to exactly reproduce your issue on their machines.
Please do not post images of your data, code, or console output!
A MRE consists of the following items:
- a minimal dataset, necessary to demonstrate the problem
- the minimal runnable code necessary to reproduce the issue, which can be run on the given dataset
- all necessary information on the used
librarys, the R version, and the OS it is run on, perhaps a
- in the case of random processes, a seed (set by
set.seed()) to enable others to replicate exactly the same results as you do
For examples of good MREs, see section “Examples” at the bottom of help pages on the function you are using. Simply type e.g.
help(mean), or short
?mean into your R console.
Providing a minimal dataset
Usually, sharing huge data sets is not necessary and may rather discourage others from reading your question. Therefore, it is better to use built-in datasets or create a small “toy” example that resembles your original data, which is actually what is meant by minimal. If for some reason you really need to share your original data, you should use a method, such as
dput(), that allows others to get an exact copy of your data.
You can use one of the built-in datasets. A comprehensive list of built-in datasets can be seen with
data(). There is a short description of every data set, and more information can be obtained, e.g. with
?iris, for the ‘iris’ data set that comes with R. Installed packages might contain additional datasets.
Creating example data sets
Preliminary note: Sometimes you may need special formats (i.e. classes), such as factors, dates, or time series. For these, make use of functions like:
as.xts, … Example:
d <- as.Date("2020-12-30")
class(d) #  "Date"
x <- rnorm(10) ## random vector normal distributed x <- runif(10) ## random vector uniformly distributed x <- sample(1:100, 10) ## 10 random draws out of 1, 2, ..., 100 x <- sample(LETTERS, 10) ## 10 random draws out of built-in latin alphabet
m <- matrix(1:12, 3, 4, dimnames=list(LETTERS[1:3], LETTERS[1:4])) m # A B C D # A 1 4 7 10 # B 2 5 8 11 # C 3 6 9 12
set.seed(42) ## for sake of reproducibility n <- 6 dat <- data.frame(id=1:n, date=seq.Date(as.Date("2020-12-26"), as.Date("2020-12-31"), "day"), group=rep(LETTERS[1:2], n/2), age=sample(18:30, n, replace=TRUE), type=factor(paste("type", 1:n)), x=rnorm(n)) dat # id date group age type x # 1 1 2020-12-26 A 27 type 1 0.0356312 # 2 2 2020-12-27 B 19 type 2 1.3149588 # 3 3 2020-12-28 A 20 type 3 0.9781675 # 4 4 2020-12-29 B 26 type 4 0.8817912 # 5 5 2020-12-30 A 26 type 5 0.4822047 # 6 6 2020-12-31 B 28 type 6 0.9657529
Note: Although it is widely used, better do not name your data frame
df() is an R function for the density (i.e. height of the curve at point
x) of the F distribution and you might get a clash with it.
Copying original data
If you have a specific reason, or data that would be too difficult to construct an example from, you could provide a small subset of your original data, best by using
dput throws all information needed to exactly reproduce your data on your console. You may simply copy the output and paste it into your question.
dat (from above) produces output that still lacks information about variable classes and other features if you share it in your question. Furthermore, the spaces in the
type column make it difficult to do anything with it. Even when we set out to use the data, we won’t manage to get important features of your data right.
id date group age type x 1 1 2020-12-26 A 27 type 1 0.0356312 2 2 2020-12-27 B 19 type 2 1.3149588 3 3 2020-12-28 A 20 type 3 0.9781675
Subset your data
To share a subset, use
subset() or the indices
iris[1:4, ]. Then wrap it into
dput() to give others something that can be put in R immediately. Example
dput(iris[1:4, ]) # first four rows of the iris data set
Console output to share in your question:
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")), row.names = c(NA, 4L), class = "data.frame")
dput, you may also want to include only relevant columns, e.g. dput(mtcars[1:3, c(2, 5, 6)])
Note: If your data frame has a factor with many levels, the
dput output can be unwieldy because it will still list all the possible factor levels even if they aren’t present in the subset of your data. To solve this issue, you can use the
droplevels() function. Notice below how species is a factor with only one level, e.g.
dput(droplevels(iris[1:4, ])). One other caveat for
dput is that it will not work for keyed
data.table objects or for grouped
grouped_df) from the
tidyverse. In these cases you can convert back to a regular data frame before sharing,
Producing minimal code
Combined with the minimal data (see above), your code should exactly reproduce the problem on another machine by simply copying and pasting it.
This should be the easy part but often isn’t. What you should not do:
- showing all kinds of data conversions; make sure the provided data is already in the correct format (unless that is the problem, of course)
- copy-paste a whole script that gives an error somewhere. Try to locate which lines exactly result in the error. More often than not, you’ll find out what the problem is yourself.
What you should do:
- add which packages you use if you use any (using
- test run your code in a fresh R session to ensure the code is runnable. People should be able to copy-paste your data and your code in the console and get the same as you have.
- if you open connections or create files, add some code to close them or delete the files (using
- if you change options, make sure the code contains a statement to revert them back to the original ones. (eg
op <- par(mfrow=c(1,2)) ...some code... par(op))
Providing necessary information
In most cases, just the R version and the operating system will suffice. When conflicts arise with packages, giving the output of
sessionInfo() can really help. When talking about connections to other applications (be it through ODBC or anything else), one should also provide version numbers for those, and if possible, also the necessary information on the setup.
If you are running R in R Studio, using
rstudioapi::versionInfo() can help report your RStudio version.
If you have a problem with a specific package, you may want to provide the package version by giving the output of
packageVersion("name of the package").
set.seed() you may specify a seed1, i.e. the specific state, R’s random number generator is fixed. This makes it possible for random functions, such as
runif() and lots of others, to always return the same result, Example:
set.seed(42) rnorm(3) #  1.3709584 -0.5646982 0.3631284 set.seed(42) rnorm(3) #  1.3709584 -0.5646982 0.3631284
1 Note: The output of
set.seed() differs between R >3.6.0 and previous versions. Specify which R version you used for the random process, and don’t be surprised if you get slightly different results when following old questions. To get the same result in such cases, you can use the
(Here’s my advice from How to write a reproducible example. I’ve tried to make it short but sweet).
How to write a reproducible example
You are most likely to get good help with your R problem if you provide a reproducible example. A reproducible example allows someone else to recreate your problem by just copying and pasting R code.
You need to include four things to make your example reproducible: required packages, data, code, and a description of your R environment.
Packages should be loaded at the top of the script, so it’s easy to
see which ones the example needs.
The easiest way to include data in an email or Stack Overflow question is to use
dput()to generate the R code to recreate it. For example, to recreate the
mtcarsdataset in R,
I’d perform the following steps:
- Copy the output
- In my reproducible script, type
mtcars <-then paste.
Spend a little bit of time ensuring that your code is easy for others to
Make sure you’ve used spaces and your variable names are concise, but
Use comments to indicate where your problem lies
Do your best to remove everything that is not related to the problem.
The shorter your code is, the easier it is to understand.
Include the output of
sessionInfo()in a comment in your code. This summarises your R
environment and makes it easy to check if you’re using an out-of-date
You can check you have actually made a reproducible example by starting up a fresh R session and pasting your script in.
Before putting all of your code in an email, consider putting it on Gist github. It will give your code nice syntax highlighting, and you don’t have to worry about anything getting mangled by the email system.
Personally, I prefer “one” liners. Something along the lines:
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, list3 = letters)
The data structure should mimic the idea of the writer’s problem and not the exact verbatim structure. I really appreciate it when variables don’t overwrite my own variables or god forbid, functions (like
Alternatively, one could cut a few corners and point to a pre-existing data set, something like:
library(vegan) data(varespec) ord <- metaMDS(varespec)
Don’t forget to mention any special packages you might be using.
If you’re trying to demonstrate something on larger objects, you can try
my.df2 <- data.frame(a = sample(10e6), b = sample(letters, 10e6, replace = TRUE))
If you’re working with spatial data via the
raster package, you can generate some random data. A lot of examples can be found in the package vignette, but here’s a small nugget.
library(raster) r1 <- r2 <- r3 <- raster(nrow=10, ncol=10) values(r1) <- runif(ncell(r1)) values(r2) <- runif(ncell(r2)) values(r3) <- runif(ncell(r3)) s <- stack(r1, r2, r3)
If you need some spatial object as implemented in
sp, you can get some datasets via external files (like ESRI shapefile) in “spatial” packages (see the Spatial view in Task Views).
library(rgdal) ogrDrivers() dsn <- system.file("vectors", package = "rgdal") ogrListLayers(dsn) ogrInfo(dsn=dsn, layer="cities") cities <- readOGR(dsn=dsn, layer="cities")