如何制作一个出色的 R 可重现示例
Posted
技术标签:
【中文标题】如何制作一个出色的 R 可重现示例【英文标题】:How to make a great R reproducible example 【发布时间】:2011-08-23 05:24:40 【问题描述】:在与同事讨论性能、教学、发送错误报告或在邮件列表和 *** 上搜索指导时,经常会询问 reproducible example 并始终提供帮助。
您对创建优秀示例有哪些建议?如何以文本格式粘贴来自r 的数据结构?您还应该包含哪些其他信息?
除了使用dput()
、dump()
或structure()
,还有其他技巧吗?什么时候应该包含library()
或require()
语句?除了c
、df
、data
等,还应该避免哪些保留字?
如何制作出色的 r 可重现示例?
【问题讨论】:
我对问题的范围感到困惑。人们似乎在询问关于 SO 或 R-help 的问题(如何“重现错误”)时跳出了对可重现示例的解释。帮助页面中可重现的 R 示例呢?在包演示中?在教程/演示文稿中? @baptiste :同样减去错误。我解释的所有技术都用于包帮助页面,以及我提供的关于 R 的教程和演示文稿中 数据有时是限制因素,因为结构可能过于复杂而无法模拟。从私有数据生成公共数据:***.com/a/10458688/742447 in ***.com/questions/10454973/… 【参考方案1】:这是一个很好的guide。
最重要的一点是:编写一小段代码,我们可以运行它来查看问题所在。一个有用的函数是dput()
,但是如果你有非常大的数据,那么你可能想要制作一个小样本数据集或者只使用前 10 行左右。
编辑:
此外,请确保您自己确定了问题所在。该示例不应是带有“在第 200 行出现错误”的整个 R 脚本。如果您使用 R(我喜欢 browser()
)和 Google 中的调试工具,那么您应该能够真正确定问题出在哪里,并重现一个出现同样问题的简单示例。
【讨论】:
【参考方案2】:就个人而言,我更喜欢“一个”衬里。类似的东西:
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
包处理空间数据,您可以生成一些随机数据。可以在包 vignette 中找到很多示例,但这里有一个小块。
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】:基本上,minimal reproducible example (MRE) 应该能让其他人完全在他们的机器上重现您的问题。
MRE 由以下项目组成:
最小的数据集,是演示问题所必需的 重现错误所需的最小可运行代码,可在给定数据集上运行 所有必要信息关于使用的软件包、R 版本和运行它的操作系统。 在随机过程的情况下,种子(由set.seed()
设置)用于重现性
有关良好 MRE 的示例,请参阅您正在使用的函数的帮助文件底部的“示例”部分。只需键入例如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函数(即曲线的高度在指向F分布的x
),你可能会与它发生冲突。
复制原始数据
如果您有特定原因,或者数据太难构建示例,您可以提供原始数据的一小部分,最好使用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
子集您的数据
如果共享一个子集,请使用 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
时,您可能还希望仅包含相关列,例如输出(mtcars[1:3, c(2, 5, 6)])
注意:如果您的数据框有多个级别的因子,dput
输出可能会很笨拙,因为它仍然会列出所有可能的因子级别,即使它们不是存在于您的数据子集中。要解决此问题,您可以使用droplevels()
函数。请注意下面的物种是如何只有一个水平的因素,例如dput(droplevels(iris[1:4, ]))
。 dput
的另一个警告是它不适用于键控 data.table
对象或来自 tidyverse
的分组 tbl_df
(类 grouped_df
)。在这些情况下,您可以在共享之前转换回常规数据框,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")
的输出来提供包版本。
种子
使用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注意:set.seed()
的输出在 R >3.6.0 和以前的版本之间有所不同。指定您用于随机过程的 R 版本,如果您在回答旧问题时得到的结果略有不同,请不要感到惊讶。要在这种情况下获得相同的结果,您可以在set.seed()
之前使用RNGversion()
函数(例如:RNGversion("3.5.2")
)。
【讨论】:
【参考方案4】: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 行和 3 列命名 '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。
【讨论】:
【参考方案5】:(这是我来自 How to write a reproducible example 的建议。我试图让它简短但甜蜜)。
如何编写可重现的示例
如果您提供可重现的示例,您最有可能在您的 R 问题上获得很好的帮助。一个可重现的示例允许其他人通过复制和粘贴 R 代码来重现您的问题。
您需要包括四件事以使您的示例可重现:所需的包、数据、代码和您的 R 环境的描述。
包应该加载在脚本的顶部,所以很容易 看看示例需要哪些。
在电子邮件或 Stack Overflow 问题中包含 数据 的最简单方法是使用 dput()
生成 R 代码以重新创建它。例如,要在 R 中重新创建 mtcars
数据集,
我会执行以下步骤:
-
在 R 中运行
dput(mtcars)
复制输出
在我的可重现脚本中,输入 mtcars <-
然后粘贴。
花一点时间确保您的代码易于其他人使用 阅读:
确保你使用了空格并且你的变量名是简洁的,但是 内容丰富
使用 cmets 指出您的问题所在
尽最大努力删除与问题无关的所有内容。 代码越短,越容易理解。
在代码的注释中包含sessionInfo()
的输出。这总结了您的R
环境,并且可以轻松检查您是否使用过时的
包。
您可以通过启动一个新的 R 会话并粘贴您的脚本来检查您是否确实制作了一个可重现的示例。
在将所有代码放入电子邮件之前,请考虑将其放入 Gist github。它将为您的代码提供漂亮的语法高亮显示,您不必担心电子邮件系统会破坏任何内容。
【讨论】:
reprex
in tidyverse
是一个很好的包,用于生成最小的、可重复的示例:github.com/tidyverse/reprex
我经常收到带有代码的电子邮件。我什至会收到附有包含代码的 Word 文档的电子邮件。有时我什至会收到附有包含代码截图的 Word 文档的电子邮件。
如果是图对象呢? dput()
不幸地返回了很长的向量行,用于图形。
与sf
tibble 等空间数据相同。根据我的经验,即使减少到只有几行,这些似乎也不能很好地与dput
等工具配合使用。【参考方案6】:
有时,无论您多么努力地尝试,问题确实无法用较小的数据重现,并且不会发生在合成数据上(尽管展示您如何生成合成数据集是有用的不重现问题,因为它排除了一些假设)。
可能需要将数据发布到网络上的某个地方并提供 URL。 如果数据不能向公众公开,但可以完全共享,那么您可以提议通过电子邮件将其发送给感兴趣的各方(尽管这会减少将费心去做)。 我实际上还没有看到这样做,因为无法发布他们的数据的人对发布任何形式的数据都很敏感,但在某些情况下,如果数据被充分匿名/加扰,人们仍然可以发布数据似乎是合理的/以某种方式轻微损坏。如果您不能做到这两个,那么您可能需要聘请顾问来解决您的问题......
编辑:关于匿名化/加扰的两个有用的 SO 问题:
How to create example data set from private data (replacing variable names and levels with uninformative place holders)? Given a set of random numbers drawn from a continuous univariate distribution, find the distribution【讨论】:
为了生成合成数据集,the answers to this question 给出了有用的例子,包括fitdistr
和fitdistrplus
的应用。
我真的想要一些关于提供样本空间数据的建议,例如在几何列中具有大量坐标的 sf tibble。即使只有几行数据,这些似乎也不会使用 dput 完全复制到剪贴板。有可以在 reprex 中使用的内置 sf 数据集,但有时需要提供自己的数据样本,因为它是特定于该数据的某些东西导致问题的原因。跨度>
【参考方案7】:
从 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
")
【讨论】:
【参考方案8】:到目前为止的答案对于可重复性部分来说显然很棒。这只是为了澄清可重现的示例不能也不应该是问题的唯一组成部分。不要忘记解释你希望它看起来像什么以及你的问题的轮廓,而不仅仅是你到目前为止是如何尝试到达那里的。代码不够;你也需要文字。
这是一个可重复的示例,说明应避免做什么(取自真实示例,更改名称以保护无辜者):
以下是我遇到问题的示例数据和部分功能。
code
code
code
code
code (40 or so lines of it)
我怎样才能做到这一点?
【讨论】:
【参考方案9】:通常您需要一些数据作为示例,但是,您不想发布确切的数据。要使用已建立的库中的一些现有 data.frame,请使用 data 命令将其导入。
例如,
data(mtcars)
然后做题
names(mtcars)
your problem demostrated on the mtcars data set
【讨论】:
许多内置数据集(如流行的mtcars
和iris
数据集)实际上并不需要使用data
调用。【参考方案10】:
要快速创建数据的dput
,您只需将数据复制(一部分)到剪贴板并在 R 中运行以下命令:
对于 Excel 中的数据:
dput(read.table("clipboard", sep="\t", header=TRUE))
对于 .txt 文件中的数据:
dput(read.table("clipboard", sep="", header=TRUE))
如有必要,您可以更改后者中的sep
。
当然,这只有在您的数据在剪贴板中时才有效。
【讨论】:
【参考方案11】:受这篇文章的启发,我现在使用了一个方便的函数,
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 上工作,输出会自动复制到剪贴板,这样您就可以简单地运行它,然后将其粘贴到您的问题中。
源代码在这里:
GitHub - pubR/reproduce.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
参数。示例:
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==
【讨论】:
【参考方案12】:如果您有一个无法使用dput()
轻松放入脚本的大型数据集,请将您的数据发布到pastebin 并使用read.table
加载它们:
d <- read.table("http://pastebin.com/raw.php?i=m1ZJuKLH")
灵感by Henrik。
【讨论】:
【参考方案13】:我有一个非常简单有效的方法来制作上面没有提到的 R 示例。 你可以先定义你的结构。例如,
mydata <- data.frame(a=character(0), b=numeric(0), c=numeric(0), d=numeric(0))
>fix(mydata)
然后您可以手动输入数据。这对于较小的示例而不是大示例非常有效。
【讨论】:
...然后dput(mydata)
你的前端是什么?有一个完整的答案会很好。等制作一个可以直接循环的数据,如for (d in data) ...
。【参考方案14】:
可重现的代码是获得帮助的关键。然而,有许多用户可能对粘贴他们的一大块数据持怀疑态度。例如,他们可能正在处理敏感数据或收集用于研究论文的原始数据。
出于任何原因,我认为在公开粘贴之前有一个方便的功能来“变形”我的数据会很好。 SciencesPo
包中的 anonymize
函数非常愚蠢,但对我来说,它与 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
【讨论】:
【参考方案15】:我想知道http://old.r-fiddle.org/ 链接是否是一种非常巧妙的分享问题的方式。它会收到一个唯一的 ID,甚至可以考虑将其嵌入 SO。
【讨论】:
【参考方案16】:如果您的数据中有一个或多个 factor
变量,您希望使用 dput(head(mydata))
进行重现,请考虑将 droplevels
添加到其中,以便最小化不存在的因素水平数据集不包含在您的dput
输出中,以使示例最小:
dput(droplevels(head(mydata)))
【讨论】:
【参考方案17】:指南:
您设计问题的主要目标应该是让读者尽可能容易地理解并在他们的系统上重现您的问题。这样做:
-
提供输入数据
提供预期的输出
简明扼要地解释您的问题
如果你有超过 20 行文本 + 代码,你可能可以回去简化
尽可能简化代码,同时保留问题/错误
这确实需要一些工作,但它似乎是一个公平的权衡,因为您要求其他人为您工作。
提供数据:
内置数据集
到目前为止的最佳选择是依赖内置数据集。这使得其他人很容易解决您的问题。在 R 提示符下键入 data()
以查看您可以使用哪些数据。一些经典的例子:
iris
mtcars
ggplot2::diamonds
(外接包,不过几乎人人都有)
Inspect the built-in datasets to find one suitable for your problem.
如果您可以重新表述您的问题以使用内置数据集,则您更有可能获得好的答案(和赞成票)。
自生成数据
如果您的问题特定于现有数据集中未表示的数据类型,请提供 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
对于您的问题,人们更容易理解您想要快速做的事情。如果您的预期结果很大且难以处理,那么您可能还没有充分考虑如何简化您的问题(见下文)。
简明扼要地解释你的问题
要做的主要事情是在您提出问题之前尽可能简化您的问题。重新构建问题以使用内置数据集将在这方面有很大帮助。你也会经常发现,只要通过简化的过程,你就会回答你自己的问题。
这里有一些好问题的例子:
与built in data set 与user generated data在这两种情况下,用户的问题几乎肯定不在于他们提供的简单示例。相反,他们将问题的性质抽象出来,并将其应用到一个简单的数据集中来提出他们的问题。
为什么还要回答这个问题?
此答案侧重于我认为的最佳做法:使用内置数据集并以最小的形式提供您期望的结果。最突出的答案集中在其他方面。我不希望这个答案上升到任何突出地位。这只是为了让我可以在 cmets 中将其链接到新手问题。
【讨论】:
【参考方案18】:我正在开发wakefield package 来解决快速共享可重现数据的需求,有时dput
适用于较小的数据集,但我们处理的许多问题要大得多,通过@ 共享如此大的数据集987654325@ 是不切实际的。
关于:
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
.. ... ... ... ... ... ... ... ...
【讨论】:
【参考方案19】:以下是我的一些建议:
尝试使用默认的 R 数据集 如果您有自己的数据集,请将它们包含在dput
中,以便其他人可以更轻松地帮助您
不要使用install.package()
,除非真的有必要,如果你只使用require
或library
,人们会理解的
尽量简洁,
有一些数据集 尽量简单地描述您需要的输出 在提问之前自己动手 上传图片很容易,有的话就上传图 还包括您可能遇到的任何错误所有这些都是可重现示例的一部分。
【讨论】:
你在这里并没有真正添加任何实质内容。dput()
之前已经提到过,其中大部分只是重申标准 SO 准则。
我遇到了示例中包含的install.package
函数的问题,这并不是真正必要的(在我看来)。此外,使用默认的 R 数据集将使重现性更容易。 SO 指南没有专门讨论这些主题。此外,它旨在发表我的意见,这些是我遇到最多的。【参考方案20】:
除了我发现非常有趣的所有上述答案之外,有时它可能非常简单,因为它在这里讨论: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))
【讨论】:
【参考方案21】:请不要像这样粘贴您的控制台输出:
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")
)
我们不能直接复制粘贴。
为了使问题和答案可正确重现,请尝试在发布之前删除 +
和 >
,然后将 #
用于输出和 cmets,如下所示:
#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")
还有一件事,如果您使用过某个包中的任何函数,请提及该库。
【讨论】:
您是手动删除>
并添加#
,还是有自动的方法?
@BCArg 我手动删除了>
。但是,为了添加#
,我在RStudio
编辑器中使用Ctrl+Shift+C
快捷方式。【参考方案22】:
最好使用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 时为 1.23,否则为 3.21,但我没有得到任何结果”更清楚。即使在这个愚蠢的例子中,我认为代码比文字更清晰。使用testthat
可以让您的助手专注于代码,从而节省时间,并让他们在发布之前知道他们已经解决了您的问题
【讨论】:
【参考方案23】:您可以使用reprex 执行此操作。
作为mt1022 noted,“...用于生成最小、可重现示例的好包是来自tidyverse 的“reprex”。
根据Tidyverse:
“reprex”的目标是打包有问题的代码,以便其他人可以运行它并感受到你的痛苦。
tidyverse 网站上给出了一个示例。
library(reprex)
y <- 1:4
mean(y)
reprex()
我认为这是创建可重现示例的最简单方法。
【讨论】:
当我使用的函数不是来自 base R 时出现错误,这是预期的吗? 您是否在 reprex 中加载了您的库?否则代码不能独立重现以上是关于如何制作一个出色的 R 可重现示例的主要内容,如果未能解决你的问题,请参考以下文章