【问题标题】:Use R and Openxlsx to output a list of dataframes as worksheets in a single Excel file使用 R 和 Openxlsx 将数据框列表输出为单个 Excel 文件中的工作表
【发布时间】:2017-08-27 19:49:24
【问题描述】:

我有一组 CSV 文件。我想将它们打包并将数据导出到包含多个工作表的单个 Excel 文件中。我将 CSV 文件作为一组数据框读取。

我的问题是如何在openxlsx 中构造命令,我可以手动完成,但我遇到了列表构造问题。具体如何添加数据框作为命名列表的子组件,然后作为参数传递给write.xlsx()

示例

好的,所以我先列出磁盘上的 CSV 文件,并在内存中生成一组数据帧...

# Generate a list of csv files on disk and shorten names... 
filePath <- "../02benchmark/results/results_20170330/"
filePattern <- "*.csv"
fileListwithPath = list.files(path = filePath, pattern = filePattern, full.names = TRUE)
fileList = list.files(path = filePath, pattern = filePattern, full.names = FALSE)

datasets <- gsub("*.csv$", "", fileList)
datasets <- gsub("sample_", "S", datasets)
datasets

# Now generate the dataframes for each csv file...
list2env(
  lapply(setNames(fileListwithPath, make.names(datasets)),
         read.csv), envir = .GlobalEnv)

示例输出:

dput(datasets)
c("S10000_R3.3.2_201703301839", "S10000_T4.3.0_201703301843", 
"S20000_R3.3.2_201703301826", "S20000_T4.3.0_201703301832", "S280000_R3.3.2_201704020847", 
"S280000_T4.3.0_201704021100", "S290000_R3.3.2_201704020447", 
"S290000_T4.3.0_201704020702", "S30000_R3.3.2_201703301803", 
"S30000_T4.3.0_201703301817", "S310000_R3.3.2_201704012331", 
"S310000_T4.3.0_201704020242", "S320000_R3.3.2_201704011827", 
"S320000_T4.3.0_201704012128", "S330000_R3.3.2_201704011304", 
"S330000_T4.3.0_201704011546", "S340000_R3.3.2_201704010652", 
"S340000_T4.3.0_201704011010", "S350000_R3.3.2_201704010020", 
"S350000_T4.3.0_201704010404", "S360000_R3.3.2_201703311819", 
"S360000_T4.3.0_201703312134", "S370000_R3.3.2_201703310914", 
"S370000_T4.3.0_201703311301", "S380000_R3.3.2_201703310134", 
"S380000_T4.3.0_201703310509", "S390000_R3.3.2_201703301846", 
"S390000_T4.3.0_201703302252", "S40000_R3.3.2_201703301738", 
"S40000_T4.3.0_201703301752", "S50000_R3.3.2_201703301707", "S50000_T4.3.0_201703301724", 
"S60000_R3.3.2_201703301624", "S60000_T4.3.0_201703301647", "S70000_R3.3.2_201703301535", 
"S70000_T4.3.0_201703301602", "S80000_R3.3.2_201703301430", "S80000_T4.3.0_201703301508", 
"S90000_R3.3.2_201703301324", "S90000_T4.3.0_201703301400")

现在我们有一组数据框,我们希望创建一个包含多个工作表的单个 excel 文件...

wb <- createWorkbook()
saveWorkbook(wb, 'output.xlsx')

lapply(names(myList), function(x) write.xlsx(myList[[x]], 'output.xlsx', sheetName=x, append=TRUE))

问题:

问题是我可以手动创建列表结构并且可以确认它有效,但我似乎无法自动构建列表。

myList <- sapply(datasets,function(x) NULL)
names(myList)
str(myList)
myList$S10000_R3.3.2_201703301839 <- eval(S10000_R3.3.2_201703301839)

因此:

> str(myList)
List of 40
 $ S10000_R3.3.2_201703301839 :'data.frame':    43 obs. of  4 variables:
  ..$ function.: Factor w/ 42 levels "DF add random number vector",..: 30 25 38 42 36 39 40 29 26 22 ...
  ..$ user     : num [1:43] 2.144 0.263 0.024 0.068 0.008 ...
  ..$ system   : num [1:43] 0.63 0.065 0.001 0.004 0 ...
  ..$ elapsed  : num [1:43] 12.274 1.104 0.047 0.115 0.009 ...
 $ S10000_T4.3.0_201703301843 : NULL
 $ S20000_R3.3.2_201703301826 : NULL
 ...

具体问题:如何将每个数据框追加到列表中...

myList <- lapply( myList, function(x) eval(x) )

我在这里的 lapply 做错了什么?上面的 lapply() 不会遍历列表并将数据框附加到名称列表条目。

i.e. myList$S10000_R3.3.2_201703301839 <- eval(S10000_R3.3.2_201703301839)
> str(myList)
    List of 40
     $ S10000_R3.3.2_201703301839 :'data.frame':    43 obs. of  4 variables:
      ..$ function.: Factor w/ 42 levels "DF add random number vector",..: 30 25 38 42 36 39 40 29 26 22 ...
      ..$ user     : num [1:43] 2.144 0.263 0.024 0.068 0.008 ...
      ..$ system   : num [1:43] 0.63 0.065 0.001 0.004 0 ...
      ..$ elapsed  : num [1:43] 12.274 1.104 0.047 0.115 0.009 ...
     $ S10000_T4.3.0_201703301843 : NULL
     $ S20000_R3.3.2_201703301826 : NULL
     ...

