追加多个大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,那将会更有帮助。向下滚动以获取指向 bugs 和 feature 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)
并将输出发送给我。我认为这不是文件的大小,而是类型和内容。没错,fread
和 rbindlist
在 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 存储为列表元素的多个数据帧的完全外连接