使用accumarray函数进行索引,找到最大/最小值。

3

我有一个向量和一个大小相同的单元格数组(其中包含重复字符串),单元格数组定义了分组。我希望找到每个分组中向量的最小值/最大值。

例如:

value = randperm(5) %# just an example, non-unique in general
value =
     4     1     2     3     5
group = {'a','b','a','c','b'};
[grnum, grname] = grp2idx(group);

我会用ACCUMARRAY函数来实现这个功能:
grvalue = accumarray(grnum,value,[],@max);

因此,我有一个带有唯一组名(grname)的新单元格数组和一个新向量(grvalue)。

grname = 
    'a'
    'b'
    'c'
grvalue =
     4
     5
     3

我还需要找到包含在新向量中的旧向量值的位置索引。

gridx = 1 5 4

任何想法?不一定要使用accumarray函数,但我正在寻找快速向量化的解决方案。

1
你使用哪个版本的Matlab? - Jonas
@Jonas:2012b,Windows7 x64。 - yuk
2个回答

1

当面对类似的问题时,我想出了这个解决方案:

  • 在.m文件中定义以下函数:

    function i=argmax(x)
    [~,i]=max(x);
    end
    
  • 然后你可以找到最大值的位置:

    gridx = accumarray(grnum,grnum,[],@(i)i(argmax(value(i))) );
    
  • 以及最大值:

    grvalue = value(gridx);
    

(*如果我正确理解了你的问题)


1
我能看到的最好的向量化答案是:

gridx = arrayfun(@(grix)find((grnum(:)==grix) & (value(:)==grvalue(grix)),1),unique(grnum));

但我不能称之为“快速”的向量化解决方案。arrayfun非常有用,但通常不比循环更快。


然而,最快的答案并不总是向量化的。如果我按照您编写的代码重新实现,但使用更大的数据集:
nValues = 1000000;
value = floor(rand(nValues,1)*100000);
group = num2cell(char(floor(rand(nValues,1)*4)+'a'));
tic;
[grnum, grname] = grp2idx(group);
grvalue = accumarray(grnum,value,[],@max);
toc;

我的电脑显示的是0.886秒的tic/toc时间。(注意,所有的tic/tock时间都来自于运行函数的第二次,以避免一次性pcode生成。)
添加“向量化”(实际上是arrayfun)一行gridx计算会导致0.975秒的tic/toc时间。不错,进一步调查表明,大部分时间都被grp2idx调用消耗了。
如果我们将其重新实现为非向量化的简单循环,包括gridx计算,就像这样:
tic
[grnum, grname] = grp2idx(group);
grvalue = -inf*ones(size(grname));
gridx = zeros(size(grname));
for ixValue = 1:length(value)
    tmpGrIdx = grnum(ixValue);
    if value(ixValue) > grvalue(tmpGrIdx)
        grvalue(tmpGrIdx) = value(ixValue);
        gridx(tmpGrIdx) = ixValue;
    end
end
toc

计时器 tic/toc 的时间约为0.847秒,略快于原始代码。
进一步来说,大部分时间似乎都浪费在了单元数组的内存访问上。例如:
tic; groupValues = double(cell2mat(group')); toc  %Requires 0.754 seconds
tic; dummy       =       (cell2mat(group')); toc  %Requires 0.718 seconds

如果您最初将组名定义为数字数组(例如,我将使用上面定义的groupValues),则即使使用相同的代码,时间也会大大减少:
groupValues = double(cell2mat(group'));  %I'm assuming this is precomputed
tic
[grnum, grname] = grp2idx(groupValues);
grname = num2cell(char(str2double(grname))); %Recapturing your original names
grvalue = -inf*ones(size(grname));
gridx = zeros(size(grname));
for ixValue = 1:length(value)
    tmpGrIdx = grnum(ixValue);
    if value(ixValue) > grvalue(tmpGrIdx)
        grvalue(tmpGrIdx) = value(ixValue);
        gridx(tmpGrIdx) = ixValue;
    end
end
toc

这会产生一个0.16秒的滴答声/嘀嗒声时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接