我错过了什么?感谢所有帮助。是的,我很确定我遗漏了一些明显的东西......但是......我很难过。

【问题讨论】:

    标签: r excel lapply xlsx openxlsx


    【解决方案1】:

    这是openxlsx的解决方案:

    ## create data;
    dataframes <- split(iris, iris$Species)
    
    # create workbook
    wb <- createWorkbook()
     
    #Iterate the same way as PavoDive, slightly different (creating an anonymous function inside Map())
    Map(function(data, nameofsheet){     
    
        addWorksheet(wb, nameofsheet)
        writeData(wb, nameofsheet, data)
     
    }, dataframes, names(dataframes))
             
    ## Save workbook to excel file 
    saveWorkbook(wb, file = "file.xlsx", overwrite = TRUE)
    

    .. 然而,openxlsx 也可以使用它的函数 openxlsx::write.xlsx 来实现这一点,因此您只需为对象提供数据帧列表和文件路径,openxlsx 足够聪明,可以创建列表作为 xlsx 文件中的工作表。我在这里用Map() 发布的代码是如果您想以特定方式格式化工作表。

    【讨论】:

      【解决方案2】:

      我认为可能值得使用 purrr 包中的imap 函数添加解决方案,因为它提供了一种方便的机制来访问列表的名称​​和索引一次调用元素:

      imap_xxx(x, ...) 是索引映射,如果x 有名称,则为map2(x, names(x), ...) 的简写,如果没有名称,则为map2(x, seq_along(x), ...)。如果您需要同时计算元素的值和位置,这将非常有用。

      imap解决方案

      关于可重复性的虚拟数据。

      lst_data <- list(cars = mtcars, air = airmiles)
      wb <- openxlsx::createWorkbook()
      purrr::imap(
          .x = lst_data,
          .f = function(df, object_name) {
              openxlsx::addWorksheet(wb = wb, sheetName = object_name)
              openxlsx::writeData(wb = wb, sheet = object_name, x = df)
          }
      )
      t_file <- tempfile(pattern = "test_df_export", fileext = ".xlsx")
      saveWorkbook(wb = wb, file = t_file)
      

      【讨论】:

      • 您可以使用iwalk 避免控制台上出现任何消​​息
      【解决方案3】:

      我没有你的数据框,所以我无法测试这个,但是下面的代码和我需要读写 Excel 文件时使用的方法类似。下面的代码使用xlsx 包,因为这是我熟悉的,但如果您需要使用openxlsx,希望您可以调整它。

      library(xlsx)
      

      首先,将文件读入列表。像这样的:

      filePath <- "../02benchmark/results/results_20170330/"
      filePattern <- "*.csv"
      fileListwithPath = list.files(path = filePath, 
                                    pattern = filePattern, 
                                    full.names = TRUE)
      fileList = list.files(path = filePath, pattern = filePattern, full.names = FALSE)
      fileListwithPath = setNames( fileListwithPath, 
                                   list.files(path = filePath, pattern = filePattern))
      df.list = lapply(fileListwithPath, read.csv)
      
      # Now we rename the List Names for use in worksheets...
      # Remove .csv and sample_ prefix used in filenames...
      # Reult in workbook S<size>_<R version>_<date>
      names(df.list) <- gsub("\\.csv$","", names(df.list))
      names(df.list) <- gsub("sample_","S", names(df.list))
      

      您现在有一个列表,其中每个元素都是一个数据框,每个元素的名称都是文件的名称。现在,让我们将每个数据框写入同一个 Excel 工作簿中的不同工作表,然后将文件保存为 xlsx 文件:

      wb = createWorkbook()
      
      lapply( names(df.list), 
              function(df) {
                sheet = createSheet(wb, df)
                addDataFrame(df.list[[df]], sheet = sheet, row.names = FALSE)
                } )
      
      saveWorkbook(wb, "My_workbook.xlsx")
      

      为了说明,我已将 csv 文件的读取和写入分开,但您可以将它们组合成一个函数,读取每个单独的 csv 文件并将其写入单个 Excel 工作簿中的新工作表。

      【讨论】:

      • 谢谢。您提供的代码有效 - 非常感谢。我需要花一点时间熟悉列表操作。如果我修复我的原始代码,我会发布一个解释。你让我继续前进 - 非常感谢你的帮助。良好的因果报应和感激之情飞扬。
      • 我很想知道如何将目标工作表的名称从 df.list$sample_10000_R3.3.2_201703301839.csv 更改为 df.list$S10000_R3.3.2_201703301839 - 我知道很简单,但是...
      • 查看更新。我添加了一行来删除每个文件名末尾的“.csv”。
      • eipi10 - 谢谢,我更新了您的示例以反映最终代码。在读取带有lapply() 的文件后,我必须更改df.list 的名称。非常感谢您的帮助:-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-28
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-07
      相关资源
      最近更新 更多