MATLAB中忽略NaN时从累计行值计算平均值

MATLAB中忽略NaN时从累计行值计算平均值,matlab,nan,accumarray,Matlab,Nan,Accumarray,我正在寻找如何优雅地解决以下问题的建议。虽然在我的具体案例中,性能不是一个问题,但我希望您能对良好实践提出意见 提前谢谢 简短版本: 我试图根据一些逻辑平均矩阵行,同时忽略NaN值。我当前拥有的代码没有以我想要的方式处理NaN值 长版本: 我的数据以以下方式构建: 一列(第一列)的“箱子”。每个箱子的行数不是恒定的。容器不必是整数。行是预先排序的 可变数量的数据列,可能包括NAN 下面是一个例子: DATA = [... 180 NaN NaN 1.733 180

我正在寻找如何优雅地解决以下问题的建议。虽然在我的具体案例中,性能不是一个问题,但我希望您能对良好实践提出意见

提前谢谢

简短版本: 我试图根据一些逻辑平均矩阵行,同时忽略NaN值。我当前拥有的代码没有以我想要的方式处理NaN值

长版本: 我的数据以以下方式构建:

  • 一列(第一列)的“箱子”。每个箱子的行数不是恒定的。容器不必是整数。行是预先排序的
  • 可变数量的数据列,可能包括NAN
下面是一个例子:

DATA = [...
180     NaN     NaN     1.733
180     NaN     NaN     1.703
200     0.720   2.117   1.738
200     0.706   2.073   1.722
200     0.693   2.025   1.723
200     NaN     NaN     1.729
210     NaN     NaN     1.820
210     NaN     NaN     1.813
210     NaN     NaN     1.805
240     NaN     NaN     1.951
240     NaN     NaN     1.946
240     NaN     NaN     1.946
270     NaN     NaN     2.061
270     NaN     NaN     2.052
300     0.754   2.356   2.103
300     0.758   2.342   2.057
300     NaN     NaN     2.066
300     NaN     NaN     2.066 ];
所需结果是一个矩阵,在第一列中包含唯一的“箱”,在其余列中表示“未被NaN破坏”,例如:

  • 如果对于特定列+bin,只有NaN(在上面的示例中:第一个数据列+bin 210)-结果将是NaN
  • 如果特定列+bin混合了NAN和数字,则结果将是有效数字的平均值。在上面的示例中:第1列数据+bin 200应给出
    (0.720+0.706+0.693)/3=0.7063
    ——注意此列+bin的除法为3(而不是4)
下面是上述示例的预期结果:

RES = [...
180     NaN     NaN     1.718
200     0.7063  2.072   1.728
210     NaN     NaN     1.812
240     NaN     NaN     1.948
270     NaN     NaN     2.056
300     0.756   2.349   2.074 ];
到目前为止,我尝试的是: 这是我设法从几个来源编译的一些代码。对于只包含NaN或数字的列+bin,它工作得很好

nDataCols=size(DATA,2)-1;
[u,m,n] = unique(DATA(:,1));
sz = size(m);
N=accumarray(n,1,sz);

RES(length(u),nDataCols) = 0; %Preallocation

for ind1 = 1:nDataCols
    RES(:,ind1)=accumarray(n,DATA(:,ind1+1),sz)./N;
end

RES= [u,RES];
以下是我目前得到的信息:

RES = [...
180     NaN     NaN     1.718
200     NaN     NaN     1.728
210     NaN     NaN     1.812
240     NaN     NaN     1.948
270     NaN     NaN     2.056
300     NaN     NaN     2.074 ];
附笔
  • 如果可能的话,使用电子表格软件(如MS Excel)更容易做到这一点——我很想听听你的想法
  • 在每列的基础上进行计算是我目前关于如何处理这个问题的想法。我只是想知道是否有一种方法可以把它推广到马上得到完整的矩阵

  • 一种可能的方法是:在第一列中查找更改(利用其预先排序的事实)并应用于每个行块:

    ind = find(diff([-inf; (DATA(:,1)); inf])~=0); %// value changed: start of block
    r = arrayfun(@(n) nanmean(DATA(ind(n):ind(n+1)-1,:)), 1:numel(ind)-1, 'uni', 0);
    RES = vertcat(r{:});
    
    您可以用显式循环替换
    arrayfun
    。这就避免了单元引入的开销:

    ind = find(diff([-inf; (DATA(:,1)); inf])~=0); %// value changed: start of block
    RES = zeros(numel(ind)-1, size(DATA,2)); %// preallocate
    for n = 1:numel(ind)-1 %// loop over blocks
        RES(n,:) = nanmean(DATA(ind(n):ind(n+1)-1,:));
    end
    

    您的方法也可以使用。您只需使用
    nanmean
    函数的句柄调用
    accumarray
    。这不需要对第一列进行预排序

    nDataCols = size(DATA,2)-1;
    [u, ~, n] = unique(DATA(:,1));
    RES = zeros(length(u), nDataCols); %// Preallocation
    for ind1 = 1:nDataCols
        RES(:,ind1) = accumarray(n, DATA(:,ind1+1), [], @nanmean);
    end
    RES = [u, RES];
    

    这是另一个解决方案,尽管效率极低。此外,输出数组将所有
    NaN
    值设置为0。就说这对学术研究有好处吧。以下是我所做的步骤:

  • 对于第一列中的每个ID,找到一个唯一的列表
  • 对于其他列,将每列拆分为一个单元格数组
  • 创建一个新的单元格数组,其中每列都会附加此单元格数组中每个元素的第一列
  • 筛选出每个单元格数组中包含
    NaN
    值的行
  • 对于过滤结果的每一列,使用
    mean
    作为函数句柄运行
    accumarray
  • 使用步骤1中的ID,为每个
    accumarray
    结果编制索引,并将其转换回矩阵

  • 结果是:

    RES = 
    
    180.0000         0         0    1.7180
    200.0000    0.7063    2.0717    1.7280
    210.0000         0         0    1.8127
    240.0000         0         0    1.9477
    270.0000         0         0    2.0565
    300.0000    0.7560    2.3490    2.0730
    


    正如你所看到的,效率很低——特别是我打了大量的
    cellfun
    电话,但我想这仍然是一个学术性的例子

    +1用于明确提出的问题。如果新用户能提出这样的问题就好了:)@Amro&rayryeng-谢谢大家。这是因为我不认为用不适定的问题折磨潜在的读者是获得答案的好方法:)和rayryeng——如果你有其他解决方案,我不介意听@路易斯-让我很快提出这个建议…:D@Dev-我相信你一定会得到我的选票!:-)@Dev iL-这是一个很好的问题,有一个很好的答案-。我的最爱之一,由我们的常驻专家回答-路易斯·门多+1-非常好。不知道nanmean的事。这也是寻找过渡的好方法。我的书中还有一个。@Luis-我喜欢你的第一个解决方案的精髓(事实上它是无循环的),但是通过单元似乎会产生额外的开销,因此从性能角度看,这是一个不太可扩展的想法。。。你会说我的担心是没有根据的吗?+1我实际上更喜欢第二种解决方案。请注意,
    nanmean
    是统计工具箱的一部分,但是,如果你没有自己的版本,编写自己的版本是很容易的。@Luis-我重新考虑了这个问题,并注意到了一些我想让你注意的事情:你和我使用的预分配方法的不同-你使用
    zero
    ,而我在角落里分配一个零。|虽然很奇怪,但我这么做的原因是我曾经听过的一次研讨会总结出来的。长话短说-使用
    zero
    会对性能产生很大影响(正确到2012年)。(披露:我个人并没有对此进行基准测试)我只是想提到,截至2015a,如果统计工具箱不可用,
    mean(…,'omitnan')
    是标准MATLAB的一部分。您不应该因为您对特定问题的解决方案在您看来效率低下而气馁。。。有时效率比效率更重要。一般来说,由于您花时间回答并看到了其他人的建议,因此您直接受益,其他人也将在将来因这个问题而停下来:-)
    RES = 
    
    180.0000         0         0    1.7180
    200.0000    0.7063    2.0717    1.7280
    210.0000         0         0    1.8127
    240.0000         0         0    1.9477
    270.0000         0         0    2.0565
    300.0000    0.7560    2.3490    2.0730