面向项目的工作流

 珍妮·布莱恩

我很荣幸本周在IASC-ARS/NZSA会议由奥克兰大学统计系主办。会议主题之一是庆祝罗斯·伊哈卡(Ross Ihaka)的成就,他与罗伯特·绅士(Robert Gentleman)于1992年创办了R。我的演讲包括了关于建立你的R生活的建议,以最大限度地提高效率并减少挫折感。

生成了两张特定的幻灯片#rstats推特上有很多讨论和惊愕:

如果R脚本的第一行是

setwd(“C:\Users\jenny\path\只有\我\有”)

我会到你的办公室点燃电脑🔥.

如果R脚本的第一行是

rm(列表=ls())

我会到你的办公室点燃电脑🔥.

我支持这些强烈的观点,但仅凭他们自己,就威胁要承诺纵火没有多大帮助!我来解释一下为什么?这些习惯可以是有害的,可能表示工作流程很尴尬。请随意讨论更多关于社区.rstudio.com.

警告:只有你才能决定你有多在乎这件事。这些实践的重要性与您的代码是否将由其他人、在其他机器上以及在未来运行有很大关系。如果你目前的做法符合你的目的,那么就去快乐吧。

工作流与产品

让我们区分一下你因为个人品味和习惯而做的事情(“工作流程”)与作为项目本质的逻辑和输出(“产品”)。这些是您工作流程的一部分:

  • 用于编写R代码的编辑器。
  • 主目录的名称。
  • 午餐前你运行的R代码。

我认为这些显然是产品:

  • 原始数据。
  • 有人需要在您的原始数据上运行R代码以获得结果,包括显式的库()调用以加载必要的程序包。

理想情况下,您不会将任何有关工作流的内容硬连接到产品中。与工作流相关的操作应该由您以交互方式执行,使用适合您的设置的任何方法,但不要内置到脚本中。

自营项目

我建议将每个数据分析组织为项目:您计算机上的一个文件夹,其中包含与该特定工作相关的所有文件。我是假设这是一个R工作室项目,尽管这是下面讨论的一个很好的实现。

编写任何驻留R脚本都假定它将从一个新的R进程运行,并将工作目录设置为项目目录。它在自己的工作空间或文件夹中创建了所需的一切,并且它没有触及任何未创建的内容。例如,它没有安装额外的软件包(我的另一个烦恼)。

这种约定保证了项目可以在您的计算机上或其他计算机上移动,并且仍然“正常工作”。我认为这是唯一一种实用的惯例,可以在不同的计算机或用户之间以及随着时间的推移创建可靠、礼貌的行为。这个约定对R来说既不是新的,也不是唯一的。

这就像同意我们都会向左或向右行驶一样。文明的一个标志是以公共安全的名义,遵守一些限制你行为的惯例。

开发环境的使用

您会注意到这里给出的工作流建议如下如果使用IDE,则更容易实现(综合开发环境).R工作室是一个很好的例子(我今天使用的),但还有很多其他例子,包括:Emacs+ESS(在RStudio之前我用了大约15年),vim+Nvim-R,Visual Studio+RTVS.

因果关系的方向:长期编码人员不会将他们的工作组织成独立的项目,并使用相对路径因为他们使用IDE。他们使用IDE因为这样可以更容易地遵循标准做法,例如这些做法。

怎么了setwd()?

我在中运行了很多学生代码统计545一开始,我看到很多R脚本如下所示:

库(ggplot2)setwd(“/Users/jenny/cubddly_brocoli/verbose_unicular/foofy/data”)df<-read.delim(“raw_foofy_data.csv”)p<-ggplot(df,aes(x,y))+地理点()ggsave(“../f无花果/foofy_scatterplot.png”)

The chance of thesetwd()命令对除作者之外的任何人都有期望的效果(使文件路径有效)是0%。从现在起,它也不太可能为作者或计算机工作一两年。该项目不是独立的和可移植的。为了重新创建和扩展此绘图,幸运的接收者需要手动编辑一个或多个路径,以反映项目在其机器上的位置。当你在2天内第73次这样做时,当你标记作业时,你开始幻想着点燃犯罪者的电脑。

这种使用setwd()这也暗示了使用者在一个R过程中完成所有工作,并在从一个项目切换到另一个项目时手动切换档位。这种工作流程使得一次处理多个项目很不愉快,而且很容易让一个项目上的工作意外泄漏到另一个项目的后续工作中(例如,对象、加载的包、会话选项)。

使用项目和here包

你如何避免setwd()在每个脚本的顶部?

  • 将每个逻辑项目组织到计算机上的一个文件夹中。
  • 确保顶级文件夹以这样的方式进行广告宣传。这可以很简单,只要有一个名为.在这里或者,如果您使用RStudio和/或Git,它们都会留下完成任务的特征文件。
  • 使用此处()函数此处包装在读取或写入文件时构建路径。创建相对于顶级目录的路径。
  • 无论何时处理这个项目,都要从项目的顶级目录启动R流程。如果从shell启动R,光盘请先将其保存到正确的文件夹。

