我正在寻找一种方法,使我的以 R 笔记本为中心的工作流程更具可重复性,并且随后更容易使用 Docker 进行容器化。对于我的中型数据分析项目,我使用一个非常简单的结构:一个与 .Rproj 关联的文件夹和一个 index.html(这是 Github Pages 的登录页面),其中包含其他文件夹,其中包含笔记本、数据、脚本等。这种简单的“1 GitHub repo = 1 Rproj”结构也适用于 Github Pages 呈现的我的 nb.html 文件。
.
└── notebooks_project
├── notebook_1
│ ├── notebook_1.Rmd
│ └── ...
├── notebook_2
│ ├── notebook_2.Rmd
│ └── ...
├── notebooks_project.Rproj
├── README.md
├── index.html
└── .gitignore
我希望保留这个使用 R 笔记本作为文学编程工具和控制文档的工作流程(请参阅RMarkdown Driven Development),因为它似乎非常适合中等可重现的分析项目。不幸的是,缺乏关于使用 Rmd 的工作流的文档renv
,尽管它似乎与它很好地集成。
首先,谢一辉在这里暗示,与使用 renv 对单个 Rmd 文档相关的方法包括:renv::activate()
、renv::use()
和renv::embed()
. renv::activate()
只做部分工作renv::init()
:加载项目并获取init.R
. 据我了解,如果项目已经初始化,它会执行此操作,但它的行为就像renv::init()
项目未初始化:发现依赖项,将它们复制到 renv 全局包缓存,写入多个文件(.Rprofile、renv/activate.R、renv/ .gitignore,.Rbuildignore)。renv::use()
在独立的 R 脚本中运行良好,其中脚本的依赖项直接在该脚本中指定,我们需要在运行相关脚本时自动安装和加载这些包。renv::embed()
只是将 的紧凑表示嵌入renv.lock
到笔记本的代码块中 - 它通过添加具有依赖关系的代码块并删除对 .Rmd 的调用来更改渲染/保存时的 .Rmd renv::embed()
。据我了解,对于可重现的独立笔记本来说,使用renv::embed()
and可能就足够了。renv::use()
不过,我不介意将锁定文件放在目录中或保留 renv 库,只要它们都在同一个目录中。
其次,与RStudio 包管理器renv
一起使用,为后续的 Binder 或 Docker 需求做准备。Grant McDermott在这里提供了一些有用的代码(我认为可能在 .Rprofile 或 .Rmd 本身中)并提供了它的基本原理:
锁定文件是针对 RSPM 作为默认包存储库(即从哪里下载包)的引用,而不是通常的 CRAN 镜像之一。除其他外,这使得跨不同包版本的时间旅行和在 Linux 上快速安装预编译的 R 包二进制文件成为可能。
第三,我想使用here
包来处理相对路径。这似乎是让笔记本在传输时或在 Docker 容器内运行时运行的方法。不幸here::here()
的是,查找 .Rproj 并将在我的上层文件夹(即notebooks_project
)中找到它。.here
可以放置的文件会here::set_here()
覆盖此行为,使其here::here()
按预期指向笔记本文件夹(即notebook1
)。不幸的是,该.here
文件仅在重新启动 R 会话或运行时生效unloadNamespace("here")
(在此处记录)。
到目前为止,这是我尝试过的:
---
title: "<br> R Notebook Template"
subtitle: "RMardown Report"
author: "<br> Claudiu Papasteri"
date: "`r format(Sys.time(), '%d %m %Y')`"
output:
html_notebook:
code_folding: hide
toc: true
toc_depth: 2
number_sections: true
theme: spacelab
highlight: tango
font-family: Arial
---
```{r setup, include = FALSE}
# Set renv activate the current project
renv::activate()
# Set default package source by operating system, so that we automatically pull in pre-built binary snapshots, rather than building from source.
# This can also be appended to .Rprofile
if (Sys.info()[["sysname"]] %in% c("Linux", "Windows")) { # For Linux and Windows use RStudio Package Manager (RSPM)
options(repos = c(RSPM = "https://packagemanager.rstudio.com/all/latest"))
} else {
# For Mac users, we default to installing from CRAN/MRAN instead, since RSPM does not yet support Mac binaries.
options(repos = c(CRAN = "https://cran.rstudio.com/"))
# options(renv.config.mran.enabled = TRUE) ## TRUE by default
}
options(renv.config.repos.override = getOption("repos"))
# Install (if necessary) & Load packages
packages <- c(
"tidyverse", "here"
)
renv::install(packages, prompt = FALSE) # install packages that are not in cache
renv::hydrate(update = FALSE) # install any packages used in the Rnotebook but not provided, do not update
renv::snapshot(prompt = FALSE)
# Set here to Rnotebook directory
here::set_here()
unloadNamespace("here") # need new R session or unload namespace for .here file to take precedence over .Rproj
rrRn_name <- fs::path_file(here::here())
# Set kintr options including root.dir pointing to the .here file in Rnotebook directory
knitr::opts_chunk$set(root.dir = here::here())
# ???
renv::use(lockfile = here::here("renv.lock"), attach = TRUE) # automatic provision an R library when Rnotebook is run and load packages
# renv::embed(path = here::here(rrRn_name), lockfile = here::here("renv.lock")) # if run this embeds the renv.lock inside the Rnotebook
renv::status()$synchronized
```
我希望我的 nobooks 能够在本地(已安装、缓存依赖项和初始化项目的位置)以及转移到其他系统时无需更改代码即可运行。每个笔记本都应该有自己的 renv 设置。
我有很多问题:
- 我的 renv 序列有什么问题?每次运行(初始化和之后)都调用是
renv::activate()
要走的路吗?我应该使用renv::use()
而不是renv::install()
andrenv::hydrate()
吗?renv::embed()
即使每个笔记本文件夹都应该有其renv.lock
和库,对于可重现的工作流程是否更好?renv
激活时还会创建一个 .Rproj 文件(例如notebook1.Rproj
),从而破坏了我的简单 1 repo = 1 Rproj - 这应该与我有关吗? - renv-RSPM 工作流程看起来很棒,但是将该脚本存储在 .Rprofile 中而不是将其存储在 Rmd 本身中是否有任何优势?
- 有没有更好的使用方法
here
?这unloadNamespace("here")
似乎很老套,但似乎是保留.here
文件使用的唯一方法。