【问题标题】:Concatenate long list of big matrices efficiently有效地连接一长串大矩阵
【发布时间】:2018-02-10 12:47:36
【问题描述】:

我有一个 4 Gb 的 csv 文件要加载到我的 16Gb 机器中,freadread.csv 无法立即加载它,它们返回内存错误。

所以我决定逐块读取文件,它工作了(大约一小时后),如果我信任 RStudio 中的环境选项卡和 1.2保存为 RDS 时为 Gb。

我现在遇到的问题是将所有内容连接回一个大的data.frame。据我了解rbindlist 是最有效的解决方案(或者是bind_rows?),但在我的情况下它仍然使用太多内存。

我想我可以通过在n 的列表项n 上使用rbindlist 来解决这个问题,然后递归直到我得到最终列表。这个n 数字必须手动校准,而且这个过程真的很难看(除了这个烦人的 csv 导入之外)。

我想到的另一个想法是找到一种方法,从我加载的数据中提供一个 SQLite 数据库,然后从 R 中查询它(我只会在数据)。

我能做得比这更好吗?

我的数据仅由 integerdouble 组成,如果有影响的话。

【问题讨论】:

  • 如果你的数据只是数字,为什么要构造一个data.frame(为什么不是矩阵?/稀疏矩阵)
  • 将数据放入数据库
  • @docendo-discimus 我会很高兴如果我得到一个大矩阵,虽然在这一点上我认为将它转换为 data.frame 不会花费太多@
  • 我会选择 sqlite。阅读这个问题的第一个答案:stackoverflow.com/questions/4332976/…
  • 是的,bigmemory 很棒...不幸的是,有一段时间它不适用于 windows,所以我停止使用它(我必须在我的公司使用 windows)...但是现在看来它又可以在 MS OS 上使用了,很高兴知道

标签: r rbindlist large-data


【解决方案1】:

根据 cmets 中的提示,我最终检查了这个 solution,尽管我最终可能最终会接受 @CPak 的解决方案(我会及时编辑这篇文章并提供最终信息)。

对于我的具体情况,我以这种方式使用它,首先创建数据库并用我的表提供它:

library(RSQLite)
library(dplyr)
# define dbpath (ending with ".SQLite" to be clean), my_table_name, csv_path
db <- dbConnect(SQLite(), dbname = dbpath) # will create databse if it doesn't exist, and a connection
dbWriteTable(conn=db, name=my_table_name, value=csv_path, row.names=FALSE, header=TRUE) # creates table in DB
dbDisconnect(db) 

然后访问它:

db    <- dbConnect(SQLite(), dbname= dbpath) # creates a connection to db
my_table <- tbl(db, my_table_name)

然后 my_table 的行为很像data.frame,我认为有一些限制,但对于基本操作来说它工作得很好。

创建的数据库与csv 源的大小大致相同,因此比RDS 文件大4 倍左右,但最大的优势是不需要将其加载到内存中。

编辑:readr::read_csv_chunkedchunked::read_csv_chunkwise 可能值得调查

【讨论】:

    【解决方案2】:

    听起来像 bigmemory 的功能可能刚好能解决您的问题

    require(bigmemory)
    

    读取文件

    您可以使用big.matrix 读取文件

    read.big.matrix(filename, sep = ",", header = FALSE, col.names = NULL,
        row.names = NULL, has.row.names = FALSE, ignore.row.names = FALSE,
        type = NA, skip = 0, separated = FALSE, backingfile = NULL,
        backingpath = NULL, descriptorfile = NULL, binarydescriptor = FALSE,
        extraCols = NULL, shared = TRUE)
    

    节省内存

    即使是像iris 这样的简单示例,您也可以看到内存节省

    x <- as.big.matrix(iris)
    options(bigmemory.allow.dimnames=TRUE)
    colnames(x) <- c("A", "B", "C", "D", "E")
    
    object.size(x)
    # 664 bytes
    
    object.size(iris)
    # 7088 bytes
    

    子集

    子集big.matrices 是有限的,但mwhich 提供了一些功能

    如果column 1 is &lt;= 5column 2 &lt;= 4 则为子集

    x[mwhich(x, 1:2, list(c(5), c(4)), list(c('le'), c('le')), 'AND'),]
    
    #       A   B   C   D E
    # 2   4.9 3.0 1.4 0.2 1
    # 3   4.7 3.2 1.3 0.2 1
    # 4   4.6 3.1 1.5 0.2 1
    # 5   5.0 3.6 1.4 0.2 1
    # etc
    

    注意子集运算的结果是一个正则矩阵。您可以使用 as.big.matrix() 将常规矩阵转换为 big.matrix

    最小值、最大值、平均值等

    biganalytics 通过big.matrices 提供更多功能

    require(biganalytics)
    
    colmin(x, cols = 1:2, na.rm = FALSE)
    #   A   B 
    # 4.3 2.0
    
    colmax(x, cols = 1:2, na.rm = FALSE)
    #   A   B 
    # 7.9 4.4 
    

    输出

    最后你可以输出为big.matrix

    write.big.matrix(...)
    

    【讨论】:

    • 这太好了,谢谢,我在一张较小的桌子上测试过,它看起来真的很快。我认为这可能是最好的解决方案,直到数据真的很大并且无法加载,或者我们需要更多的空闲 RAM,然后使用 SQLite 成为更好的选择。我现在正在我的大数据集上测试两种方式,并将添加带有RSQLite 的解决方案作为完整性的答案。
    • 我认为您在“节省内存”部分遗漏了一些东西,似乎任何 big.matrix 对象重 664 字节,请尝试:x &lt;- as.big.matrix(rbind(iris,iris,iris,iris,iris,iris,iris,iris,iris,iris,iris));object.size(x)。奇怪的是,当我保存 RDS 时,我只保存了 1kb。所以也许 bigmemory 只建立与文件的连接?制作这个 664 字节的变量仍然需要大约 2 个小时,所以我想某些数据一定在某个地方,我想保存它,但我不知道如何保存。
    • 是的,big.matrices 本质上是指向文件的指针。我没有意识到它总是相同的 object.size 但事后看来这是有道理的。基础数据本身是相同的……因此,如果要将完整矩阵保存为 RDS 文件,则需要将其转换为常规矩阵,而这与目的不符。 bigmemory 让您对数据进行惰性操作,因此如果您只需要数据的子集,它可以让您仅将子集(而不是完整矩阵)加载到内存中。
    • 那我需要测试readRDS是否比read.big.matrix快,如果是,我会在一轮read.big.matrix之后保存为RDS :)。
    猜你喜欢
    • 2018-04-16
    • 2012-09-02
    • 2013-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多