比 Get["raggedmatrix.mx"] 更快的大数据导入方法?

10

有没有人可以建议一种替代方法来导入60个大约650 MByte的.mx文件列表中的几个GByte数字数据(以.mx形式)?

- 这里太大了 - 研究问题涉及到双倍于可用RAM(16)的大约34GB数据的简单统计操作。为了处理数据大小问题,我只是将事情分开,并使用Get / Clear策略进行运算。

它确实起作用,但调用Get ["bigfile.mx"]需要相当长的时间,所以我想知道在PostgreSQL或MySQL或人们用于大量数值数据的任何数据库中使用BLOB或其他是否更快。

所以我的问题真正是: 如何以最有效的方式处理Mathematica中真正大的数据集导入?

我还没有尝试过,但我认为DataBaseLink的SQLImport比Get ["bigfile.mx"]慢。

有人有经验分享吗?

(如果这不是非常具体的编程问题,对不起,但这确实可以帮助我继续在Mathematica中寻找最佳解决方案的那些耗时的工作中前进。)


1
这对你有帮助吗?(https://dev59.com/eWsz5IYBdhLWcg3w_NAD)另一个相关问题(https://dev59.com/rWsy5IYBdhLWcg3w5CBr)。 - user616736
@yoda 注意,Rolf正在使用Mathematica的本地二进制格式MX,在我的经验中,当使用Import/ReadList时比其他任何格式都要快。我不知道BinaryReadList... - Szabolcs
@RolfMertig,当您使用Get[]时,我假设您正在从本地驱动器读取?从非本地驱动器(如网络驱动器)读取可能会导致速度变慢。在我的环境中,这两种情况之间存在数量级的差异。 - Arnoud Buzing
@RolfMertig 我很感兴趣知道你最终采用了什么解决方案,因为我目前也面临着类似的问题。 - Szabolcs
我采用了基于 .mx 文件的方法,并仔细地在 Do 循环中清除大表达式。这是一个旧项目,我偶然又看了一眼,但不想花太多时间在上面。 - Rolf Mertig
显示剩余5条评论
2个回答

3

这是一个想法:

您说您有一个无序的矩阵,即不同长度的列表。我假设其中包含浮点数。

您可以将矩阵扁平化以获得单个长的压缩 1D 数组(如果需要,使用Developer`ToPackedArray进行打包),并单独存储子列表的起始索引。然后在导入数据后重新构建无序矩阵。


这里展示了在Mathematica内部(即在导入后)从巨大的扁平列表中提取子列表的速度非常快。

data = RandomReal[1, 10000000];

indexes = Union@RandomInteger[{1, 10000000}, 10000];    
ranges = #1 ;; (#2 - 1) & @@@ Partition[indexes, 2, 1];

data[[#]] & /@ ranges; // Timing

{0.093, Null}

或者存储一个子列表长度的序列,使用 Mr.Wizard 的 dynamicPartition 函数,它恰好可以做到这一点。我的观点是,在内核中以平面格式存储数据并对其进行分区将会增加可忽略的开销。


将打包数组作为 MX 文件导入速度非常快。我只有 2 GB 的内存,所以无法在非常大的文件上测试,但在我的机器上,打包数组的导入时间总是不到一秒钟。这将解决导入未打包数据可能会更慢的问题(尽管如我在主问题的评论中所说,我无法复制您提到的极端缓慢情况)。


如果 BinaryReadList 很快(它现在并不像读取 MX 文件那样快,但看起来 在 Mathematica 9 中它将显著加快速度),您可以将整个数据集存储为一个大型二进制文件,而无需将其分成单独的 MX 文件。然后,您可以像这样导入文件的相关部分:

首先创建一个测试文件:

In[3]:= f = OpenWrite["test.bin", BinaryFormat -> True]

In[4]:= BinaryWrite[f, RandomReal[1, 80000000], "Real64"]; // Timing
Out[4]= {9.547, Null}

In[5]:= Close[f]

打开它:
In[6]:= f = OpenRead["test.bin", BinaryFormat -> True]    

In[7]:= StreamPosition[f]

Out[7]= 0

跳过前五百万个条目:

In[8]:= SetStreamPosition[f, 5000000*8]

Out[8]= 40000000

阅读500万条记录:

In[9]:= BinaryReadList[f, "Real64", 5000000] // Length // Timing    
Out[9]= {0.609, 5000000}

阅读所有剩余条目:

In[10]:= BinaryReadList[f, "Real64"] // Length // Timing    
Out[10]= {7.782, 70000000}

In[11]:= Close[f]

(作为比较,Get通常在此处从MX文件中读取相同的数据,时间不到1.5秒。我用的是WinXP。)
编辑如果您愿意花时间,编写一些C代码,另一个想法是创建一个库函数(使用Library Link),将文件内存映射(Windows链接)并将其直接复制到一个MTensor对象中(MTensor只是一个紧凑的Mathematica数组,在库链接的C端看到)。

你试过我的dynamicPartition(或者只是核心的dynP)函数在工具包帖子中吗?我相信它应该比你提出的更快一点。如果是这样,你会包含一个链接吗? - Mr.Wizard
@Mr.Wizard 我在这里的观点仅仅是想表明,将数据以平面格式存储并在内核中进行分区不会增加明显的开销(而不是找到最佳的分区方式)。当然,我已经添加了链接。 - Szabolcs
@Mr.Wizard 为什么不是呢?你的函数完全做了我在这里展示的相同的事情!我只是指出这不是答案的重点(这只是一些想法,而不是完整的答案)。 - Szabolcs

1

我认为两种最好的方法是:

1)在*.mx文件上使用Get,

2)或者读取数据并将其保存在某些二进制格式中,然后编写一个LibraryLink代码,并通过它来读取这些内容。当然,这样做的缺点是您需要转换MX内容。但也许这是一个选项。

一般来说,使用MX文件的Get非常快。

确定这不是交换问题吗?

编辑1:您还可以编写导入转换器:tutorial/DevelopingAnImportConverter


这不是一个交换问题。问题在于我有比RAM容量更多的数据,因此我必须按顺序读取数据的部分,并且这需要多次完成,所以如果读取MX文件需要半分钟,那么这是可以察觉到的。事情确实“有效”,只是需要超过一天的CPU时间来完成所有操作(有一个外部优化循环),因此我正在考虑如何加快速度。 - Rolf Mertig
我能否使用LibraryLink代码读取一块数据并通过命令将其交换到磁盘上?目前,我需要多次获取/清除相同的MX文件,基本上我想加快速度。 - Rolf Mertig
我从未做过这个,所以我有点谨慎,但我认为这应该是可能的。 - user1054186
在开始使用C语言编码之前,我建议先确保文件读取是瓶颈所在,或许还可以进行优化调整。 - user1054186
@ruebenko 谢谢分享,我不知道我们可以编写自定义导入程序! - Szabolcs
@Szabolcs,是的,这很重要,因为存在数百万种格式,可以扩展导入/导出。 - user1054186

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