如何制作一个很好的R可重复的例子

时间:2011-05-11 11:12:02

标签: r r-faq

在与同事讨论表现,教学,发送错误报告或在邮件列表上搜索指导时,以及在Stack Overflow上,reproducible example经常被问到并且总是有用。

您有什么建议创建优秀示例的提示?如何以文本格式粘贴的数据结构?您应该包括哪些其他信息?

除了使用dput()dump()还是structure()之外还有其他技巧吗?您应该何时加入library()require()声明?除了cdfdata等之外,还应避免使用哪些保留字?

如何成为一个出色的可重现的例子?

23 个答案:

答案 0 :(得分:1603)

答案 1 :(得分:562)

(这是我How to write a reproducible example的建议。我试图让它变短但很甜蜜)

如何编写可重现的示例。

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

为了使您的示例可以重现,您需要包含四件事:所需的包,数据,代码和R环境的描述。

  • 软件包应该加载到脚本的顶部,因此很容易 看看这个例子需要哪些。

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

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

    • 确保您使用了空格,并且您的变量名称简洁,但是 信息

    • 使用注释来指出问题所在

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

  • 在代码中的注释中包含sessionInfo()的输出。这总结了您的 R. 环境,可以轻松检查您是否使用过时 封装

您可以通过启动新的R会话并粘贴脚本来检查您是否已经实际制作了可重现的示例。

在将所有代码放入电子邮件之前,请考虑将其放在Gist github上。它将为您的代码提供良好的语法突出显示,并且您不必担心电子邮件系统会破坏任何内容。

答案 2 :(得分:289)

就个人而言,我更喜欢“一个”衬里。一些事情:

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

答案 3 :(得分:266)

受到这篇帖子的启发,我现在使用了一个方便的功能  当我需要发布到StackOverflow时reproduce(<mydata>)


快速说明

如果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 x 102.我想抽样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==

答案 4 :(得分:186)

这是一个很好的指南:

http://www.r-bloggers.com/three-tips-for-posting-good-questions-to-r-help-and-stack-overflow/

但最重要的是:确保你制作一小段我们可以运行的代码来查看问题所在。一个有用的函数是dput(),但是如果你有非常大的数据,你可能想要制作一个小样本数据集,或者只使用前10行左右。

编辑:

还要确保您确定问题出在哪里。该示例不应该是整个R脚本,“On line 200存在错误”。如果您使用R中的调试工具(我喜欢browser())和谷歌,您应该能够真正确定问题所在,并重现一个简单的例子,其中同样的事情出错。

答案 5 :(得分:159)

R-help邮件列表中有posting guide,其中包含提问和回答问题,包括生成数据的示例:

  

示例:有时它会有所帮助   提供一个小例子   实际上可以运行。例如:

     

如果我有一个矩阵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")
  

这个词特别重要。您应该瞄准最小可重现的示例,这意味着数据和代码应该尽可能简单来解释问题。

编辑:漂亮的代码比丑陋的代码更容易阅读。使用style guide

答案 6 :(得分:153)

自R.2.14(我猜)以来,您可以将数据文本表示直接提供给read.table:

