shirewoman2
shirewoman2

Reputation: 1950

Function in my package to create an R markdown document fails with certain working directories -- Can I hack around this?

I've written a function inside a package that will take some data the user provides and make a pretty little Word document with a graph of those data. However, while the function works great when the working directory is a local drive, it fails when the drive is anything else. The errors are:

Quitting from lines 6-9 (skeleton.Rmd) Error in png(..., res = dpi, units = "in") : unable to start png() device In addition: Warning messages: 1: In dir.create(dirname(name), recursive = TRUE) : cannot create dir '\myemployer.com\data\sites', reason 'Permission denied' 2: In png(..., res = dpi, units = "in") : unable to open file '\myemployer.com\data\sites\myworkingdirectory/figure-docx/myplot-1.png' for writing 3: In png(..., res = dpi, units = "in") : opening device failed

I do have permission to write to my current working directory in general. In fact, when I call on a similar function in the same package that makes a table instead of a graph, it works fine.

I think what's going on is that Rmarkdown or knitr needs to temporarily save a png file of the graph in order to place it in the Word file, and it's saving it somewhere it doesn't have permission to do so. Can I get around this behavior by specifying something with rmarkdown::render?

I don't know how exactly to make a reproducible example when I'm referring to multiple files and the file structure of my package, but here's my best attempt at what info is needed. Here's the an example of the function:

 myFun <- function(mydata, filename){
    G <- ggplot2::ggplot(mydata, ggplot2::aes(x = A, y = B)) + 
       ggplot2::geom_point()
         
    OutPath <- dirname(filename)
    FileName <- basename(filename)
        
    rmarkdown::render(system.file("rmarkdown/templates/myplot/skeleton/skeleton.Rmd", 
       package="mypackage"), output_dir = OutPath,
       output_file = FileName, quiet = TRUE)
 }
 

The file structure to get to skeleton.Rmd from the top level of my package: mypackage/inst/rmarkdown/templates/myplot/skeleton/skeleton.Rmd

The actual R markdown document is something like this:

---
title: "My Plot"
output: word_document
---

```{r}
G
```

and the error message "Quitting from lines 6-9" refers to the chunk where G is outputted.

To call on this, you would use:

 myFun(mydata = data.frame(A = 1:10, B = 1:10),
       filename = "my file.docx")
 

and, if it's working, you'd get a Word file in the current working directory called "my file.docx" that contains a graph.

Is there some way to work around rmarkdown's need to temporarily save this png file? Can I set the directory for this temporary file to something else where it would have write permissions?

Upvotes: 4

Views: 381

Answers (2)

Waldi
Waldi

Reputation: 41260

A possible workaround would be to render the Markdown in a temporary directory, and to copy the generated file to its destination, see:

myFun <- function(mydata, filename){
  G <- ggplot2::ggplot(mydata, ggplot2::aes(x = A, y = B)) + 
    ggplot2::geom_point()
  
  tmpdir <- tempdir()
  on.exit(unlink(tmpdir))
  
  OutPath <- dirname(filename)
  FileName <- basename(filename)
  
  rmarkdown::render(system.file("rmarkdown/templates/myplot/skeleton/skeleton.Rmd", 
                                package="mypackage"), output_dir = tmpdir,
                    output_file = FileName, quiet = TRUE)
  file.copy(file.path(tmpdir,FileName,OutPath, overwrite = T))
  
}

Upvotes: 3

user63230
user63230

Reputation: 4708

Following on from the comments, this may help. You could make the plot within the rmd file:

---
title: "Untitled"
date: "6/27/2022"
output: html_document
params:
  input_df: NULL
---

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

## plot

```{r pressure, echo = FALSE}
G <- ggplot2::ggplot(mydata_rmarkdown, ggplot2::aes(x = A, y = B)) + 
     ggplot2::geom_point()
G
```

And alter you function to something like:

myFun <- function(mydata, filename){
  OutPath <- dirname(filename)
  FileName <- basename(filename)
  
  rmarkdown::render(system.file("rmarkdown/templates/myplot/skeleton/skeleton.Rmd", 
                                package = "mypackage"), output_dir = OutPath,
                    params = list(input_df = mydata)
                    output_file = FileName, quiet = TRUE)
}

Calling the function:

df1 <- data.frame(A = 1:10, B = 1:10)
df2 <- data.frame(A = 10:20, B = 10:20)

myFun(mydata = df1, filename = "my file.docx")
myFun(mydata = df2, filename = "my file.docx")

Its pretty limited in its current form as you would need df1 and df2 to have the same variable names etc. but may help you along.

Upvotes: 1

Related Questions