我有一个非常大的表(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)))
上面这些我都试过了,[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-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是一种更好的方法。
我想以最简单的形式贡献基于spark的解决方案:
# Test Data ---------------------------------------------------------------
set.seed(123)
bigdf <-
data.frame(
dim = sample(letters, replace = T, 4e7),
fact1 = rnorm(4e7),
fact2 = rnorm(4e7, 20, 50)
)
tmp_csv <- fs::file_temp(pattern = "big_df", ext = ".csv")
readr::write_csv(x = bigdf, file = tmp_csv)
# Spark -------------------------------------------------------------------
# Installing if needed
# sparklyr::spark_available_versions()
# sparklyr::spark_install()
library("sparklyr")
sc <- spark_connect(master = "local")
# Uploading CSV
system.time(tbl_big_df <- spark_read_csv(sc = sc, path = tmp_csv))
Spark生成了相当不错的结果:
>> system.time(tbl_big_df <- spark_read_csv(sc = sc, path = tmp_csv))
user system elapsed
0.278 0.034 11.747
这是在32GB内存的MacBook Pro上测试的。
讲话
Spark,通常不应该能够“赢得”针对速度优化的软件包。尽管如此,我还是想用Spark给出一个答案:
对于一些评论和回答,如果流程无法工作,使用Spark可能是一个可行的替代方案
从长远来看,将尽可能多的数据敲入data.frame可能会在以后出现问题,因为在该对象上尝试其他操作并达到体系结构的性能极限
我认为对于这样的问题,任务是处理1e7或更多行,应该考虑Spark。即使有可能将这些数据“锤击”到单个数据框架中,但这感觉还是不对。在部署模型时,该对象可能难以使用并产生问题,等等。
而不是传统的阅读。我觉得fread是一个更快的函数。
指定额外的属性,如只选择所需的列,指定colclasses和字符串作为因素,将减少导入文件的时间。
data_frame <- fread("filename.csv",sep=",",header=FALSE,stringsAsFactors=FALSE,select=c(1,4,5,6,7),colClasses=c("as.numeric","as.character","as.numeric","as.Date","as.Factor"))
奇怪的是,多年来一直没有人回答这个问题的底部,尽管这是一个很重要的问题——data.frames只是具有正确属性的列表,所以如果你有大量的数据,你不想使用as.data.frame或类似的列表。简单地将列表就地“转换”为数据帧要快得多:
attr(df, "row.names") <- .set_row_names(length(df[[1]]))
class(df) <- "data.frame"
这不会复制数据,所以它是即时的(不像所有其他方法)。它假设您已经相应地在列表中设置了names()。
[至于将大数据加载到R中——就我个人而言,我将它们按列转储到二进制文件中,并使用readBin()——这是迄今为止最快的方法(除了映射),并且只受磁盘速度的限制。与二进制数据相比,解析ASCII文件本质上是缓慢的(即使是在C语言中)。