Automatizar la lectura de archivos zip en R
decompression (8)
Acabo de escribir una función basada en top read.zip que puede ayudar ...
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, ...)
}
Necesito automatizar R para leer un archivo de datos csv que esté en un archivo zip.
Por ejemplo, yo escribiría:
read.zip(file = "myfile.zip")
E internamente, lo que se haría es:
- Descomprima
myfile.zip
en una carpeta temporal - Lee el único archivo contenido en él usando
read.csv
Si hay más de un archivo en el archivo zip, se produce un error.
Mi problema es obtener el nombre del archivo contenido en el archivo zip, ordenado para proporcionarle el comando read.csv
. Alguien sabe como hacerlo?
ACTUALIZAR
Aquí está la función que escribí basada en la respuesta de @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)
}
Ya que estaré trabajando con más archivos dentro de tempdir()
, creé un nuevo directorio dentro de él, para no confundirme con los archivos. Espero que sea de utilidad!
Encontré este hilo mientras intentaba automatizar la lectura de varios archivos csv desde un archivo zip. Adapté la solución al caso más amplio. No lo he probado para nombres de archivo raros o similares, pero esto es lo que funcionó para mí, así que pensé que lo compartiría:
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)}
Este es un enfoque que estoy usando y que se basa en gran medida en la respuesta de @Corned Beef Hash Map. Aquí están algunos de los cambios que hice:
Mi enfoque utiliza el
data.table
fread()
del paquetefread()
, que puede ser rápido (por lo general, si está comprimido, los tamaños pueden ser grandes, por lo que puede ganar mucha velocidad aquí).También ajusté el formato de salida para que sea una lista con nombre, donde cada elemento de la lista lleva el nombre del archivo. Para mí, esta fue una adición muy útil.
En lugar de usar expresiones regulares para examinar los archivos capturados por list.files, utilizo el argumento de
pattern
list.file()
.Finalmente, confiando en
fread()
y creando un argumento para el que podría proporcionar algo como""
oNULL
o"."
, puedes usar esto para leer muchos tipos de archivos de datos; de hecho, puedes leer en varios tipos de a la vez (si tu .zip contiene .csv, .txt en ambos, por ejemplo). Si solo desea algunos tipos de archivos, puede especificar el patrón para que solo los use.
Aquí está la función real:
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)
}
Lo siguiente refina las respuestas anteriores. FUN podría ser read.csv, cat, o cualquier cosa que desee, siempre que el primer argumento acepte una ruta de archivo. P.ej
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(...)))
}
Otra solución usando 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)), ...)
}
Otro enfoque que utiliza fread
del paquete 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
...
)
}
Basado en la respuesta / actualización de @ joão-daniel
Puedes usar unzip
para descomprimir el archivo. Solo menciono esto, ya que no queda claro en tu pregunta si lo sabías. En lo que respecta a la lectura del archivo. Una vez que haya extraído el archivo a un directorio temporal ( ?tempdir
), solo use list.files
para encontrar los archivos que fueron volcados en el directorio temporal. En su caso, esto es solo un archivo, el archivo que necesita. read.csv
usando read.csv
es bastante sencillo:
l = list.files(temp_path)
read.csv(l[1])
asumiendo que su ubicación tempdir
se almacena en temp_path
.
Si tiene instalado zcat en su sistema (como es el caso de linux, macos y cygwin) también podría usar:
zipfile<-"test.zip"
myData <- read.delim(pipe(paste("zcat", zipfile)))
Esta solución también tiene la ventaja de que no se crean archivos temporales.