如何制作出色的R可重现示例


2473

与同事讨论性能,教学,发送错误报告或在邮件列表以及堆栈溢出此处寻求指导时,经常会问到一个可重复的示例,并且总是很有帮助。

您建立出色范例的秘诀是什么?如何从中粘贴数据结构以文本格式?您还应该包括哪些其他信息?

在另外还有其他招数来使用dput()dump()structure()?您何时应包含library()require()声明?其中保留的话应该避免一个,此外cdfdata等?

一个人如何成就伟大 可复制的例子?


34
我对问题的范围感到困惑。人们似乎在对SO或R帮助(如何“重现错误”)提出问题时,已经对可重现示例的解释跳了起来。帮助页面中的可复制R示例如何?在包装演示中?在教程/演示中?
baptiste

15
@baptiste:相同减去错误。我所解释的所有技术都在软件包帮助页面中使用,并且在有关R的教程和演示中都使用了
Joris Meys 2011年

33
数据有时是限制因素,因为结构可能太复杂而无法模拟。要从私有数据中生成公共数据,请执行以下操作:stackoverflow.com/questions/10454973/…中的stackoverflow.com/a/10458688/742447
-Décarie

Answers:


1726

一个最小的可复制示例包括以下各项:

  • 一个最小的数据集,是证明问题所必需的
  • 再现错误所需的最小可运行代码,可以在给定数据集上运行
  • 有关所用软件包,R版本和运行它的系统的必要信息。
  • 在随机过程的情况下,set.seed()可重现性的种子(由设置)1

有关良好的最小可复制示例的示例,请参见所使用功能的帮助文件。通常,此处给出的所有代码都满足最小可重现示例的要求:提供数据,提供最少的代码,并且一切都可运行。还要查看有关堆栈溢出的问题,其中有很多支持。

产生最小数据集

在大多数情况下,只需提供带有某些值的向量/数据帧即可轻松完成此操作。或者,您可以使用大多数软件包随附的内置数据集之一。
可以查看内置数据集的完整列表library(help = "datasets")。每个数据集都有一个简短的描述,例如,?mtcars其中“ mtcars”是列表中的数据集之一,可以获得更多信息。其他软件包可能包含其他数据集。

制作矢量很容易。有时有必要在其中添加一些随机性,并且有大量的函数可以实现此目的。sample()可以将向量随机化,或者给出仅包含几个值的随机向量。letters是包含字母的有用向量。这可以用来做因素。

一些例子:

  • 随机值:x <- rnorm(10)用于正态分布,x <- runif(10)用于均匀分布,...
  • 一些值的排列:x <- sample(1:10)对于矢量1:10,以随机顺序排列。
  • 随机因素: x <- sample(letters[1:4], 20, replace = TRUE)

对于矩阵,可以使用matrix(),例如:

matrix(1:10, ncol = 2)

可以使用创建数据帧data.frame()。应该注意命名数据框中的条目,并且不要使其过于复杂。

一个例子 :

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

对于某些问题,可能需要特定的格式。对于这些,可以使用任何的提供as.someType:功能as.factoras.Dateas.xts结合的载体和/或数据帧的技巧,...这些。

复制您的数据

如果你有一些数据,这将是太难使用这些技巧来构建,那么你可以随时让你的原始数据的一个子集,使用head()subset()或指数。然后使用dput()可以立即将其放到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")

如果您的数据框具有多个级别的因数,则dput输出可能会很笨拙,因为即使它们在数据子集中不存在,它仍会列出所有可能的因素级别。要解决此问题,可以使用该droplevels()功能。请注意以下物种是如何仅具有一个水平的因素:

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

使用时dput,您可能还希望仅包含相关列:

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

另一个警告dput是,它不适用于键data.table对象或来自的分组tbl_df(类grouped_dfdplyr。在这些情况下,您可以在共享之前转换回常规数据帧dput(as.data.frame(my_data))

最坏的情况是,您可以提供一个文本表示形式,可以使用text参数读取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)

产生最少的代码

这应该是容易的部分,但通常不是。您不应该做的是:

  • 添加所有类型的数据转换。确保提供的数据已经采用正确的格式(当然,除非是问题所在)
  • 复制并粘贴会给出错误的整个函数/代码块。首先,尝试找出哪些行完全导致错误。通常,您会发现问题出在哪里。

