针对稀疏矩阵交换优化的文件格式

时间:2018-02-13 16:08:44

标签: python r matlab format sparse-matrix

我想将一个稀疏的数字矩阵(整数,但它可以是浮点数)保存到文件中进行数据交换。对于稀疏矩阵,我指的是一个矩阵,其中高百分比的值(通常为90%)等于0.稀疏在这种情况下与文件格式无关,而与矩阵的实际内容有关。

矩阵的格式如下:

        col1   col2   ....
row1  int1_1 int1_2   ....
row2  int2_1   ....   ....
....  ....     ....   .... 

通过使用文本文件(制表符分隔),文件大小为4.2G。我可以使用哪种文件格式(最好是无处不在的文件格式,例如.txt文件)来轻松加载和保存这种稀疏数据矩阵?我们通常使用Python / R / Matlab,因此首选支持这些格式。

2 个答案:

答案 0 :(得分:1)

你有几个解决方案,但通常你需要做的就是输出非零元素的索引以及值。让我们假设您要导出到单个文本文件。

生成数组

让我们首先生成一个10000 x 5000的稀疏数组,填充约10%(由于复制的索引,它会少一点):

N = 10000; 
M = 5000; 
rho = .1; 
rN = ceil(sqrt(rho)*N);
rM = ceil(sqrt(rho)*M);
S = sparse(N, M); 
S(randi(N, [rN 1]), randi(M, [rM 1])) = randi(255, rN, rM);

如果您的数组未存储为稀疏数组,则可以使用(其中M是完整数组)创建它:

S = sparse(M);

另存为文本文件

现在我们将以下列格式保存矩阵 row_indx col_indx值 row_indx col_indx值 row_indx col_indx值

这是通过提取行索引和列索引以及数据值然后将其保存到循环中的文本文件来完成的:

[n, m, s] = find(S);
fid = fopen('Sparse.txt', 'wt');
arrayfun(@(n, m, s) fprintf(fid, '%d\t%d\t%d\n', n, m, s), n, m, s);
fclose(fid);

如果基础数据不是整数,那么您可以在最后一个输出上使用%f标志,例如(以小数点后15位保存)

arrayfun(@(n, m, s) fprintf(fid, '%d\t%d\t%.15f\n', n, m, s), n, m, s);

将此与完整数组进行比较:

fid = fopen('Full.txt', 'wt'); 
arrayfun(@(n) fprintf(fid, '%s\n', num2str(S(n, :))), (1:N).'); 
fclose(fid);

在这种情况下,稀疏文件大约为50MB,完整文件大约为170MB,表示效率为3。这是预期的,因为我需要为数组的每个非零元素保存3个数字,并且〜10%的数组被填充,与完整数组相比,需要保存的数量大约为30%。

对于浮点格式,由于索引的大小与浮点值相比要小得多,因此保存更大。

在Matlab中,提取数据的一种快捷方法是保存由以下字符串给出的字符串:

mat2str(S)

这基本上是相同的但是将它包装在稀疏命令中以便在Matlab中轻松加载 - 需要在其他语言中解析它以便能够读取它。该命令告诉你如何重新创建数组,暗示你也可能需要在文件中存储矩阵的大小(我建议在第一行中进行,因为你可以读取它并在解析文件的其余部分之前创建稀疏矩阵。

另存为二进制文件

一种更有效的方法是保存为二进制文件。假设数据和索引可以存储为无符号16位整数,您可以执行以下操作:

[n, m, s] = find(S);
fid = fopen('Sparse.dat', 'w');
fwrite(fid, size(S), 'uint16');
fwrite(fid, [n m s], 'uint16');
fclose(fid);

然后阅读数据:

fid = fopen('Sparse.dat', 'r');
sz = fread(fid, 2, 'uint16');
s = reshape(fread(fid, 'uint16'), [], 3);
s = sparse(s(:, 1), s(:, 2), s(:, 3), sz(1), sz(2));
fclose(fid);

现在我们可以检查它们是否相等:

isequal(S, s)

保存完整数组:

fid = fopen('Full.dat', 'w');
fwrite(fid, full(S), 'uint16');
fclose(fid);

比较稀疏和完整文件大小,我得到21MB和95MB。

几点说明:

  1. 使用单个写/读命令比循环更快(更多),因此最后一种方法是最快的,也是最节省空间的。
  2. 可以保存为二进制整数的最大索引/数据值大小为2 ^ n - 1,其中n是bitdepth。在我的16位(uint16)示例中,它对应于0..65,535的范围。通过它的声音,您可能需要使用32位甚至64位来存储索引。
  3. 通过将索引保存为一种数据类型(例如uint32)而将实际值保存为另一种数据类型(例如uint8),可以获得更高的效率。但是,这增加了保存和阅读的复杂性。
  4. 您仍然希望首先存储矩阵大小,正如我在二进制示例中所示。
  5. 如果需要,您可以将值存储为双精度数,但索引应始终为整数。同样,额外的复杂性,但可行。

答案 1 :(得分:0)

我找到了Feather格式(目前不支持Matlab,afaik)。

this section中提供了有关Pandas中读写和内存性能的一些比较。

它还提供对Julia语言的支持。

编辑:

我发现在我的情况下这种格式使用的磁盘空间比.txt更多,这可能会提高I / O的性能。压缩压缩缓解了问题,但在写作期间压缩seems to not be supported yet