小编典典

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

python

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

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

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

一个人如何成为伟大 [R 可复制的例子?


阅读 130

收藏
2021-01-20

共1个答案

小编典典

基本上,一个最小的可复制示例(MWE)应该使其他人可以在他们的计算机上准确复制您的问题。

MWE由以下各项组成:

  • 一个最小数据集,需要说明问题
  • 重现错误所需的最小可运行代码,可以在给定数据集上运行
  • 有关所用软件包,R版本及其运行的操作系统的所有必要信息。
  • 如果是随机过程,则为可重复性设置种子(由设置set.seed())
  • 有关良好的MWE的示例,请参见所使用功能的帮助文件底部的“示例”部分。只需输入例如help(mean),或?mean在您的R控制台中短按。

提供最小的数据集
通常,不必共享庞大的数据集,而可能会使其他人不愿阅读您的问题。因此,最好使用内置数据集或创建一个类似于原始数据的小的“玩具”示例,这实际上是minimal的含义。如果出于某些原因确实需要共享原始数据,则应使用诸如的方法,该方法dput()允许其他人获取数据的精确副本。

内置数据集
您可以使用内置数据集之一。可以查看完整的内置数据集列表data()。每个数据集都有一个简短的描述,例如,可以使用获得?iris有关R附带的’iris’数据集的更多信息。已安装的程序包可能包含其他数据集。

创建示例数据集
初步说明:有时您可能需要特殊的格式(即类),例如因子,日期或时间序列。对于这些,充分利用类似的功能:as.factor,as.Date,as.xts,…例:

d <- as.Date("2020-12-30")

在哪里

class(d)
# [1] "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

注意:尽管它被广泛使用,但最好不要为数据框命名df,因为df()它是R函数x的F分布密度(即点处曲线的高度),因此可能会引起冲突。

复制原始数据
如果您有特定的原因,或者难以构建示例的数据,则最好使用来提供原始数据的一小部分dput。

为什么要使用dput()?

dput在控制台上抛出准确复制数据所需的所有信息。您可以简单地复制输出并将其粘贴到您的问题中。

dat如果从问题中共享,则从上面进行调用会产生仍然缺少有关变量类和其他功能的信息的输出。此外,type列中的空格使其很难执行任何操作。即使开始使用数据,我们也无法正确获取您数据的重要功能。

  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

子集数据

share共享一个子集,使用head()subset()或索引iris[1:4, ]。然后将其包装起来,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")), row.names = c(NA, 
4L), class = "data.frame")

使用时dput,您可能还希望仅包含相关列,例如dput(mtcars [1:3,c(2,5,6)])

注意:如果您的数据框具有多个级别的因数,则dput输出可能会很笨拙,因为即使它们在数据子集中不存在,它仍会列出所有可能的因素级别。要解决此问题,可以使用该droplevels()功能。请注意以下内容,物种是一个只有一个水平的因子,例如dput(droplevels(iris[1:4, ]))。另一个警告dput是,它不适用于键data.table对象或来自的分组tbl_df(类grouped_dftidyverse。在这些情况下,您可以在共享之前转换回常规数据帧dput(as.data.frame(my_data))

产生最少的代码
结合最少的数据(请参见上文),您的代码应该可以通过简单地复制和粘贴在另一台计算机上准确地再现问题。

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

  • 显示各种数据转换;确保提供的数据已经采用正确的格式(当然,除非是问题所在)
  • 复制并粘贴整个脚本,该脚本会在某处出现错误。尝试找到确切导致错误的行。通常,您会发现问题出在哪里。
    您应该做什么:

  • 如果使用任何软件包,请添加您使用的软件包(使用library())

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

提供必要的信息

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

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

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

Seed

使用set.seed()您可以指定种子1,即特定状态,R的随机数生成器是固定的。这使得随机功能,如sample(),rnorm(),runif()和许多其他人的,总是返回相同的结果,例如:

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

2021-01-20