您应该做的是:

  • 添加如果使用任何软件包(使用library())应使用的软件包
  • 如果您打开连接或创建文件,请添加一些代码以将其关闭或删除文件(使用unlink()
  • 如果更改选项,请确保代码包含一条语句以将其还原为原始选项。(例如op <- par(mfrow=c(1,2)) ...some code... par(op)
  • 在一个新的空R会话中测试您的代码,以确保该代码可运行。人们应该能够在控制台中复制粘贴您的数据和代码,并获得与您完全相同的信息。

提供更多信息

在大多数情况下,仅R版本和操作系统就足够了。当软件包发生冲突时,提供输出sessionInfo()确实可以提供帮助。在谈论与其他应用程序的连接(通过ODBC或其他方式)时,还应提供这些应用程序的版本号,并在可能的情况下还提供有关安装程序的必要信息。

如果您在R Studio中运行R,则使用rstudioapi::versionInfo()有助于报告RStudio版本。

如果您对特定的软件包有疑问,则可能需要通过提供输出来提供该软件包的版本packageVersion("name of the package")


1 注意:的输出set.seed()在R> 3.6.0和以前的版本之间有所不同。请务必指定用于随机过程的R版本,如果在遵循旧问题时得到的结果略有不同,请不要感到惊讶。为了在这种情况下获得相同的结果,可以在RNGversion()前面使用-function set.seed()(例如:)RNGversion("3.5.2")


6
dput如果数据帧很大并且问题是由数据帧的中间产生的,该如何使用?有没有一种方法可以dput用来重现数据的中间部分(例如第60到70行)?
BgnR 2014年

27
@BgnR您可以使用索引提取数据框的一部分,例如:tmp <- mydf[50:70,]后跟dput(mydf)。如果数据帧确实很大,请尝试找出问题,然后提交引起问题的几行。
Joris Meys 2014年

4
@JorisMeys:是否有办法递归地告诉headdput限制数据到N级?我正在尝试提出可复制的示例,我的数据是数据帧的列表。因此,这dput(head(myDataObj))似乎还不够,因为它会生成14MB的输出文件。
Aleksandr Blekh

5
@JorisMeys:仅供参考-在上面的评论中作为独立问题发布了问题:stackoverflow.com/questions/25127026/…
Aleksandr Blekh 2014年

4
@Konrad最好的办法是链接到文件,并提供最小的命令来读取该文件。这比复制粘贴dput()的输出要麻烦得多
Joris Meys 2016年

589

(这是我从“ 如何编写可重现的示例”中获得的建议。我试图使它简短而又可爱)

如何编写可重现的示例。

如果您提供一个可复制的示例,则很可能会在R问题上获得良好的帮助。一个可复制的示例允许其他人仅通过复制和粘贴R代码来重新创建您的问题。

要使示例可重现,需要包括四件事:所需的程序包,数据,代码和R环境的描述。

  • 程序包应加载到脚本的顶部,因此很容易看到示例需要哪些程序包。

  • 在电子邮件或堆栈溢出问题中包含数据的最简单方法是使用dput()生成R代码来重新创建它。例如,要mtcars在R中重新创建数据集,我将执行以下步骤:

    1. dput(mtcars)在R中运行
    2. 复制输出
    3. 在我的可复制脚本中,键入mtcars <-然后粘贴。
  • 花一点时间来确保您的代码易于他人阅读:

    • 确保您使用了空格,并且变量名简洁明了,但内容丰富

    • 使用注释指示您的问题所在

    • 尽力删除与该问题无关的所有内容。
      您的代码越短,越容易理解。

  • sessionInfo()在代码的注释中包含的输出。这概括了您的R环境,并使您可以轻松检查是否使用了过时的软件包。

您可以通过启动一个新的R会话并粘贴您的脚本来检查您是否确实制作了一个可复制的示例。

在将所有代码放入电子邮件之前,请考虑将其放在Gist github上。它将为您的代码提供出色的语法高亮显示,并且您不必担心电子邮件系统会烦扰任何事情。


24
reprexin tidyverse是一个很好的软件包,用于产生最少的,可重现的示例:github.com/tidyverse/reprex
mt1022 2015年

19
我通常会收到包含代码的电子邮件。我什至收到带有包含代码的附件Word文档的电子邮件。有时,我什至会收到带有附件Word文档的电子邮件,这些文档包含代码的代码。
哈德利

304

就个人而言,我更喜欢“一个”衬垫。大致情况:

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)

数据结构应该模仿作者问题的概念,而不是精确的逐字记录结构。当变量不会覆盖我自己的变量或上帝禁止使用函数(例如df)时,我真的很感激。

或者,可以偷偷摸摸地指出一个预先存在的数据集,例如:

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

不要忘记提及您可能正在使用的任何特殊软件包。

如果您想在较大的物体上演示一些东西,可以尝试

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

如果您通过raster软件包使用空间数据,则可以生成一些随机数据。在包装插图中可以找到很多示例,但这是一个小块。

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)

