如何组织大型 R 计划?

当我从事任何复杂的 R 项目时,我的脚本很快就会变得冗长和混乱。

我可以采用哪些实践来使我的代码始终是一种乐趣?我在想

  • 在源文件中放置函数
  • 何时将某些内容突破到另一个源文件
  • 主文件中应该包含的内容
  • 将函数作为组织单元使用(考虑到 R 使访问全局状态变得困难,这是否值得)
  • 缩进/折线实践。
    • 对待(像... ?
    • 写上一两行?

基本上,组织大型 R 脚本的经验法则是什么?

32090 次浏览

标准的答案是使用软件包——请参阅 编写 R 扩展手册以及网络上的不同教程。

它给你

  • 按主题组织代码的准自动方法
  • 强烈建议您编写一个帮助文件,让您考虑接口
  • 通过 R CMD check做了很多精神检查
  • 增加回归测试的机会
  • 以及名称空间的方法。

仅仅在代码上运行 source()就可以处理非常短的代码片段。其他所有内容都应该放在一个包中——即使您不打算发布它,因为您可以为内部存储库编写内部包。

至于“如何编辑”部分,R 内部手册在第6节有优秀的 R 编码标准。否则,我倾向于在 Emacs 的 ESS 模式中使用默认值。

2008年8月13日更新: 大卫 · 史密斯刚刚在博客上谈到了 谷歌 R 风格指南

我也同意。使用 package.bone ()函数开始。即使您认为您的代码可能永远不会再次运行,它也可能有助于激励您创建更加通用的代码,从而节省以后的时间。

至于访问全局环境,使用 < <-运算符很容易,但是不鼓励这样做。

我一直想搞清楚如何编写软件包,但没有投入时间。对于每个迷你项目,我都将所有低级函数保存在一个名为“ function/”的文件夹中,并将它们源代码保存到一个单独的名称空间中,这个名称空间是我显式创建的。

下面的代码行将在搜索路径上创建一个名为“ myfuns”的环境(如果它不存在的话) ,并使用。R 文件放在我的‘ function/’目录中(使用 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 ]和 <-的使用还有其他“约定”,正如在 userR 的各种演示文稿(通常是主题演讲)中建议的赋值操作符一样!会议,但我不认为这些都是严格的(虽然[ ,drop = FALSE ]对于您不确定所期望的输入的程序是有用的)。

对于交互式使用和小脚本来说,R 是可以的,但是对于大型程序我不会使用它。我将使用主流语言进行大部分编程,并将其封装在 R 接口中。

由于还没有学会如何编写软件包,我总是通过寻找子脚本来组织。它类似于写作类,但不像写作类那样复杂。它在编程上并不优雅,但是我发现我会随着时间的推移建立分析。一旦我有了一个可以工作的大段,我通常会将它移动到一个不同的脚本,并且只是源代码,因为它将使用工作区对象。也许我需要从几个来源导入数据,对它们进行排序,找到交叉点。我可能会把这一部分添加到另一个脚本中。但是,如果您想为其他人分发您的“应用程序”,或者它使用某些交互式输入,那么一个包可能是一个很好的途径。作为一名研究人员,我很少需要分发我的分析代码,但我经常需要增强或调整它。

这听起来可能有点显而易见,特别是如果你是一个程序员,但这里是我如何思考逻辑和物理单位的代码。

我不知道这是否是您的情况,但是当我在 R 中工作时,我很少在开始时考虑一个大型的复杂程序。我通常从一个脚本开始,并将代码分成逻辑上可分的单元,通常使用函数。将数据操作和可视化代码放在各自的函数中等等。这些函数被分组在文件的一个部分中(数据操作在顶部,然后是可视化,等等)。最终,您需要考虑如何更容易地维护您的脚本并降低缺陷率。

函数的细粒度和粗粒度会有所不同,有各种各样的经验法则: 例如15行代码,或者“一个函数应该负责执行一个由其名称标识的任务”,等等。你的情况会有所不同。由于 R 不支持引用调用,所以当涉及到传递数据框架或类似结构时,我通常不会让函数粒度太细。但这可能是对我刚开始使用 R 时一些愚蠢的性能错误的过度补偿。

什么时候将逻辑单元提取到它们自己的物理单元中(比如源文件和更大的分组,比如包) ?我有两个案子。首先,如果文件变得太大,并且在逻辑上不相关的单元之间滚动是一件烦人的事情。第二,如果我有可以被其他程序重用的函数。我通常首先将一些分组的单元(比如数据操作函数)放到一个单独的文件中。然后我可以从任何其他脚本来源这个文件。

如果要部署函数,那么需要开始考虑包。由于各种原因,我不会在生产环境中部署 R 代码,也不会为了其他人的重用而部署 R 代码(简而言之: org 文化倾向于使用其他语言,关注性能、 GPL 等)。另外,我倾向于不断地改进和添加源文件集合,并且在进行更改时我宁愿不处理包。因此,你应该查看其他包相关的答案,如德克的,在这方面的更多细节。

最后,我认为你的问题不一定特别针对 R。我真的推荐阅读 Steve McConnell 的代码完成,它包含了很多关于这些问题和编码实践的智慧。

我简明扼要的回答是:

  1. 仔细编写你的函数,确定足够的输出和输入;
  2. 限制全局变量的使用;
  3. 使用 S3对象,并酌情使用 S4对象;
  4. 将函数放入包中,特别是当函数调用 C/Fortran 时。

我相信 R 在生产环境中的使用越来越多,因此对可重用代码的需求比以前更大了。我发现这个解释器比以前健壮多了。毫无疑问,R 比 C 慢100-300倍,但通常瓶颈集中在几行代码上,这些代码可以委托给 C/C + + 。我认为将 R 在数据处理和统计分析方面的优势委托给另一种语言是错误的。在这些情况下,性能损失很低,而且在任何情况下都值得节省开发工作。如果仅仅是执行时间的问题,我们都会编写汇编程序。

把我算作另一个喜欢包裹的人。我承认在必要的时候(比如发布)之前,我写手册页和小插图的能力很差,但是这确实是一个捆绑源代码 doe 的好方法。另外,如果您认真对待维护代码,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 的系统,其中每个文件都是一个模块。那样会更好,但这看起来没问题。

我也一直在寻找正确的工作流程的圣杯放在一起的 R 大型项目。去年,我发现了一个叫 RSuite的软件包,当然,这就是我要找的。这个 R 包是为部署大型 R 项目而明确开发的,但是我发现它可以用于小型、中型和大型 R 项目。我将在一分钟内(下面)给出现实世界示例的链接,但首先,我想解释用 rsuite构建 R 项目的新范例。

注意,我不是 rsuite的创建者或开发者。

  1. 我们在使用 RStudio 时做项目的方法都是错误的; 我们的目标不应该是创建一个项目或包,而应该是创建一个更大范围的项目或包。在 rSuite 中,您可以创建一个超级项目或主项目,其中包含所有可能的组合中的标准 R 项目和 R 包。

  2. 通过拥有一个 R 超级项目,您不再需要 Unix make来管理底层的 R 项目; 您在顶层使用 R 脚本。我给你看看。当您创建一个 rSuite 主项目时,您会得到以下文件夹结构:

enter image description here

  1. 文件夹 R是放置项目管理脚本的地方,这些脚本将取代 make

  2. 文件夹 packagesrsuite保存组成超级项目的所有包的文件夹。您还可以复制粘贴一个不能从互联网上访问的包,rSuite 也会构建它。

  3. 文件夹 deploymentrsuite写入包 DESCRIPTION文件中指示的所有包二进制文件的地方。所以,这使得,本身,你投射完全可重现的错误时间。

  4. rsuite为所有操作系统提供了一个客户端。我都试过了。但是您也可以将其安装为 RStudio 的 addin

  5. rsuite还允许您在自己的文件夹 conda中构建独立的 conda安装。这不是一个环境,而是从您的机器中的 Anaconda 派生的物理 Python 安装。这与 R 的 SystemRequirements一起工作,您可以从中从任何您想要的 conda 通道安装所有您想要的 Python 包。

  6. 您还可以创建本地存储库,以便在脱机时提取 R 包,或者希望更快地构建整个系统。

  7. 如果需要,还可以将 R 项目构建为 zip 文件并与同事共享。只要您的同事安装了相同的 R 版本,它就会运行。

  8. 另一个选择是用 Ubuntu、 Debian 或 CentOS 构建整个项目的容器。因此,与其在项目构建中共享 zip 文件,不如在准备运行项目时共享整个 Docker容器。

我已经用 rsuite做了很多实验,寻找完全的可重复性,并避免依赖于在全局环境中安装的软件包。这是错误的,因为一旦安装了包更新,项目通常会停止工作,特别是那些对具有特定参数的函数进行特定调用的包。

我开始实验的第一件事是 bookdown电子书。我从来没有这么幸运地拥有一本能够通过六个月以上时间考验的账簿。因此,我所做的就是将原始的 bookdown 项目转换为遵循 rsuite框架。现在,我不必担心更新我的全局 R 环境,因为该项目在 deployment文件夹中有自己的一组包。

接下来我做的事情是创建机器学习项目,但在 rsuite的方式。一个主人,在顶部编排项目,所有子项目和包都在主人的控制之下。它确实改变了您使用 R 编写代码的方式,使您的工作效率更高。

从那以后,我开始使用我的一个新软件包 rTorch。这是可能的,在很大程度上,因为 rsuite; 它让您思考和去大。

不过我有个建议。学习 rsuite并不容易。因为它提供了一种创建 R 项目的新方法,所以感觉很难。不要对第一次尝试感到沮丧,继续攀登直到你成功。它需要对您的操作系统和文件系统有先进的了解。

我希望有一天 RStudio能让我们像 rsuite那样从菜单中生成编配项目。那会很棒的。

相关网址:

RSuite GitHub repo

R4ds 登记表

克拉斯和闪亮的教程

现代潜水书籍套装

可解释的 _ ml-rSuite

与 R- 套件一起学习

Clark-intro _ ml-rsuit

Hindman-bookdown-rSuite

统计学重新思考套件

Fread-基准测试-集合

Dataviz-rsuite

零售-市场细分-教程

电信客户流失教程

菌核病菌