要继续我们的示例,请在美食目录。现在代码如下所示:

库(ggplot2)图书馆(此处)df<-read.delim(此处为“数据”,“raw_foofy_data.csv”)p<-ggplot(df,aes(x,y))+地理点()ggsave(这里是(“图”,“foofy_scatterplot.png”)

对于在项目文件夹中遵循有关启动R的约定的任何人,此操作都将在不进行编辑的情况下运行。事实上,如果R的工作目录位于项目中的任何位置,它甚至可以工作,即它可以从子文件夹中工作。这与knitr/rmakdown在工作目录和包开发/检查工作流中的默认行为配合得很好。

请阅读here包学习关于更多功能,例如标记顶级目录和使用进行故障排除dr_此处()。我还写了一篇更详细的赞歌到这个包之前。

R工作室项目

这种工作风格是如此重要,以至于RStudio对项目(大写“P”)。可以将新文件夹或现有文件夹指定为项目。这意味着RStudio会留下一个文件,例如。,美食。Rproj项目,该文件夹用于存储特定于该项目的设置。

双击.项目文件打开RStudio的新实例,工作目录和文件浏览器指向项目文件夹。here包知道这一点以及.项目是它识别项目顶级文件夹的方法之一。

RStudio完全支持基于项目的工作流,可以轻松地从一个项目切换到另一个,同时打开多个项目,重新启动最近使用的项目,等等。

怎么了rm(列表=ls())?

以这个object-nuking命令开头的数据分析脚本也很常见:

rm(列表=ls())

就像努力编写工作目录一样,这非常具有启发性使用者在一个R过程中工作,当他们从一个项目到另一个项目。这反过来表明,发展经常发生在已使用的长时间运行的R流程中,而不是新鲜干净的流程中。

问题是rm(列表=ls())事实上,不会创建新的R流程。它所做的只是从全局工作空间中删除用户创建的对象。

R景观的许多其他变化无形中持续存在,并可能对后续发展产生深远影响。所有已加载的包仍然可用。任何已设置为非默认值的选项都保持不变。工作目录不受影响(当然,这就是为什么我们看到setwd()这里也经常这样!)。

为什么这很重要?它使您的脚本容易受到执行之前在此R流程中运行的内容的隐藏依赖性的影响rm(列表=ls()).

  • 您可以使用包中的函数,而不包括必要的库()呼叫。您的合作者将无法运行此脚本。
  • 您可以编写分析代码,假设stringsAsFactors=假但下周,当你重新启动R时,一切都将莫名其妙地被打破。
  • 您可能会编写与某个随机工作目录相关的路径,然后在下个月找不到任何内容或结果没有出现在预期的位置时会感到困惑。

解决方案是编写每个脚本,假设它将在新的R进程中运行。你是怎么采用这种风格的?关键步骤:

  • 用户级设置:不保存.R数据当你退出R并且不加载时.R数据当你启动R时。
    • 在RStudio中,可以在“首选项”的“常规”选项卡中请求此行为。
    • 如果您从shell运行R,请在您的.bash_profile文件:alias R=“R--no-save--no-restore-data”.
  • 不要在你的.R配置文件这会影响R代码的运行方式,例如加载诸如dplyr或ggplot之类的包或设置诸如stringsAsFactors=假.
  • 日常工作习惯:经常重新启动R,并从头开始重新运行开发不足的脚本。
    • 如果使用RStudio,请使用菜单项会话>重新启动R(右)或相关的键盘快捷键Ctrl+Shift+F10(Windows和Linux)或Command+Shift+F10(Mac OS)。您可以将所有代码重新运行到使用Ctrl+Alt+B(Windows和Linux)或Command+Option+B(Mac OS)。
    • 如果从shell运行R,请使用Ctrl+D退出,然后R(右)以重新启动。

这需要你完全接受来源是真实的:

源代码是真实的。这些对象是源代码的实现。每个用户修改对象的源都放在一个或多个特定目录中,以便以后进行编辑和检索。–来自ESS手册

这并不意味着你的脚本需要经过完美的润色并准备好在远程服务器上无人值守运行。脚本可能会很混乱交互式执行,但仍然是完成.如果你在什么时候清理它们需要。

创建需要很长时间的对象怎么样?将该位单独隔离编写脚本并将宝贵的对象写入文件saveRDS(my_precious,此处(“results”,“my_precious.rds”)。现在您可以开发脚本通过以下方式重新装载贵重物品的下游工作my_precious<-readRDS(此处为“结果”,“my_precoius.rds”)。破坏数据是一个好主意分析成逻辑的、孤立的部分。

最后,rm(列表=ls())对你要求帮助你解决R问题的任何人都怀有敌意。如果他们从自己的工作中抽出一点时间来帮助调试您的代码,他们的慷慨将得到回报,因为他们失去了所有以前的工作。当然,如果您的助手已经接受了这里推荐的所有实践,这很容易恢复,但仍然令人恼火。当这种情况在一个学期内发生100次时,它重新点燃了由上周的惨败引发的电脑纵火幻想setwd().