如果您需要使用中实现的空间对象sp,则可以通过“空间”包中的外部文件(例如ESRI shapefile)获得一些数据集(请参阅任务视图中的空间视图)。

library(rgdal)
ogrDrivers()
dsn <- system.file("vectors", package = "rgdal")[1]
ogrListLayers(dsn)
ogrInfo(dsn=dsn, layer="cities")
cities <- readOGR(dsn=dsn, layer="cities")

1
恕我直言,在使用sample或时应runif谨慎set.seed。至少,这是我在生成依赖于采样或随机数生成的示例时收到的建议。
康拉德

1
@Konrad我同意,但这可能要视情况而定。如果您只是尝试生成一些数字,则可能不需要种子,但是如果您想了解需要固定数字的特定内容,则必须使用种子。
RomanLuštrik18年

1
使用种子imo总是更好,它使将自己的解决方案与预期输出进行比较,比较它们之间的解决方案变得更加容易,并且这种方式使不知道(也不需要知道)功能的用户喜欢runifsample不感到困惑他们无法获得相同的数据。
Moody_Mudskipper

2
@mikey您看过usmap包了吗?
罗曼·卢斯特里克(RomanLuštrik),

2
@mikey tigris软件包从人口普查局以各种格式下载shapefile
camille

277

受此文章的启发,
reproduce(<mydata>)当我需要发布到StackOverflow时,我现在使用了一个方便的函数。


快速说明

如果myData是要复制的对象的名称,请在R中运行以下命令:

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

reproduce(myData)

细节:

此函数是dput以下程序的智能包装器:

  • 自动对大型数据集进行采样(基于大小和类别。可以调整样本大小)
  • 创建一个dput输出
  • 允许你指定哪个列出口
  • 附加到它的前面,objName <- ...以便可以轻松地复制粘贴,但是...
  • 如果在Mac上运行,则输出会自动复制到剪贴板,这样您就可以简单地运行它,然后粘贴到您的问题中。

来源在这里可用:


例:

# 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为100 x102。我想对10行采样,并指定几列

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

提供以下输出:

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

还要注意,输出的整体都在一条漂亮的长行中,而不是一整段的短线。这样可以更轻松地阅读SO问题帖子,也更易于复制和粘贴。


2013年10月更新:

现在,您可以指定要占用多少行文本输出(即,将粘贴到StackOverflow中的内容)。lines.out=n为此使用参数。例:

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

    ==X==============================================================X==
         Copy+Paste this part. (If on a Mac, it is already copied!)
    ==X==============================================================X==

 DF <- structure(list(id = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 25L,25L, 25L), .Label
      = c("A", "B", "C", "D", "E", "F", "G", "H","I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U","V", "W", "X", "Y"), class = "factor"),
      X1 = c(809L, 81L, 862L,747L, 224L, 721L, 310L, 53L, 853L, 642L),
      X2 = c(926L, 409L,825L, 702L, 803L, 63L, 319L, 941L, 598L, 830L),
      X16 = c(447L,164L, 8L, 775L, 471L, 196L, 30L, 420L, 47L, 327L),
      X22 = c(335L,164L, 503L, 407L, 662L, 139L, 111L, 721L, 340L, 178L)), .Names = c("id","X1",
      "X2", "X16", "X22"), class = "data.frame", row.names = c(1L,2L, 3L, 4L, 5L, 6L, 7L, 98L, 99L, 100L))

    ==X==============================================================X==

