我有一个非常大的表(3000万行),我想在r中作为数据框架加载,read.table()有很多方便的特性,但似乎在实现中有很多逻辑会减慢速度。在我的例子中,我假设我事先知道列的类型,表不包含任何列标题或行名,也没有任何需要担心的病态字符。
我知道使用scan()将表读入为列表可以相当快,例如:
datalist <- scan('myfile',sep='\t',list(url='',popularity=0,mintime=0,maxtime=0)))
但我试图将其转换为数据框架的一些尝试似乎将上述性能降低了6倍:
df <- as.data.frame(scan('myfile',sep='\t',list(url='',popularity=0,mintime=0,maxtime=0))))
有更好的办法吗?或者完全不同的解决问题的方法?
上面这些我都试过了,[1]做得最好。我只有8gb的内存
循环20个文件,每个5gb, 7列:
read_fwf(arquivos[i],col_types = "ccccccc",fwf_cols(cnpj = c(4,17), nome = c(19,168), cpf = c(169,183), fantasia = c(169,223), sit.cadastral = c(224,225), dt.sitcadastral = c(226,233), cnae = c(376,382)))
这是几年后的一次更新
这个答案是旧的,而R已经继续了。调整阅读。表跑得快一点没有什么好处。你的选择是:
Using vroom from the tidyverse package vroom for importing data from csv/tab-delimited files directly into an R tibble. See Hector's answer.
Using fread in data.table for importing data from csv/tab-delimited files directly into R. See mnel's answer.
Using read_table in readr (on CRAN from April 2015). This works much like fread above. The readme in the link explains the difference between the two functions (readr currently claims to be "1.5-2x slower" than data.table::fread).
read.csv.raw from iotools provides a third option for quickly reading CSV files.
Trying to store as much data as you can in databases rather than flat files. (As well as being a better permanent storage medium, data is passed to and from R in a binary format, which is faster.) read.csv.sql in the sqldf package, as described in JD Long's answer, imports data into a temporary SQLite database and then reads it into R. See also: the RODBC package, and the reverse depends section of the DBI package page. MonetDB.R gives you a data type that pretends to be a data frame but is really a MonetDB underneath, increasing performance. Import data with its monetdb.read.csv function. dplyr allows you to work directly with data stored in several types of database.
Storing data in binary formats can also be useful for improving performance. Use saveRDS/readRDS (see below), the h5 or rhdf5 packages for HDF5 format, or write_fst/read_fst from the fst package.
原来的答案
无论你是否使用read,都有一些简单的事情可以尝试。表或扫描。
设置nrows=数据中的记录数量(扫描中的nmax)。
确保你的评论。Char =""关闭注释的解释。
使用read.table中的colClasses显式定义每列的类。
设置多。line=FALSE也可以提高扫描的性能。
如果这些都不起作用,那么使用一个概要包来确定哪些行减慢了速度。也许你可以写一个删减版的read。表基于结果。
另一种方法是在将数据读入R之前过滤数据。
或者,如果问题是必须定期读入,那么使用这些方法一次性读入数据,然后使用save saveRDS将数据帧保存为二进制blob,然后下次使用load readRDS可以更快地检索它。
这个问题之前在R-Help上被问到过,所以值得回顾一下。
一个建议是使用readChar(),然后用strsplit()和substr()对结果进行字符串操作。您可以看到readChar所涉及的逻辑比read.table要少得多。
我不知道这里内存是否是一个问题,但您可能还想看看HadoopStreaming包。它使用Hadoop,这是一个MapReduce框架,设计用于处理大型数据集。为此,您将使用hsTableReader函数。这是一个例子(但是学习Hadoop有一个学习曲线):
str <- "key1\t3.9\nkey1\t8.9\nkey1\t1.2\nkey1\t3.9\nkey1\t8.9\nkey1\t1.2\nkey2\t9.9\nkey2\"
cat(str)
cols = list(key='',val=0)
con <- textConnection(str, open = "r")
hsTableReader(con,cols,chunkSize=6,FUN=print,ignoreKey=TRUE)
close(con)
这里的基本思想是将数据导入分解成块。您甚至可以使用一个并行框架(例如snow),并通过分割文件来并行运行数据导入,但对于大型数据集来说,这是没有帮助的,因为您将遇到内存限制,这就是为什么map-reduce是一种更好的方法。
一开始我没有看到这个问题,几天后我问了一个类似的问题。我将记下我之前的问题,但我认为我应该在这里添加一个答案,以解释我如何使用sqldf()来做到这一点。
关于将2GB或更多的文本数据导入R数据帧的最佳方法,已经有了一些讨论。昨天我写了一篇关于使用sqldf()将数据导入SQLite作为暂存区,然后将它从SQLite吸到r的博客文章,这对我来说真的很好。我能够在不到5分钟的时间内提取2GB(3列,40mm行)的数据。相比之下,read.csv命令运行了一整夜,始终没有完成。
下面是我的测试代码:
设置测试数据:
bigdf <- data.frame(dim=sample(letters, replace=T, 4e7), fact1=rnorm(4e7), fact2=rnorm(4e7, 20, 50))
write.csv(bigdf, 'bigdf.csv', quote = F)
在运行以下导入例程之前,我重新启动R:
library(sqldf)
f <- file("bigdf.csv")
system.time(bigdf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F)))
我让下面这行写了一整晚,但始终没有写完:
system.time(big.df <- read.csv('bigdf.csv'))
另一种选择是使用vroom包。现在在CRAN。
Vroom不加载整个文件,它索引每条记录所在的位置,并在稍后使用它时读取。
只按使用付费。
请参阅vroom介绍,开始使用vroom和vroom基准。
基本的概述是,对一个大文件的初始读取将会快得多,而对数据的后续修改可能会稍微慢一些。所以根据你的用途,这可能是最好的选择。
查看下面vroom基准测试的简化示例,关键部分是超快的读取时间,但稍微播种操作,如聚合等。
package read print sample filter aggregate total
read.delim 1m 21.5s 1ms 315ms 764ms 1m 22.6s
readr 33.1s 90ms 2ms 202ms 825ms 34.2s
data.table 15.7s 13ms 1ms 129ms 394ms 16.3s
vroom (altrep) dplyr 1.7s 89ms 1.7s 1.3s 1.9s 6.7s
Often times I think it is just good practice to keep larger databases inside a database (e.g. Postgres). I don't use anything too much larger than (nrow * ncol) ncell = 10M, which is pretty small; but I often find I want R to create and hold memory intensive graphs only while I query from multiple databases. In the future of 32 GB laptops, some of these types of memory problems will disappear. But the allure of using a database to hold the data and then using R's memory for the resulting query results and graphs still may be useful. Some advantages are:
(1)数据一直加载在数据库中。当重新打开笔记本电脑时,只需在pgadmin中重新连接所需的数据库。
(2) R的确可以比SQL做更多漂亮的统计和绘图操作。但是我认为SQL比R更适合于查询大量的数据。
# Looking at Voter/Registrant Age by Decade
library(RPostgreSQL);library(lattice)
con <- dbConnect(PostgreSQL(), user= "postgres", password="password",
port="2345", host="localhost", dbname="WC2014_08_01_2014")
Decade_BD_1980_42 <- dbGetQuery(con,"Select PrecinctID,Count(PrecinctID),extract(DECADE from Birthdate) from voterdb where extract(DECADE from Birthdate)::numeric > 198 and PrecinctID in (Select * from LD42) Group By PrecinctID,date_part Order by Count DESC;")
Decade_RD_1980_42 <- dbGetQuery(con,"Select PrecinctID,Count(PrecinctID),extract(DECADE from RegistrationDate) from voterdb where extract(DECADE from RegistrationDate)::numeric > 198 and PrecinctID in (Select * from LD42) Group By PrecinctID,date_part Order by Count DESC;")
with(Decade_BD_1980_42,(barchart(~count | as.factor(precinctid))));
mtext("42LD Birthdays later than 1980 by Precinct",side=1,line=0)
with(Decade_RD_1980_42,(barchart(~count | as.factor(precinctid))));
mtext("42LD Registration Dates later than 1980 by Precinct",side=1,line=0)