通过单元数组的一个维连接子单元而不在MATLAB中使用循环

时间:2014-11-03 03:39:56

标签: arrays matlab vectorization cells cell-array

我有一个单元格数组。每个单元格包含一个可变长度的向量。例如:

example_cell_array=cellfun(@(x)x.*rand([length(x),1]),cellfun(@(x)ones(x,1), num2cell(ceil(10.*rand([7,4]))), 'UniformOutput', false), 'UniformOutput', false)

我需要将单元格的内容连接到一个维度,然后对每个连接的向量执行操作,为我的单元格数组中的每一列生成标量(例如sum() - 实际操作很复杂,耗时,并且不能自然地进行矢量化 - 特别是对于不同长度的工作者而言。

我可以轻松地使用循环(对于我的连接矢量和示例),如下所示:

[M N]=size(example_cell_array);
result=zeros(1,N);
cat_cell_array=cell(1,N);
for n=1:N
    cat_cell_array{n}=[];
    for m=1:M
        cat_cell_array{n}=[cat_cell_array{n};example_cell_array{m,n}];
    end
end
result=cell2mat(cellfun(@(x)sum(x), cat_cell_array, 'UniformOutput', false))

不幸的是,这太慢了。 (我的细胞阵列是1Mx5,每个细胞中的载体长度从100到200)

是否有一种简单的方法可以生成连接的单元格数组,其中单元格中包含的向量已经在一个维度上连接起来了?

类似的东西:

dim=1;
cat_cell_array=(?concatcells?(dim,example_cell_array);

编辑: 由于很多人一直在测试解决方案:仅供参考,我应用于每个连接向量的函数是circ_kappa(x),可从Circular Statistics Toolbox获得

4 个答案:

答案 0 :(得分:2)

对于连接本身,听起来你可能想要cat的函数形式:

for n=1:N
    cat_cell_array{n} = cat(1, example_cell_array{:,n});
end

这将连接原始输入数组中每列中单元格中的所有数组。

答案 1 :(得分:2)

某些方法可能会建议您使用example_cell_array{..}解包数字数据,然后在连接后将其打包回更大的单元格以形成cat_cell_array。然后,您需要再次从该连接的单元格数组中解压缩数字数据,以便在每个单元格上执行操作

现在,在我看来,如果example_cell_array不是您的预期输出之一,那么这种多种解包和打包方法将不会有效。所以,考虑到所有这些,让我在这里提出两种方法。


Loopy方法

第一个是for循环代码 -

data1 =  vertcat(example_cell_array{:}); %// extract all numeric data for once
starts = [1 sum(cellfun('length',example_cell_array),1)]; %// intervals lengths
idx = cumsum(starts); %// get indices to work on intervals basis
result  = zeros(1,size(example_cell_array,2)); 
%// replace this with "result(size(example_cell_array,2))=0;" for performance
for k1 = 1:numel(idx)-1
    result(k1) = sum(data1(idx(k1):idx(k1+1)-1));
end

因此,您需要使用实际的操作编辑sum


几乎向量化的方法

如果example_cell_array有很多列,我的第二个建议是几乎矢量化方法,尽管它在少量列中表现不佳。现在,此代码在第一行使用cellfun来获取连接版本中每个单元格的长度。 cellfun基本上是循环代码的包装器,但就运行时而言这并不是非常昂贵,这就是为什么我将这种方法归类为几乎矢量化的方法。

代码是 -

lens = sum(cellfun('length',example_cell_array),1); %// intervals lengths
maxlens = max(lens);
numlens = numel(lens);
array1(maxlens,numlens)=0;
array1(bsxfun(@ge,lens,[1:maxlens]')) = vertcat(example_cell_array{:}); %//'
result = sum(array1,1);

您现在需要做的是使用array1实现创建的掩码,使bsxfun以列为基础运行操作。因此,如果array1M x 5大小的数组,则需要使用掩码从每列中选择有效元素,然后对这些元素执行操作。如果您需要有关掩蔽问题的更多信息,请与我们联系。

希望其中一种方法适合你!


快速测试:使用250000x5大小的example_cell_array,快速测试表明sum操作的这两种方法都能很好地执行,并提供{400x 1}}加速我最后问题中的代码。

答案 2 :(得分:1)

您可以定义如下函数:

cellcat = @(C) arrayfun(@(k) cat(1, C{:, k}), 1:size(C,2), 'uni', 0);

然后使用

>> cellcat(example_cell_array)
ans = 
    [42x1 double]    [53x1 double]    [51x1 double]    [47x1 double]

答案 3 :(得分:0)

我认为您希望在不使用cat_cell_array循环的情况下生成for。如果是这样,您可以按如下方式进行:

cat_cell_array=cellfun(@(x) cell2mat(x),num2cell(example_cell_array,1),'UniformOutput',false);

根据我的说法,以上行可以替换整个for循环。然后,您可以通过此cat_cell_array计算复杂函数。

如果只有result对您很重要并且您不想存储cat_cell_array,那么您可以在一行中执行所有操作(不建议用于可读性):

result=cell2mat(cellfun(@(x)sum(x), cellfun(@(x) cell2mat(x),num2cell(example_cell_array,1),'Uni',false), 'Uni', false));