196

这是一个很好的指南

最重要的一点是:只需确保您编写一小段代码即可运行以查看问题所在。一个有用的功能是dput(),但是如果您有非常大的数据,则可能要制作一个小的样本数据集或仅使用前10行左右。

编辑:

还要确保您确定问题出在哪里。该示例不应是带有“在线200出现错误”的整个R脚本。如果您使用R(我喜欢browser())和Google中的调试工具,则您应该能够真正确定问题出在哪里,并复制一个琐碎的示例,说明同一件事出了问题。


165

R-help邮件列表的发布指南涵盖了提问和回答问题,包括生成数据的示例:

示例:有时候,它有助于提供一个可以实际运行的小示例。例如:

如果我有一个矩阵x,如下所示:

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

我如何将其转换为具有8行,三列名为“ row”,“ col”和“ value”的数据框,它们的维名称分别为“ row”和“ col”的值,如下所示:

  > x.df
     row col value
  1    A   x      1

...
(答案可能是:

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

这个词特别重要。您应该以最小的可复制示例为目标,这意味着数据和代码应尽可能简单以解释问题。

编辑:漂亮的代码比丑陋的代码更容易阅读。使用样式指南


164

从R.2.14开始(我想),您可以将数据文本表示直接提供给read.table

 df <- read.table(header=TRUE, 
  text="Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa
") 

3
@ sebastian-c对于制作可复制的示例有什么好处?:)
TMS

@TMS认真考虑一下,如果询问者提供了数据并且问题很小(但可能有一些解决方案),则它可能会更快,并且您仍然可以按照所有步骤进行操作。
sebastian-c 2014年

146

有时,无论您如何努力,使用较小的数据实际上都无法重现该问题,并且使用合成数据也不会发生该问题(尽管显示如何生成重现该问题的合成数据集很有用,因为它排除了一些假设)。

  • 可能需要将数据发布到某个地方的Web并提供URL。
  • 如果这些数据不能公开发布,但可以共享,那么您可以通过电子邮件将其发送给感兴趣的各方(尽管这样可以减少工作人员的数量)在上面)。
  • 我实际上还没有看到这样做,因为无法发布数据的人对发布任何形式的数据都很敏感,但是似乎有些合理的做法是,即使数据被充分匿名/加扰/损坏,仍然可以发布数据某种程度上来说。

如果您不能做任何一个,那么您可能需要聘请顾问来解决您的问题...

编辑:两个有用的匿名/加扰的问题:


1
为了生成综合数据集,该问题的答案给出了有用的示例,包括fitdistr和的应用fitdistrplus
Iterator

137

到目前为止,对于可重复性部分,答案显然是非常好的。这仅仅是为了阐明可复制的示例不能也不应该成为问题的唯一组成部分。不要忘了解释您希望它看起来像什么以及问题的轮廓,而不仅仅是解释您到目前为止如何尝试实现。代码是不够的。您还需要文字。

以下是可避免的操作的可复制示例(从真实示例中得出,为了保护无辜者,更改了名称):


以下是示例数据和我遇到的部分功能。

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

我该如何实现?



124

我有一个非常简单有效的方法来制作上面没有提到的R示例。您可以先定义您的结构。例如,

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

>fix(mydata)

当您执行“修复”命令时,您将获得此弹出框

然后,您可以手动输入数据。对于较小的示例而不是较大的示例,这是有效的。


18
...然后dput(mydata)
GSee

你的前端是什么?有一个完整的答案将是很好的。等等制作一个可以直接循环的数据for (d in data) {...}
莱奥波尔德·赫兹(LéoLéopoldHertz),2016年

119

要快速创建dput数据,您只需将数据复制(一部分)到剪贴板中,然后在R中运行以下命令:

用于Excel中的数据:

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

用于txt文件中的数据:

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

您可以sep根据需要在后者中进行更改。当然,这仅在您的数据位于剪贴板中时才有效。


116

指导原则:


编写问题的主要目的应该是使读者尽可能容易地在其系统上理解和再现您的问题。为此:

  1. 提供输入数据
  2. 提供预期的输出
  3. 简要说明您的问题
    • 如果您有超过20行的文字和代码,则可以返回并简化
    • 在保留问题/错误的同时尽可能简化代码

