快速读取非常大的表作为数据框

ε祈祈猫儿з 提交于 2020-02-26 16:09:15

我有非常大的表(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楼

奇怪的是,即使这是很重要的问题,多年来也没有人回答问题的最底层部分data.frame只是具有正确属性的列表,因此,如果您有大量数据,则不想将其用作as.data.frame或类似的列表。 只需将列表“就地”转换为数据框就可以更快:

attr(df, "row.names") <- .set_row_names(length(df[[1]]))
class(df) <- "data.frame"

这不会复制数据,因此它是立即的(与所有其他方法不同)。 假定您已经在列表上设置了相应的names()

[关于将大数据加载到R中-个人而言,我将它们按列转储到二进制文件中并使用readBin() -这是迄今为止最快的方法(除了mmapping),并且仅受磁盘速度的限制。 与二进制数据相比,解析ASCII文件天生就很慢(甚至是C语言)。]


#2楼

下面是一个例子,它利用freaddata.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]

标准读表

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

优化的读取表

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.32

恐惧

require(data.table)
system.time(DT <- fread("test.csv"))                                  
 ##    user  system elapsed 
##    3.12    0.01    3.22

sqldf

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.73

ff / 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

#3楼

几年后的更新

这个答案很旧,R继续前进。 调整read.table使其运行得更快一点,这几乎没有什么好处。 您的选择是:

  1. 使用data.table fread将csv / tab分隔的文件中的数据直接导入R中。请参见mnel的答案

  2. readr使用read_table (自2015年4月起在CRAN上使用)。 这很像上面的fread 。 链接中的自述文件解释了这两个函数之间的区别( readr当前声称比data.table::fread “慢1.5-2倍”)。

  3. read.csv.rawiotools提供了用于快速读取CSV文件的第三个选项。

  4. 尝试在数据库而不是平面文件中存储尽可能多的数据。 (以及更好的永久存储介质,数据以二进制格式传递到R和从R read.csv.sql ,这是更快的。) sqldf包中的sqldf ,如JD Long的答案所述 ,将数据导入到临时文件中。 SQLite数据库,然后将其读入R。另请参见: RODBC程序包,以及DBI程序包页面的反向依赖部分。 MonetDB.R为您提供了一种伪装成数据帧的数据类型,但实际上是其下面的MonetDB,从而提高了性能。 使用其monetdb.read.csv函数导入数据。 dplyr允许您直接使用存储在几种类型的数据库中的数据。

  5. 以二进制格式存储数据对于提高性能也很有用。 使用saveRDS / readRDS (见下文),该h5rhdf5为HDF5格式,或包write_fst / read_fstfst包。


原始答案

无论您使用read.table还是scan,都有一些简单的方法可以尝试。

  1. 设置nrows = 数据中的记录数scan nmax )。

  2. 确保comment.char=""关闭注释的解释。

  3. 使用read.table colClasses显式定义每列的类。

  4. 设置multi.line=FALSE也可以提高扫描性能。

如果这些都不起作用,则使用性能分析软件包之一来确定哪些行使速度变慢。 也许您可以根据结果编写read.table的简化版本。

另一种选择是在将数据读入R之前对其进行过滤。

或者,如果问题在于您必须定期读取数据,则可以使用这些方法一次读取数据,然后使用以下方法将数据帧另存为二进制blob: save saveRDS ,那么下次您可以使用来更快地检索它 load readRDS


#4楼

以前曾在R-Help询问过此内容,因此值得回顾。

一种建议是使用readChar() ,然后使用strsplit()substr()对结果进行字符串处理。 您可以看到readChar涉及的逻辑远少于read.table。

我不知道这里是否存在内存问题,但是您可能还想看看HadoopStreaming软件包 。 这使用了HadoopHadoop是一个旨在处理大型数据集的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是更好的方法。


#5楼

我最初没有看到这个问题,几天后又问了类似的问题。 我将把上一个问题记下来,但是我想在这里添加一个答案,以解释我是如何使用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'))
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!