Dynamically naming the output file in a custom R-markdown function
Asked Answered
A

1

3

I found the function below here. It works great, but I would like to dynamically name the output file 'analysis.docx', using the title of the document, the author and the current date.

title: thetitle
author: myinititals
date: "`r Sys.Date()`"
knit: (function(inputFile, encoding) { 
          out_dir <- 'test';
          rmarkdown::render(inputFile,
                            encoding=encoding, 
                            output_file=file.path(dirname(inputFile), out_dir, 'analysis.docx')) })

How do I make 'analysis.docx' dynamic in this case?

I found some more information here, but not my desired answer.

Adame answered 18/2, 2022 at 12:16 Comment(1)
Interesting question. Since the title and author can also include dynamic content via arbitrary R expressions, it seems to me you'd have to do some sort of partial render first to figure out what they resolve to, and then do a final render() when the information is available to name the output file. Or alternatively, somehow extract these during the render and just rename the output file after the fact.Supersensitive
S
7

If the fields that you want to use don't include R expressions, you can use yaml_front_matter() to extract their values and use those to construct the name for the output file:

---
title: "Untitled"
author: "Jane Doe"
date: "18/02/2022"
output: word_document
knit: >
  (function(input_file, encoding) {
    metadata <- rmarkdown::yaml_front_matter(input_file)
    output_file <- with(metadata, paste(title, "by", author))
    rmarkdown::render(input = input_file, output_file = output_file)
  })
---

204 No Content

If your fields do include R expressions, this becomes a little more involved. You can apply the same principle, but now instead of getting the front matter from the RMarkdown file, you get it from the intermediate Markdown file generated during the rendering process. Then rename the result.

That could look something like this:

---
title: "Untitled"
author: "Jane Doe"
date: "`r Sys.Date()`"
output: word_document
knit: >
  (function(input_file, encoding) {
    # Render, keeping intermediate files for extracting front matter
    md_dir <- tempdir()
    output_file_temp <- rmarkdown::render(
      input = input_file,
      output_file = tempfile(),
      intermediates_dir = md_dir,
      clean = FALSE
    )
    
    # Get the rendered front matter from the intermediate Markdown file
    md_file <- fs::path_ext_set(fs::path_file(input_file), ".knit.md")
    metadata <- rmarkdown::yaml_front_matter(fs::path(md_dir, md_file))
    
    # Build the output file name based on rendered metadata
    output_name <- with(metadata, paste(title, "by", author, "on", date))

    # Add the file extension and move to the working directory
    output_ext <- fs::path_ext(output_file_temp)
    output_file <- fs::path_ext_set(output_name, output_ext)
    fs::file_move(output_file_temp, output_file)

    message("Output moved to: ", output_file)
  })
---

204 No Content
Supersensitive answered 18/2, 2022 at 13:57 Comment(18)
It might be a nice feature in the rmarkdown package to allow output_file in render() to be a function that has access to the rendered metadata.Supersensitive
Thank you very much! I'm getting the error Error: unexpected ',' in "('C:/Users/user/OneDrive/path/Untitled.Rmd'," I am having a hard time debugging the function. Do you have any idea what might be the cause?Adame
That seems like a typo to me. Are you entering that path somewhere? It should probably be "C:/Users/user/OneDrive/path/Untitled.Rmd".Supersensitive
I'm not.. that is the point..Adame
I see, that's odd. What steps have you taken that lead to the error?Supersensitive
As your code did not seem to include any special references, I simply replaced the old function I had with your function. I also tried your solution for when the fields do not include R expressions. I get exactly the same error, maybe that helps?Adame
Did you include the > after knit: to make the function a multi-line string? Could you also try just copying the entire file and rendering that? I'm thinking there's probably something wrong with how the YAML is formatted.Supersensitive
I did include the >, Just rendering your code by itself in a new file does run, however the file created is Output created: C:\Users\user\AppData\Local\Temp\Rtmp6jjWck\file61ec765f3927.docx.Adame
Thanks for trying that! Yeah that message is the last one generated by rmarkdown::render(). It's then moved in the function to the result file. It should be in your working directory. Maybe it would be clearer to include a message for that (added it now). That said, I find it very strange that the function doesn't work with your actual file, then. Did you edit in the output directory or anything like that where there could have been a typo?Supersensitive
Ah okay! I found the file created by your script, so the script works! Thank you very much. From this point I think I can figure it out by myself. I will just keep adding lines from my original script to the new one, until it breaks to figure out what makes it break. Once again thank you very much for your amazing function and enormous patience!Adame
Okay great, glad to hear that! You're very welcome, this was an interesting problem. I also took a moment to file a feature request on rmarkdown GitHub, so if that goes through this might become easier in the future: github.com/rstudio/rmarkdown/issues/2314Supersensitive
For future reference, I found the reason for the error. Apparently you cannot specify anything related to the output after knit: >.Adame
Oh that's strange! Thanks for sharing that.Supersensitive
Related question: #71751649Adame
The best solution I have found so far. What if the title depends of the result of a variable that is create later in the code? I have tried this approach using the code here bookdown.org/yihui/rmarkdown-cookbook/dynamic-yaml.html and it's not working. Thanks.Lucais
@Lucais I think you'd have to use a two-stage render, first rendering the R Markdown document to Markdown and capturing the variable that you want to use in the title. Then update the title in the Markdown document YAML front matter, and use Pandoc to convert the modified Markdown into the final output format.Supersensitive
Thanks, sound like it requires some extra work. I'll check it out.Lucais
It works for me on RMarkdown, thanks! but, how to do this for a Quarto document?Lowe

© 2022 - 2024 — McMap. All rights reserved.