Мне нужно автоматизировать 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 ()
, Я создал внутри него новый каталог, чтобы меня не путали с файлами. Надеюсь, это может быть полезно!
Вы можете использовать unzip
, чтобы распаковать файл. Я просто упоминаю об этом, поскольку из вашего вопроса не ясно, знали ли вы это. Что касается чтения файла. После того, как вы извлекли файл во временный каталог (?tempdir
), просто используйте list.files
, чтобы найти файлы, которые были выгружены во временный каталог. В вашем случае это всего лишь один файл, файл, который вам нужен. Считать его с помощью read.csv
тогда довольно просто:
l = list.files(temp_path)
read.csv(l[1])
при условии, что ваше tempdir
местоположение сохранено в temp_path
.
Следующее уточняет приведенные выше ответы. 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(...)))
}
Вот подход, который я использую, который в значительной степени основан на ответе @Corned Beef Hash Map . Вот некоторые из изменений, которые я сделал:
Мой подход использует пакет 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)
}
Если у вас установлен zcat в вашей системе (как в случае с linux, macos и cygwin), вы также можете использовать:
zipfile<-"test.zip"
myData <- read.delim(pipe(paste("zcat", zipfile)))
Преимущество этого решения в том, что временные файлы не создаются .
Другой подход, использующий fread
из пакета 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
...
)
}
Основан на ответе / обновлении @ joão-daniel
Я только что написал функцию на основе 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, ...)
}