当我承担任何复杂的 R 项目时,我的脚本很快就会变得冗长和混乱。
我可以采用哪些实践来使我的代码始终令人愉快地工作?我正在考虑类似的事情
- 在源文件中放置函数
- 何时将某些内容分解为另一个源文件
- 主文件中应该包含什么
- 使用函数作为组织单位(考虑到 R 使得访问全局状态变得困难,这是否值得)
- 缩进/换行实践。
- 对待(像 {?
- 将 )} 之类的内容放在 1 行或 2 行上?
基本上,您组织大型 R 脚本的经验法则是什么?
当我承担任何复杂的 R 项目时,我的脚本很快就会变得冗长和混乱。
我可以采用哪些实践来使我的代码始终令人愉快地工作?我正在考虑类似的事情
基本上,您组织大型 R 脚本的经验法则是什么?
标准答案是使用包——请参阅编写 R 扩展手册以及网络上的不同教程。
它给你
R CMD check
只是运行source()
代码适用于非常短的片段。其他所有内容都应该在一个包中——即使您不打算发布它,因为您可以为内部存储库编写内部包。
至于“如何编辑”部分,R Internals手册在第 6 节中有出色的R 编码标准。否则,我倾向于在Emacs 的 ESS 模式中使用默认值。
2008 年 8 月 13 日更新: David Smith 刚刚发布了有关Google R 样式指南 的博客。
我喜欢将不同的功能放在他们自己的文件中。
但我不喜欢 R 的包系统。比较难用。
我更喜欢轻量级的替代方案,将文件的函数放置在环境中(所有其他语言都称为“命名空间”)并附加它。例如,我制作了一组“util”函数,如下所示:
util = new.env()
util$bgrep = function [...]
util$timeit = function [...]
while("util" %in% search())
detach("util")
attach(util)
这都在一个文件util.R中。当您获取它时,您将获得环境“util”,以便您可以调用util$bgrep()
等;但此外,这个attach()
电话使它如此公正bgrep()
和直接地工作。如果您没有将所有这些函数放在它们自己的环境中,它们会污染解释器的顶级名称空间(ls()
显示的名称空间)。
我试图模拟 Python 的系统,其中每个文件都是一个模块。那会更好,但这似乎还可以。
这可能听起来有点明显,尤其是如果您是程序员,但这是我对代码的逻辑和物理单元的看法。
我不知道这是否是您的情况,但是当我在 R 中工作时,我很少从一个大型复杂程序开始。我通常从一个脚本开始,将代码分成逻辑上可分离的单元,通常使用函数。数据操作和可视化代码被放置在它们自己的函数中,等等。这些函数被组合在文件的一个部分中(顶部的数据操作,然后是可视化等)。最终,您要考虑如何让您更轻松地维护脚本并降低缺陷率。
您使函数的细粒度/粗粒度会有所不同,并且有各种经验法则:例如 15 行代码,或“一个函数应该负责执行一项由其名称标识的任务”等。您的里程会有所不同. 由于 R 不支持按引用调用,因此当涉及传递数据帧或类似结构时,我通常会使我的函数过于细化。但这可能是对我刚开始使用 R 时一些愚蠢的性能错误的过度补偿。
何时将逻辑单元提取到它们自己的物理单元中(如源文件和更大的分组如包)?我有两个案例。首先,如果文件变得太大并且在逻辑上不相关的单元之间滚动会很烦人。其次,如果我有其他程序可以重用的功能。我通常首先将一些分组单元(例如数据操作函数)放入一个单独的文件中。然后我可以从任何其他脚本中获取此文件。
如果你要部署你的功能,那么你需要开始考虑包。由于各种原因(简而言之:组织文化更喜欢其他语言、对性能的担忧、GPL 等),我不会在生产中部署 R 代码或供其他人重用。此外,我倾向于不断完善并添加到我的源文件集合中,并且我宁愿在进行更改时不处理包。因此,您应该查看其他与软件包相关的答案,例如 Dirk 的,以了解有关这方面的更多详细信息。
最后,我认为您的问题不一定针对 R。我真的建议您阅读 Steve McConnell 的 Code Complete,其中包含有关此类问题和整个编码实践的大量智慧。
我的简洁回答:
我相信 R 在生产中的使用越来越多,因此对可重用代码的需求比以前更大。我发现解释器比以前强大得多。毫无疑问,R 比 C 慢 100-300 倍,但通常瓶颈集中在几行代码,可以委托给 C/C++。我认为将 R 在数据处理和统计分析方面的优势委托给另一种语言是错误的。在这些情况下,性能损失很低,无论如何都值得节省开发工作量。如果只考虑执行时间,我们都会编写汇编程序。
我一直想弄清楚如何编写包,但没有投入时间。对于我的每个迷你项目,我将所有低级函数保存在一个名为“functions/”的文件夹中,并将它们源到我明确创建的单独命名空间中。
以下代码行将在搜索路径上创建一个名为“myfuncs”的环境,如果它尚不存在(使用附加),并使用我的“functions/”目录中的 .r 文件中包含的函数填充它(使用系统源)。我通常将这些行放在我的主脚本的顶部,用于调用高级函数(调用低级函数)的“用户界面”。
if( length(grep("^myfuncs$",search()))==0 )
attach("myfuncs",pos=2)
for( f in list.files("functions","\\.r$",full=TRUE) )
sys.source(f,pos.to.env(grep("^myfuncs$",search())))
当您进行更改时,您始终可以使用相同的行重新获取它,或者使用类似的东西
evalq(f <- function(x) x * 2, pos.to.env(grep("^myfuncs$",search())))
评估您创建的环境中的添加/修改。
我知道这很笨拙,但避免了过于正式(但如果你有机会我确实鼓励包系统 - 希望我将来会以这种方式迁移)。
至于编码约定,这是我在美学方面看到的唯一一件事(我喜欢它们并且松散地遵循,但我在 R 中没有使用太多花括号):
http://www1.maths.lth.se/help/R/RCC/
还有其他关于使用 [,drop=FALSE] 和 <- 的“约定”,正如赋值运算符在用户的各种演示文稿(通常是主题演讲)中所建议的那样!会议,但我认为这些都不是严格的(尽管 [,drop=FALSE] 对于您不确定所需输入的程序很有用)。
把我算作另一个支持包裹的人。我承认在我必须(即被释放)之前/当我必须(即被释放)之前,我在编写手册页和小插曲方面非常糟糕,但它为捆绑源代码提供了一种真正方便的方式。另外,如果您认真维护您的代码,Dirk 提出的所有要点都会体现在 plya 中。
我也同意。使用 package.skeleton() 函数开始。即使您认为您的代码可能永远不会再次运行,它也可能有助于激励您创建更通用的代码,从而节省您以后的时间。
至于访问全局环境,使用 <<- 操作符很容易,但不鼓励这样做。
我还没有学会如何编写包,我总是通过采购子脚本来组织。它类似于写作课程,但没有那么复杂。它在编程上并不优雅,但我发现我会随着时间的推移建立分析。一旦我有一个可以工作的大部分,我经常将它移动到不同的脚本并直接获取它,因为它将使用工作区对象。也许我需要从多个来源导入数据,对所有数据进行排序并找到交叉点。我可能会将该部分放入一个附加脚本中。但是,如果您想为其他人分发您的“应用程序”,或者它使用一些交互式输入,那么包可能是一个不错的途径。作为一名研究人员,我很少需要分发我的分析代码,但我经常需要对其进行扩充或调整。
我也一直在寻找合适的工作流程的圣杯来组合一个 R 大型项目。去年,我发现了这个名为rsuite的包,当然,这正是我要找的。这个 R 包是专门为部署大型 R 项目而开发的,但我发现它可以用于小型、中型和大型 R 项目。我将在一分钟内给出真实示例的链接(如下),但首先,我想解释一下使用rsuite
.
笔记。我不是rsuite
.
我们用 RStudio 做的项目都错了;目标不应该是创建一个项目或一个包,而是一个更大的范围。在 rsuite 中,您可以创建一个超级项目或主项目,它以所有可能的组合保存标准 R 项目和 R 包。
通过拥有一个 R 超级项目,您不再需要 Unixmake
来管理下面的 R 项目的较低级别;您在顶部使用 R 脚本。我来给你展示。当你创建一个 rsuite 主项目时,你会得到这个文件夹结构:
该文件夹R
是您放置项目管理脚本的地方,这些脚本将替换make
.
该文件夹packages
是rsuite
保存构成超级项目的所有包的文件夹。您还可以复制粘贴无法从 Internet 访问的包,并且 rsuite 也会构建它。
该文件夹deployment
将rsuite
写入包文件中指示的所有包二进制DESCRIPTION
文件。所以,这本身就可以让你在整个时间里投射出完全可重现的东西。
rsuite
带有适用于所有操作系统的客户端。我已经测试了所有这些。但您也可以将它安装addin
为 RStudio 的。
rsuite
还允许您在其自己的文件夹中构建一个独立的conda
安装conda
。这不是一个环境,而是从您的机器中的 Anaconda 派生的物理 Python 安装。这与 R's 一起使用SystemRequirements
,您可以从您想要的任何 conda 频道安装您想要的所有 Python 包。
您还可以创建本地存储库以在离线时拉取 R 包,或者想要更快地构建整个东西。
如果需要,您还可以将 R 项目构建为 zip 文件并与同事共享。如果您的同事安装了相同的 R 版本,它将运行。
另一种选择是在 Ubuntu、Debian 或 CentOS 中构建整个项目的容器。因此,您无需与项目构建共享 zip 文件,而是与Docker
准备运行的项目共享整个容器。
我一直在尝试rsuite
寻找完全可重复性,并避免依赖于在全球环境中安装的软件包。这是错误的,因为一旦您安装包更新,该项目通常会停止工作,特别是那些对具有某些参数的函数进行非常特定调用的包。
我开始尝试的第一件事是bookdown
电子书。我从来没有幸运地拥有一本能够经受住超过六个月的时间考验的书籍。所以,我所做的是将原始的 bookdown 项目转换为遵循rsuite
框架。现在,我不必担心更新我的全局 R 环境,因为该项目在deployment
文件夹中有自己的一组包。
我做的下一件事是创建机器学习项目,但rsuite
顺便说一句。一个master,顶部的编排项目,所有的子项目和包都在master的控制之下。它确实改变了您使用 R 编码的方式,让您更有效率。
之后,我开始使用我的一个名为rTorch
. 这在很大程度上是可能的,因为rsuite
; 它可以让你思考并做大。
不过有一点建议。学习rsuite
并不容易。因为它提供了一种创建 R 项目的新方法,所以感觉很难。不要在第一次尝试时感到沮丧,继续爬斜坡直到你成功。它需要您的操作系统和文件系统的高级知识。
我希望有一天我们可以像从菜单中RStudio
那样生成编排项目。rsuite
这一定非常棒。
链接:
R 可以用于交互式使用和小型脚本,但我不会将它用于大型程序。我会为大多数编程使用主流语言并将其包装在 R 接口中。