2013-04-26 50 views
6

我需要处理大量的混合类型的表格数据 - 字符串和双打。一个标准问题,我会想。 Matlab中处理这个问题的最好的数据结构是什么?混合类型的Matlab数据结构 - 什么是时间+空间高效?

Cellarray绝对不是答案。这是非常低效的内存。 (测试如下所示)。数据集(来自统计工具箱)在时间和空间上都非常低效。这给我留下了结构阵列或数组结构。我对下面的时间和内存的所有四个不同选项做了测试,在我看来,数组结构对于我测试的东西来说是最好的选择。

我对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 

================== CODE: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); 

============= ===== CODE: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 
+0

虽然'数据集'确实很慢,但您的计时非常缓慢。我在访问时获取“数据集:0.7s”,而其他人的顺序与您的相同。我在32位的WinXP – Amro 2013-04-26 19:57:37

回答

1

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

不过,如果你打算使用MATLAB反正我还是会选择cellarrays,也就是说,如果你不需要在字段名结构形式数据的语法引用。

使用cellarrays时,请记住,每个单元施加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 

而且,你叫什么数组的结构通常用“标量”结构来引用,而不是结构数组(或非标量结构)。

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

3

使Matlab代码节省空间和时间的方法是使用大型基元数组 - 即双数,整数或字符数组。这给你一个更紧密的内存布局,并让你做矢量化操作。

对于表格数据,每列在类型上将是同质的,但不同的列可能是不同的类型,并且通常会有比列多得多的行。而且,您经常会对列的所有元素或列的蒙版选择进行操作(比较或数学),从而使其自身进行向量化操作。因此将每列存储为一个列数组,希望是基元。您可以将这些列粘贴到结构体的字段或单元格向量的元素中;在性能方面并不重要,结构形式将更加可读,看起来更像一张表。一个二维单元阵列或其他数据结构将所有元素分解到他们自己的小型mxarrays中并不会令人满意。也就是说,如果你有一个10,000行10列的表,你希望有一个10长的单元数组或10个字段的结构,每个这些字段或元素都包含一个10,000长的基本列向量。

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

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

不幸的是,Matlab中的字符串很糟糕。你想摆脱手机。你有几个选项。

  • 如果列中的字符串是大约相同的长度,并且不具有在其中任何长字符串,可以存储的字符串作为2 d char阵列的矢量。这是内存中的一个连续阵列,比单元阵列更节省空间,并且可能比较操作更快等等。它也是Matlab的本地字符串表示之一,所以普通的字符串函数将与它一起工作。
  • 如果字符串的基数很低(也就是说,不同值的数量相对于元素的总数量很小),则可以将它们编码为“符号”,将它们存储为原始ints数组,它们是索引in到不同的字符串值列表。函数uniqueismember可以帮助实现这些编码。只要您只是在进行平等测试而不进行排序,这些编码的字符串列将以数字速度运行。
  • 我相信其中一个工具箱,也许是dataset的工具箱,支持“分类器”或“分类”变量,这些变量基本上是低基数编码的现成实现。
  • 不要打扰Java字符串;通过Matlab到Java屏障的开销将使其成为净亏损。
  • 希望有人现在已经拿出了别的东西。

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

+0

+1上运行R2013a,很好地解释了这个问题,并提供了深刻的见解。此外,OOP开销问题明显改善了性能。举例来说,在这个测试中(在R2013a中),对于'dataset'访问只有0.7s,而OP报告的36秒是 – Amro 2013-04-27 14:56:32

+0

。当然,还有改进的空间。如果您有权访问最新版本 – Amro 2013-04-27 15:03:32

+0

,那么您可以更新基准测试结果将会很有趣。是的,升级听起来像个好主意。非OOP性能也应该提高。但是怕别人不得不做更新的基准测试 - 我改变了工作,目前还没有Matlab许可证。 – 2013-04-27 18:50:03