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

Posted

技术标签:

【中文标题】混合类型的Matlab数据结构 - 啥是时间+空间效率?【英文标题】:Matlab data structure for mixed type - what's time + space efficient?混合类型的Matlab数据结构 - 什么是时间+空间效率? 【发布时间】:2013-04-21 01:06:15 【问题描述】:

我需要处理大量混合类型的表格数据 - 字符串和双精度。一个标准的问题,我想。 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  

================== 代码:测试#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.(colsiii) = c(:,iii);
      else
          sac.(colsiii) = 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);

================== 代码:测试#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.(colsiii) = c(:,iii);
    else
        sac.(colsiii) = 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.(colsiii) = cell2mat(d(:,iii));
end
te = toc(te);
fprintf('Cellarray(doubles)->struct of arrays:d->sad: %0.3fs\n', te);
%% 
clear iii cols te;
whos

【问题讨论】:

虽然dataset 确实很慢,但您的时间却非常慢。我在访问时获得dataset:0.7s,而其他人的顺序与您的顺序相同。我在 32 位 WinXP 上运行 R2013a 【参考方案1】:

使 Matlab 代码节省空间和时间的方法是使用大型基元数组 - 即双精度、整数或字符数组。这为您提供了更紧凑的内存布局,并允许您进行矢量化操作。

对于表格数据,每一列的类型都是同质的,但不同的列可能属于不同的类型,而且行数通常比列数多得多。而且您经常会对列的所有元素或列的掩码选择进行操作 - 比较或数学,这适用于矢量化操作。因此,将每一列存储为列数组,希望是基元。您可以将这些列粘贴在结构的字段或单元向量的元素中;在性能方面并不重要,结构形式将更具可读性并且看起来更像一个表格。将所有元素分解为自己的小型 mxarray 的二维元胞数组或其他数据结构将无法正常执行。

也就是说,如果您有一个 10,000 行 x 10 列的表,您希望有一个 10 长的元胞数组或 10 字段结构,其中每个字段或元素都包含一个 10,000 长的原始列向量。

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

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

不幸的是,Matlab 中的字符串有点糟糕。你想远离牢房。你有几个选择。

如果列中的字符串长度大致相同,并且其中没有任何长字符串,则可以将字符串向量存储为二维char 数组。这是内存中的单个连续数组,比单元数组更节省空间,并且对于比较操作等可能更快。它也是 Matlab 的原生字符串表示形式之一,因此普通的字符串函数可以使用它。 如果字符串的基数较低(即,不同值的数量相对于元素总数而言很小),您可以将它们编码为“符号”,将它们存储为原始整数数组,这些整数是到不同字符串值的列表。 uniqueismember 函数可以帮助实现这些编码。只要您只是进行相等性测试而不是排序,这些编码的字符串列就会以数字速度运行。 我相信其中一个工具箱,可能是带有dataset 的那个,支持“分类器”或“分类”变量,它们基本上是低基数编码的现成实现。 不要为 Java 字符串烦恼;跨越 Matlab 到 Java 障碍的开销将使其成为净损失。 希望现在有人想出了别的办法。

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

【讨论】:

+1 得到很好的解释和有见地的答案。此外,OOP 开销问题也确实提高了性能。例如,在此测试中(在 R2013a 中),dataset 的访问时间仅为 0.7 秒,而 OP 报告的时间为 36 秒 当然还有改进的余地。如果您可以访问最新版本,如果您可以更新您的基准测试结果将会很有趣 谢谢。是的,升级听起来是个好主意。非 OOP 性能也应该提高。不过,我怕别人必须做一个更新的基准测试——我换了工作,目前没有 Matlab 许可证。【参考方案2】:

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

但是,如果您仍然打算使用 MATLAB,我仍然会选择 cellarrays,也就是说,如果您不需要以 fieldnames的形式对数据进行语法引用> 如结构

使用元胞数组时,请记住每个元胞会产生 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   

另外,你所说的数组结构通常被称为“标量”结构,而不是结构数组(或非标量结构)。

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

【讨论】:

以上是关于混合类型的Matlab数据结构 - 啥是时间+空间效率?的主要内容,如果未能解决你的问题,请参考以下文章

优化算法混合增强灰狼优化布谷鸟搜索算法(AGWOCS)含Matlab源码 1331期

单目标优化求解基于matlab粒子群混沌混合蝴蝶优化算法求解最优目标问题(HPSOBOA)含Matlab源码 1538期

用MATLAB和C++2008混合编程,出现MATLAB:I18n:MissingICUData - ICU data not found

MATLAB-数据类型

C语言啥是位段,位段的定义求答案

回归预测基于matlab麻雀算法SSA优化混合核KELM回归预测含Matlab源码 1646期