How to 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!
tl;dr
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
library
s, the R version, and the OS it is run on, perhaps asessionInfo()
- 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.
Built-in datasets
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.factor
, as.Date
, as.xts
, ... Example:
d <- as.Date("2020-12-30")
where
class(d)
# [1] "Date"
Vectors
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
Matrices
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
Data frames
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
, because 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
.
Why use dput()
?
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.
Calling 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 head()
, 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")
When using 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 tbl_df
(class grouped_df
) from the tidyverse
. In these cases you can convert back to a regular data frame before sharing, dput(as.data.frame(my_data))
.
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
library()
) - 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
unlink()
) - 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")
.
Seed
Using 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 sample()
, rnorm()
, runif()
and lots of others, to always return the same result, Example:
set.seed(42)
rnorm(3)
# [1] 1.3709584 -0.5646982 0.3631284
set.seed(42)
rnorm(3)
# [1] 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 RNGversion()
-function before set.seed()
(e.g.: RNGversion("3.5.2")
).
creating reproducible example using reprex package in r where a local file is being read
By default, reprex strongly encourages execution in the session temp directory. But sometimes it is unavoidable to refer to a specific local file, so yes, there has to be a way to do this.
To request that all work be done in current working directory, set outfile = NA
. (More generally, you can use the outfile
argument to specify a base file name and path.)
If I submit this reprex, with working directory set to my home directory:
reprex({
getwd()
writeLines(c("V1,V2","a,b"), "precious_data.csv")
list.files(pattern = "*.csv")
read.csv("precious_data.csv")
},
outfile = NA,
venue = "so"
)
I get this output:
getwd()
#> [1] "/Users/jenny"
writeLines(c("V1,V2","a,b"), "precious_data.csv")
list.files(pattern = "*.csv")
#> [1] "precious_data.csv"
read.csv("precious_data.csv")
#> V1 V2
#> 1 a b
Created on 2018-09-19 by the reprex package (v0.2.1)
Using outfile = NA
or outfile = "path/to/desired/file/base"
is the general pattern for asserting control over the location of all files generated by reprex()
.
How can I create a new column in R with a count of observations in another column in the dataset?
One way to approach this is to group_by
multiple columns, if the information is repeated for a given individual (which it does in this example). You will get these columns in your results in the end. Also, you can summarise
where the DIAGNOSIS
is not "X" - instead of count
, so that you will get zero for cases where DIAGNOSIS
is "X".
library(dplyr)
pr %>%
group_by(GROUP, ID, AGE, WEIGHT) %>%
summarise(NUMBER = sum(DIAGNOSIS != "X"))
Output
GROUP ID AGE WEIGHT NUMBER
<dbl> <dbl> <dbl> <dbl> <int>
1 1 1 34 72 3
2 1 2 61 70 0
3 1 3 45 101 1
4 1 4 23 56 2
5 2 5 55 62 1
6 2 6 43 78 1
7 2 7 56 60 2
8 3 8 49 55 3
9 3 9 61 79 0
10 3 10 74 89 1
11 4 11 51 67 0
12 4 12 46 60 1
13 4 13 75 105 1
How can I create a reproducible example of a SpatRaster (terra)?
You can create objects from scratch like this:
library(terra)
r <- rast()
s <- rast(ncols=22, nrows=25, nlyrs=5, xmin=0)
See ?terra::rast
for additional arguments you can use and for alternative approaches.
You can also use a file that ships with R. For example:
f <- system.file("ex/elev.tif", package="terra")
r <- rast(f)
You can also create from scratch a new SpatRaster with (mostly) the same properties with what is returned by
as.character(r)
And then recreate it with something like
r <- rast(ncols=95, nrows=90, nlyrs=1, xmin=5.74166666666667, xmax=6.53333333333333, ymin=49.4416666666667, ymax=50.1916666666667, names=c('elevation'), crs='GEOGCRS[\"WGS 84\",DATUM[\"World Geodetic System 1984\",ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],CS[ellipsoidal,2],AXIS[\"geodetic latitude (Lat)\",north,ORDER[1],ANGLEUNIT[\"degree\",0.0174532925199433]],AXIS[\"geodetic longitude (Lon)\",east,ORDER[2],ANGLEUNIT[\"degree\",0.0174532925199433]],ID[\"EPSG\",4326]]')
r <- init(r, "cell")
If you cannot replicate your error with example data, this may give you a hint about the problem. Does it have to do with NA
s? The file being on disk? The file format? One tricky situation is where there is a difference if the real file is much larger. You can simulate a large file by setting terraOptions(todisk=TRUE)
and using a steps
argument in a function, e.g.
b <- clamp(x, steps=5)
If none of that allows you to replicate an error, your last resort is to provide a link to the file so that others can download it. If you cannot do that, then at least show
the content of the SpatRaster x
with show(x)
and provide the code to create a similar object with as.character(x)
Related Topics
How to Remove Rows With Any Zero Value
Calculate Difference Between Values in Consecutive Rows by Group
Create and Assign Multiple New Dataframe Columns in Ifelse Statement
Split Column At Delimiter in Data Frame
How to Call an Object With the Character Variable of the Same Name
Transform Year/Week to Date Object
Access Lapply Index Names Inside Fun
Make the Background of a Graph Different Colours in Different Regions
Opposite of %In%: Exclude Rows With Values Specified in a Vector
Delete Rows That Exist in Another Data Frame
How to Create a Consecutive Group Number
How to Select Variables in an R Dataframe Whose Names Contain a Particular String
How to Debug "Contrasts Can Be Applied Only to Factors With 2 or More Levels" Error
How to Trim Leading and Trailing White Space