嵌套 lapply 以从嵌套文件结构中读取表 - R

时间:2021-03-16 13:39:37

标签: r data.table lapply

如果此问题之前已得到充分回答,请提前道歉。我在这里查了各种 lapply 问题,但还没有找到一个完全像这种情况的。

我有 4 家公司的数据,它们有 8 个不同日期的 .xlsx 文件。为清楚起见,共有 32 个 .xlsx 文件(每个公司日期组合 1 个)。八个日期是路径中的上层文件结构,其中第一个文件夹是“01-01-19”,第二个是“02-01-19”等。在每个文件夹中,四个公司有不同的文件我可以使用 grepl 准确识别它是哪家公司的名称。

我的目标是根据文件名读取特定范围的 .xlsx 文件,然后将特定公司的所有数据表放在一起rbindrbindlist

我目前的方法是使用 lapply 遍历文件名(日期,例如“01-01-19”)。然后有一次,我在具有特定公司 .xlsx 文件的较低级别文件夹结构中,我可以再次使用嵌套的 lapply 循环读取每个表作为 data.table。我使用 if-else 语句来检查公司名称,因为每家公司都有不同数量的观察值,这些观察值在所有时间段内都是一致的(即 co1 在每个时期将始终有 100 个观察值,但 co2 每个时期可能有 300 个观察值,等等。 )

我目前的方法如下:

# get the files which are indexed by date - e.g. "01-01-2019"
files <- list.files(path=file.path(input_fld,"co_data"))

# nested lapplys to 1. loop through the outer files (dates) and 2. loop through the inner four files (companies) conditionally
co_dt_list <- lapply(files, function(x) {
  
  # get the files inside the index_data
  in_files <- list.files(path=file.path(input_fld,"co_data",x))
  
  # the second lapply to loop through each of the company names individually
  lapply(in_files, function(y) {
    
    if (grepl("co1", y) == TRUE) {
      
      # if the file name has "co1" in the name, read this as a data.table
      data.table(read_excel(file.path(input_fld, "co_data", x, y), range = "A15:Q115"))
      
    } else if (grepl("co2", y) == TRUE) {
      
      # if the file name has "co2" in the name, read this as a data.table
      data.table(read_excel(file.path(input_fld, "co_data", x, y), range = "A15:Q315"))
      
    } else if (grepl("co3", y) == TRUE) {

      # if the file name has "co3" in the name, read this as a data.table
      data.table(read_excel(file.path(input_fld, "co_data", x, y), range = "A15:Q515"))
      
    } else {

      # else read in this as a data.table
      data.table(read_excel(file.path(input_fld, "co_data", x, y), range = "A15:Q715"))
      
    }
  })
})

这个循环在功能上按所写的方式工作,但让我得到了不想要的输出。它将每个日期索引为第一个列表索引,将公司索引为第二个(内部)列表索引。换句话说,data.tables 的第一个列表将是 01-01-19 的 co1、co2、co3 和 co4,而第二个列表将与 02-01-19 等相同。

我为下面的输出创建了一些合成数据。请原谅提前组合列表的手动工作。


# generate a date list
date_list <- seq.Date(as.Date("2019-01-01"), as.Date("2019-08-01"), by = "month")

# generate fake datasets by date
co1 <- lapply(date_list, function(x) { data.table(co = 1, date = x, data = rnorm(100)) })
co2 <- lapply(date_list, function(x) { data.table(co = 2, date = x, data = rnorm(300)) })
co3 <- lapply(date_list, function(x) { data.table(co = 3, date = x, data = rnorm(500)) })
co4 <- lapply(date_list, function(x) { data.table(co = 4, date = x, data = rnorm(700)) })

# put them together in current list format
list_1 <- list(co1[[1]], co2[[1]], co3[[1]], co4[[1]])
list_2 <- list(co1[[2]], co2[[2]], co3[[2]], co4[[2]])
list_3 <- list(co1[[3]], co2[[3]], co3[[3]], co4[[3]])
list_4 <- list(co1[[4]], co2[[4]], co3[[4]], co4[[4]])
list_5 <- list(co1[[5]], co2[[5]], co3[[5]], co4[[5]])
list_6 <- list(co1[[6]], co2[[6]], co3[[6]], co4[[6]])
list_7 <- list(co1[[7]], co2[[7]], co3[[7]], co4[[7]])
list_8 <- list(co1[[8]], co2[[8]], co3[[8]], co4[[8]])

# this is what the output looks like 
list_output <- list(list_1, list_2, list_3, list_4,
                    list_5, list_6, list_7, list_8)

我的问题是:有没有一种简单的方法可以 a) 将我当前形式的 list_output 和 rbindrbindlist 每家公司的 data.tables 相互转换?或者,b) 是否有更好的方法来构建初始嵌套的 lapply 循环以获得由公司而不是日期索引的输出?

谢谢!

1 个答案:

答案 0 :(得分:1)

这是一个仅使用 data.table 的解决方案。如果您的列表元素为 id 列正确命名会有所帮助:

rbindlist(lapply(list_output, rbindlist, idcol = "list_inner"), idcol = "list_outer")

这提供了一个整洁的表格,您可以根据需要进行过滤:

       list_outer list_inner co       date       data
    1:          1          1  1 2019-01-01  1.3593594
    2:          1          1  1 2019-01-01  1.1514190
    3:          1          1  1 2019-01-01 -0.5982100
    4:          1          1  1 2019-01-01  0.2675609
    5:          1          1  1 2019-01-01 -0.7936896
   ---                                               
12796:          8          4  4 2019-08-01  2.3255672
12797:          8          4  4 2019-08-01 -1.6454320
12798:          8          4  4 2019-08-01  0.2871383
12799:          8          4  4 2019-08-01  0.7126838
12800:          8          4  4 2019-08-01 -1.3650915
相关问题