MATLAB中忽略NaN时从累计行值计算平均值
我正在寻找如何优雅地解决以下问题的建议。虽然在我的具体案例中,性能不是一个问题,但我希望您能对良好实践提出意见 提前谢谢 简短版本: 我试图根据一些逻辑平均矩阵行,同时忽略NaN值。我当前拥有的代码没有以我想要的方式处理NaN值 长版本: 我的数据以以下方式构建:MATLAB中忽略NaN时从累计行值计算平均值,matlab,nan,accumarray,Matlab,Nan,Accumarray,我正在寻找如何优雅地解决以下问题的建议。虽然在我的具体案例中,性能不是一个问题,但我希望您能对良好实践提出意见 提前谢谢 简短版本: 我试图根据一些逻辑平均矩阵行,同时忽略NaN值。我当前拥有的代码没有以我想要的方式处理NaN值 长版本: 我的数据以以下方式构建: 一列(第一列)的“箱子”。每个箱子的行数不是恒定的。容器不必是整数。行是预先排序的 可变数量的数据列,可能包括NAN 下面是一个例子: DATA = [... 180 NaN NaN 1.733 180
- 一列(第一列)的“箱子”。每个箱子的行数不是恒定的。容器不必是整数。行是预先排序的
- 可变数量的数据列,可能包括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应给出
——注意此列+bin的除法为3(而不是4)(0.720+0.706+0.693)/3=0.7063
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 ];
附笔
一种可能的方法是:在第一列中查找更改(利用其预先排序的事实)并应用于每个行块:
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。就说这对学术研究有好处吧。以下是我所做的步骤:
NaN
值的行mean
作为函数句柄运行accumarray
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