2012-01-24 13 views
23

Potrzebuję zautomatyzować R, aby odczytać plik danych CSV, który jest w pliku zip.Automatyczne odczytywanie plików zip w R

Na przykład, chciałbym wpisać:

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

i wewnętrznie, co byłoby wykonane jest:

  • Rozpakuj myfile.zip do folderu tymczasowego
  • odczytać tylko pliki znajdujące się na nim za pomocą read.csv

Jeśli jest więcej niż jeden plik do pliku zip, zgłaszany jest błąd.

Mój problem polega na tym, aby uzyskać nazwę pliku zawartego w pliku zip, zgodnie z zamówieniem, aby uzyskać polecenie read.csv. Czy ktoś wie, jak to zrobić?

UPDATE

Oto funkcja napisałem na podstawie @Paul odpowiedź:

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

Ponieważ będziemy pracować z większą liczbą plików wewnątrz tempdir(), stworzyłem nową dir wewnątrz niego, więc nie mylę się z plikami. Mam nadzieję, że może się przydać!

+0

możliwe duplikaty? pod adresem: 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

Właściwie pierwszy link, z którym nie jest związany, ponieważ mój problem nie był 't rozpakowanie pliku, ale aby uzyskać nazwę plików wewnątrz zip. Ale tak, drugi pokazuje polecenie 'list.files', które było (jak dotąd) nieznane przeze mnie. –

+0

@jdanielnd: możesz dostać się do nazw plików w pliku zip używając 'unzip (file, list = TRUE)', jak użyłem w mojej odpowiedzi. –

Odpowiedz

9

Możesz użyć unzip, aby rozpakować plik. Wspominam o tym, ponieważ z twojego pytania nie wynika jednoznacznie, czy to wiedziałeś. W odniesieniu do czytania pliku. Po wyodrębnieniu pliku do katalogu tymczasowego (?tempdir) wystarczy użyć pliku list.files, aby znaleźć pliki, które zostały porzucone w katalogu tymczasowym. W twoim przypadku jest to tylko jeden plik, którego potrzebujesz. Czytając go za pomocą read.csv jest wtedy dość prosta:

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

zakładając swoją lokalizację tempdir jest przechowywany w temp_path.

+0

Tego właśnie szukałem! Próbowałem użyć 'system (" ls ")', ale nie zwróciło obiektu R - jak wektor. Dzięki! –

+0

@ System JoãoDaniel ("ls") 'nie jest sposób, aby przejść tutaj, ale' system ("ls", intern = TRUE) 'jest prawdopodobnie to, co masz nadzieję na – Dason

11

Innym rozwiązaniem użyciu 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

Jeśli zcat zainstalowane w systemie (co ma miejsce w przypadku systemów Linux, MacOS i Cygwin) można również użyć:

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

ten Rozwiązanie ma także tę zaletę, że nie są tworzone żadne pliki tymczasowe.

4

Znalazłem ten wątek, ponieważ próbowałem zautomatyzować odczytywanie wielu plików CSV z pliku ZIP. Zaadaptowałem rozwiązanie do szerszej sprawy. Nie testowałem go do dziwnych nazw plików lub podobne, ale to, co pracował dla mnie, więc pomyślałem, że podzielę:

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

Musiałem użyć' rekursywny = PRAWDA' w 'list.files()'; Ponadto, zamiast używać 'grep()' do podzbioru w drugiej definicji 'files', możesz po prostu użyć argumentu' pattern' w 'list.files':' files <- list.files (zipdir, recursive = PRAWDA, wzorzec = "\\. Csv $" '. Wprowadziłem również usprawnienie nazewnictwa do zwróconej listy,' names (csv.data) <- gsub (". + \\ /", "", pliki, perl = T) '. Mogę dodać te zmiany jako nową odpowiedź, ale nie wahaj się zaktualizować swojego podejścia .Dziękuję! – rbatt

+1

@rbatt Świetna opinia. Wciąż byłem nowy dla R, kiedy to pisałem, więc nie wiedziałem, aby szukać opcje takie jak 'wzorzec' i' rekursywny' wątpię, że będę edytować odpowiedź, ale chciałbym zobaczyć Twój kod. –

1

Poniższa poprawia powyższe odpowiedzi. FUN może być read.csv, cat lub cokolwiek chcesz, pod warunkiem, że pierwszy argument przyjmie ścieżkę pliku. Na przykład.

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

Właśnie napisałem funkcję opartą na górnym read.zip, które może pomóc ...

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

Oto podejście używam, która opiera się w dużej mierze na @Corned Beef Hash Mapa „s answer. Oto niektóre ze zmian zrobiłem:

  • Moje podejście wykorzystuje data.table pakiet na fread(), który może być szybko (zazwyczaj jeśli jest spakowany, rozmiary mogą być duże, więc stoisz zyskać dużo prędkości tutaj!).

  • Poprawiłem również format wyjściowy tak, że jest to lista nazwana, gdzie każdy element listy jest nazwany po pliku. Dla mnie był to bardzo przydatny dodatek.

  • Zamiast używać wyrażeń regularnych przesiać przez plikach pochwycony przez list.files, mogę skorzystać z list.file() „s pattern argument.

  • Wreszcie, powołując się na fread() i dokonując pattern argument, który mógłby podać coś "" lub NULL lub ".", można to wykorzystać do odczytania w wielu typów plików danych; w rzeczywistości, możesz czytać w wielu typach naraz (jeśli twój .zip zawiera .csv, .txt w obu, na przykład). Jeśli są tylko niektóre typy plików, które chcesz, możesz określić wzorzec, aby używać ich tylko.

Oto rzeczywista funkcja:

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

Innym rozwiązaniem, które wykorzystuje fread z pakietu 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 
    ... 
) 
} 

Na podstawie odpowiedzi/aktualizacji przez @ Joao-Daniel

Powiązane problemy