2012-01-24 19 views
23

Ho bisogno di automatizzare R per leggere un file di dati csv che è in un file zip.Automatizza lettura file zip in R

Ad esempio, vorrei digito:

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

che internamente, quello che sarebbe stato fatto è:

  • Unzip myfile.zip in una cartella temporanea
  • Leggi l'unico file in esso contenuti usando read.csv

Se ce n'è più di uno file nel file zip, viene generato un errore.

Il mio problema è ottenere il nome del file contenuto nel file zip, in modo da fornirgli il comando read.csv. Qualcuno sa come farlo?

UPDATE

Ecco la funzione che ho scritto basandomi su @ Paolo risposta:

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

Dal Lavorerò con più file all'interno del tempdir(), ho creato una nuova directory al suo interno, quindi non mi confondo con i file. Spero possa essere utile!

+0

possibili duplicati? all'indirizzo: http://stackoverflow.com/questions/3053833/using-r-to-download-zipped-data-file-extract-and-import-data; http://stackoverflow.com/questions/7044808/using-r-to-download-gzipped-data-file-extract-and-import-data/7045059#7045059 – aatrujillob

+0

In realtà il primo collegamento non è correlato, poiché il mio problema non era 'decomprimere il file, ma per ottenere il nome dei file all'interno dello zip. Ma sì, il secondo mostra il comando 'list.files', che era (finora) sconosciuto da me. –

+0

@jdanielnd: puoi ottenere i nomi dei file nel file zip usando 'unzip (file, list = TRUE)', come ho usato nella mia risposta. –

risposta

9

È possibile utilizzare unzip per decomprimere il file. Ho appena detto questo perché non è chiaro dalla tua domanda se lo sapevi. Per quanto riguarda la lettura del file. Una volta estratto il file in una directory temporanea (?tempdir), è sufficiente utilizzare list.files per trovare i file che sono stati trasferiti nella directory temporanea. Nel tuo caso questo è solo un file, il file che ti serve. Leggendolo usando read.csv è quindi molto semplice:

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

assumendo la posizione di tempdir è memorizzato in temp_path.

+0

Questo è proprio quello che stavo cercando! Stavo cercando di usare 'system (" ls ")' ma non ha restituito un oggetto R - come un vettore. Grazie! –

+0

@ JoãoDaniel 'system (" ls ")' non è il modo di andare qui ma 'system (" ls ", intern = TRUE)' è probabilmente quello che speravi per – Dason

11

Un'altra soluzione utilizzando 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

Se avete zcat installato sul vostro sistema (che è il caso di Linux, MacOS, e Cygwin) si potrebbe anche usare:

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

Questo la soluzione ha anche il vantaggio che non vengono creati file temporanei.

4

Ho trovato questo thread mentre stavo cercando di automatizzare la lettura di più file CSV da un file zip. Ho adattato la soluzione al caso più ampio. Non ho ancora testato per i nomi di file strani o simili, ma questo è ciò che ha funzionato per me, così ho pensato di condividere:

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)} 
+0

Ho dovuto usare 'recursive = TRUE' in 'list.files()'; Inoltre, invece di usare 'grep()' per sottoinsieme nella seconda definizione di 'files', puoi semplicemente usare l'argomento' pattern' in 'list.files':' files <- list.files (zipdir, ricorsivo = TRUE, pattern = "\\. Csv $" 'Ho anche apportato un miglioramento di denominazione all'elenco restituito,' names (csv.data) <- gsub (". + \\ /", "", files, perl = T) '. Potrei aggiungere queste modifiche come una nuova risposta, ma sentitevi liberi di aggiornare il vostro approccio Grazie! – rbatt

+1

@rbatt Ottimo feedback Ero ancora nuovo a R quando l'ho scritto così non sapevo di cercare opzioni come 'pattern' e' recursive'. Dubito che modificherò la mia risposta ma mi piacerebbe vedere il tuo codice. Grazie! –

1

Di seguito affina le risposte di cui sopra. FUN potrebbe essere read.csv, cat, o qualsiasi cosa ti piaccia, a patto che il primo argomento accetti un percorso file. Per esempio.

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(...))) 
} 
0

Ho appena scritto una funzione basata su top read.zip che può aiutare ...

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, ...) 
} 
2

Qui è un approccio che sto usando che si basa pesantemente su @Corned Beef Hash Mappa 's answer. Qui ci sono alcuni dei cambiamenti che ho fatto:

  • mio approccio fa uso del pacchetto di fread()data.table, che può essere veloce (in genere, se è compresso, le dimensioni potrebbero essere di grandi dimensioni, in modo da da guadagnare un molta velocità qui!).

  • Ho anche regolato il formato di output in modo che si tratti di un elenco con nome, in cui ogni elemento della lista prende il nome dal file. Per me, questa era un'aggiunta molto utile .

  • Invece di usare espressioni regolari per vagliare i file afferrato da list.files, mi fanno uso di list.file() s' pattern argomento.

  • Infine, affidandosi a fread() e facendo pattern un argomento a cui si potrebbe fornire qualcosa come "" o NULL o ".", è possibile utilizzare questo per leggere in molti tipi di file di dati; infatti, puoi leggere in più tipi contemporaneamente (se il tuo .zip contiene .csv, .txt in entrambi vuoi, ad es.). Se ci sono solo alcuni tipi di file che si desidera, è possibile specificare il modello per utilizzare solo quelli.

Ecco la funzione attuale:

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) 
} 
1

Un altro approccio che utilizza fread dal pacchetto data.table

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 
    ... 
) 
} 

Sulla base della risposta/aggiornamento @ joão-Daniel