提问人:Dan Goldstein 提问时间:8/12/2009 最后编辑:smciDan Goldstein 更新时间:2/6/2021 访问量:33795
如何组织大型 R 程序?
How to organize large R programs?
问:
当我进行任何复杂的 R 项目时,我的脚本很快就会变得冗长且令人困惑。
我可以采用哪些做法来使我的代码始终令人愉快地使用?我在想这样的事情
- 函数在源文件中的位置
- 何时将某些内容分解到另一个源文件
- 主文件中应该包含哪些内容
- 使用函数作为组织单位(鉴于 R 使访问全局状态变得困难,这是否值得)
- 缩进/换行做法。
- 对待 ( like {?
- 将 )} 之类的东西放在 1 行或 2 行上?
基本上,组织大型 R 脚本的经验法则是什么?
答:
标准答案是使用包 -- 请参阅编写 R 扩展手册以及 Web 上的其他教程。
它给你
- 一种按主题组织代码的准自动方式
- 强烈建议您编写帮助文件,让您思考界面
- 通过以下方式进行大量健全性检查
R CMD check
- 添加回归测试的机会
- 以及命名空间的手段。
只需运行代码即可处理非常短的代码段。其他所有内容都应该在一个包中 -- 即使你不打算发布它,因为你可以为内部存储库编写内部包。source()
至于“如何编辑”部分,R Internals 手册在第 6 节中具有出色的 R 编码标准。否则,我倾向于在 Emacs 的 ESS 模式下使用默认值。
2008 年 8 月 13 日更新:大卫·史密斯(David Smith)刚刚在博客上写了一篇关于Google R风格指南的文章。
评论
我也同意。使用 package.skeleton() 函数开始。即使你认为你的代码可能永远不会再运行,它也可能有助于激励你创建更通用的代码,从而节省你以后的时间。
至于访问全球环境,这对<<运营商来说很容易,尽管不鼓励这样做。
我一直想弄清楚如何编写包,但没有投入时间。对于我的每个小项目,我都会将所有低级函数保存在一个名为“functions/”的文件夹中,并将它们源到我显式创建的单独命名空间中。
以下代码行将在搜索路径上创建一个名为“myfuncs”的环境(如果该环境尚不存在)(使用 attach),并使用我的“functions/”目录中的 .r 文件中包含的函数填充它(使用 sys.source)。我通常把这些行放在我的主脚本的顶部,用于“用户界面”,从中调用高级函数(调用低级函数)。
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] 和 <-,还有其他“约定”,正如赋值运算符在 useR 的各种演示(通常是主题演讲)中建议的那样!会议,但我认为其中任何一个都不是严格的(尽管 [,drop=FALSE] 对于您不确定您期望的输入的程序很有用)。
R 可用于交互式使用和小型脚本,但我不会将其用于大型程序。我会在大多数编程中使用主流语言,并将其包装在 R 接口中。
评论
Rcpp
由于还没有学会如何编写包,我一直通过采购子脚本来组织。它类似于编写课程,但并不涉及。它在程序上并不优雅,但我发现随着时间的推移,我建立了分析。一旦我有一个大部分可以工作,我经常把它移动到不同的脚本中,然后只是获取它,因为它将使用工作区对象。也许我需要从多个来源导入数据,对所有来源进行排序并找到交叉点。我可能会把这一部分放到一个额外的脚本中。但是,如果你想为其他人分发你的“应用程序”,或者它使用一些交互式输入,那么包可能是一个不错的途径。作为一名研究人员,我很少需要分发我的分析代码,但我经常需要增强或调整它。
评论
这听起来可能有点明显,特别是如果你是一名程序员,但以下是我对代码的逻辑和物理单元的看法。
我不知道这是否是你的情况,但是当我在 R 中工作时,我很少一开始就考虑一个大型复杂的程序。我通常从一个脚本开始,并将代码分成逻辑上可分离的单元,通常使用函数。数据操作和可视化代码被放置在它们自己的函数中,等等。这些函数被组合在文件的一个部分(顶部的数据操作,然后是可视化等)。最终,您需要考虑如何更轻松地维护脚本并降低缺陷率。
函数的细粒度/粗粒度会有所不同,并且有各种经验法则:例如,15 行代码,或者“一个函数应该负责执行一项由其名称标识的任务”等。您的里程会有所不同。由于 R 不支持按引用调用,因此在涉及传递数据帧或类似结构时,我通常会使函数过于细粒度。但这可能是对我刚开始使用 R 时一些愚蠢的性能错误的过度补偿。
何时将逻辑单元提取到它们自己的物理单元(如源文件和更大的分组,如包)中?我有两个案例。首先,如果文件变得太大,并且在逻辑上不相关的单元之间滚动是一种烦恼。其次,如果我有可以被其他程序重用的功能。我通常首先将一些分组单元(例如数据操作函数)放入一个单独的文件中。然后,我可以从任何其他脚本中获取此文件。
如果要部署函数,则需要开始考虑包。出于各种原因,我不会在生产环境中部署 R 代码或供其他人重用(简而言之:组织文化更喜欢其他语言、对性能、GPL 等的关注)。此外,我倾向于不断完善和添加到我的源文件集合中,我宁愿在进行更改时不处理包。因此,您应该查看其他与软件包相关的答案,例如 Dirk 的答案,以获取有关这方面的更多详细信息。
最后,我认为你的问题不一定是 R 特有的。我真的推荐阅读 Steve McConnell 的 Code Complete,其中包含了很多关于此类问题和编码实践的智慧。
我简明扼要的回答:
- 仔细编写函数,确定足够通用的输出和输入;
- 限制全局变量的使用;
- 使用 S3 对象,并在适当的情况下使用 S4 对象;
- 将函数放在包中,尤其是当函数调用 C/Fortran 时。
我相信 R 在生产中的使用越来越多,因此对可重用代码的需求比以前更大。我发现口译员比以前强大得多。毫无疑问,R 比 C 慢 100-300 倍,但通常瓶颈集中在几行代码上,这些代码可以委托给 C/C++。我认为将 R 在数据操作和统计分析方面的优势委托给另一种语言是错误的。在这些情况下,性能损失很低,无论如何都值得节省开发工作。如果只有执行时间是问题,我们都会编写汇编程序。
把我算作另一个支持包裹的人。我承认在编写手册页和小插图方面非常糟糕,直到我必须(即被释放)为止,但它提供了一种非常方便的方式来捆绑源代码母鹿。另外,如果你认真地维护你的代码,Dirk 提出的要点都会出现在 plya 中。
我喜欢在他们自己的文件中放置不同的功能。
但我不喜欢 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 的系统,其中每个文件都是一个模块。那会更好,但这似乎没问题。
评论
sys.source
MyEnv <- attach(NULL, name=s_env); sys.source(file, MyEnv)
sys.source2
我也一直在寻找将 R 大型项目组合在一起的正确工作流程的圣杯。去年,我找到了这个叫做 rsuite 的软件包,当然,这就是我一直在寻找的。这个 R 包是为部署大型 R 项目而显式开发的,但我发现它可以用于小型、中型和大型 R 项目。我将在一分钟内提供指向实际示例的链接(如下所示),但首先,我想解释使用 .rsuite
注意。我不是 的创建者或开发者。rsuite
我们一直在用 RStudio 做错项目;目标不应该是创建项目或包,而应该是创建更大的范围。在 rsuite 中,您可以创建一个超级项目或主项目,其中包含所有可能的组合中的标准 R 项目和 R 包。
通过拥有 R 超级项目,您不再需要 Unix 来管理底层 R 项目的较低级别;在顶部使用 R 脚本。让我告诉你。创建 rsuite 主项目时,将获得以下文件夹结构:
make
该文件夹是放置项目管理脚本的位置,这些脚本将替换 .
R
make
该文件夹是保存组成超级项目的所有包的文件夹。您也可以复制粘贴无法从 Internet 访问的软件包,rsuite 也会构建它。
packages
rsuite
该文件夹将写入包文件中指示的所有包二进制文件。因此,这本身就使得您投射出完全可重复的 accros 时间。
deployment
rsuite
DESCRIPTION
rsuite
附带适用于所有操作系统的客户端。我已经测试了它们。但您也可以将其安装为 RStudio。addin
rsuite
还允许您在其自己的文件夹中构建独立安装。这不是一个环境,而是从计算机中的 Anaconda 派生的物理 Python 安装。这与 R 一起使用,您可以从任何所需的 conda 通道安装所需的所有 Python 包。conda
conda
SystemRequirements
还可以创建本地存储库,以便在脱机时拉取 R 包,或者想要更快地生成整个内容。
如果需要,还可以将 R 项目生成为 zip 文件并与同事共享。它将运行,前提是你的同事安装了相同的 R 版本。
另一种选择是在 Ubuntu、Debian 或 CentOS 中构建整个项目的容器。因此,您无需与项目生成共享 zip 文件,而是将整个容器与准备运行的项目共享。
Docker
我一直在尝试寻找完全的可重复性,并避免依赖于在全局环境中安装的软件包。这是错误的,因为一旦您安装了包更新,项目通常会停止工作,特别是那些对具有某些参数的函数进行非常具体调用的包。rsuite
我开始尝试的第一件事是电子书。我从来没有幸运地在超过六个月的时间考验中幸存下来。因此,我所做的是将原始的bookdown项目转换为遵循框架。现在,我不必担心更新我的全局 R 环境,因为该项目在文件夹中有自己的一组包。bookdown
rsuite
deployment
我做的下一件事是创建机器学习项目,但碍事了。主控项目,在顶部编排项目,所有子项目和包都在主控的控制之下。它确实改变了你使用 R 编码的方式,使你的工作效率更高。rsuite
在那之后,我开始在我的一个名为 .这在很大程度上是可能的,因为;它可以让你思考并做大事。rTorch
rsuite
不过有一条建议。学习并不容易。因为它提供了一种创建 R 项目的新方法,所以感觉很难。第一次尝试时不要灰心,继续攀登斜坡,直到成功为止。它需要对操作系统和文件系统有深入的了解。rsuite
我希望有一天我们可以像从菜单中一样生成编排项目。那太棒了。RStudio
rsuite
链接:
IntroMachineLearningWithR-rsuite
评论
ProjectTemplate