如何将几个大型data.table对象附加到单个data.table中并快速导出到csv而不会耗尽内存?

Bri*_*n D 8 memory csv r sqldf data.table

对此的简单回答是"购买更多内存",但我希望得到更具建设性的答案,并在此过程中学到一些东西.

我运行Windows 7 64位,内存为8GB.

我有几个非常大的.csv.gz文件(大约450MB未压缩),它们具有我读入R并执行一些处理的完全相同的标题信息.然后,我需要将处理过的R对象组合成一个主对象,并在磁盘上写回.csv.

我在多组文件上执行相同的操作.例如,我有5个文件夹,每个文件夹中包含6个csv.gz文件.我需要最终得到5个主文件,每个文件夹一个.

我的代码如下所示:

for( loop through folders ){
    master.file = data.table()

    for ( loop through files ) {
        filename = list.files( ... )
        file = as.data.table ( read.csv( gzfile( filename ), stringsAsFactors = F ))
        gc()

        ...do some processing to file...

        # append file to the running master.file
        if ( nrow(master.file) == 0 ) {
            master.file = file
        } else {
            master.file = rbindlist( list( master.file, file) )
        }
        rm( file, filename )
        gc()
    }

    write.csv( master.file, unique master filename, row.names = FALSE )

    rm( master.file )
    gc()

}
Run Code Online (Sandbox Code Playgroud)

此代码不起作用.我cannot allocate memory在写出最终的csv之前得到了错误.我在运行此代码时正在观察资源监视器,并且不明白为什么要使用8GB的RAM来执行此处理.所有文件大小的总和大约是2.7GB,所以我期望R将使用的最大内存为2.7GB.但write.csv操作似乎使用与您正在编写的数据对象相同的内存量,因此如果您在内存中有一个2.7GB的对象并尝试将其写出来,那么您将使用5.6 GB的内存.

这个明显的现实,加上使用一个for循环,其中内存似乎没有得到充分释放似乎是问题.

我怀疑我可以使用这里这里sqldf提到的包但是当我将语句设置为等于R变量时,我最终得到了相同的内存不足错误.sqldf

Bri*_*n D 5

2013年12月23日更新 - 以下解决方案可在R中运行而不会耗尽内存(感谢@AnandaMahto).
这种方法的主要警告是,您必须绝对确保每次读入和写出的文件具有完全相同的标题列,或者R处理代码必须确保这一点,因为write.table确实如此不要为你检查.

for( loop through folders ){

    for ( loop through files ) {

        filename = list.files( ... )
        file = as.data.table ( read.csv( gzfile( filename ), stringsAsFactors = F ))
        gc()

        ...do some processing to file...

        # append file to the running master.file
        if ( first time through inner loop) {
            write.table(file, 
                        "masterfile.csv", 
                        sep = ",", 
                        dec = ".", 
                        qmethod = "double", 
                        row.names = "FALSE")
        } else {
            write.table(file,
                        "masterfile.csv",
                        sep = ",",
                        dec = ".",
                        qmethod = "double",
                        row.names = "FALSE",
                        append = "TRUE",
                        col.names = "FALSE")
        }
        rm( file, filename )
        gc()
    }
    gc()
}
Run Code Online (Sandbox Code Playgroud)

我的初步解决方案

for( loop through folders ){

    for ( loop through files ) {
        filename = list.files( ... )
        file = as.data.table ( read.csv( gzfile( filename ), stringsAsFactors = F ))
        gc()

        ...do some processing to file...

        #write out the file
        write.csv( file, ... )
        rm( file, filename )
        gc()
    }        
    gc()
}
Run Code Online (Sandbox Code Playgroud)

然后我下载并安装了GnuWin32的sed软件包,并使用Windows命令行工具附加文件,如下所示:

copy /b *common_pattern*.csv master_file.csv
Run Code Online (Sandbox Code Playgroud)

这会将名称中包含文本模式"common_pattern"的所有单个.csv文件,标题和所有文件一起附加在一起.

然后我使用sed.exe删除除第一个标题行以外的所有标题行,如下所示:

"c:\Program Files (x86)\GnuWin32\bin\sed.exe" -i 2,${/header_pattern/d;} master_file.csv
Run Code Online (Sandbox Code Playgroud)

-i告诉sed只是覆盖指定的文件(就地).

2,$告诉sed查看从第2行到最后一行的范围($)

{/header_pattern/d;}告诉sed查找范围内的所有行,其中包含文本"header_pattern"并d删除这些行为了

确保这样做我想要它要做的,我先打印出我打算删除的行.

"c:\Program Files (x86)\GnuWin32\bin\sed.exe" -n 2,${/header_pattern/p;} master_file.csv
Run Code Online (Sandbox Code Playgroud)

像魅力一样,我只是希望我能在R中做到这一切.

  • @hadley'实现一切'似乎有点强大.永远不能?你怎么测量`gc()`做了什么?你在那篇文章中提到"尽管你已经在其他地方读到了什么",但是没有详细说明 - 他们是不是比你更聪明,还是只是错了?我相信有不同级别的垃圾收集,每20个触发一次更深的垃圾(iirc) - 您是否意识到这一点?你完全确定你写的是什么吗? (5认同)
  • 使用手动`gc()`,@ hadley [This](https://dl.dropboxusercontent.com/u/74885015/test_gc_hadley.R)似乎运行得更快.刚刚在虚拟机上测试过(Win XP 32位,512MB RAM + 3GB交换文件).行数"fread"读取对于生成不同的时间非常重要.我不知道是由于R-还是特定于操作系统的东西.可能是我做错了(代码远非漂亮和高效)但看起来不像安慰剂.在低RAM,高交换文件场景中运行`gc()`可能是次优阈值? (2认同)
  • @Hadley如果你声称`?gc`的手册提出了微弱的声明,那么你就可以表明这一点.从我所看到的,你刚才同意调用`gc()`_可能会做某些事情_.这就是反对"强调"声明"调用gc()明确无法实现"所需的全部内容.既然你是一名科学家,我就在这种情况下挑战你的科学推理.你还没有回答我的问题,你是否知道不同级别的垃圾收集,并且每隔20(iirc)就会发生更深层次的收集.您是否有任何其他人支持您的索赔? (2认同)