【问题标题】:in matlab, calculate mean in a part of one column where another column satisfies a condition在matlab中,计算一列中另一列满足条件的部分的平均值
【发布时间】:2012-10-09 16:32:40
【问题描述】:

我是 matlab 的新手,我很好奇如何做到这一点:

我有一个相当大的 (27000x11) 矩阵,第 8 列包含一个有时会发生变化但对于 2000 行(不一定是连续的)保持不变的数字。

我想计算第 8 列具有相同值的那些行的第 3 列条目的平均值。这适用于第 8 列的每个值。 我还想将第 3 列的平均值绘制为第 8 列值的函数,但如果我能得到一个包含 [mean_of_3rd,8th] 的新矩阵 (2x2),我可以这样做。

例如:(为方便起见,使用较小的矩阵)

1 2 3 4 5
3 7 5 3 2
1 3 2 5 3
4 5 7 5 8
2 4 7 4 4

由于第 4 列在第 1 行和第 5 行中具有相同的值,我想计算 2 和 4 的平均值(第 2 列的相应元素,斜体粗体)并将其与第 4 列一起放入另一个矩阵中价值。 3 和 5(粗体)相同,因为第 4 列的值相同。

3 4
4 5

等等...这是否可能以简单的方式实现?

【问题讨论】:

  • 下次你应该更仔细地解释你需要什么。根据您的问题,您可能会认为您处理的是整数,而不是实数。因此,人们花时间解决了与您想要的不同的问题..
  • (直到现在才看到你的问题)真实的。看来我已经收到了罗迪的解决方案,谢谢你的时间! :) 是的,我本来可以更具体的

标签: matlab matrix extract conditional-statements


【解决方案1】:

使用全能的、未被充分利用的accumarray

这一行给出了第 2 列累积的第 4 列的平均值:

means = accumarray( A(:,4) ,A(:,2),[],@mean)

这一行给出了每组元素的数量:

count = accumarray( A(:,4) ,ones(size(A(:,4))))

现在,如果您只想过滤至少出现一次的那些:

>> filtered = means(count>1)

filtered =

     3
     4

这仅适用于第 4 列中的正整数。


计算每个集合中元素数量的另一种可能性:

 count = accumarray( A(:,4) ,A(:,4),[],@numel)

【讨论】:

  • 看我的回答。你知道为什么accumarray 比 for 循环慢吗? AFAIK,accumarray 是内置的,运行速度应该比 JIT 循环快...
  • @RodyOldenhuis,尝试关闭 JIT 并再次执行基准测试
  • 啊,可能是因为mean。请参阅@angainor 的回答。
  • 关闭 JIT 如何更好地比较 accumarray 与 for-loop?
  • @RodyOldenhuis,您可以查看 JIT 是否负责加速,以及其中的多少。
【解决方案2】:

基于 Andrey 和 Rody 的想法略微改进的方法。我们不能直接使用 accumarray,因为数据是实数,而不是整数。 但是,我们可以使用 unique 来查找重复条目的索引。然后我们对整数进行操作。

% get unique entries in 4th column
[R, I, J] = unique(A(:,4));

% count the repeating entries: now we have integer indices!
counts = accumarray(J, 1, size(R));

% sum the 2nd column for all entries
sums   = accumarray(J, A(:,2), size(R));

% compute means
means  = sums./counts;

% choose only the entries that show more than once in 4th column
inds   = counts>1;
result = [means(inds) R(inds)];

以下合成数据的时间比较:

A=randi(100, 1000000, 5);

% Rody's solution
Elapsed time is 0.448222 seconds.

% The above code
Elapsed time is 0.148304 seconds.

【讨论】:

  • @angainor 我注意到如果某些数据是 NaN,这个想法就行不通。如果我有一个类似的数据集,但其中一些值是 NaN(为了计算平均值,我希望忽略它),我该怎么办?
【解决方案3】:

我的官方回答:

A4 = A(:,4);
R = unique(A4);   

means = zeros(size(R));
inds  = false(size(R));

for jj = 1:numel(R)        
    I = A4==R(jj);
    sumI = sum(I);        
    inds(jj)  = sumI>1;
    means(jj) = sum(A(I,2))/sumI;        
end

result = [means(inds) R(inds)];

这是因为以下原因。以下是我们提出的所有替代方案,以剖析形式:

%# sample data
A = [
    1 2 3 4 5
    3 7 5 3 2
    1 3 2 5 3
    4 5 7 5 8
    2 4 7 4 4];

%# accumarray
%# works only on positive integers in A(:,4)
tic
for ii = 1:1e4
    means = accumarray( A(:,4) ,A(:,2),[],@mean);
    count = accumarray( A(:,4) ,ones(size(A(:,4))));
    filtered = means(count>1);
end
toc

%# arrayfun
%# works only on integers in A(:,4)
tic
for ii = 1:1e4
    B = arrayfun(@(x) A(A(:,4)==x, 2), min(A(:,4)):max(A(:,4)), 'uniformoutput', false);
    filtered = cellfun(@mean, B(cellfun(@(x) numel(x)>1, B)) );    
end
toc


%# ordinary loop
%# works only on integers in A(:,4)    
tic
for ii = 1:1e4

    A4 = A(:,4);
    R = min(A4):max(A4);

    means = zeros(size(R));
    inds  = false(size(R));
    for jj = 1:numel(R)
        I = A4==R(jj);
        sumI = sum(I);        
        inds(jj) = sumI>1;
        means(jj) = sum(A(I,2))/sumI; 
    end

    filtered = means(inds);   
end
toc

结果:

Elapsed time is 1.238352 seconds.  %# (accumarray)
Elapsed time is 7.208585 seconds.  %# (arrayfun + cellfun)
Elapsed time is 0.225792 seconds.  %# (for loop)

普通的循环显然是要走的路。

注意内部循环中没有mean。这是因为mean 不是 Matlab 内置函数(至少在 R2010 上),因此在循环中使用它会使循环不符合 JIT 编译条件,这会使其速度减慢 10 倍以上。使用上面的表格将循环加速到几乎是accumarray 解决方案速度的 5.5 倍。

根据您的评论判断,更改循环以处理A(:,4) 中的所有条目(不仅仅是整数)几乎是微不足道的:

A4 = A(:,4);
R = unique(A4);   

means = zeros(size(R));
inds  = false(size(R));
for jj = 1:numel(A4)
    I = A4==R(jj);
    sumI = sum(I);        
    inds(jj) = sumI>1;
    means(jj) = sum(A(I,2))/sumI;
end

filtered = means(inds); 

我将复制粘贴到顶部作为我的官方答案:)

【讨论】:

  • 谢谢你的两个建议,首先 accumarray 是一个很棒的函数,我将来可能会有很多用处,但是因为我的矩阵由 10 范围内的实数组成^-4 到 10^3 的小数位数不同,它似乎对我不起作用。我尝试(只是用整数尝试一下)取矩阵的 10^6 倍的绝对值 :),它在一列中给了我 900 万个零……(这与大小无关原始矩阵,很奇怪),或者更正确的是,最后一个单元格中有 8 999 999 个零和 3*10^-4 :)
  • @user1729770:我已经编辑了我的答案,也许现在这更适合你的原因。
  • Rody,我认为您的官方回答存在一些错误。如果您不介意,我会进行编辑。如果我错了,请纠正我。
  • 我还添加了第 4 列的收集 - OP 想要这样。 +1 否则用于速度分析,尽管我稍后会继续努力;)
猜你喜欢
  • 2018-09-17
  • 2022-11-18
  • 1970-01-01
  • 2021-09-13
  • 1970-01-01
  • 1970-01-01
  • 2023-02-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多