如何以编程方式编辑 R 降价 YAML 标头?



以这个 Rmd 文件为例 - https://github.com/rstudio/learnr/blob/master/inst/tutorials/ex-setup-r/ex-setup-r.Rmd

YAML 标头具有此内容 -

output:
learnr::tutorial:
progressive: true
allow_skip: true

我想把它改成——

output: 
ioslides_presentation:
widescreen: true

有没有办法以编程方式进行此编辑,即我可以编写一些将 Rmd 文件作为输入、编辑 YAML 标头并生成新 Rmd 文件的函数吗?

谢谢!

我认为一个快速函数可以做到这一点。

change_yaml_matter <- function(input_file, ..., output_file) {
input_lines <- readLines(input_file)
delimiters <- grep("^---\s*$", input_lines)
if (!length(delimiters)) {
stop("unable to find yaml delimiters")
} else if (length(delimiters) == 1L) {
if (delimiters[1] == 1L) {
stop("cannot find second delimiter, first is on line 1")
} else {
# found just one set, assume it is *closing* the yaml matter;
# fake a preceding line of delimiter
delimiters <- c(0L, delimiters[1])
}
}
delimiters <- delimiters[1:2]
yaml_list <- yaml::yaml.load(input_lines[ (delimiters[1]+1):(delimiters[2]-1) ])
dots <- list(...)
yaml_list <- c(yaml_list[ setdiff(names(yaml_list), names(dots)) ], dots)
output_lines <- c(
if (delimiters[1] > 0) input_lines[1:(delimiters[1])],
strsplit(yaml::as.yaml(yaml_list), "n")[[1]],
input_lines[ -(1:(delimiters[2]-1)) ]
)
if (missing(output_file)) {
return(output_lines)
} else {
writeLines(output_lines, con = output_file)
return(invisible(output_lines))
}
}

...在哪里是你想要的。意思是:如果你想替换yaml物质的output:组件,那么你给出一个命名列表作为output=list(...)

如果我使用我在上一个答案中使用的 rmarkdown 文档,那么保持不变,它看起来像这样:

readLines("~/StackOverflow/1883604/62095186.Rmd")
#  [1] "---"                                              
#  [2] "title: Hello"                                     
#  [3] "output: html_document"                            
#  [4] "params:"                                          
#  [5] "  intab: TRUE"                                    
#  [6] "---"                                              
#  [7] ""                                                 
#  [8] "# Headline 1"                                     
#  [9] ""                                                 
# [10] "## Headline 2 `r if (params$intab) "{.tabset}"`"
# [11] ""                                                 
# [12] "### Headline 3 in a tab"                          
# [13] ""                                                 
# [14] "### Headline 4 in a tab"                          
# [15] ""                                                 
# [16] "### Headline 5 in a tab"                          
# [17] ""                                                 
# [18] ""                                                 

为了更改output部分,我添加一个嵌套的命名列表,如下所示:

change_yaml_matter("~/StackOverflow/1883604/62095186.Rmd", 
output=list(ioslides_presentation=list(widescreen=TRUE)))
#  [1] "---"                                              
#  [2] "title: Hello"                                     
#  [3] "params:"                                          
#  [4] "  intab: yes"                                     
#  [5] "output:"                                          
#  [6] "  ioslides_presentation:"                         
#  [7] "    widescreen: yes"                              
#  [8] "---"                                              
#  [9] ""                                                 
# [10] "# Headline 1"                                     
# [11] ""                                                 
# [12] "## Headline 2 `r if (params$intab) "{.tabset}"`"
# [13] ""                                                 
# [14] "### Headline 3 in a tab"                          
# [15] ""                                                 
# [16] "### Headline 4 in a tab"                          
# [17] ""                                                 
# [18] "### Headline 5 in a tab"                          
# [19] ""                                                 
# [20] ""                                                 

你可以改变yaml问题的任何部分。(我怀疑,您唯一无法更改的是,如果您碰巧有名为input_fileoutput_file的 yaml 参数。如果您实际上拥有带有这些 yaml 顶级参数的 Rmd 文件,那么您可以轻松地将此处的命名参数重命名为其他内容,例如Mxyzptlk和其他内容......您不太可能在生产中看到这些内容。

笔记:

  • 这没有将任何内容保存到文件中,您必须自己执行此操作。将output_file="path/to/new.RMd"添加到您的呼叫中,它将写入一个新文件。
  • 当您在参数中包含output_file=时,如果您选择不捕获返回值,它将显示为不返回任何内容。这是由于我返回时invisible;如果您确实想要查看和保存,要么捕获到变量并查看该变量,要么将函数调用包装在 paren 中,如(change_yaml_matter(...)).

YAML 的诀窍是知道yaml::会将每个顶级视为列表的命名元素,并且其内容以相同的方式递归列出。例如

str(yaml::yaml.load("
---
top1:
level2a:
level3a: 123
level3b: 456
level2b: 789
top2: quux
---"))
# List of 2
#  $ top1:List of 2
#   ..$ level2a:List of 2
#   .. ..$ level3a: int 123
#   .. ..$ level3b: int 456
#   ..$ level2b: int 789
#  $ top2: chr "quux"

要分配新值,只需提供嵌套的命名列表。

我稍微修改了一下。

使用给定的版本,如果更改了现有的 yaml 元素,则会将其移动到 yaml 标头的末尾。

通过我的修改,具有更改值的现有元素将保留其在标题中的位置。

change_yaml_matter <- function(input_file, ..., output_file) {
input_lines <- readLines(input_file)
delimiters <- grep("^---\s*$", input_lines)
if (!length(delimiters)) {
stop("unable to find yaml delimiters")
} else if (length(delimiters) == 1L) {
if (delimiters[1] == 1L) {
stop("cannot find second delimiter, first is on line 1")
} else {
# found just one set, assume it is *closing* the yaml matter;
# fake a preceding line of delimiter
delimiters <- c(0L, delimiters[1])
}
}
delimiters <- delimiters[1:2]
yaml_list <- yaml::yaml.load(
input_lines[ (delimiters[1]+1):(delimiters[2]-1) ])
dots <- list(...)

for (element_name in names(dots)){
if(element_name %in% names(yaml_list)) {
yaml_list[element_name] <- dots[element_name]
} else {
yaml_list <- c(yaml_list,dots[element_name]) 
}
}

output_lines <- c(
if (delimiters[1] > 0) input_lines[1:(delimiters[1])],
strsplit(yaml::as.yaml(yaml_list), "n")[[1]],
input_lines[ -(1:(delimiters[2]-1)) ]
)
if (missing(output_file)) {
return(output_lines)
} else {
writeLines(output_lines, con = output_file)
return(invisible(output_lines))
}
}

最新更新