这确实需要一些工作,但似乎是一个公平的权衡,因为您正在要求其他人为您工作。

提供数据:


内置数据集

到目前为止,最好的选择是依赖内置数据集。这使得其他人很容易处理您的问题。键入data()于R提示,看看数据是提供给您。一些经典的例子:

  • iris
  • mtcars
  • ggplot2::diamonds (外部包装,但几乎每个人都有)

有关如何查找适合您的问题的数据集,请参见此SO QA

如果您能够改写问题以使用内置数据集,则更有可能获得良好的答案(并赞成)。

自行生成的数据

如果您的问题是特定于现有数据集中未表示的数据类型的,则提供R代码,该代码生成问题可能表现在的最小数据集。例如

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

现在,尝试回答我的问题的人可以复制/粘贴这两行,并立即开始解决问题。

输出

作为最后的手段,您可以使用dput将数据对象转换为R代码(例如dput(myData))。我之所以说这是“不得已而为之”,dput是因为的输出通常很笨拙,讨厌复制粘贴,并且掩盖了其余的问题。

提供预期的输出:


曾经有人说:

预期输出的图片价值1000字

-一个非常聪明的人

如果您可以添加类似“我希望得到此结果”之类的内容:

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

对于您的问题,人们更有可能快速了解您正在尝试做什么。如果您的预期结果很大并且很笨拙,那么您可能还没有对如何简化问题进行足够的考虑(请参阅下文)。

简要说明您的问题


主要要做的是在提出问题之前尽可能简化您的问题。重新构建问题以使用内置数据集将在这方面有很大帮助。您还会经常发现,仅通过简化过程,您就能解决自己的问题。

以下是一些好问题的示例:

在这两种情况下,用户提供的简单示例几乎都不是用户的问题。相反,他们抽象了问题的性质,并将其应用于简单的数据集以提出问题。

为什么对这个问题还有另一个答案?


这个答案集中在我认为是最佳实践的方面:使用内置数据集,并以最小的形式提供结果。最突出的答案集中在其他方面。我不希望这个答案能引起人们的关注。这仅仅是在这里,以便我可以在对新手问题的评论中链接到它。


113

可复制的代码是获得帮助的关键。但是,许多用户可能甚至对粘贴他们的大部分数据都持怀疑态度。例如,他们可能正在使用敏感数据或收集的原始数据以供研究论文使用。出于任何原因,我认为最好有一个方便的功能来在公开粘贴之前“变形”我的数据。anonymize包中的功能SciencesPo非常愚蠢,但对我来说,它与dput功能的结合很好。

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

然后我将其匿名化:

> 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

在应用匿名化和dput命令之前,可能还需要采样一些变量而不是整个数据。

    # sample two variables without replacement
> anonymize(sample.df(dt,5,vars=c("Y","X")))
   Y    X
1 a1 -0.4
2 a1  0.6
3 a2 -2.4
4 a1 -1.4
5 a2  3.6

102

通常,您需要一些数据作为示例,但是,您不想发布确切的数据。要在已建立的库中使用一些现有的data.frame,请使用data命令将其导入。

例如,

data(mtcars)

然后解决问题

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

13
许多内置数据集(例如流行数据集mtcarsiris数据集)实际上不需要使用该data调用。
格雷戈尔·托马斯

92

如果您有无法使用轻松将其放入脚本的大型数据集dput(),请将数据发布到pastebin并使用read.table以下命令加载:

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

@Henrik启发。


90

我正在开发wakefield软件包,以解决快速共享可重现数据的需求,有时dput对于较小的数据集也能正常工作,但我们要处理的许多问题都更大,通过这样的方式共享如此大的数据集dput是不切实际的。

关于:

akefield允许用户共享最少的代码来重现数据。用户进行设置n(行数)并指定任意数量的预设变量函数(当前有70个),这些变量函数可以模拟真实的数据(如性别,年龄,收入等)。

安装:

目前(2015-06-11),wakefield是GitHub软件包,但在编写单元测试后最终将转到CRAN。要快速安装,请使用:

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

例:

这是一个例子:

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

这将产生:

    ID  Race Age    Sex     Hour  IQ Height  Died
