【问题标题】:Iterating over CSVs to different dataframes based on file names根据文件名将 CSV 迭代到不同的数据帧
【发布时间】:2021-10-05 14:57:26
【问题描述】:

我有一个数据框,其中包含一堆 .CSV 文件的名称。它在下面的 sn-p 中看起来如何:

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

  1. 使用 .CSV 文件中的所有结果创建一个数据框,文件名中包含 -callers-
  2. 使用 .CSV 文件中的所有结果创建一个数据框,文件名中包含 -results
  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
    

【问题讨论】:

    标签: r dplyr tidyr


    【解决方案1】:

    创建 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)}
       
    }
    

    【讨论】:

    • 迭代rbinding 效率低下,每次调用它都会复制内存中的所有现有数据。如果这些是大对象,则没有必要。我建议使用lapply(而不是for)或外部list 来存储每个人results,然后在最后使用do.call(rbind, ...)
    • 对于后一个选项,尝试results &lt;- list(),然后在循环results &lt;- c(results, data) 内,然后在循环后results &lt;- do.call(rbind, results)
    • 有道理,谢谢
    • @denisafonin 感谢您发布答案。问题是,data 似乎只返回最后一个文件的内容。它实际上并没有将结果绑定在一起。
    • @r2evans 我遇到了你提出的这个问题。你的建议是什么样的?
    【解决方案2】:

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

    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)
    

    【讨论】:

      猜你喜欢
      • 2018-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-20
      • 1970-01-01
      • 2020-01-05
      • 2021-03-20
      • 1970-01-01
      相关资源
      最近更新 更多