如何组织大型R程序?

时间:2009-08-12 13:50:45

标签: r package conventions code-organization project-organization

当我进行任何复杂的R项目时,我的脚本会很快变得混乱。

我可以采用哪些做法,以便我的代码永远乐于与之合作?我正在考虑像

这样的事情
  • 在源文件中放置函数
  • 何时将某些内容分解为其他源文件
  • 主文件中应包含哪些内容
  • 使用函数作为组织单位(考虑到R使得难以访问全局状态,这是否值得)
  • 缩进/换行做法。
    • 治疗(如{?
    • 把东西放在1或2行?

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

11 个答案:

答案 0 :(得分:69)

标准答案是使用包 - 请参阅Writing R Extensions手册以及网络上的不同教程。

它给你

  • 按主题组织代码的准自动方式
  • 强烈建议您撰写帮助文件,让您考虑界面
  • 通过R CMD check
  • 进行了大量的健全性检查
  • 有机会添加回归测试
  • 以及命名空间的方法。

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

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

2008年8月13日更新: David Smith刚刚发表了关于Google R Style Guide的博文。

答案 1 :(得分:50)

我喜欢在自己的文件中添加不同的功能。

但我不喜欢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的系统,其中每个文件都是一个模块。那会更好,但这似乎没问题。

答案 2 :(得分:33)

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

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

你的功能有多细/粗粒度会有所不同,并且有各种经验法则:例如: 15行代码,或“一个函数应该负责执行一个由其名称标识的任务”等。您的里程会有所不同。由于R不支持call-by-reference,因此当我涉及传递数据帧或类似结构时,我通常会使我的函数太精细。但是,当我第一次开始使用R时,这可能会对一些愚蠢的性能错误进行过度补偿。

何时将逻辑单元提取到自己的物理单元(如源文件和更大的分组包)?我有两个案例。首先,如果文件太大并且在逻辑上不相关的单元之间滚动是一个烦恼。其次,如果我有其他程序可以重用的功能。我通常首先将一些分组单元(比如数据操作函数)放入一个单独的文件中。然后我可以从任何其他脚本中获取此文件。

如果您要部署您的功能,那么您需要开始考虑包。我没有在生产中部署R代码或者由于各种原因而被其他人重复使用(简要来说:org culture更喜欢其他语言,对性能的担忧,GPL等)。此外,我倾向于不断改进并添加到我的源文件集合中,当我进行更改时,我宁愿不处理包。因此,您应该查看其他与软件包相关的答案,例如Dirk's,了解有关此方面的更多详细信息。

最后,我认为你的问题不一定特别适用于R.我真的建议阅读Steve McConnell编写的Code Complete,其中包含了很多关于此类问题和编码实践的智慧。

答案 3 :(得分:19)

我同意Dirk的建议!恕我直言,将程序从简单的脚本组织到文档化的软件包,对于R编程,就像从Word切换到TeX / LaTeX进行编写一样。 我建议您查看Friedrich Leisch撰写的非常有用的 Creating R Packages: A Tutorial

答案 4 :(得分:15)

我的简明回答:

  1. 仔细编写您的功能,确定足够的输出和输入;
  2. 限制使用全局变量;
  3. 使用S3对象,并在适当情况下使用S4对象;
  4. 将函数放在包中,尤其是在函数调用C / Fortran时。
  5. 我相信R越来越多地用于生产,因此对可重用代码的需求比以前更大。我发现解释器比以前更强大。毫无疑问,R比C慢100-300倍,但通常瓶颈集中在几行代码上,可以委托给C / C ++。我认为将R在数据处理和统计分析方面的优势委托给另一种语言是错误的。在这些情况下,性能损失很低,在任何情况下都值得节省开发工作。如果单独执行时间,我们都会编写汇编程序。

答案 5 :(得分:10)

我一直想知道如何编写包但没有投入时间。对于我的每个迷你项目,我将所有低级函数保存在一个名为“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())))

评估您创建的环境中的添加/修改。

我知道这是kludgey,但避免过于正式(但如果你有机会我鼓励包装系统 - 希望我将来会以这种方式迁移)。

至于编码惯例,这是我唯一看到的关于美学的东西(我喜欢它们并且松散地遵循但我不会在R中使用过多的花括号):

http://www1.maths.lth.se/help/R/RCC/

关于使用[,drop = FALSE]和&lt; - 作为赋值运算符在useR的各种演示文稿(通常是主题演讲)中建议,还有其他“约定”!会议,但我不认为这些都是严格的(虽然[,drop = FALSE]对你不确定你期望的输入的程序有用。)

答案 6 :(得分:6)

算我为另一个赞成包裹的人。我承认在编写手册页和小插图时非常差,直到我必须(即被释放),但它是一个真正方便的方法来捆绑源母公司。另外,如果你认真对待维护你的代码,那么Dirk带来的所有观点都会进入plya。

答案 7 :(得分:4)

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

至于访问全局环境,使用&lt;&lt; - 运算符很容易,但不建议这样做。

答案 8 :(得分:3)

尚未学习如何编写包,我总是通过采购子脚本来组织。它类似于编写类但不涉及。它不是编程优雅,但我发现随着时间的推移我会建立分析。一旦我有一个大的部分工作,我经常将它移动到一个不同的脚本,只是源它,因为它将使用工作区对象。也许我需要从几个来源导入数据,对所有数据进行排序并找到交叉点。我可能会将该部分放入另一个脚本中。但是,如果您想为其他人分发“应用程序”,或者它使用一些交互式输入,那么程序包可能是一个很好的途径。作为一名研究人员,我很少需要分发我的分析代码,但我需要对其进行补充或调整。

答案 9 :(得分:1)

我也一直在寻找正确的工作流程,以完成一个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. packages文件夹是rsuite包含组成超级项目的所有软件包的文件夹。您也可以复制粘贴无法从Internet访问的软件包,rsuite也会构建它。

  3. deployment文件夹是rsuite写入文件夹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电子书。我从来没有足够幸运地拥有一本记本,以经受住六个月以上的时间考验。因此,我所做的就是将原始的书本项目转换为遵循rsuite框架。现在,我不必担心更新全局R环境,因为该项目在deployment文件夹中有自己的一组软件包。

我接下来要做的是创建机器学习项目,但是以rsuite的方式进行。一个主要的,协调的项目在顶部,所有子项目和程序包都在该主要项目的控制之下。它确实改变了您使用R编码的方式,从而提高了生产力。

此后,我开始研究一个名为rTorch的新软件包。这在很大程度上是由于rsuite导致的;它可以让您思考并不断壮大。

虽然有一条建议。学习rsuite并不容易。因为它提供了创建R项目的新方法,所以很难。初次尝试时不要惊慌,继续爬坡直到您成功。它需要您的操作系统和文件系统的高级知识。

我希望有一天RStudio允许我们从菜单中生成rsuite这样的编排项目。太棒了。

链接:

RSuite GitHUb repo

r4ds bookdown

keras and shiny tutorial

moderndive-book-rsuite

interpretable_ml-rsuite

IntroMachineLearningWithR-rsuite

clark-intro_ml-rsuite

hyndman-bookdown-rsuite

statistical_rethinking-rsuite

fread-benchmarks-rsuite

dataviz-rsuite

retail-segmentation-h2o-tutorial

telco-customer-churn-tutorial

sclerotinia_rsuite

答案 10 :(得分:-5)

R可以用于交互式使用和小脚本,但我不会将它用于大型程序。我会在大多数编程中使用主流语言并将其包装在R接口中。