1  001 White  33   Male 00:00:00 104     74  TRUE
2  002 White  24   Male 00:00:00  78     69 FALSE
3  003 Asian  34 Female 00:00:00 113     66  TRUE
4  004 White  22   Male 00:00:00 124     73  TRUE
5  005 White  25 Female 00:00:00  95     72  TRUE
6  006 White  26 Female 00:00:00 104     69  TRUE
7  007 Black  30 Female 00:00:00 111     71 FALSE
8  008 Black  29 Female 00:00:00 100     64  TRUE
9  009 Asian  25   Male 00:30:00 106     70 FALSE
10 010 White  27   Male 00:30:00 121     68 FALSE
.. ...   ... ...    ...      ... ...    ...   ...

72

如果您factor想在数据中复制一个或多个变量dput(head(mydata)),请考虑将droplevels其添加到变量中,以使最小化数据集中不存在的因子水平不包含在dput输出中,以便使示例最小

dput(droplevels(head(mydata)))


47

请不要这样粘贴您的控制台输出:

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

我们无法直接复制粘贴。

为了正确再现问题和答案,请在发布前尝试删除+&,>然后#输入以下内容:

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

还有一件事,如果您使用了某些软件包中的任何功能,请提及该库。


2
删除>#手动添加,还是有自动方法?
BCArg

3
@BCArg我>手动删除。但是,除了#,我Ctrl+Shift+CRStudio编辑器中使用了快捷方式。
user2100721

33

您可以使用reprex进行此操作。

作为mt1022指出,“......好的包装生产最小的,可重复的例子是‘reprex’tidyverse ”。

根据Tidyverse

“ reprex”的目标是将您有问题的代码打包为其他人可以运行它并感到您的痛苦。

tidyverse网站上提供了一个示例。

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

我认为这是创建可复制示例的最简单方法


33

除了上述所有我认为非常有趣的答案外,有时在这里讨论也很容易:- 如何制作一个最小的可复制示例以帮助R

创建随机向量的方法有很多,创建一个100个数字的向量,R中的随机值四舍五入到2位小数 ,R中的随机矩阵

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

请注意,有时由于各种原因(例如维度等),很难共享给定的数据。但是,当要创建可重现的数据示例时,上述所有答案都是非常重要的,思考和使用非常重要。但是请注意,为了使数据能够代表原始数据(如果OP无法共享原始数据),最好在数据示例中添加一些信息,例如(如果我们将数据称为mydf1)

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

此外,应该知道数据的类型,长度和属性,这些数据可以是数据结构

#found based on the following 
typeof(mydf1), what it is.
length(mydf1), how many elements it contains.
attributes(mydf1), additional arbitrary metadata.

#If you cannot share your original data, you can str it and give an idea about the structure of your data
head(str(mydf1))

28

这是我的一些建议:

  • 尝试使用默认的R数据集
  • 如果您有自己的数据集,请将它们包括在内dput,以便其他人可以更轻松地为您提供帮助
  • install.package()除非确实有必要,否则不要使用,人们会理解您是否只是使用requirelibrary
  • 尽量简洁

    • 有一些数据集
    • 尝试尽可能简单地描述您需要的输出
    • 在问问题之前先自己做
  • 上载图像很容易,因此如果有,则上载图
  • 还包括您可能遇到的任何错误

所有这些都是可复制示例的一部分。


1
您尚未在此处真正添加任何实质内容。dput()前面已经提到过,其中很多只是重申标准的SO准则。
Rich Scriven

1
install.package对示例中包含的功能有疑问,但实际上并没有必要(我认为)。此外,使用默认R数据集将使可重现性更加容易。SO准则没有专门讨论这些主题。此外,它是要给出我的意见,而这是我最常遇到的意见。
TheRimalaya '16

18

最好使用testthat软件包中的函数来显示您期望发生的事情。因此,其他人可以更改您的代码,直到代码正常运行为止。这减轻了想要帮助您的人的负担,因为这意味着他们不必解码您的文字描述。例如

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

比“我认为x对于y等于或超过10时x等于1.23,否则为3.21清晰,但我没有得到结果”。即使在这个愚蠢的示例中,我也认为代码比文字更清晰。使用testthat可以使您的助手将精力集中在代码上,从而节省时间,并且在他们发布代码之前,使他们可以知道他们已经解决了您的问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.