根据文件名将 CSV 迭代到不同的数据帧

Iterating over CSVs to different dataframes based on file names

我有一个数据框,其中包含一堆 .CSV 文件的名称。它在下面的代码片段中看起来是如何工作的:

我想做的是将这些 .CSV 中的每一个转换成一个数据框,附加每个的结果。我要做的是根据文件名中的内容创建三个不同的数据框:

  1. 使用文件名中包含 -callers- 的 .CSV 文件的所有结果创建一个数据框
  2. 创建一个数据框,其中包含文件名中包含 -results 的 .CSV 文件的所有结果
  3. 使用 .CSV 文件的所有结果创建一个数据框,文件名中包含 -script_results-

如果我在下面的数据框中使用第一个 .CSV,则将 .CSV 文件实际转换为数据框的命令如下所示:

data <- aws.s3::s3read_using(read.csv, object = "s3://abc-testtalk/08182020-testpilot-arizona-results-08-18-2020--08-18-2020-168701001.csv")

但我想做的是:

  1. 使用s3read_using函数

    迭代Key下的所有.csv文件
  2. 根据上面列出的文件名将它们放在三个单独的数据框中

    Key
    08182020-testpilot-arizona-results-08-18-2020--08-18-2020-168701001.csv
    08182020-testpilot-arizona-results-08-18-2020--08-18-2020-606698088.csv
    08182020-testpilot-arizona-script_results-08-18-2020--08-18-2020-114004469.csv
    08182020-testpilot-arizona-script_results-08-18-2020--08-18-2020-450823767.csv
    08182020-testpilot-iowa-callers-08-18-2020-374839084.csv
    08182020-testpilot-maine-callers-08-18-2020-396935866.csv
    08182020-testpilot-maine-results-08-18-2020--08-18-2020-990912614.csv
    08182020-testpilot-maine-script_results-08-18-2020--08-18-2020-897037786.csv
    08182020-testpilot-michigan-callers-08-18-2020-367670258.csv
    08182020-testpilot-michigan-follow-ups-08-18-2020--08-18-2020-049435266.csv
    08182020-testpilot-michigan-results-08-18-2020--08-18-2020-544974900.csv
    08182020-testpilot-michigan-script_results-08-18-2020--08-18-2020-239089219.csv
    08182020-testpilot-nevada-callers-08-18-2020-782329503.csv
    08182020-testpilot-nevada-results-08-18-2020--08-18-2020-348644934.csv
    08182020-testpilot-nevada-script_results-08-18-2020--08-18-2020-517037762.csv
    08182020-testpilot-new-hampshire-callers-08-18-2020-134150800.csv
    08182020-testpilot-north-carolina-callers-08-18-2020-739838755.csv
    08182020-testpilot-pennsylvania-callers-08-18-2020-223839956.csv
    08182020-testpilot-pennsylvania-results-08-18-2020--08-18-2020-747438886.csv
    08182020-testpilot-pennsylvania-script_results-08-18-2020--08-18-2020-546894204.csv
    08182020-testpilot-virginia-callers-08-18-2020-027531377.csv
    08182020-testpilot-virginia-follow-ups-08-18-2020--08-18-2020-419338697.csv
    08182020-testpilot-virginia-results-08-18-2020--08-18-2020-193170030.csv
    

创建 3 个空数据框。您可能还需要指明与要附加的每个文件中的列名相匹配的列名:

results <- data.frame()
script_results <- data.frame()
callers <- data.frame()

然后遍历 file_name 并将其读入 data 对象。有条件地在每个文件的名称中包含什么模式(“-results-”,“-script_results-”或“-caller-”,它将附加到正确的数据帧:

for (file in file_name) {
  data <- aws.s3::s3read_using(read.csv, object = paste0("s3://abc-testtalk/", file))
  
  if (grepl(file, "-results-")) { results <- rbind(results, data)}
  if (grepl(file, "-script_results-")) { script_results <- rbind(script_results, data)}
  if (grepl(file, "-callers-")) { callers <- rbind(callers, data)}
   
}

作为@JohnFranchak 对 map_dfr 的建议(可能工作得很好)的替代方法,我在评论中引用的方法看起来像这样:

alldat <- lapply(setNames(nm = dat$file_name),
                 function(obj) aws.s3::s3read_using(read.csv, object = obj))

callers <- do.call(rbind, alldat[grepl("-callers-", names(alldat))])
results <- do.call(rbind, alldat[grepl("-results-", names(alldat))])
script_results <- do.call(rbind, alldat[grepl("-script_results-", names(alldat))])
others <- do.call(rbind, alldat[!grepl("-(callers|results|script_results)-", names(alldat))])

do.call(rbind, ...) 部分类似于 dplyr::bind_rowsdata.table::rbindlist,因为它接受帧列表,结果是单个帧。一些差异:

  • do.call(rbind, ...) 确实要求所有列以相同顺序存在于所有帧中。从外部强制执行此操作并不难(例如,添加缺失的列、重新排列),但它不是自动的。
  • data.table::rbindlist 会抱怨相同的条件(缺少列或不同的顺序),但它有 fill=use.names= 需要设置的参数 TRUE
  • dplyr::bind_rows 将默认按名称填充和行绑定,没有消息或警告。 (我不同意默认的静音总是好的,但这是最简单的。)

最后,我使用setNames(nm=..)只是为每个对象分配文件名。这不是绝对必要的,因为我们仍然有 dat$file_name,但我发现对于两个单独的对象,不小心更改(删除、追加或重新排序)其中一个而不是另一个是可行的,所以我更喜欢将名称和对象(框架)完美地联系在一起。这两个调用在生成的命名列表中相对相同:

lapply(setNames(nm = dat$file_name), ...)
sapply(dat$file_name, ..., simplify = FALSE)