我有非常大的表(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))))
有没有更好的方法呢?或者很可能完全不同的方法来解决问题?
几年后的更新
这个答案很老了,R继续前进.调整read.table
运行速度更快一点也没有什么好处.你的选择是:
使用fread
在data.table
导入CSV从/制表符分隔的文件中的数据直接导入R.见MNEL的答案.
使用read_table
中readr
(从2015年4月在CRAN).这很像fread
上面的工作.链接中的自述文件解释了两个功能之间的差异(readr
目前声称"慢1.5-2倍" data.table::fread
).
read.csv.raw
from iotools
提供了快速读取CSV文件的第三个选项.
尝试在数据库而不是平面文件中存储尽可能多的数据.(除了作为一种更好的永久存储介质,数据以二进制格式传递给R和从R传递,这更快.)read.csv.sql
在sqldf
包中,如JD Long的回答所述,将数据导入临时SQLite数据库,然后读取它进入R.参见:RODBC
包,以及DBI
包页面的反向部分.MonetDB.R
为您提供一个数据类型,假装是一个数据框,但实际上是一个MonetDB,提高了性能.导入数据及其monetdb.read.csv
功能. dplyr
允许您直接处理存储在多种类型数据库中的数据.
以二进制格式存储数据对于提高性能也很有用.使用saveRDS
/ readRDS
(见下文),HDF5格式的包或/ h5
或rhdf5
包中的write_fst
/ .read_fst
fst
原来的答案
无论您使用read.table还是scan,都可以尝试一些简单的事情.
Set nrows
= 数据中的记录数(nmax
in scan
).
确保comment.char=""
关闭注释的解释.
使用colClasses
in 显式定义每列的类read.table
.
设置multi.line=FALSE
还可以提高扫描性能.
如果这些都不起作用,那么使用其中一个分析包来确定哪些行减慢了速度.也许你可以read.table
根据结果编写一个减少版本.
另一种方法是在将数据读入R之前过滤数据.
或者,如果问题是您必须定期读取它,那么使用这些方法一次读取数据,然后将数据帧保存为二进制blob save
saveRDS
,然后下次你可以更快地检索它 load
readRDS
.
这里是利用一个例子fread
,从data.table
1.8.7
这些示例来自帮助页面fread
,我的Windows XP Core 2 duo E8400上有时序.
library(data.table) # Demo speedup n=1e6 DT = data.table( a=sample(1:1000,n,replace=TRUE), b=sample(1:1000,n,replace=TRUE), c=rnorm(n), d=sample(c("foo","bar","baz","qux","quux"),n,replace=TRUE), e=rnorm(n), f=sample(1:1000,n,replace=TRUE) ) DT[2,b:=NA_integer_] DT[4,c:=NA_real_] DT[3,d:=NA_character_] DT[5,d:=""] DT[2,e:=+Inf] DT[3,e:=-Inf]标准read.table
write.table(DT,"test.csv",sep=",",row.names=FALSE,quote=FALSE) cat("File size (MB):",round(file.info("test.csv")$size/1024^2),"\n") ## File size (MB): 51 system.time(DF1 <- read.csv("test.csv",stringsAsFactors=FALSE)) ## user system elapsed ## 24.71 0.15 25.42 # second run will be faster system.time(DF1 <- read.csv("test.csv",stringsAsFactors=FALSE)) ## user system elapsed ## 17.85 0.07 17.98优化read.table
system.time(DF2 <- read.table("test.csv",header=TRUE,sep=",",quote="", stringsAsFactors=FALSE,comment.char="",nrows=n, colClasses=c("integer","integer","numeric", "character","numeric","integer"))) ## user system elapsed ## 10.20 0.03 10.32FREAD
require(data.table) system.time(DT <- fread("test.csv")) ## user system elapsed ## 3.12 0.01 3.22sqldf
require(sqldf) system.time(SQLDF <- read.csv.sql("test.csv",dbname=NULL)) ## user system elapsed ## 12.49 0.09 12.69 # sqldf as on SO f <- file("test.csv") system.time(SQLf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F))) ## user system elapsed ## 10.21 0.47 10.73ff/ffdf
require(ff) system.time(FFDF <- read.csv.ffdf(file="test.csv",nrows=n)) ## user system elapsed ## 10.85 0.10 10.99综上所述:
## user system elapsed Method ## 24.71 0.15 25.42 read.csv (first time) ## 17.85 0.07 17.98 read.csv (second time) ## 10.20 0.03 10.32 Optimized read.table ## 3.12 0.01 3.22 fread ## 12.49 0.09 12.69 sqldf ## 10.21 0.47 10.73 sqldf on SO ## 10.85 0.10 10.99 ffdf
我最初没有看到这个问题,并在几天后问了一个类似的问题.我将把我之前的问题记下来,但我想我会在这里添加一个答案来解释我以前是怎么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'))
奇怪的是,多年来没有人回答问题的底部部分,即使这是一个重要的部分 - data.frame
只是具有正确属性的列表,所以如果你有大数据,你不想使用as.data.frame
或类似的列表.简单地将列表"转"为就地数据框要快得多:
attr(df, "row.names") <- .set_row_names(length(df[[1]])) class(df) <- "data.frame"
这不会使数据副本立即生成(与所有其他方法不同).它假定您已经相应地设置names()
了列表.
[至于将大数据加载到R中 - 我个人将它们按列转储到二进制文件中并使用readBin()
- 这是迄今为止最快的方法(除了映射)并且仅受磁盘速度的限制.与二进制数据相比,解析ASCII文件本质上很慢(即使在C中).
这是之前在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)
这里的基本思想是将数据导入到块中.您甚至可以使用其中一个并行框架(例如雪)并通过分割文件并行运行数据导入,但最有可能的是大型数据集无法帮助,因为您将遇到内存限制,这就是为什么map-reduce是一种更好的方法.
另一种方法是使用该vroom
程序包。现在在CRAN上。
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
还有一点值得一提.如果您有一个非常大的文件,您可以动态计算使用的行数(如果没有标题)(bedGraph
您的工作目录中文件的名称):
>numRow=as.integer(system(paste("wc -l", bedGraph, "| sed 's/[^0-9.]*\\([0-9.]*\\).*/\\1/'"), intern=T))
然后你可以使用read.csv
,read.table
...
>system.time((BG=read.table(bedGraph, nrows=numRow, col.names=c('chr', 'start', 'end', 'score'),colClasses=c('character', rep('integer',3))))) user system elapsed 25.877 0.887 26.752 >object.size(BG) 203949432 bytes