混合类型的Matlab数据结构 - 什么时间+空间效率?

时间:2013-04-26 19:04:19

标签: matlab performance data-structures large-data bigdata

我需要处理大量混合类型的表格数据 - 字符串和双精度数据。我想是一个标准问题。 Matlab中用于处理此问题的最佳数据结构是什么?

Cellarray绝对不是答案。它的内存效率极低。 (测试如下所示)。数据集(来自统计工具箱)的时间和空间非常低效。这留下了structarray或数组结构。我在下面对时间和内存的所有四个不同选项进行了测试,在我看来,数组的结构是我测试的东西的最佳选择。

我对Matlab相对较新,坦率地说这有点令人失望。无论如何 - 寻找关于我是否遗漏某些东西,或者我的测试是否准确/合理的建议。除了访问/转换/内存使用之外,我还缺少其他注意事项,因为我使用这些东西编写代码更多。 (我正在使用R2010b)

* * 测试#1:访问速度访问数据项。

cellarray:0.002s
dataset:36.665s       %<<< This is horrible
structarray:0.001s
struct of array:0.000s

* * 测试#2:转换速度和内存使用情况我从此测试中删除了数据集。

Cellarray(doubles)->matrix:d->m: 0.865s
Cellarray(mixed)->structarray:c->sc: 0.268s
Cellarray(doubles)->structarray:d->sd: 0.430s
Cellarray(mixed)->struct of arrays:c->sac: 0.361s
Cellarray(doubles)->struct of arrays:d->sad: 0.887s
  Name           Size               Bytes  Class     Attributes
    c         100000x10            68000000  cell                
    d         100000x10            68000000  cell                
    m         100000x10             8000000  double              
    sac            1x1             38001240  struct              
    sad            1x1              8001240  struct              
    sc        100000x1             68000640  struct              
    sd        100000x1             68000640  struct  

