访问 R Markdown 参数文件的原始文件名

7

我希望为用户提供方便的方式来定义输入文件。为此,我在Markdown中使用参数功能。如果我“使用参数编织”,则会要求输入文件。

有没有办法检索文件名?因为我在Markdown中创建了一些不同的文件,并且我想使用输入文件的文件名作为前缀。到目前为止,文件被上传到临时目录中,原始文件名已经丢失。

如何通过下拉菜单获取文件名和位置并插入到我的Markdown文档中?我不希望用户手动编写路径和文件名。

---
title: "Untitled"
date: "11/16/2021"
output: html_document
params:
  date_file:
    label: "date file"
    value: 'dates.tsv'
    input: file
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

## R Markdown

Filename: `r params$date_file`

1
很难推断出当你点击“使用参数编织”时会发生什么,因为RStudio在新的R会话中运行rmarkdown::render。这意味着你不能从自己的会话中使用debug进入函数调用。如果你在这里找不到满意的答案,那么在他们的GitHub上提出问题可能是值得的,或者在他们的GitHub上说要提出问题的地方。 - Mikael Jagan
2个回答

5

你可以通过嵌入Shiny应用程序,让用户在渲染文档中选择文件。但需要注意的是,涉及到用户选择依赖项的所有表达式都必须包裹在reactive()中。如果你想教授R语言的话,这显然并不理想,但如果有帮助或激发更好的解答,以下是一个例子:

## Create a TSV file for testing
cat("x\ty\n1\t2\n3\t4\n", file = "test.tsv")

---
title: "Untitled"
output: html_document
runtime: shiny
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

Users can select a file by interacting with an embedded Shiny application.

```{r ui, echo=FALSE}
inputPanel(
  fileInput("file", label = "Select a TSV file:", accept = ".tsv")
)
```

User input is stored in a named list-like object `input`, 
but this object can only be accessed inside of a _reactive context_.

```{r test1, error=TRUE}
input$file$name
## Name of file selected by user ... _not_ an absolute or relative path
fn <- reactive(input$file$name)
fn
## Absolute path of temporary copy of file
dp <- reactive(input$file$datapath)
dp
```

Don't be misled by how `fn` and `dp` are printed. They are not 
strings, but _reactive expressions_. As as a result, they, too, 
must be handled inside of a reactive context.

```{r test2}
class(fn)
reactive(class(fn()))
```

Some more examples:

```{r test3, error=TRUE}
## Define reactive data
dd <- reactive(read.table(file = dp(), sep = "\t", header = TRUE))
## Do stuff with it
reactive(names(dd()))
reactive(nrow(dd()))
## Write object to file in _working directory_
reactive(saveRDS(dd(), file = sub("tsv$", "rds", fn())))
```

嗨,Mikael,有趣的解决方案...是否有一种方法可以获取文件的绝对/相对路径,而不仅仅是文件名... 虽然不完全是我要找的,但它给了我在我的脚本中使用它的新选项... - drmariod
1
不幸的是,input$file$name 只保留绝对路径中最后一个路径分隔符后面的部分。如果您将文件保存到您的工作目录(我认为这是一个好习惯,因为用户不会期望在他们的系统其他地方创建文件),那么没有绝对路径也没关系。 - Mikael Jagan
我稍微更新了.Rmd文件。我错误地将input$file$name传递给read.table,而应该传递input$file$datapath - Mikael Jagan

4
作为使用Shiny运行时的替代方案,您还可以使用Shiny Gadgets自定义Knit按钮行为相结合(据我理解,这在您使用“带参数编织”时主要发生)。
您需要两个东西:运行小工具的函数和编织时运行的函数。
该小工具本质上是一个Shiny应用程序,但您可以使用miniUI中的专门UI元素。作为迷你应用程序,您可以做很多事情,但这是一个基本实现。
library(shiny)
library(miniUI)

get_file_param <- function(){
  
  ui <- miniPage(
    miniContentPanel(
      
      fileInput("dateFile", "Date File")
      
      # ...Other UI elements to collect other parameters... 
      
    ),
    miniTitleBar(
      NULL,
      right = miniButtonBlock(
        miniTitleBarCancelButton(),
        miniTitleBarButton("done", "Done", primary = TRUE))
    )
    
  )
  
  server <- function(input, output, session){
    
    # Handle the Done button being pressed
    observeEvent(input$done, {
      # Return the full file info data.frame. See ?shiny::fileInput
      stopApp(input$dateFile)
    })
    
  }
  
  runGadget(
    app = ui, 
    server = server, 
    viewer = paneViewer() #dialogViewer("Knit with Parameters - Custom UI")
  )
  
}

knit函数将调用小工具,然后在调用rmarkdown::render时使用其输出

knit_with_file <- function(input, ...){
  
  fileInfo <- get_file_param()
  
  rmarkdown::render(
    input,
    params = list(
      date_file = list(name = fileInfo$name, datapath = fileInfo$datapath)
    ),
    envir = globalenv()
  )
  
}

为了自定义编织按钮,您需要在文档YAML头中的knit字段提供一个函数。R Markdown Cookbook建议您将其放入软件包中,但我将上述两个函数都放在文件(“knitFx.R”)中,rmarkdown文档将从中获取源代码。
---
title: "Untitled"
date: "11/16/2021"
output: html_document
params:
  date_file: 'dates.tsv'
knit: (function(input, ...) {

    source("knitFx.R")
    
    knit_with_file(input, ...)

  })
---
  
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

## R Markdown

**Original Name of Uploaded File**: *`r params$date_file$name`*

**Local Temp File Path**: *`r params$date_file$datapath`*

当用户点击“Knit”时,将显示一个UI以选择文件。根据上述实现,namedatapath将可用于在rmarkdown文档中使用。
以下是呈现的HTML文档: enter image description here

注意,参数date_file的默认值是字符串,而期望的值是列表。如果默认值实际使用,可能会导致问题。YAML头中的该字段是否可以被简单地删除? - Mikael Jagan
你不能从YAML头中删除字段,因为rmarkdown::render将会把它的params参数与之匹配。相反,你可以提供一个列表作为默认值。类似于date_file: !r list(name = 'dates.tsv', datapath = './dates.tsv') - Marcus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接