与HDF5或netCDF相比,使用.Rdata文件的缺点是什么?

27

我被要求更改一款软件,该软件目前导出的是 .Rdata 文件,现在需要导出一个“平台无关二进制格式”,例如HDF5或netCDF。给出了两个原因:

  1. .Rdata 文件只能被 R 读取
  2. 根据操作系统或架构不同,二进制信息的存储方式也会不同

我还发现,“R数据导入导出手册”没有讨论 .Rdata 文件,但它确实讨论了 HDF5 和 netCDF。

一个R-help 讨论表明,.Rdata 文件是平台无关的。

问题:

  1. 这些担忧在多大程度上是合理的?
    • 例如,Matlab 是否可以在不调用 R 的情况下读取 .Rdata 文件?
  2. 其他格式是否比 .Rdata 文件更有用?
  3. 是否可能编写脚本,创建所有 .Rdata 文件的 .hdf5 类似物,最小化对程序本身的更改?

你无法存储所有类型的R对象,就像.Rdata可以做到的那样。如果只是值的数组和通常的维度元数据,那么应该没有问题,但这仍然不会是一对一的替换。你真的是指所有的.Rdata文件吗? - mdsumner
@mdsumner 你说得很对。我有一些对象是列表和列表的数据框或特殊类别对象(如函数、mcmc.objects)的列表,它们在其他应用程序中可能具有更多或更少的效用。我可能会将函数保留为Rdata格式,或者我可以将它们打印为文本文件以供文档目的。mcmc.objects 我可以先转换。 - David LeBauer
2
这真的取决于你的受众。除了通常用于统计计算之外,我对R并不了解太多,但如果你要导出地理空间和/或科学数据,你的客户可能会想要NetCDF。 - Jordan Bentley
4个回答

28

以下是各种回答:

  1. 选项丰富 首先,这个问题是合理的,但你列出的选择范围比应该更窄。HDF5/netCDF4 是一个很好的选择,可以很好地与Python、Matlab和许多其他系统配合使用。HDF5 在很多方面都优于 Python 的 pickle 存储 - 查看 PyTables ,很可能会看到它的速度提升效果很好。Matlab 曾经(现在也可能)有一些关于如何存储大型单元格(或者也可能是结构体)数组的问题,存储在 HDF5 中速度非常慢。这不是不能做到,而是非常慢。这是 Matlab 的问题,不是 HDF5 的问题。虽然这些都是很好的选择,但你还可以考虑 HDF5 是否足够:如果你有一些非常大的文件,并且可以从专有编码中获益,无论是为了加快访问速度还是压缩。在任何语言中进行原始二进制存储并设计类似于 bigmemory 的文件存储是很容易的 - 你甚至可以在其他语言中使用 bigmemory 文件,这是一个非常简单的格式。HDF5 当然是一个很好的起点,但是在处理非常大的数据集时,尤其需要注意,没有一个通用的数据存储和访问解决方案。(对于较小的数据集,你还可以查看协议缓冲区或其他序列化格式;Dirk 为在 R 中使用这些格式创建了 RProtoBuf。)有关压缩,请参见下一个建议。

  2. 大小 正如 Dirk 所提到的,文件格式可以描述为应用程序中立和应用程序相关。另一个轴是与领域无关(或领域不知情)或领域相关(领域智能 ;-)) 存储。如果你对数据的产生方式有一定的了解,特别是任何可用于压缩的信息,那么你可能能够构建比标准压缩器更好的格式。这需要一些工作。与 gzip 和 bzip 不同的备选压缩器也允许你分析大量的数据并开发适当的压缩“字典”,这样你就可以获得比 .Rdat 文件更好的压缩效果。对于许多种数据集,存储表格中不同行之间的差异是一个更好的选择 - 这可以导致更大的可压缩性(例如,可能会出现很多 0),但只有你知道这是否适用于你的数据。

  3. 速度和访问 .Rdat 不支持随机访问。它没有内置的并行 I/O 支持(虽然您可以将其序列化到并行 I/O 存储中,如果您希望)。这里有许多事情可以做来改善速度和访问,但是不断地在 .Rdat 上粘贴东西而不是切换到不同的存储机制并消除速度和访问问题是千刀万剐的。这不仅是 HDF5 的优势:我经常使用多核函数来并行化其他 I/O 方法,例如 bigmemory

  4. 更新能力 R 没有很好的方法来添加对象到 .Rdat 文件中。据我所知,它没有提供任何“查看器”来允许用户可视检查或搜索一组 .Rdat 文件。据我所知,它也没有为文件中的对象提供内置版本记录-保留方式。(我通过文件中的单独对象来记录生成对象的脚本的版本,但我将在未来的迭代中将其外包给 SQLite。)HDF5 具有所有这些功能。(此外,随机访问会影响数据的更新-.Rdat 文件,您必须保存整个对象。)

  5. 社区支持 尽管我主张使用自己的格式,但对于极端数据大小而言。在许多语言中构建库非常有助于减少交换数据的摩擦。对于大多数简单数据集(并且简单仍然在大多数情况下意味着“相当复杂”)或中等到相当大的数据集,HDF5 是一个很好的格式。当然,在专用系统上可以打败它。尽管如此,这是一个不错的标准,将意味着较少的组织努力将花费在支持专有或应用程序特定格式上。我曾经看到组织在超过生成数据的应用程序使用的格式多年后仍然坚持该格式,只是因为已经编写了大量代码以在该应用程序的格式中加载和保存,并且已经以其格式存储了 GB 或 TB 的数据(这可能会发生在您和 R 的某一天,但这源于一个以字母“S”开头以字母“S”结尾的不同统计套件)。这是未来工作的严重摩擦。如果您使用广泛的标准格式,则可以更轻松地在其与其他广泛标准之间移植:很可能其他人也决定解决相同的问题。试试吧-如果您现在进行转换器但实际上不进行转换以供使用,那么至少您已经创建了一个工具,其他人可以在必要时接手并使用它移动到另一种数据格式。

  6. 内存 使用.Rdat文件时,您必须loadattach它才能访问对象。大多数情况下,人们会load这个文件。好吧,如果这个文件非常大,就会占用很多内存。因此,要么更聪明地使用attach,要么将对象分为多个文件。对于访问对象的小部分来说,这是相当麻烦的。为此,我使用内存映射。HDF5允许随机访问文件的部分,因此您不需要加载所有数据才能访问其中的一小部分。这只是事情工作方式的一部分。因此,即使在R中,也有比.Rdat文件更好的选择。

  7. 转换脚本至于你关于编写脚本的问题 - 是的,你可以编写一个脚本,将对象加载并保存到HDF5中。但是,除非你对即将创建的内容有很好的理解,否则不建议在巨大的异构文件集上执行此操作。对于我的数据集,我无法设计出这样的脚本:里面有太多独特的对象,创建一个大型的HDF5文件库是荒谬的。最好把它想象成启动一个数据库:你将存储什么、如何存储以及它将如何表示和访问?

