【问题标题】:read files & set file names in the same dplyr pipe在同一个 dplyr 管道中读取文件并设置文件名
【发布时间】:2019-09-21 09:44:52
【问题描述】:

这是一个棘手的 dplyr & purrr 问题 我想将以下代码简化为一个 dplyr 管道:

filenames <- list.files(path = data.location, pattern = "*.csv") %>%
  map_chr(function(name) gsub(paste0('(.*).csv'), '\\1', name))

files.raw <- list.files(path = data.location, pattern = "*.csv", full.names = TRUE) %>%
  map(read_csv) %>%
  setNames(filenames)

我尝试执行 this solution 但它失败了,因为文件名必须与 read_csv() 的完整路径 (full.names = TRUE) 一起使用,但我想分配没有完整路径的文件名。

换句话说,这行得通——但只能在文件名中使用完整路径:

list.files(path = data.location, pattern = "*.csv", full.names = TRUE) %>%
  { . ->> filenames } %>%
  map(read_csv) %>%
  setNames(filenames)

但这没有:

list.files(path = data.location, pattern = "*.csv", full.names = TRUE) %>%
{ map_chr(., function(name) gsub(paste0(data.location, '/(.*).csv'), '\\1', name)) ->> filenames } %>%
  map(read_csv) %>% 
  setNames(filenames)

有没有办法让 map_chr 与保存 (-&gt;&gt; filenames) 一起使用,或者有没有更简单的方法可以完全避免保存到临时变量(文件名)?

【问题讨论】:

    标签: r dplyr pipe purrr


    【解决方案1】:

    要在一个没有中间值的管道中执行此操作,并且类似于@Ronak Shah,为什么不先设置名称,然后读入 CSV? Ronak 嵌套了 setNames 调用,但可以将其放入管道中以使其更具可读性:

    library(tidyverse)
    list.files(path = data.location, pattern = "*.csv", full.names = TRUE) %>%
        setNames(., sub("\\.csv$", "", basename(.))) %>% 
        map(read_csv)
    

    【讨论】:

    • 谢谢,但这不起作用(之前尝试过),因为通过设置名称,basename() 删除了文件路径,因此read_csv 找不到文件
    • data.location % setNames(., sub("\\.csv$", "", basename(.))) %>% 地图(打印)$a'/Users/pgcudahy/Desktop/csvs/a.csv' $b'/Users/pgcudahy/Desktop/csvs/b.csv' $c'/Users/pgcudahy/Desktop/csvs /c.csv'
    • 好的! @pccudahy 我错误地认为我已经尝试过您的解决方案。现在我试过了,我承认它比我的解决方案更好,因为它更短更直观。所以我现在将其标记为解决方案。
    【解决方案2】:

    我们只能使用tidyverse 函数来做到这一点

    library(readr)
    library(purrr)
    library(dplyr)
    all_files <- list.files(path = data.location, pattern = "*\\.csv", full.names = TRUE) 
     map(all_files, read_csv) %>%
              set_names(str_remove(basename(all_files), "\\.csv$")) 
    

    【讨论】:

      【解决方案3】:

      试试这个方法:

      all_files <- list.files(path = data.location, pattern = "*.csv", full.names = TRUE) 
      
      purrr::map(all_files, readr::read_csv) %>%
            setNames(sub("\\.csv$", "", basename(all_files)))
      

      这里,我们首先获取所有文件的完整路径,使用read_csv读取它。我们可以使用basename 仅获取文件名并从中删除"csv" 并使用setNames 分配名称。


      要在一个管道中做到这一点,我们可以做到

      list.files(path = data.location, pattern = "*.csv", full.names = TRUE)  %>%
          {setNames(map(., read_csv), sub("\\.csv$", "", basename(.)))}
      

      【讨论】:

      • 这确实是一种重用文件路径的聪明方法,但你能把它变成一个 dplyr 管道吗?
      • @AgileBean 这让它有点难看,但我已经更新了答案以在一个管道中执行此操作。
      • 我明白你的想法,你是对的,它比你的第一个解决方案更难看。使它更具可读性似乎是不可能的。不过我会尝试...
      【解决方案4】:

      灵感来自

      1. @Ronak Shah 的以下回答
      2. @G 建议的中间分配。格罗腾迪克here

      我整理了以下解决方案

      1. 将文件的读取和命名合并到一个 dplyr 管道中(我最初的目标)
      2. 使代码更易于阅读(我一直隐含的目标)——这对合作者或一般人来说都很重要。

      所以这是我的一体化 dplyr 管道:

      list.files(path = data.location, pattern = "*.csv", full.names = TRUE)  %>%
        { filenames <<- . } %>%
        map(read_csv) %>%
        setNames(filenames %>% basename %>% map(~gsub(paste0('(.*).csv'), '\\1', .x)))
      

      上述解决方案中唯一棘手的部分是使用赋值运算符&lt;&lt;-,因为&lt;- 不起作用。如果您想使用后者,您可以通过将整个第二部分放入括号中来实现 - @G 也建议。上述帖子中的格洛腾迪克:

      list.files(path = data.location, pattern = "*.csv", full.names = TRUE)  %>%
      {
        { filenames <- . } %>%
          map(read_csv) %>%
          setNames(filenames %>% basename %>% map(~gsub(paste0('(.*).csv'), '\\1', .x)))
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多