'Listing to knitr environment (R-markdown error when using list2env in R chunks)

Edit: I figured out that the issue stems from the yaml code. It comes from applying this solution to dynamically name files. I believe it creates a parent environment within the knitr code, because of which list2env creates unexpected behaviour

Just to be clear, the code below runs perfectly in R.

I am using list2env with envir = .GlobalEnv in my knitr code. This does not work because it creates objects outside the knitr environment (I am getting an error like this when I run my R code in R-markdown, because the objects created only exist in the global environment). I am trying to either find an alternative to list2env or a way to tell list2env to list to the current knitr environment.

Preparation in R

# setwd to an empty folder
setwd("C:/../testing_environment")

library(writexl)
library(readxl)

# Example data
write_xlsx(mtcars, "mt_cars.xlsx")
write_xlsx(mtcars, "mt_car_s.xlsx")

Code to knit in R-markdown (link for installation):

---
title: thetitle
author: myinititals
output:
  word_document
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')) })
```{r}

library(writexl)
library(readxl)

setwd("C:/../testing_environment")

paths <- list.files(pattern="*.xlsx")
read_all_sheets <- 
  function(path) sapply(excel_sheets(path), read_excel, path = path, USE.NAMES = TRUE, simplify = FALSE)
xl_list <- sapply(paths, read_all_sheets, USE.NAMES = TRUE, simplify = FALSE)

# List to environment - ISSUE
for (i in seq_along(xl_list)) {
  list2env(xl_list[[i]], envir = .GlobalEnv)
}

# THIS LINE CRASHES THE CODE
names(mt_car_s)  

```

Error:

enter image description here

I have checked what happens by removing names(mt_car_s) and doing:

list2env(xl_list, envir = .GlobalEnv)
## <environment: R_GlobalEnv>
ls()
## [1] "i"               "paths"           "read_all_sheets"          
## [4] "xl_list"

But it simply does not list the objects within xl_list.

Is there any way to get these files into the environment? For example code to write to the created knitr environment? If not, what are other possible solutions to prevent this behaviour?

EDIT 2, for a list of list you can do:

for (i in seq_along(xl_list)) {
    n <- names(xl_list[[i]])
    print(n)
    for (j in seq_along(n)) {
      assign(n[j], xl_list[[i]][[j]])
  }
}


Solution 1:[1]

The environment in which an R Markdown document is rendered can be changed with the envir option to rmarkdown::render(). In general, there's no guarantee that the rendering environment is the global environment or a child of it -- so you should not try to assign to .GlobalEnv.

Instead, assign to the current environment by replacing .GlobalEnv with environment() in the list2env() call.

Solution 2:[2]

Using assign() (by default into current environment) can probably achieve what you want. Here I remove the ".xlsx" from the names in your list xl_list so that the first level of the nested list has the names that you want to become objects in your current environment, and the names in those objects are the sheet names

---
title: "Sample Document"
output:
  word_document
---
    
```{r}
library(writexl)
library(readxl)

setwd("C:/../testing_environment")

paths <- list.files(pattern="*.xlsx")
read_all_sheets <-function(path) sapply(excel_sheets(path), read_excel, path = path, USE.NAMES = TRUE, simplify = FALSE)
xl_list <- sapply(paths, read_all_sheets, USE.NAMES = TRUE, simplify = FALSE)

# remove file extension from e.g. mt_car_s.xlsx
names(xl_list) <- gsub("\\.xlsx", "", names(xl_list))

# assign each element of the list 
# (itself a list of sheets/data frames)
# an object name in current environment
for (i in seq_along(xl_list)) {
  n <- names(xl_list[i])
  assign(n, xl_list[[i]])
}

names(mt_car_s)
names(mt_car_s$Sheet1)
```

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mikko Marttila
Solution 2 Andrew Brown