R语言工作流程:脚本和项目

https://wp.me/p80aHo-2u8

Table of Contents

脚本

到目前为止,你一直使用控制台来运行代码。这是一个很好的起点,但当你创建更复杂的 ggplot2 图形以及更长的 dplyr 管道时,你会发现这里很快就会变得非常拥挤。为了给自己更多的工作空间,请使用脚本编辑器。你可以通过点击“File(文件)”菜单、选择“New File(新建文件)”,然后选择“R script(R 脚本)”,或者使用键盘快捷键 Cmd/Ctrl + Shift + N 来打开它。现在你会看到四个面板,就像图 6.1 所示一样。脚本编辑器非常适合用来试验你的代码。当你想要修改某些内容时,你不必重新输入整段内容;你只需要编辑脚本并重新运行即可。而当你已经编写出能够正常运行、并实现你想要效果的代码之后,你就可以将其保存为脚本文件,之后随时方便地返回继续使用。

运行代码

脚本编辑器是构建复杂的 ggplot2 图形或进行长串 dplyr 处理的绝佳场所。要高效地使用脚本编辑器,关键在于记住最重要的一个键盘快捷键:Cmd/Ctrl + Enter。这个快捷键会在控制台中执行当前的 R 表达式。例如,看看下面这段代码。

# 1) 加载需要的 R 包
library(dplyr) # 提供数据处理/管道(filter、group_by、summarize 等)函数
library(nycflights13) # 提供 nycflights13 数据集(航班数据)
# 2) 筛选:只保留“未被取消”的航班记录
# - dep_delay:起飞延误(如果航班被取消,通常会对应为 NA)
# - arr_delay:到达延误(如果航班被取消,通常也会对应为 NA)
# - 这里的作用是:排除 dep_delay 或 arr_delay 为 NA 的行
not_cancelled <- flights |>
filter(!is.na(dep_delay)█, !is.na(arr_delay))
# 3) 分组:按航班日期(年、月、日)分成多个组
# group_by(year, month, day) 让后续的 summarize 在每个日期组内分别计算
not_cancelled |>
group_by(year, month, day) |>
summarize(
# 4) 汇总:对每个日期组计算平均起飞延误
# - mean(dep_delay):该日期组内所有 dep_delay 的平均值
# - summarize 会返回一个“每组一行”的结果数据框
mean = mean(dep_delay)
)

如果你的光标位于 █ 处,按下 Cmd/Ctrl + Enter 将运行生成 not_cancelled 的完整命令。它还会把光标移动到下一条语句(以 not_cancelled |> 开头)。因此,你可以通过反复按 Cmd/Ctrl + Enter,轻松地一步步浏览/执行你的完整脚本。

我们建议你始终在脚本的开头先写好你需要的包。这样,当你把代码分享给别人时,他们就能很容易地看出需要安装哪些包。不过请注意:你在分享的脚本中绝对不要包含 install.packages()。因为如果别人没有足够小心,你把脚本交给他们后,它可能会在他们的电脑上做出一些改变——这是不太体贴的做法。

在后续章节中学习时,我们强烈建议你从脚本编辑器开始,并练习你的键盘快捷键。久而久之,你用这种方式把代码发送到控制台会变得非常自然,甚至都不用再去刻意思考。

保存与命名

RStudio 会在你退出时自动保存脚本编辑器中的内容,并在你重新打开时自动重新加载。然而,还是建议你避免使用 Untitled1、Untitled2、Untitled3 之类的文件名,而是要把你的脚本保存起来,并给它们起有信息量的名字。

给文件起名为 code.R 或 myscript.R 可能会很诱人,但在选择文件名之前,你应该多想一想。文件命名有三个重要原则如下:

  • 文件名应当可被机器读取:避免使用空格、符号和特殊字符。不要依赖大小写敏感来区分不同文件。
  • 文件名应当便于人类阅读:用文件名来描述文件里包含的内容。
  • 文件名应当与默认排序方式友好兼容:让文件名以数字开头,这样按字母顺序排序时,它们就会按照实际使用的顺序排列。

