【问题标题】:how to sum the elements specified by a cell in matlab?如何对matlab中单元格指定的元素求和?
【发布时间】:2013-10-02 06:52:56
【问题描述】:

我有一个大矩阵 M (nxm)。我将把存储在向量中的索引指定的一些元素总结为单元格元素。有许多组索引,因此单元格具有多个元素。例如

M = rand(2103, 2030);
index{1} = [1 3 2 4 53 5 23 3];
index{2} = [2 3 1 3 23 10234 2032];
% ...
index{2032} = ...;

我要总结索引处的所有元素{1},总结索引处的所有元素{2} ...,现在我使用循环

sums = zeros(1, 2032);
for n=1:2032
  sums(n) = sum(M(index{n}));
end

我想知道是否有任何方法可以使用单行命令而不是循环来做到这一点。使用循环非常慢。

【问题讨论】:

    标签: matlab cell-array


    【解决方案1】:

    可能是cellfun的经典用法

    sums = cellfun(@(idx) sum(M(idx)), index);
    

    编辑:这里是一个大型案例的基准测试,表明这种方法比 for 循环稍慢,但比 Eitan T 的方法快

    M = rand(2103, 2030);
    index = cell(1, 2032);
    index{1} = [1 3 2 4 53 5 23 3];
    index{2} = [2 3 1 3 23 10234 2032];
    
    for n=3:2032
        index{n} = randi(numel(M), 1, randi(10000));
    end
    
    N = 1e1;
    sums = zeros(1, 2032);
    tic
    for kk = 1:N
        for n=1:2032
            sums(n) = sum(M(index{n}));
        end
    end
    toc
    
    tic
    for kk = 1:N
        sums = cellfun(@(idx) sum(M(idx)), index);
    end
    toc
    
    tic
    for kk = 1:N
        sums = cumsum(M([index{:}]));
        sums = diff([0, sums(cumsum(cellfun('length', index)))]);
    end
    toc
    

    结果

    Elapsed time is 2.072292 seconds.
    Elapsed time is 2.139882 seconds.
    Elapsed time is 2.669894 seconds.
    

    【讨论】:

    • 这更整洁,但我怀疑它是否更快。如果您主要关心的是效率,我建议您根据循环对其进行基准测试。
    • @EitanT 我看不到您所指的数量级。在我的编辑中查看基准测试。
    • 那是因为您正在为仅包含两个单元格的 index 运行它。我将添加我自己的基准测试代码。
    • @EitanT 不,我不是,代码的第 6-8 行有一个 for 循环来填充索引元胞数组。
    • @MohsenNosratinia 啊,你说得对,我错过了。那么,您运行的是哪个版本的 MATLAB?
    【解决方案2】:

    也许不如 cellfun 单线那么优雅,但运行速度快了一个数量级以上:

    sums = cumsum(M([index{:}]));
    sums = diff([0, sums(cumsum(cellfun('length', index)))]);
    

    对于大型输入,它的运行速度甚至比 JIT 加速循环快大约 4 或 5 倍。请注意,当index 中的每个单元格包含一个包含超过~2000 个元素的向量时,与循环(和cellfun)相比,这种方法的性能开始下降。

    基准测试

    M = rand(2103, 2030);
    I = ceil(numel(M) * rand(2032, 10));
    index = mat2cell(I, ones(size(I, 1), 1), size(I, 2));
    N = 100;
    
    tic
    for k = 1:N
        sums = zeros(1, numel(index));
        for n = 1:numel(sums)
            sums(n) = sum(M(index{n}));
        end
    end
    toc
    
    tic
    for k = 1:N
        sums = cellfun(@(idx) sum(M(idx)), index);
    end
    toc
    
    tic
    for k = 1:N
        sums = cumsum(M([index{:}]));
        sums2 = diff([0, sums(cumsum(cellfun('length', index)))]);
    end
    toc
    

    在 MATLAB 2012a(在 2.27GHz 16 核 Intel Xeon 处理器上运行的 Windows Server 2008 R2)中执行此操作时,我得到:

    Elapsed time is 0.579783 seconds.
    Elapsed time is 1.789809 seconds.
    Elapsed time is 0.111455 seconds.
    

    【讨论】:

    • 请给我们看看基准测试代码好吗?我发现你的代码比较慢
    • @MohsenNosratinia 根据我们的最新发现添加了评论:)
    • 谢谢!当数据变大时,看看不同方法的表现总是很有趣。
    • @MohsenNosratinia 是的,我也很高兴看到 cellfun 在最新版本的 MATLAB 中还不错。
    猜你喜欢
    • 1970-01-01
    • 2020-05-25
    • 2016-09-15
    • 1970-01-01
    • 2013-08-06
    • 1970-01-01
    • 2018-07-22
    • 1970-01-01
    • 2012-11-23
    相关资源
    最近更新 更多