==================代码:TEST#1

  %% cellarray
  c = cell(100000,10);
  c(:,[1,3,5,7,9]) = num2cell(zeros(100000,5));
  c(:,[2,4,6,8,10]) = repmat( {'asdf'}, 100000, 5);
  cols = strcat('Var', strtrim(cellstr(num2str((1:10)'))))';
  te = tic;
  for iii=1:1000
      x = c(1234,5);
  end
  te = toc(te);
  fprintf('cellarray:%0.3fs\n', te);
  %% dataset
  ds = dataset( { c, cols{:} } );
  te = tic;
  for iii=1:1000
      x = ds(1234,5);
  end
  te = toc(te);
  fprintf('dataset:%0.3fs\n', te);
  %% structarray
  s = cell2struct( c, cols, 2 );
  te = tic;
  for iii=1:1000
      x = s(1234).Var5;
  end
  te = toc(te);
  fprintf('structarray:%0.3fs\n', te);
  %% struct of arrays
  for iii=1:numel(cols)
      if iii/2==floor(iii/2) % even => string
          sac.(cols{iii}) = c(:,iii);
      else
          sac.(cols{iii}) = cell2mat(c(:,iii));
      end
  end
  te = tic;
  for iii=1:1000
      x = sac.Var5(1234);
  end
  te = toc(te);
  fprintf('struct of array:%0.3fs\n', te);

==================代码:TEST#2

%% cellarray
% c - cellarray containing mixed type 
c = cell(100000,10);
c(:,[1,3,5,7,9]) = num2cell(zeros(100000,5));
c(:,[2,4,6,8,10]) = repmat( {'asdf'}, 100000, 5);
cols = strcat('Var', strtrim(cellstr(num2str((1:10)'))))';
% c - cellarray containing doubles only
d = num2cell( zeros( 100000, 10 ) );
%% matrix
% doubles only
te = tic;
m = cell2mat(d);
te = toc(te);
fprintf('Cellarray(doubles)->matrix:d->m: %0.3fs\n', te);
%% structarray
% mixed
te = tic;
sc = cell2struct( c, cols, 2 );
te = toc(te);
fprintf('Cellarray(mixed)->structarray:c->sc: %0.3fs\n', te);
% doubles
te = tic;
sd = cell2struct( d, cols, 2 );
te = toc(te);
fprintf('Cellarray(doubles)->structarray:d->sd: %0.3fs\n', te);
%% struct of arrays
% mixed
te = tic;
for iii=1:numel(cols)
    if iii/2==floor(iii/2) % even => string
        sac.(cols{iii}) = c(:,iii);
    else
        sac.(cols{iii}) = cell2mat(c(:,iii));
    end
end
te = toc(te);
fprintf('Cellarray(mixed)->struct of arrays:c->sac: %0.3fs\n', te);
% doubles
te = tic;
for iii=1:numel(cols)
    sad.(cols{iii}) = cell2mat(d(:,iii));
end
te = toc(te);
fprintf('Cellarray(doubles)->struct of arrays:d->sad: %0.3fs\n', te);
%% 
clear iii cols te;
whos

2 个答案:

答案 0 :(得分:3)

使Matlab代码具有空间和时间效率的方法是使用大型基元数组 - 即双精度数,整数数组或字符数组。这使您在内存中的布局更紧凑,并允许您进行矢量化操作。

对于表格数据,每列在类型上将是同质的,但不同的列可能是不同的类型,并且通常比列有更多的行。而且你经常会对列的所有元素或列的蒙板选择进行操作 - 比较或数学运算 - 这有助于向量化操作。因此,将每个列存储为列数组,希望是原始数据。您可以将这些列粘贴在结构的字段或单元格向量的元素中;在性能方面并不重要,结构形式将更具可读性,看起来更像是一个表格。一个二维单元阵列或其他数据结构将所有元素分解为它们自己的小型mxarray并不会令人满意。

也就是说,如果你有一个10,000行到10列的表,你想拥有一个10长的单元阵列或10场结构,每个字段或元素都有一个10,000长的基本列向量。

dataset对象对象基本上是一个包含如前所述的列数组结构的包装器,卡在一个对象中。但是Matlab中的对象比常规结构和单元具有更大的开销;您在每次访问时都要支付一个或多个方法调用。看看Is MATLAB OOP slow or am I doing something wrong?(完全披露:这是我的答案之一)。

您设置的测试并不表示Matlab代码的性能有多好,因为它正在进行标量单元素访问。也就是说,它通过循环在每次传递时支付列然后行元素访问。如果您的Matlab代码正在执行此操作,那么您已经失去了运气。为了快速,您需要在循环之外弹出列 - 也就是说,将昂贵的列访问操作提升到外部循环或设置代码 - 然后执行矢量化操作(如+,{{1}整个列向量上的},'&lt;',==等等,或循环遍历原始数字向量(JIT可以优化)。如果你这样做,那么ismember和其他基于对象的表格结构可以有不错的表现。

不幸的是,Matlab中的字符串很糟糕。你想远离手枪手。你有几个选择。

  • 如果列中的字符串长度大致相同,并且您没有任何长字符串,则可以将字符串向量存储为2-D dataset数组。这是内存中的单个连续数组,并且比单元阵列更节省空间,并且对于比较操作等可能更快。它也是Matlab的本地字符串表示之一,因此正常的字符串函数可以使用它。
  • 如果字符串是低基数(即,不同值的数量相对于元素总数而言较小),则可以将它们编码为“符号”,将它们存储为原始数组的数组,这些数组是索引到不同字符串值的列表。 charunique函数可以帮助实现这些编码。只要您只是进行相等测试而不进行排序,这些编码的字符串列将以数字速度运行。
  • 我相信其中一个工具箱,也许是ismember的工具箱,支持“分类器”或“分类”变量,这些变量基本上是低基数编码的现成实现。
  • 不要打扰Java Strings;跨越Matlab-to-Java屏障的开销将使其成为净损失。
  • 希望现在有人想出了别的东西。

如果您必须坚持使用cellstrs,请将它们作为cellstr列向量存储在结构内部,如上所述;这样,当您实际操作字符串列时,您只需支付单元格访问的价格。

答案 1 :(得分:1)

我想说如果你需要管理大量数据,那么MATLAB不是最好的选择。寻找合适的数据库并最终将您需要的数据导入MATLAB。

但是,如果您计划使用MATLAB,我仍然会选择 cellarrays ,即如果您不需要以 fieldnames 如结构

使用cellarray时,请记住每个单元格会产生112字节的开销。因此,我会为每个列创建一个单元格(不是每个标量双精度的单元格):

c = cell(1,10);
c(1,1:2:10) = num2cell(rand(1e5,5),1);
c(1,2:2:10) = {cellstr(repmat('asdf', 100000, 1))};

和记忆(没有时间变化):

Name           Size               Bytes  Class    Attributes
c              1x10            38000600  cell   

此外,您所谓的数组结构通常称为“标量”结构,而不是结构数组(或非标量结构)。

如果我没记错的话,当你开始嵌套字段时,结构体的读/写性能往往会降低(我需要找到特定的线程)。