df <- read.table(header=T, 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
") 

答案 7 :(得分:140)

有时,无论您尝试多么努力,问题都无法通过较小的数据再现,并且合成数据不会发生问题(尽管显示您如何生成合成数据集非常有用重现问题,因为它排除了一些假设)。

  • 可能需要在某处将数据发布到网上并提供URL。
  • 如果数据无法向公众发布但可以共享,那么您可以通过电子邮件将其发送给感兴趣的各方(尽管这样可以减少人数懒得去工作)。
  • 我实际上并没有看到这样做,因为无法发布数据的人对于以任何形式发布数据都很敏感,但似乎有理由认为,如果有足够的匿名/加扰,人们仍然可以发布数据/在某种程度上略有损坏。

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

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

答案 8 :(得分:130)

到目前为止,答案对于可重复性部分来说显然是很好的。这仅仅是为了澄清一个可重复的例子不能也不应该是问题的唯一组成部分。不要忘记解释你想要它的样子和问题的轮廓,而不仅仅是你到目前为止试图到达那里的方式。代码还不够;你也需要单词。

这是一个可以重复的例子,说明要避免做什么(从一个真实的例子中提取,名称改为保护无辜者):


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

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

我怎样才能做到这一点?


答案 9 :(得分:116)

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

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

>fix(mydata)

When you execute 'fix' command, you will get this pop-up box

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

答案 10 :(得分:115)

要快速创建dput数据,您只需将数据(一段)复制到剪贴板并在R中运行以下内容:

表示Excel中的数据:

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

用于txt文件中的数据:

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

如有必要,您可以更改后者中的sep。 这只适用于您的数据当然在剪贴板中的情况。

答案 11 :(得分:110)

准则:


制定问题的主要目的应该是让读者尽可能轻松地理解并在他们的系统上重现您的问题。为此:

  1. 提供输入数据
  2. 提供预期输出
  3. 简明扼要地解释你的问题
    • 如果你有超过20行的文字+代码,你可以回去简化
    • 尽可能简化代码,同时保留问题/错误
  4. 这确实需要一些工作但似乎是公平的权衡,因为你要求别人为你工作。

    提供数据:


    内置数据集

    到目前为止的最佳选择是依靠内置数据集。这使得其他人可以轻松解决您的问题。在R提示符下键入data()以查看可用的数据。一些经典的例子:

    • 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

    作为最后的手段,您可以使用dput将数据对象转换为R代码(例如dput(myData))。我说作为&#34;最后的手段&#34;因为dput的输出通常相当笨拙,很难复制粘贴,并且模糊了你的其余问题。

    提供预期输出:


    有人说过:

      

    预期输出的图片价值1000字

         

    - 一个非常聪明的人

    如果您可以添加类似&#34;我希望得到这个结果&#34;:

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

    对于您的问题,人们更有可能快速了解您的目标。如果您的预期结果很大且难以处理,那么您可能还没有充分考虑如何简化问题(参见下文)。

    简洁地解释你的问题


    要做的主要事情是在提出问题之前尽可能简化问题。重新构建问题以使用内置数据集将在这方面提供很多帮助。您还经常会发现,只需通过简化过程,您就可以回答自己的问题。

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

    在这两种情况下,用户的问题几乎肯定不是他们提供的简单示例。相反,他们抽象出问题的本质,并将其应用于一个简单的数据集来询问他们的问题。

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


    这个答案侧重于我认为最佳实践:使用内置数据集并以最小的形式提供您期望的结果。最突出的答案集中在其他方面。我不希望这个答案升到任何突出位置;这只是为了让我可以在对新手问题的评论中链接到它。

答案 12 :(得分:107)

可重复的代码是获得帮助的关键。但是,有许多用户可能会怀疑甚至粘贴一大块数据。例如,他们可能正在处理敏感数据或收集用于研究论文的原始数据。出于任何原因,我认为在公开粘贴数据之前有一个方便的功能来“变形”我的数据会很好。包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

答案 13 :(得分:96)

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

如,

data(mtcars)

然后解决问题

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

答案 14 :(得分:88)

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

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

@Henrik的启发。

答案 15 :(得分:84)

我正在开发wakefield package以满足快速共享可重复数据的需求,有时dput适用于较小的数据集,但我们处理的许多问题要大得多,共享如此之大通过dput设置的数据是不切实际的。

关于:

wakefield允许用户共享最少的代码来重现数据。用户设置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
.. ...   ... ...    ...      ... ...    ...   ...

答案 16 :(得分:68)

如果您希望数据中有一个或多个factor变量可以与dput(head(mydata))重现,请考虑向其中添加droplevels,以便将最小化数据集中不存在的内容未包含在dput输出中,以便使示例最小

dput(droplevels(head(mydata)))

答案 17 :(得分:59)

我想知道http://old.r-fiddle.org/链接是否是分享问题的一种非常巧妙的方式。它会收到一个唯一的ID,甚至可以考虑将其嵌入到SO中。

答案 18 :(得分:42)

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

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

我们无法直接复制粘贴。

要使问题和答案正确重现,请尝试删除+&amp; >发布之前#并将#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") 添加到输出和评论中,如下所示:

docker run --net="host" -it --entrypoint bash $KAFKA_DOCKER_IMAGE "\$KAFKA_HOME/bin/kafka-topics.sh --create --zookeeper localhost:2181 --topic $1 --partitions $num_partitions --replication-factor $replication_factor"

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

答案 19 :(得分:28)

除了我发现非常有趣的所有上述答案之外,有时可能很容易,因为这里讨论的是: - HOW TO MAKE A MINIMAL REPRODUCIBLE EXAMPLE TO GET HELP WITH R

有许多方法可以在R

中生成随机向量Create a 100 number vector with random values in R rounded to 2 decimals或随机矩阵
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

此外,应该知道数据的类型,长度和属性,可以是Data structures

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

答案 20 :(得分:27)

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

作为mt1022 noted,“......用于生成最小,可重现的示例的良好包装是”reprex“来自tidyverse”。

根据Tidyverse

  

“reprex”的目标是以一种其他人可以运行它并感受到你的痛苦的方式打包你有问题的代码。

tidyverse网站上给出了一个例子。

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

我认为这是最简单的方法来创建可重现的示例。

答案 21 :(得分:24)

以下是我的一些建议:

  • 尝试使用默认R数据集
  • 如果您有自己的数据集,请将其添加到dput,以便其他人可以更轻松地帮助您
  • 除非确实有必要,否则不要使用install.package(),只要您使用requirelibrary
  • ,人们就会明白
  • 尽量简明扼要,

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

所有这些都是可重复的例子的一部分。

答案 22 :(得分:14)

使用testthat包中的函数来显示您期望发生的内容是个好主意。因此,其他人可以更改您的代码,直到它运行没有错误。这减轻了那些想要帮助你的人的负担,因为这意味着他们不必解码你的文字描述。例如

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

比#34更清晰;我认为对于等于或超过10的y,x将为1.23,否则为3.21,但我得不到结果&#34;。即使在这个愚蠢的例子中,我认为代码比单词更清晰。使用testthat可让您的帮助程序专注于代码,从而节省时间,并且在发布之前为他们提供了解问题的方法