在与同事讨论绩效、教学、发送错误报告或搜索邮件列表和Stack Overflow上的指导时,经常会询问一个可重复的示例,并且总是很有用。

你有什么建议来创建一个优秀的例子?如何以文本格式粘贴r中的数据结构?您还应包括哪些其他信息?

除了使用dput()、dump()或structure()之外,还有其他技巧吗?什么时候应该包含library()或require()语句?除了c、df、data等之外,应该避免哪些保留字。?

如何做出一个伟大的、可重复的例子?


当前回答

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

对于Excel中的数据:

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

对于.txt文件中的数据:

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

如果需要,可以更改后者中的sep。当然,只有当您的数据在剪贴板中时,这才有效。

其他回答

可复制代码是获得帮助的关键。然而,许多用户可能对粘贴哪怕是一大块数据都持怀疑态度。例如,他们可能在处理敏感数据,或者在研究论文中使用收集的原始数据。

出于任何原因,我认为在公开粘贴数据之前,有一个方便的函数来“变形”我的数据会很好。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

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

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

这比“我认为如果y等于或超过10,x将为1.23,否则为3.21,但我都没有得到结果”更清楚。即使在这个愚蠢的例子中,我认为代码比单词更清楚。使用testthat可以让你的助手专注于代码,这节省了时间,并且可以让他们在发布问题之前知道他们已经解决了你的问题

(这是我如何写一个可复制的例子的建议。我试图让它简短而甜蜜)。

如何编写可复制的示例

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

为了使示例具有可复制性,您需要包括四个方面:所需的包、数据、代码和R环境的描述。

包应该在脚本的顶部加载,因此很容易看看示例需要哪些。在电子邮件或堆栈溢出问题中包含数据的最简单方法是使用dput()生成R代码以重新创建它。例如,要在R中重新创建mtcars数据集,我将执行以下步骤:在R中运行dput(mtcars)复制输出在我的可复制脚本中,键入mtcars<-然后粘贴。花一点时间确保您的代码易于其他人使用内容如下:确保使用了空格,变量名称简洁,但是提供有用信息的使用注释指出问题所在尽最大努力删除与问题无关的所有内容。代码越短,越容易理解。在代码的注释中包含sessionInfo()的输出。这总结了您的R环境,并使其易于检查您是否使用了过时的包裹

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

在将所有代码放入电子邮件之前,请考虑将其放在Gistgithub上。它会给你的代码提供很好的语法高亮显示,你不必担心任何东西会被电子邮件系统破坏。

这是一个很好的指南。

最重要的一点是:制作一小段代码,我们可以运行它来了解问题所在。一个有用的函数是dput(),但是如果您有非常大的数据,那么您可能需要制作一个小样本数据集,或者只使用前10行左右。

编辑:

此外,确保您确定了问题所在。示例不应该是一个完整的R脚本,其中包含“在第200行出现错误”。如果您使用R(我爱浏览器())和Google中的调试工具,那么您应该能够真正确定问题所在,并重现一个同样错误的小例子。

受到这篇文章的启发,我现在使用了一个方便的功能,当我需要发布到堆栈溢出时,repeat(<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上工作,输出会自动复制到剪贴板,这样您就可以简单地运行它,然后将其粘贴到问题中。

可在以下位置获得来源:

GitHub-pubR/repeat.R


例子:

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

还要注意,整个输出都是一个漂亮的单行,而不是一段高高的分段。这使得在Stack Overflow问题帖子上更容易阅读,也更容易复制和粘贴。


2013年10月更新:

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

复制(DF,列=c(1:3,17,23),行.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==