我有非常大的表(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楼
下面是一个例子,它利用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]
标准读表
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
使其运行得更快一点,这几乎没有什么好处。 您的选择是:
使用
data.table
fread
将csv / tab分隔的文件中的数据直接导入R中。请参见mnel的答案 。在
readr
使用read_table
(自2015年4月起在CRAN上使用)。 这很像上面的fread
。 链接中的自述文件解释了这两个函数之间的区别(readr
当前声称比data.table::fread
“慢1.5-2倍”)。read.csv.raw
的iotools
提供了用于快速读取CSV文件的第三个选项。尝试在数据库而不是平面文件中存储尽可能多的数据。 (以及更好的永久存储介质,数据以二进制格式传递到R和从R
read.csv.sql
,这是更快的。)sqldf
包中的sqldf
,如JD Long的答案所述 ,将数据导入到临时文件中。 SQLite数据库,然后将其读入R。另请参见:RODBC
程序包,以及DBI
程序包页面的反向依赖部分。MonetDB.R
为您提供了一种伪装成数据帧的数据类型,但实际上是其下面的MonetDB,从而提高了性能。 使用其monetdb.read.csv
函数导入数据。dplyr
允许您直接使用存储在几种类型的数据库中的数据。以二进制格式存储数据对于提高性能也很有用。 使用
saveRDS
/readRDS
(见下文),该h5
或rhdf5
为HDF5格式,或包write_fst
/read_fst
从fst
包。
原始答案
无论您使用read.table还是scan,都有一些简单的方法可以尝试。
设置
nrows
= 数据中的记录数 (scan
nmax
)。确保
comment.char=""
关闭注释的解释。使用
read.table
colClasses
显式定义每列的类。设置
multi.line=FALSE
也可以提高扫描性能。
如果这些都不起作用,则使用性能分析软件包之一来确定哪些行使速度变慢。 也许您可以根据结果编写read.table
的简化版本。
另一种选择是在将数据读入R之前对其进行过滤。
或者,如果问题在于您必须定期读取数据,则可以使用这些方法一次读取数据,然后使用以下方法将数据帧另存为二进制blob: save
saveRDS
,那么下次您可以使用来更快地检索它 load
readRDS
。
#4楼
一种建议是使用readChar()
,然后使用strsplit()
和substr()
对结果进行字符串处理。 您可以看到readChar涉及的逻辑远少于read.table。
我不知道这里是否存在内存问题,但是您可能还想看看HadoopStreaming软件包 。 这使用了Hadoop , 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是更好的方法。
#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'))
来源:oschina
链接:https://my.oschina.net/stackoom/blog/3159637