我需要处理大量混合类型的表格数据 - 字符串和双精度数据。我想是一个标准问题。 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
答案 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
和其他基于对象的表格结构可以有不错的表现。
dataset
数组。这是内存中的单个连续数组,并且比单元阵列更节省空间,并且对于比较操作等可能更快。它也是Matlab的本地字符串表示之一,因此正常的字符串函数可以使用它。char
和unique
函数可以帮助实现这些编码。只要您只是进行相等测试而不进行排序,这些编码的字符串列将以数字速度运行。ismember
的工具箱,支持“分类器”或“分类”变量,这些变量基本上是低基数编码的现成实现。如果您必须坚持使用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
此外,您所谓的数组结构通常称为“标量”结构,而不是结构数组(或非标量结构)。
如果我没记错的话,当你开始嵌套字段时,结构体的读/写性能往往会降低(我需要找到特定的线程)。