一旦您制定好数据转换计划,就可以使用诸如Hadoop或基本的多核功能等工具来释放您的转换程序,并尽快完成此操作。

简而言之,即使您留在R中,也最好查看其他可能的存储格式,特别是对于大型不断增长的数据集。如果您必须与其他人共享数据,或者至少提供读取或写入访问权限,则非常建议使用其他格式。没有理由花费时间维护其他语言的读取器/编写器 - 这只是数据而不是代码。:) 将您的代码集中在如何以合理的方式操作数据上,而不是花时间在存储上 - 其他人已经做得很好了。


很好的详细回答。我必须指出,Protocol Buffers并不是作为一种存储机制而发明的,而是用于在不同系统(和语言)之间进行通信。此外,RProtoBuf的大写方式不同。 :) - Dirk Eddelbuettel
@DirkEddelbuettel 哎呀,现在每个人都会得到错误了。 :) 你对存储问题的看法是正确的——我意识到这对于小对象尤其好,特别是那些可能自然地在程序、机器之间传递的对象。 - Iterator
@Iterator 我不知道你可以在.RData文件中使用attach。你能否提供更多信息(或指向一些信息的指针)?你的答案似乎表明这不会通过预先加载来使用RAM。当我尝试这样做时,虽然我没有看到RAM使用量的激增,但我的R会话变得相当不响应。它是在附加之前扫描整个文件吗? - Daniel Kessler

9

(二进制) 文件格式有两种基本类型:

  • 应用程序中立,由公共库和API支持(netCDF和HDF5都属于这一类),可以通过使用API的附加包在不同的程序和应用程序之间方便地交换数据

  • 应用程序特定,只设计为与一个程序一起使用,尽管更有效:这就是.RData所做的。

因为R是开源的,您可以从Matlab文件重新创建RData格式:没有任何阻止您编写适当的mex文件的技术原因。也许有人已经这样做了。如果两个应用程序都能很好地支持该格式,则另一条路线可能更容易。

值得一提的是,在20世纪90年代初/中期,我编写了自己的C代码,以将模拟文件写入Octave使用的二进制格式(然后我用它来切片数据)。能够使用开源软件做到这一点是一个大优势。


1
感谢您澄清这个区别,现在我明白了。 - David LeBauer
没有提到 Rprotobuf?别担心,我已经为你安利了。 :) - Iterator

5
我觉得我可以回答其中的一些问题,但不是全部。
  1. 任何人只要下定决心,可能直接读取 .Rdata 文件,但这很费力且好处不多。因此我怀疑 Matlab 是否已经这样做了。你可能记得,R 能够精确读取各种其他系统格式,正是因为有人花了很多功夫这样做。

  2. 对于文本格式,csv 看起来相当“标准”,但对于二进制格式,我不知道,而且 csv 不是一个好的标准——特别是日期和引号的处理方式差异极大(当然,它仅适用于数据表)。

  3. 当然可以!

例如:

for(f in list.files(".", pattern="\\.Rdata$") {
    e <- new.env()
    load(f, e)       # load all values into environment e
    x <- as.list(e)

    #saveInOtherFormat(x, file=sub("\\.Rdata$", ".Other", f))
}

2
在这里,您可能希望检查加载的对象是否属于可以成功保存在新格式中的类型,并进行排除/警告。 - Ben Bolker
对于二进制格式,可以看一下由一些偶尔出现在SO上的人开发的协议缓冲区和Rprotobuf。 :) - Iterator

4
第二点是错误的:二进制 .RData 文件在硬件和操作系统平台之间是可移植的。引用 ?save 帮助页面的语句如下:
所有 R 平台在二进制 save-d 文件中使用 C 整数和双精度浮点数的 XDR(大端)表示,这些文件在所有 R 平台上都可以移植。
第一点取决于数据本身以及其他程序对数据可能有哪些有用的应用。如果您的代码库使用 save() 来写入指定的数据框或矩阵对象,您可以轻松编写一个小函数 save2hdf() 将它们写成 hdf 或 ncdf 二进制文件,然后使用 sed 在代码库中将所有出现的 save( 更改为 save2hdf(。至少 ncdf 会在读取时有性能损失,但不会太严重。如果您的代码使用保存了异构对象列表等对象,则可能需要大量重新编码才能将其写出为单独的组件对象。
还要注意,在 R 中,netCDF 4 仍然存在问题。

+1 感谢澄清。字符向量可能会更加棘手(编码等等),但我想NetCDF/HDF也无法处理它们... - Ben Bolker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接