例如,假设你的项目文件夹中有如下这些文件。

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. FinalReport1),另外还有一些名字不能很好地描述它们的内容(run-first 和 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,以更准确地描述其内容。如果某个目录里有很多文件,建议在组织方面再更进一步:把不同类型的文件(脚本、图形等)放到不同的目录中。

项目

总有一天,你需要退出 R,去做别的事情,然后稍后再回来继续分析。总有一天,你会同时进行多个分析,并希望把它们彼此分开。总有一天,你需要把外部世界的数据导入 R,并把数值结果和图形从 R 再输出到外部世界。

为了应对这些现实生活中的情况,你需要做出两个决定:

  1. 什么是事实来源?你会把什么保存为对所发生事情的持久记录?
  2. 你的分析存放在哪里?

什么是权威来源?

作为初学者,你可以依赖当前的 Environment 来保存你在分析过程中创建的所有对象。不过,为了更方便地处理更大的项目或与他人协作,你的事实来源应该是 R 脚本。有了 R 脚本(以及你的数据文件),你就可以重建 Environment。只有 Environment 的话,要重建你的 R 脚本就困难得多:你要么得凭记忆重写大量代码(这一路上难免会出错),要么就得仔细翻查你的 R 历史记录。

为了帮助你把 R 脚本作为分析的事实来源,我们强烈建议你指示 RStudio 不要在会话之间保留工作区。你可以通过运行 usethis::use_blank_slate(),或者按照图 6.2 中显示的选项进行设置。这样做在短期内会让你有些不便,因为当你重新启动 RStudio 时,它将不再记得你上次运行的代码,你创建的对象或读取的数据集也不会再可用。但这种短期的不便会为你节省长期的痛苦,因为它迫使你把所有重要的步骤都记录在代码中。没有什么比在三个月后发现你只把一个重要计算的结果保存在 Environment 里,而没有把计算过程本身写进代码中,更糟糕的了。

有一组非常实用的键盘快捷键,可以配合使用,确保你已经把代码中的重要部分保存在编辑器里:

  1. 按 Cmd/Ctrl + Shift + 0/F10 重新启动 R。
  2. 按 Cmd/Ctrl + Shift + S 重新运行当前脚本。

我们每周都会把这个操作模式一起使用上百次。

    或者,如果你不使用键盘快捷键,也可以依次选择 Session > Restart R,然后高亮并重新运行当前脚本。

    如果你使用的是 RStudio Server,那么默认情况下你的 R 会话永远不会被重启。当你关闭 RStudio Server 的标签页时,感觉上像是在关闭 R,但实际上服务器仍会在后台继续运行。等你下次回来时,你会回到和离开时完全一样的位置。这也就使得定期重启 R 变得更加重要,这样你才能从一个干净的状态开始。

    你的分析存放在哪里?

    你可以通过运行 getwd() 在 R 代码中将其打印出来:

    getwd()
    #> [1] "/Users/hadley/Documents/r4ds"

    在这个 R 会话中,当前工作目录(可以把它看作“家”)位于 hadley 的 Documents 文件夹中,名为 r4ds 的子文件夹里。当你运行这段代码时,它会返回不同的结果,因为你的电脑的目录结构和 Hadley 的不一样!

    作为初学者,把你的工作目录设为主目录、文档目录,或者电脑上的任何其他奇怪目录都没问题。很快你就应该逐渐把项目整理到各自的目录中,并且在处理某个项目时,把 R 的工作目录设置为对应的目录。

    你可以在 R 中设置工作目录,但我们不建议这样做:

    setwd("/path/to/my/CoolProject")

    有一种更好的方法;一种还能让你走上像专家一样管理 R 工作之路的方法。那就是 RStudio 项目。

    RStudio 项目

    将与某个项目相关的所有文件(输入数据、R 脚本、分析结果和图表)都放在同一个目录中,是一种非常明智且常见的做法,因此 RStudio 通过 project 提供了内置支持。让我们为你创建一个 project,供你在阅读本书其余部分时使用。点击 File > New Project,然后按照图 6.3 中所示的步骤进行操作。

    将你的项目命名为 r4ds,并仔细考虑把项目放在哪个子目录中。如果你不把它存放在一个合适的位置,将来就很难找到它!

    一旦这个过程完成,你就会得到一个新 RStudio 项目。请检查你的项目“home”是否就是当前工作目录:

    getwd()
    #> [1] /Users/hadley/Documents/r4ds

    现在在脚本编辑器中输入以下命令,并将文件保存为“diamonds.R”。然后,新建一个名为“data”的文件夹。你可以通过点击 RStudio 中 Files 面板里的“New Folder”按钮来完成。最后,运行整个脚本,这会将一个 PNG 文件和一个 CSV 文件保存到你的项目目录中。

    # 加载 tidyverse 套件
    library(tidyverse)
    # 使用钻石数据集创建 carat 与 price 的六边形分箱图
    ggplot(diamonds, aes(x = carat, y = price)) +
    geom_hex()
    # 将图形保存为 PNG 文件
    ggsave("diamonds.png")
    # 将 diamonds 数据集写入 CSV 文件
    write_csv(diamonds, "data/diamonds.csv")

    退出 RStudio。查看与你的项目相关联的文件夹——注意 .Rproj 文件。双击该文件以重新打开项目。注意你会回到离开时的状态:相同的工作目录和命令历史记录,所有正在处理的文件也仍然打开着。不过,由于你遵循了我们上面的说明,你将拥有一个完全全新的环境,这保证你是从一个干净的起点开始的。

    用你最喜欢的、针对特定操作系统的方式,在电脑中搜索 diamonds.png,你不仅会找到这个 PNG 文件(不奇怪),还会找到创建它的脚本(diamonds.R)。这可是一个巨大的收获!总有一天,你会想重新制作某个图形,或者只是想弄清楚它是从哪里来的。如果你严格地用 R 代码把图形保存到文件中,而不是使用鼠标或剪贴板,那么你就能轻松地重现旧的工作成果!

    相对路径和绝对路径

    一旦进入项目中,你应该始终只使用相对路径,而不要使用绝对路径。它们有什么区别?相对路径是相对于工作目录而言的,也就是项目的 home 目录。比如,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 风格的正斜杠。

    总结

    在这一章中,你已经学会了如何在脚本(文件)和项目(目录)中组织你的 R 代码。和代码风格一样,这一开始可能会让你觉得像是在做琐事。但随着你在多个项目中积累越来越多的代码,你会逐渐体会到,前期做一点组织工作能在后面节省大量时间。

    总之,脚本和项目能为你提供一套可靠的工作流程,并且在未来也会非常有用:

    • 为每个数据分析项目创建一个 RStudio 项目。
    • 将你的脚本(用有意义的名称命名)保存在项目中,编辑它们,并分段或整体运行它们。要经常重启 R,以确保你已经把脚本中的内容都保存好了。
    • 只使用相对路径,不要使用绝对路径。
    • 这样,你所需的一切都会集中在一个地方,并且与其他你正在处理的项目清晰分开。

    评论

    发表评论

    了解 数据控|突破是我们的每一步 的更多信息

    立即订阅以继续阅读并访问完整档案。

    继续阅读