【问题标题】:Automate zip file reading in R在 R 中自动读取 zip 文件
【发布时间】:2019-06-18 11:26:50
【问题描述】:

我需要自动化 R 以将 csv 数据文件读取到 zip 文件中。

例如,我会输入:

read.zip(file = "myfile.zip")

在内部,要做的是:

  • 解压myfile.zip到一个临时文件夹
  • 使用read.csv读取其中包含的唯一文件

如果 zip 文件中有多个文件,则会引发错误。

我的问题是获取包含在 zip 文件中的文件名,以便提供它执行 read.csv 命令。有人知道怎么做吗?

更新

这是我根据@Paul 回答编写的函数:

read.zip <- function(zipfile, row.names=NULL, dec=".") {
    # Create a name for the dir where we'll unzip
    zipdir <- tempfile()
    # Create the dir using that name
    dir.create(zipdir)
    # Unzip the file into the dir
    unzip(zipfile, exdir=zipdir)
    # Get the files into the dir
    files <- list.files(zipdir)
    # Throw an error if there's more than one
    if(length(files)>1) stop("More than one data file inside zip")
    # Get the full name of the file
    file <- paste(zipdir, files[1], sep="/")
    # Read the file
    read.csv(file, row.names, dec)
}

由于我将在tempdir() 中处理更多文件,因此我在其中创建了一个新目录,因此我不会对这些文件感到困惑。我希望它可能有用!

【问题讨论】:

标签: r compression


【解决方案1】:

使用unz的另一种解决方案:

read.zip <- function(file, ...) {
  zipFileInfo <- unzip(file, list=TRUE)
  if(nrow(zipFileInfo) > 1)
    stop("More than one data file inside zip")
  else
    read.csv(unz(file, as.character(zipFileInfo$Name)), ...)
}

