使用 bigmemory 将 40 GB csv 文件读入 R

Posted

技术标签:

【中文标题】使用 bigmemory 将 40 GB csv 文件读入 R【英文标题】:Reading 40 GB csv file into R using bigmemory 【发布时间】:2013-03-10 02:15:39 【问题描述】:

这里的标题很容易解释,但我将详细说明如下。我目前解决这个问题的一些技术是基于this问题中提出的解决方案。但是,我面临着一些挑战和限制,所以我想知道是否有人可能会尝试解决这个问题。我正在尝试使用bigmemory 包解决问题,但我遇到了困难。

目前的约束:

使用具有 16 GB RAM 的 linux 服务器 40 GB CSV 的大小 行数:67,194,126,114

挑战

需要能够从 big.matrix 或等效数据结构中随机抽取较小的数据集(5-10 百万行)。 在解析为 big.matrix 或等效数据结构时,需要能够删除具有单个 NULL 实例的任何行。

到目前为止,结果并不好。显然,我在某些事情上失败了,或者我只是不太了解bigmemory documentation。所以,我想我会在这里问一下是否有人使用过

关于这条攻击线的任何提示、建议等?还是我应该换成别的东西?如果这个问题与之前的问题非常相似,我深表歉意,但我认为数据规模比之前的问题大 20 倍左右。谢谢!

【问题讨论】:

文件内容的样本怎么样? 你到底哪里失败了? .csv 文件中有哪些类型的数据——都是doubles、ints 还是其他? NULL 条目在文件中是如何表示的?有行/列名吗?而且,你试过什么?给定一个适当结构的 .csv,read.big.matrix 应该可以帮助您。 更多信息会很好,但为什么不将其导入 SQL,在那里做一些准备,然后将其加载到 R 中? 感谢您的建议。让我再看看我的数据,然后就我的问题与你们联系。 我建议查看 ff 包。您会将数据写入磁盘而不是内存。 【参考方案1】:

我不知道bigmemory,但为了满足您的挑战,您不需要读取文件。只需管道一些 bash/awk/sed/python/whatever 处理来执行您想要的步骤,即扔掉NULL 行并随机选择N 行,然后读入。

这是一个使用 awk 的示例(假设您希望从具有 1M 行的文件中获取 100 条随机行)。

read.csv(pipe('awk -F, \'BEGINsrand(); m = 100; length = 1000000;
                       !/NULL/if (rand() < m/(length - NR + 1)) 
                                 print; m--;
                                 if (m == 0) exit;
                              \' filename'
        )) -> df

我并不清楚您所说的 NULL 是什么意思,所以我使用了字面理解,但应该很容易修改它以满足您的需求。

【讨论】:

这实际上是一个非常好的答案,我曾经通过实施一个非常相似的解决方案解决了我的问题。谢谢你的回答。我会接受的。【参考方案2】:

这是一个纯 R 解决方案,可以解决从大型文本文件中采样的挑战;它还有一个额外的优点,就是随机抽取 n 个样本。虽然将行解析为字符向量并且速度相对较慢,但效率并不算太低。

我们从一个函数签名开始,我们在其中提供一个文件名、我们想要绘制的样本的大小、随机数生成器的种子(这样我们就可以重现我们的随机样本!)、一个指示是否有一个标题行,然后是一个“读取器”函数,我们将使用它来将样本解析为 R 看到的对象,包括读取器函数可能需要的附加参数 ...

fsample <-
    function(fname, n, seed, header=FALSE, ..., reader=read.csv)

该函数为随机数生成器提供种子,打开一个连接,并读入(可选)标题行

    set.seed(seed)
    con <- file(fname, open="r")
    hdr <- if (header) 
        readLines(con, 1L)
     else character()

下一步是读入一大块 n 行,初始化一个计数器,记录看到的总行数

    buf <- readLines(con, n)
    n_tot <- length(buf)

继续读取 n 行的块,当没有进一步的输入时停止

    repeat 
        txt <- readLines(con, n)
        if ((n_txt <- length(txt)) == 0L)
            break

对于每个块,绘制n_keep 行的样本,行数与当前块中总行的比例成正比。这可确保在文件中对行进行均匀采样。如果没有要保留的行,请移至下一个块。

        n_tot <- n_tot + n_txt
        n_keep <- rbinom(1, n_txt, n_txt / n_tot)
        if (n_keep == 0L)
            next

选择要保留的行和要替换的行,并更新缓冲区

        keep <- sample(n_txt, n_keep)
        drop <- sample(n, n_keep)
        buf[drop] <- txt[keep]
    

数据输入完成后,我们使用阅读器解析结果并返回结果

    reader(textConnection(c(hdr, buf), header=header, ...)

通过使用readBin 并按照 Simon Urbanek 在 R-devel mailing list 上的建议搜索换行符,该解决方案可以变得更高效,但更复杂一些。 这是完整的解决方案

fsample <-
    function(fname, n, seed, header=FALSE, ..., reader = read.csv)

    set.seed(seed)
    con <- file(fname, open="r")
    hdr <- if (header) 
        readLines(con, 1L)
     else character()

    buf <- readLines(con, n)
    n_tot <- length(buf)

    repeat 
        txt <- readLines(con, n)
        if ((n_txt <- length(txt)) == 0L)
            break

        n_tot <- n_tot + n_txt
        n_keep <- rbinom(1, n_txt, n_txt / n_tot)
        if (n_keep == 0L)
            next

        keep <- sample(n_txt, n_keep)
        drop <- sample(n, n_keep)
        buf[drop] <- txt[keep]
    

    reader(textConnection(c(hdr, buf)), header=header, ...)

【讨论】:

感谢您发布代码,并感谢您提供出色的文档。你能不能用readBin 给我指出和举例?谢谢!

以上是关于使用 bigmemory 将 40 GB csv 文件读入 R的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spark 和 Scala 清理大小约为 40GB 的 CSV/Dataframe

使用 Pandas 或其他方法比较大型 (~40GB) 文本数据

在 R 中处理大数据的有效方法

在python中计算30GB + csv文件中双引号外的新行数

多核 gzip 解压缩,将输出文件 (csv) 拆分为 1Gb/文件

如何有效且快速地将大型 (6 Gb) .csv 文件导入 R,而不会导致 R REPL 崩溃?