追加多个大数据.表; 使用colClasses和fread进行自定义数据强制; 命名管道

Jam*_*mes 8 r append fread data.table

[这是一篇文章中的多个错误报告/功能请求,但它们并不一定是孤立的.事先向怪物发帖道歉.根据help(data.table)的建议在此处发布.另外,我是R的新手; 如果我没有遵循下面代码中的最佳实践,那么道歉.我尝试着.]

1. rbindlist崩溃6*8GB文件(我有128GB RAM)

首先,我想报告使用rbindlist追加大数据.表导致R到segfault(ubuntu 13.10,打包的R版本3.0.1-3ubuntu1,从CRAN中从R安装的data.table).机器有128 GiB的RAM; 所以,鉴于数据的大小,我不应该耗尽内存.

我的代码:

append.tables <- function(files) {
    moves.by.year <- lapply(files, fread)
    move <- rbindlist(moves.by.year)
    rm(moves.by.year)
    move[,week_end := as.Date(as.character(week_end), format="%Y%m%d")]
    return(move)
}
Run Code Online (Sandbox Code Playgroud)

崩溃消息:

 append.tables crashes with this:
> system.time(move <- append.tables(files))
 *** caught segfault ***
address 0x7f8e88dc1d10, cause 'memory not mapped'

Traceback:
 1: rbindlist(moves.by.year)
 2: append.tables(files)
 3: system.time(move <- append.tables(files))
Run Code Online (Sandbox Code Playgroud)

有6个文件,每个大约8 GiB或1亿行长,8个变量,标签分隔.

2.可以fread接受多个文件名吗?

在任何情况下,我认为这里更好的方法是允许fread将文件作为文件名的向量:

files <- c("my", "files", "to be", "appended")
dt <- fread(files)
Run Code Online (Sandbox Code Playgroud)

据推测,你可以在引擎盖下更加节省内存,而不必像R的用户那样同时保留所有这些对象.

3. colClasses给出错误信息

我的第二个问题是我需要为我的一种数据类型指定一个自定义强制处理程序,但是失败了:

dt <- fread(tfile, colClasses=list(date="myDate"))
Error in fread(tfile, colClasses = list(date = "myDate")) : 
  Column name 'myDate' in colClasses not found in data
Run Code Online (Sandbox Code Playgroud)

是的,在日期的情况下,一个简单的:

    dt[,date := as.Date(as.character(date), format="%Y%m%d")]
Run Code Online (Sandbox Code Playgroud)

作品.

但是,我有一个不同的用例,即从字符转换之前从其中一个数据列中删除小数点.这里的精度非常重要(因此我们需要使用整数类型),并且从double类型强制转换为整数会导致精度损失.

现在,我可以通过一些system()调用来解决这个问题,以附加文件并通过一些sed magic(这里简化)管道它们(其中tfile是另一个临时文件):

if (has_header) {
    tfile2 <- tempfile()
    system(paste("echo fakeline >>", tfile2))
    system(paste("head -q -n1", files[[1]], ">>", tfile2))
    system(paste("tail -q -n+2", tfile2, paste(files, collapse=" "),
                 " | sed 's/\\.//' >>", tfile), wait=wait)
    unlink(tfile2)
} else {
    system(paste("cat", paste(files, collapse=" "), ">>", tfile), wait=wait)
}
Run Code Online (Sandbox Code Playgroud)

但这涉及额外的读/写周期.我有4个TiB数据要处理,这是一个额外的读写(不,不是所有的数据.表.大约1000个.)

4. fread认为命名管道是空文件

我通常会等待= TRUE.但我试图通过使tfile成为命名管道system('mkfifo', tfile),设置wait = FALSE,然后运行fread(tfile)来查看是否可以避免额外的读/写周期.然而,fread抱怨管道是一个空文件:

system(paste("tail -q -n+2", tfile2, paste(files, collapse=" "),
             " | sed 's/\\.//' >>", tfile), wait=FALSE)