【讨论】:

    【解决方案2】:

    您可以使用unzip 解压缩文件。我只是提到这一点,因为从你的问题中不清楚你是否知道这一点。关于读取文件。将文件解压缩到临时目录 (?tempdir) 后,只需使用 list.files 查找转储到临时目录的文件。在您的情况下,这只是一个文件,您需要的文件。使用read.csv 阅读它非常简单:

    l = list.files(temp_path)
    read.csv(l[1])
    

    假设您的tempdir 位置存储在temp_path

    【讨论】:

    • 这正是我想要的!我试图使用system("ls"),但它没有返回一个 R 对象——比如一个向量。谢谢!
    • @JoãoDaniel system("ls") 不是去这里的方式,但 system("ls", intern = TRUE) 可能是你所希望的
    【解决方案3】:

    我在尝试从 zip 中自动读取多个 csv 文件时发现了这个线程。我调整了解决方案以适应更广泛的情况。我还没有测试过奇怪的文件名或类似的东西,但这对我有用,所以我想我会分享:

    read.csv.zip <- function(zipfile, ...) {
    # Create a name for the dir where we'll unzip
    zipdir <- tempfile()
    # Create the dir using that name
    dir.create(zipdir)
    # Unzip the file into the dir
    unzip(zipfile, exdir=zipdir)
    # Get a list of csv files in the dir
    files <- list.files(zipdir)
    files <- files[grep("\\.csv$", files)]
    # Create a list of the imported csv files
    csv.data <- sapply(files, function(f) {
        fp <- file.path(zipdir, f)
        return(read.csv(fp, ...))
    })
    return(csv.data)}
    

    【讨论】:

    • 我不得不在list.files()中使用recursive=TRUE;此外,您可以简单地使用list.files 中的pattern 参数:files &lt;- list.files(zipdir, recursive=TRUE, pattern="\\.csv$",而不是使用grep() 来子集files 的第二个定义。我还对返回的列表进行了命名改进,names(csv.data) &lt;- gsub(".+\\/", "", files,perl=T)。我可能会将这些更改添加为新答案,但请随时更新您的方法。谢谢!
    • @rbatt 很好的反馈。当我写这篇文章时,我还是 R 新手,所以我不知道要寻找像 patternrecursive 这样的选项。我怀疑我会编辑我的答案,但我很乐意看到你的代码。谢谢!
    【解决方案4】:

    如果您的系统上安装了 zcat(linux、macos 和 cygwin 就是这种情况),您也可以使用:

    zipfile<-"test.zip"
    myData <- read.delim(pipe(paste("zcat", zipfile)))
    

    此解决方案还具有不创建临时文件的优点。

    【讨论】:

      【解决方案5】:

      这是我正在使用的一种方法,它主要基于@Corned Beef Hash Map 的answer。以下是我所做的一些更改:

      • 我的方法使用了data.table 包的fread(),它 可以很快(通常,如果它是拉链的,尺寸可能会很大,所以你 站在这里获得很多速度!)。

      • 我还调整了输出格式,使其成为命名列表,其中 列表的每个元素都以文件命名。对我来说,这是一个 非常有用的补充。

      • 而不是使用正则表达式来筛选文件 被list.files抓取,我使用list.file()pattern 论据。

      • 最后,我依靠 fread() 并将 pattern 设为 您可以提供类似 ""NULL".",你可以用它来读入多种类型的数据文件;实际上, 您可以一次阅读多种类型(如果您的 .zip 包含 .csv, .txt 你想要两者,例如)。如果只有某些类型 您想要的文件,您也可以指定仅使用这些文件的模式。

      这是实际的功能:

      read.csv.zip <- function(zipfile, pattern="\\.csv$", ...){
      
          # Create a name for the dir where we'll unzip
          zipdir <- tempfile()
      
          # Create the dir using that name
          dir.create(zipdir)
      
          # Unzip the file into the dir
          unzip(zipfile, exdir=zipdir)
      
          # Get a list of csv files in the dir
          files <- list.files(zipdir, rec=TRUE, pattern=pattern)
      
          # Create a list of the imported csv files
          csv.data <- sapply(files, 
              function(f){
                  fp <- file.path(zipdir, f)
                  dat <- fread(fp, ...)
                  return(dat)
              }
          )
      
          # Use csv names to name list elements
          names(csv.data) <- basename(files)
      
          # Return data
          return(csv.data)
      }
      

      【讨论】:

        【解决方案6】:

        以下内容对上述答案进行了细化。 FUN 可以是 read.csv、cat 或您喜欢的任何内容,前提是第一个参数将接受文件路径。例如

        head(read.zip.url("http://www.cms.gov/Medicare/Coding/ICD9ProviderDiagnosticCodes/Downloads/ICD-9-CM-v32-master-descriptions.zip", filename = "CMS32_DESC_LONG_DX.txt"))
        
        read.zip.url <- function(url, filename = NULL, FUN = readLines, ...) {
          zipfile <- tempfile()
          download.file(url = url, destfile = zipfile, quiet = TRUE)
          zipdir <- tempfile()
          dir.create(zipdir)
          unzip(zipfile, exdir = zipdir) # files="" so extract all
          files <- list.files(zipdir)
          if (is.null(filename)) {
            if (length(files) == 1) {
              filename <- files
            } else {
              stop("multiple files in zip, but no filename specified: ", paste(files, collapse = ", "))
            }
          } else { # filename specified
            stopifnot(length(filename) ==1)
            stopifnot(filename %in% files)
          }
          file <- paste(zipdir, files[1], sep="/")
          do.call(FUN, args = c(list(file.path(zipdir, filename)), list(...)))
        }
        

        【讨论】:

          【解决方案7】:

          另一种使用 data.table 包中的fread 的方法

          fread.zip <- function(zipfile, ...) {
            # Function reads data from a zipped csv file
            # Uses fread from the data.table package
          
            ## Create the temporary directory or flush CSVs if it exists already
            if (!file.exists(tempdir())) {dir.create(tempdir())
            } else {file.remove(list.files(tempdir(), full = T, pattern = "*.csv"))
            }
          
            ## Unzip the file into the dir
            unzip(zipfile, exdir=tempdir())
          
            ## Get path to file
            file <- list.files(tempdir(), pattern = "*.csv", full.names = T)
          
            ## Throw an error if there's more than one
            if(length(file)>1) stop("More than one data file inside zip")
          
            ## Read the file
            fread(file, 
               na.strings = c(""), # read empty strings as NA
               ...
            )
          }
          

          基于@joão-daniel 的回答/更新

          【讨论】:

            【解决方案8】:

            解压文件位置

            outDir&lt;-"~/Documents/unzipFolder"

            获取所有 zip 文件

            zipF &lt;- list.files(path = "~/Documents/", pattern = "*.zip", full.names = TRUE)

            解压所有文件

            purrr::map(.x = zipF, .f = unzip, exdir = outDir)

            【讨论】:

              【解决方案9】:

              我刚刚写了一个基于 top read.zip 的函数,可能会有所帮助...

              read.zip <- function(zipfile, internalfile=NA, read.function=read.delim, verbose=TRUE, ...) {
                  # function based on http://stackoverflow.com/questions/8986818/automate-zip-file-reading-in-r
              
                  # check the files within zip
                  unzfiles <- unzip(zipfile, list=TRUE)
                  if (is.na(internalfile) || is.numeric(internalfile)) {
                      internalfile <- unzfiles$Name[ifelse(is.na(internalfile),1,internalfile[1])]
                  }
                  # Create a name for the dir where we'll unzip
                  zipdir <- tempfile()
                  # Create the dir using that name
                  if (verbose) catf("Directory created:",zipdir,"\n")
                  dir.create(zipdir)
                  # Unzip the file into the dir
                  if (verbose) catf("Unzipping file:",internalfile,"...")
                  unzip(zipfile, file=internalfile, exdir=zipdir)
                  if (verbose) catf("Done!\n")
                  # Get the full name of the file
                  file <- paste(zipdir, internalfile, sep="/")
                  if (verbose) 
                      on.exit({ 
                          catf("Done!\nRemoving temporal files:",file,".\n") 
                          file.remove(file)
                          file.remove(zipdir)
                          }) 
                  else
                      on.exit({file.remove(file); file.remove(zipdir);})
                  # Read the file
                  if (verbose) catf("Reading File...")
                  read.function(file, ...)
              }
              

              【讨论】:

                猜你喜欢
                • 2020-03-06
                • 1970-01-01
                • 2020-08-11
                • 1970-01-01
                • 1970-01-01
                • 2021-07-04
                • 1970-01-01
                • 2017-03-25
                • 2023-03-19
                相关资源
                最近更新 更多