【问题标题】:Matlab: avoid for loops to find the maximum among values with same labelsMatlab:避免for循环以在具有相同标签的值中找到最大值
【发布时间】:2014-08-21 10:34:28
【问题描述】:

我需要在 matlab 中找到具有相同标签的值中的最大值,并且我试图避免使用 for 循环。

具体来说,我有一个标签数组L 和一个值数组V,大小相同。我需要生成一个数组S,其中包含L 的每个值V 的最大值。一个例子会更好地解释:

L = [1,1,1,2,2,2,3,3,3,4,4,4,1,2,3,4]

V = [5,4,3,2,1,2,3,4,5,6,7,8,9,8,7,6]

那么,输出数组 S 的值将是:

s(1) = 9 (the values V(i) such that L(i) == 1 are: 5,4,3,9 -> max = 9)

s(2) = 8 (the values V(i) such that L(i) == 2 are: 2,1,2,8 -> max = 8)

s(3) = 7 (the values V(i) such that L(i) == 3 are: 3,4,5,7 -> max = 7)

s(4) = 8 (the values V(i) such that L(i) == 4 are: 6,7,8,6 -> max = 8)

这可以通过使用 for 循环遍历数组 LV 来轻松实现,但在 Matlab 中 for 循环很慢,所以我一直在寻找更快的解决方案。有什么想法吗?

【问题讨论】:

    标签: matlab for-loop labels


    【解决方案1】:

    这是accumarray 的标准工作。

    需要考虑三种情况,越来越普遍:

    • 整数标签。
    • 整数标签,指定填充值。
    • 删除间隙;或非整数标签。一般情况。

    整数标签

    你可以使用

    S = accumarray(L(:), V(:), [], @max).';
    

    在你的例子中,这给出了

    >> L = [1 1 1 2 2 2 3 3 3 4 4 4 1 2 3 7];
    >> V = [5 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6];
    >> S = accumarray(L(:), V(:), [], @max).'
    S =
         9     8     7     8
    

    整数标签,指定填充值

    如果在L 中存在整数之间的间隙,则以上将为不存在的标签提供0 结果。如果您想更改该填充值(例如更改为NaN),请在acccumarray 中使用第五个输入参数:

    S = accumarray(L(:), V(:), [], @max, NaN).';
    

    例子:

    >> L = [1 1 1 2 2 2 3 3 3 4 4 4 1 2 3 7]; %// last element changed
    >> V = [5 4 3 2 1 2 3 4 5 6 7 8 9 8 7 6]; %// same as in your example
    >> S = accumarray(L(:), V(:), [], @max, NaN).'
    S =
         9     8     7     8   NaN   NaN     6
    

    消除间隙;或非整数标签。一般情况

    当整数标签之间的间隙很大时,使用填充值可能效率低下。在这种情况下,您可能只想获取 S 中有意义的值,而不需要填充值,即跳过不存在的标签。此外,L 不一定包含整数

    这两个问题通过在使用accumarray之前将unique应用于标签来解决:

    [~, ~, Li] = unique(L); %// transform L into consecutive integers
    S = accumarray(Li(:), V(:), [], @max, NaN).';
    

    例子:

    >> L = [1.5 1.5 1.5 2 2 2 3 3 3 4 4 4 1 2 3 7.8]; %// note: non-integer values
    >> V = [5   4   3   2 1 2 3 4 5 6 7 8 9 8 7 6  ]; %// same as in your example
    >> [~, ~, Li] = unique(L); %// transform L into consecutive integers
    >> S = accumarray(Li(:), V(:), [], @max, NaN).'
    S =
         9     5     8     7     8     6
    

    【讨论】:

    • 嗯,这很令人沮丧,所以我经常制定一个解决方案只是为了发现您发布了一个更快的单线解决方案:(...唯一的问题是您的解决方案不起作用如果L 中的值为零(如果它在V 中则没有问题)。
    • 或者L中是否有负数或非整数值
    • @TheMinion 当然不会。 accumarray 的第一个输入是一个下标数组。在 MATLAB 中有 0、负数或非整数下标是没有意义的。
    • @excaza 是的,但在 OP 中 L 是一个标签数组,不必只是正整数。在另一种情况下,作为 OP 发布的字符串数组或浮动数据点的示例,这就是我发布评论的原因,Luis 的解决方案很好,但仅适用于 L
    • @TheMinion 很抱歉让您感到沮丧 :-) 我已经更新了解决方案以涵盖一般情况。感谢您的提醒!
    【解决方案2】:
    helper=[L.', V.'];
    helper=sortrows(helper,-2);
    [~,idx,~]=unique(helper(:,1));
    S=helper(idx,2);
    

    我所做的是:我将两个数组作为列加入。然后我首先对具有最大元素的第二列进行排序。然后我得到L 中唯一值的idx,然后从V 返回相应的值。

    Luis Mendo 的解决方案更快。但据我所知,如果L 内部有零、负值或非整数,他的解决方案将不起作用:

    Luis solution: Elapsed time is 0.722189 seconds.
    My solution: Elapsed time is 2.575943 seconds.
    

    我用过:

    L= ceil(rand(1,500)*10);
    V= ceil(rand(1,500)*250);
    

    并运行代码 10000 次。

    【讨论】:

    • +1 表示方法的通用性和基准测试
    猜你喜欢
    • 1970-01-01
    • 2018-03-15
    • 1970-01-01
    • 2017-09-30
    • 1970-01-01
    • 2021-10-20
    • 2015-05-27
    • 1970-01-01
    • 2015-01-24
    相关资源
    最近更新 更多