move <- fread(tfile)
Error in fread(tfile) : File is empty: /tmp/RtmpbxNI1L/file78a678dc1999
Run Code Online (Sandbox Code Playgroud)

无论如何,这有点像黑客.

如果我有我的愿望清单,简化代码

理想情况下,我可以做这样的事情:

setClass("Int_Price")
setAs("character", "Int_Price",
    function (from) {
        return(as.integer(gsub("\\.", "", from)))
    }
)

dt <- fread(files, colClasses=list(price="Int_Price"))
Run Code Online (Sandbox Code Playgroud)

然后我有一个很好的长期data.table与适当的强制数据.

Mat*_*wle 7

更新:rbindlist错误已在commit 1100 v1.8.11中修复.来自新闻:

o修复了> 250m行发生的罕见段错误(内存分配期间整数溢出); 关闭#5305.感谢Guenter J. Hitsch的报道.


正如评论中所提到的,你应该分别提出单独的问题.但是因为他们是如此优秀的点,并在最后联系到愿望,好的,将一气呵成.

1. rbindlist在6*8GB文件上崩溃(我有128GB RAM)

请再次运行更改行:

moves.by.year <- lapply(files, fread)
Run Code Online (Sandbox Code Playgroud)

moves.by.year <- lapply(files, fread, verbose=TRUE)
Run Code Online (Sandbox Code Playgroud)

并把输出发给我 我不认为它是文件的大小,而是关于类型和内容的东西.你是正确的,fread并且rbindlist应该没有问题加载数据的48GB您128GB盒.如你所说,lapply应该返回48GB,然后rbindlist应该创建一个新的48GB单表.这应该适用于你的128GB机器,因为96GB <128GB.1亿行*6是6亿行,远低于20亿行限制,所以应该data.table没问题(还没有赶上R3中的长矢量支持,否则> 2 ^ 31行也没问题).

2.可以fread接受多个文件名吗?

好主意.如你所说,fread然后可以扫描检测其类型的所有6个文件并计算总行数.然后直接为6亿行分配一次.这将不必要地通过48GB的RAM节省搅拌.它也可能在开始读取第一个文件之前检测到第5或第6个文件中的任何异常(例如),因此如果出现问题会更快地返回.

我将此作为功能请求提交并在此处发布链接.

3. colClasses给出了一条错误消息

键入时list,类型显示在左侧=,列名称或位置向量显示在右侧.这样做是为了更容易比colClassesread.csv只接受的载体; "character"一遍又一遍地保存重复.我本可以发誓这是更好的记录,?fread但似乎没有.我来看看.

所以,而不是

fread(tfile, colClasses=list(date="myDate"))
Error in fread(tfile, colClasses = list(date = "myDate")) : 
    Column name 'myDate' in colClasses not found in data
Run Code Online (Sandbox Code Playgroud)

正确的语法是

fread(tfile, colClasses=list(myDate="date"))
Run Code Online (Sandbox Code Playgroud)

鉴于你在问题中所说的,iiuc,你真的想要:

fread(tfile, colClasses=list(character="date"))  # just fread accepts list
Run Code Online (Sandbox Code Playgroud)

要么

fread(tfile, colClasses=c("date"="character"))   # both read.csv and fread
Run Code Online (Sandbox Code Playgroud)

其中任何一个都应该将名为"date"的列加载为字符,以便您可以在强制之前对其进行操作.如果真的只是日期,那么我仍然要自动实施这种强制.你提到精度numeric只是为了提醒你也integer64可以直接阅读fread.

4. fread认为命名管道是空文件

希望现在假设前一点已经解决,这种情况会消失吗?fread通过内存映射其输入.它可以接受非文件,例如http地址和连接(tbc),为方便起见它首先写的是将完整的输入写入ramdisk,以便它可以从那里映射输入.原因fread很快就是先看完整个输入.