7 Workflow: scripts and projects
You are reading the work-in-progress second edition of R for Data Science. This chapter is largely complete and just needs final proof reading. You can find the complete first edition at https://r4ds.had.co.nz.
本章将向您介绍组织代码的两个基本工具:脚本(scripts)和项目(projects)。
7.1 Scripts
到目前为止,您已经使用控制台(console)来运行代码。 这是一个很好的起点,但是当您创建更复杂的 ggplot2 图形和更长的 dplyr 管道时,您会发现它很快就会变得拥挤。 为了给自己更多的工作空间,请使用脚本编辑器。 单击 File 菜单,选择 New File,然后选择 R script,或使用键盘快捷键 Cmd/Ctrl + Shift + N 将其打开。 现在您将看到四个窗格,如 Figure 7.1 所示。 脚本编辑器是试验代码的好地方。 当您想要更改某些内容时,不必重新输入整个内容,只需编辑脚本并重新运行即可。 一旦您编写了可以运行并执行您想要的操作的代码,您就可以将其保存为脚本文件,以便稍后轻松返回。
7.1.1 Running code
脚本编辑器是构建复杂 ggplot2 绘图或长序列 dplyr 操作的绝佳场所。 有效使用脚本编辑器的关键是记住最重要的键盘快捷键之一:Cmd/Ctrl + Enter。 这将在控制台中执行当前的 R 表达式。 例如,采用下面的代码。
library(dplyr)
library(nycflights13)
<- flights |>
not_cancelled filter(!is.na(dep_delay)█, !is.na(arr_delay))
|>
not_cancelled group_by(year, month, day) |>
summarize(mean = mean(dep_delay))
如果光标位于 █ 处,按 Cmd/Ctrl + Enter 将运行生成 not_cancelled
的完整命令。 它还会将光标移动到以下语句(以 not_cancelled |>
开头)。 这样可以通过重复按 Cmd/Ctrl + Enter 轻松地逐步执行完整的脚本。
您还可以使用 Cmd/Ctrl + Shift + S 一步执行完整的脚本,而不是逐个表达式地运行代码。 定期执行此操作是确保您捕获脚本中代码的所有重要部分的好方法。
我们建议您始终使用所需的包来启动脚本。 这样,如果您与其他人共享代码,他们可以轻松查看需要安装哪些软件包。 但请注意,您绝对不应该在共享的脚本中包含 install.packages()
。 如果他们不小心的话,交出一个会改变他们计算机上某些内容的脚本是不体贴的!
在学习后续章节时,我们强烈建议从脚本编辑器开始并练习键盘快捷键。 随着时间的推移,以这种方式向控制台发送代码将变得如此自然,您甚至不会想到它。
7.1.2 RStudio diagnostics
在脚本编辑器中,RStudio 将使用红色波浪线和侧边栏中的十字来突出显示语法错误:
将鼠标悬停在十字上即可查看问题所在:
RStudio 还会让您了解潜在的问题:
7.1.3 Saving and naming
当您退出时,RStudio 会自动保存脚本编辑器的内容,并在您重新打开时自动重新加载。 尽管如此,最好避免使用 Untitled1、Untitled2、Untitled3 等,而是保存脚本并为它们提供信息丰富的名称。
将文件命名为 code.R
或 myscript.R
可能很诱人,但在为文件选择名称之前应该仔细考虑一下。 文件命名的三个重要原则如下:
- 文件名应该是机器可读的(machine readable):避免空格、符号和特殊字符。不要依靠大小写来区分文件。
- 文件名应该是人类可读的(human readable):使用文件名来描述文件中的内容。
- 文件名应该与默认顺序配合良好:以数字开头的文件名,以便按字母顺序排序将它们按照使用的顺序排列。
例如,假设项目文件夹中有以下文件。
alternative model.R
code for exploratory analysis.r
finalreport.qmd
FinalReport.qmd
fig 1.png
Figure_02.png
model_first_try.R
run-first.r
temp.txt
这里存在各种各样的问题:很难找到先运行哪个文件、文件名包含空格、有两个同名但大小写不同的文件(finalreport
vs. FinalReport
1)、有些名称没有描述其内容(run-first
and temp
)。
这是命名和组织同一组文件的更好方法:
01-load-data.R
02-exploratory-analysis.R
03-model-approach-1.R
04-model-approach-2.R
fig-01.png
fig-02.png
report-2022-03-20.qmd
report-2022-04-02.qmd
report-draft-notes.txt
对关键脚本进行编号可以清楚地显示运行它们的顺序,并且一致的命名方案可以更容易地看到差异。 此外,数字的标签类似,报告通过文件名中包含的日期进行区分,并且 temp
被重命名为 report-draft-notes
以更好地描述其内容。 如果一个目录中有很多文件,建议进一步组织,将不同类型的文件(scripts, figures, etc.)放在不同的目录中。
7.2 Projects
有一天,您需要退出 R,去做其他事情,然后再返回分析。 有一天,您将同时进行多项分析,并且希望将它们分开。 有一天,您需要将外部世界的数据引入 R,并将 R 中的数值结果和数字发送回外部世界。
为了处理这些现实生活中的情况,您需要做出两个决定:
真实来源是什么? 您将保存什么作为所发生事件的永久记录?
您的分析在哪里?
7.2.1 What is the source of truth?
作为初学者,可以依赖当前的环境来包含您在分析过程中创建的所有对象。 但是,为了更轻松地处理大型项目或与其他人协作,您的真实来源应该是 R scripts。 使用 R scripts(和数据文件),您可以重新创建环境。 仅在您的环境中,重新创建 R scripts 要困难得多:您要么必须从内存中重新输入大量代码(一路上不可避免地会犯错误),要么必须仔细挖掘您的 R history。
为了帮助将 R scripts 保留为分析的真实来源,我们强烈建议您指示 RStudio 不要在会话之间保留工作区。 您可以通过运行 usethis::use_blank_slate()
2 或 Figure 7.2 中所示的选项来完成此操作。 这会给您带来一些短期的痛苦,因为现在当您重新启动 RStudio 时,它将不再记住您上次运行的代码,也不会再使用您创建的对象或读取的数据集。 但这种短期的痛苦可以避免长期的痛苦,因为它迫使您捕获代码中的所有重要过程。 没有什么比三个月后发现您只将重要计算的结果存储在环境中,而不是计算本身存储在代码中更糟糕的了。
有一对很棒的键盘快捷键可以协同工作,以确保您在编辑器中捕获了代码的重要部分:
- 按 Cmd/Ctrl + Shift + 0/F10 重新启动 R。
- 按 Cmd/Ctrl + Shift + S 重新运行当前脚本。
我们每周都会使用这种模式数百次。
或者,如果您不使用键盘快捷键,则可以转到 Session > Restart R,然后突出显示并重新运行当前脚本。
7.2.2 Where does your analysis live?
R 有一个强大的工作目录(working directory)概念。 这是 R 查找您要求其加载的文件的位置,也是放置您要求其保存的任何文件的位置。 RStudio 在控制台顶部显示您当前的工作目录:
您可以通过运行 getwd()
在 R 代码中打印出来:
getwd()
#> [1] "/Users/hadley/Documents/r4ds"
在此 R 会话中,当前工作目录(将其视为”home”)位于 hadley 的 Documents 文件夹中名为 r4ds 的子文件夹中。 当您运行此代码时,它会返回不同的结果,因为您的计算机的目录结构与 Hadley 的不同!
作为 R 初学者,可以将工作目录设置为主目录、文档目录或计算机上任何其他奇怪的目录。 但你已经读了这本书的七章,并且你不再是初学者了。 很快您就应该将项目组织到目录中,并且在处理项目时将 R 的工作目录设置为关联的目录。
您可以在 R 中设置工作目录,但我们不推荐这样做:
setwd("/path/to/my/CoolProject")
有一个更好的方法;这种方式还可以让您像专家一样管理您的 R 工作。 就是 RStudio project。
7.2.3 RStudio projects
将与给定项目关联的所有文件(input data, R scripts, analytical results, and figures)保存在一个目录中是一种明智且常见的做法,RStudio 通过项目(projects)对此提供了内置支持。 让我们创建一个 project 供您在学习本书其余部分时使用。 单击 File > New Project,然后按照 Figure 7.3 中所示的步骤操作。
将您的项目命名为 r4ds
,并仔细考虑将项目放在哪个子目录中。 如果您不将其存储在合理的地方,将来将很难找到它!
此过程完成后,您将获得一个专门用于本书的新 RStudio 项目。 检查项目的 “home” 是否是当前工作目录:
getwd()
#> [1] /Users/hadley/Documents/r4ds
现在在脚本编辑器中输入以下命令,然后保存文件,将其命名为 “diamonds.R”。 然后,创建一个名为 “data” 的新文件夹。 您可以通过单击 RStudio Files 窗格中的 “New Folder” 按钮来完成此操作。 最后,运行完整的脚本,将 PNG 和 CSV 文件保存到您的项目目录中。 不用担心细节,你会在本书后面学到它们。
退出 RStudio。 检查与您的项目关联的文件夹 — 注意 .Rproj
文件。 双击该文件以重新打开该项目。 请注意,您回到了上次离开的位置:它是相同的工作目录和命令历史记录,并且您正在处理的所有文件仍然打开。 然而,由于您遵循了我们上面的说明,因此您将拥有一个全新的环境,保证您从头开始。
以您最喜欢的特定于操作系统的方式,在计算机中搜索 diamonds.png
,您会找到 PNG(毫不奇怪),而且还会找到创建它的脚本(diamonds.R
)。 这是一个巨大的胜利! 有一天,您会想要重新制作一个人物,或者只是想了解它的来源。 如果您使用 R 代码严格地将图形保存到文件中,而不是使用鼠标或剪贴板,那么您将能够轻松地重现旧作品!
7.2.4 Relative and absolute paths
一旦进入项目,您应该只使用相对路径而不是绝对路径。 有什么不同? 相对路径是相对于工作目录的,即项目的主目录。 当 Hadley 在上面写入 data/diamonds.csv
时,它是 /Users/hadley/Documents/r4ds/data/diamonds.csv
的快捷方式。 但重要的是,如果 Mine 在她的计算机上运行此代码,它将指向 /Users/Mine/Documents/r4ds/data/diamonds.csv
。 这就是为什么相对路径很重要:无论 R 项目文件夹位于何处,它们都会起作用。
无论您的工作目录如何,绝对路径都指向同一位置。 根据您的操作系统,它们看起来略有不同。 在 Windows 上,它们以驱动器号(例如 C:
)或两个反斜杠(例如 \\servername
)开头,在 Mac/Linux 上,它们以斜杠”/“开头(例如 /users/hadley
)。 您永远不应该在脚本中使用绝对路径,因为它们会妨碍共享:其他人不会拥有与您完全相同的目录配置。
操作系统之间还有另一个重要的区别:如何分离路径的组成部分。 Mac 和 Linux 使用斜杠(例如 data/diamonds.csv
),Windows 使用反斜杠(例如 data\diamonds.csv
)。 R 可以使用任何一种类型(无论您当前使用什么平台),但不幸的是,反斜杠对 R 来说意味着一些特殊的东西,并且要在路径中获得单个反斜杠,您需要键入两个反斜杠! 这让生活变得令人沮丧,因此我们建议始终使用带有正斜杠的 Linux/Mac 风格。
7.3 Exercises
转到 RStudio Tips Twitter account,https://twitter.com/rstudiotips 并找到一个看起来有趣的提示。 练习使用它!
RStudio 诊断还会报告哪些其他常见错误? 阅读 https://support.posit.co/hc/en-us/articles/205753617-Code-Diagnostics 找出。
7.4 Summary
在本章中,您学习了如何在 scripts (files) 和 projects (directories) 中组织 R 代码。 就像代码风格一样,一开始这可能感觉像是在忙活。 但是,当您在多个项目中积累更多代码时,您将学会欣赏一点预先的组织如何可以为您节省大量时间。
总之,scripts 和 projects 为您提供了可靠的工作流程,将在未来为您提供良好的服务:
- 为每个数据分析项目创建一个 RStudio project。
- 将脚本(具有信息丰富的名称)保存在项目中,对其进行编辑,部分或整体运行它们。经常重新启动 R 以确保您已捕获脚本中的所有内容。
- 只使用相对路径,而不是绝对路径。
然后,您需要的所有内容都集中在一处,并与您正在处理的所有其他项目完全分开。
到目前为止,我们已经使用了 R 包中捆绑的数据集。 这样可以更轻松地对预先准备的数据进行一些练习,但显然您的数据无法以这种方式获得。 因此,在下一章中,您将学习如何使用 readr 包将数据从磁盘加载到 R 会话中。
Not to mention that you’re tempting fate by using “final” in the name 😆 The comic Piled Higher and Deeper has a fun strip on this.↩︎
If you don’t have usethis installed, you can install it with
install.packages("usethis")
.↩︎