我有一个非常大的表(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)))
一个小的附加点值得一提。如果你有一个非常大的文件,你可以在运行中计算行数(如果没有头文件)使用(其中bedGraph是你的文件在你的工作目录中的名称):
>numRow=as.integer(system(paste("wc -l", bedGraph, "| sed 's/[^0-9.]*\\([0-9.]*\\).*/\\1/'"), intern=T))
你可以在read。csv, read中使用。表格
>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
奇怪的是,多年来一直没有人回答这个问题的底部,尽管这是一个很重要的问题——data.frames只是具有正确属性的列表,所以如果你有大量的数据,你不想使用as.data.frame或类似的列表。简单地将列表就地“转换”为数据帧要快得多:
attr(df, "row.names") <- .set_row_names(length(df[[1]]))
class(df) <- "data.frame"
这不会复制数据,所以它是即时的(不像所有其他方法)。它假设您已经相应地在列表中设置了names()。
[至于将大数据加载到R中——就我个人而言,我将它们按列转储到二进制文件中,并使用readBin()——这是迄今为止最快的方法(除了映射),并且只受磁盘速度的限制。与二进制数据相比,解析ASCII文件本质上是缓慢的(即使是在C语言中)。
我想以最简单的形式贡献基于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。即使有可能将这些数据“锤击”到单个数据框架中,但这感觉还是不对。在部署模型时,该对象可能难以使用并产生问题,等等。
这个问题之前在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是一种更好的方法。
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)