追加多个大data.table;使用 colClasses 和 fread 的自定义数据强制;命名管道

Posted

技术标签:

【中文标题】追加多个大data.table;使用 colClasses 和 fread 的自定义数据强制;命名管道【英文标题】:append multiple large data.table's; custom data coercion using colClasses and fread; named pipes 【发布时间】:2014-02-08 19:40:58 【问题描述】:

[这是一篇文章中的多个错误报告/功能请求,但它们不一定是孤立的。提前为怪物帖子道歉。按照帮助(data.table)的建议在此处发布。另外,我是 R 新手;如果我没有在下面的代码中遵循最佳实践,请道歉。我在努力。]

1。 rbindlist 在 6 * 8GB 文件上崩溃(我有 128GB 内存)

首先我想报告使用 rbindlist 附加大型 data.tables 会导致 R 出现段错误(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)

崩溃消息:

 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))

有 6 个文件,每个大约 8 GiB 或 1 亿行长,包含 8 个变量,制表符分隔。

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

无论如何,我认为这里更好的方法是允许 fread 将文件作为文件名的向量:

files <- c("my", "files", "to be", "appended")
dt <- fread(files)

据推测,您可以在幕后节省更多的内存,而不必同时保留所有这些对象,因为作为 R 的用户似乎是必要的。

3。 colClasses 给出错误信息

我的第二个问题是我需要为我的一种数据类型指定a custom coercion handler,但这失败了:

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

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

    dt[,date := as.Date(as.character(date), format="%Y%m%d")]

有效。

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

现在,我可以通过一些 system() 调用来附加文件并通过一些 sed 魔法(此处简化)(其中 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)

但这涉及一个额外的读/写周期。我有 4 TiB 的数据要处理,这是很多额外的读写(不,不是全部到一个 data.table。大约 1000 个。)

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

我通常离开 wait=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

无论如何,这有点小题大做。

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

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

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

dt <- fread(files, colClasses=list(price="Int_Price"))

然后我会得到一个很好的长 data.table 与正确强制数据。

【问题讨论】:

太棒了!感谢您花时间写下这些要点。如果您可以将它们提交到data.table project page,那将会更有帮助。向下滚动以获取指向 bugsfeature requests 的链接。在 bugs 上,除非我们有一个可重现的示例,否则很难对此做任何事情。由于违反 SO 政策,这些许多问题都不太可能得到回答(甚至关闭)。 您应该将这些作为单独的功能请求 (FR)/错误提交,即使它们在您看来是集体的。 【参考方案1】:

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

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


如 cmets 中所述,您应该分别提出不同的问题。不过既然都是这么好的点,最后连成一个愿望,ok,就一口气回答吧。

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

请换行再次运行:

moves.by.year <- lapply(files, fread)

moves.by.year <- lapply(files, fread, verbose=TRUE)

并将输出发送给我。我认为这不是文件的大小,而是类型和内容。没错,freadrbindlist 在 128GB 盒子上加载 48GB 数据应该没有问题。正如你所说,lapply 应该返回 48GB,然后rbindlist 应该创建一个新的 48GB 单表。这应该适用于您的 128GB 机器,因为 96GB data.table 还没有赶上 R3 中的长向量支持,否则 > 2^31 行就可以了,也)。

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

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

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

3. colClasses 给出错误消息

当键入list 时,类型出现在= 的左侧,列名或位置的向量出现在右侧。这个想法比read.csv 中的colClasses 更容易,它只接受一个向量;以节省一遍又一遍地重复"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

正确的语法是

fread(tfile, colClasses=list(myDate="date"))

鉴于您在问题中继续说的话,iiuc,您实际上想要:

fread(tfile, colClasses=list(character="date"))  # just fread accepts list

fread(tfile, colClasses=c("date"="character"))   # both read.csv and fread

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

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

如果前面的问题得到解决,希望这种情况现在消失吗? fread 通过内存映射其输入来工作。它可以接受非文件,例如 http 地址和连接 (tbc),为了方便起见,它首先要做的是将完整的输入写入 ramdisk,以便它可以从那里映射输入。 fread 速度快的原因在于首先查看整个输入。

【讨论】:

rbindlist bug filed。 fread request updated 感谢您的帮助。我应该注意到 read.table/csv 实际上确实接受了 colClasses 的列表。例如。传递 colClasses=list(integer_var="character") 工作得很好。我不得不说 fread 对这种行为的偏离令人惊讶。 fread doesn't handle custom classes in colClasses properly @James 谢谢,会看看。不过,colClasses=list(...) 没有记录在 ?read.csv 中。我的阅读是 colClasses 应该是一个字符向量。如果您传入 list 并且它正在工作,那么这是幸运的,并且不能保证将来会工作(我假设它当前正在将列表转换为字符向量)。您看到data.table 试图提供的fread(,colClasses=list(character=150:200)) 的优势了吗?还有其他方法吗? 我确实看到了宽数据的优势,即只需指定每种类型一次。也就是说,由于新列排序可能引入的错误,我尽量避免在固定宽度数据中引用列号。也就是说,我只是对差异感到惊讶,并认为这是一个错误。但你是对的。现在我仔细查看了 read.table,它并没有准确记录我所看到的行为。也就是说,我更担心我无法为列指定自定义处理程序(请参阅我之前的评论)。这确实限制了我现在读取大型数据集的能力。

以上是关于追加多个大data.table;使用 colClasses 和 fread 的自定义数据强制;命名管道的主要内容,如果未能解决你的问题,请参考以下文章

使用 data.table (with fread) 快速读取和组合多个文件

使用带有多个语句的 data.table 进行条件过滤

在 data.table 中分配多个 lapplys?

使用 data.table 存储为列表元素的多个数据帧的完全外连接

使用 data.table 包滚动平均值到 R 中的多个变量

使用“data.table”从重复行中选择非“NA”值——当有多个分组变量时