MATLAB 中忽略 NaN 的累积行值的平均计算

Posted

技术标签:

【中文标题】MATLAB 中忽略 NaN 的累积行值的平均计算【英文标题】:Mean computation from accumulated row values while ignoring NaN in MATLAB 【发布时间】:2014-09-03 15:27:42 【问题描述】:

我正在寻找有关如何优雅地解决以下问题的建议。虽然在我的具体情况下性能不是问题,但我会感谢 cmets 提供的良好做法。

提前致谢!

短版:

我正在尝试根据某些逻辑对矩阵行进行平均,同时忽略 NaN 值。我目前拥有的代码没有按照我想要的方式处理 NaN 值。

长版:

我的数据是按以下方式构建的:

单个(第一)列“箱”。每个 bin 的行数不是恒定的。箱不必是整数。行已预先排序。 可变数量的数据列,可能包括 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 ];

期望的结果是一个矩阵,第一列包含唯一的“bins”,其余部分表示“未被 NaN 破坏”,例如:

如果对于特定的列+bin,只有 NaN(在上面的示例中:第一个数据列+bin 210) - 结果将是 NaN。 如果对于特定的列+bin,存在 NaN 和数字的混合,则结果将是有效数字的平均值。在上面的例子中:第一个数据列+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 或数字的 column+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)更容易做到这一点 - 我很想听听意见。 在每列的基础上进行计算是我目前关于如何处理这个问题的想法。我只是想知道是否有一种方法可以对其进行泛化以立即获取完整的矩阵。

【问题讨论】:

+1 用于明确提出的问题。如果只有新用户提出这样的问题:) @Amro & rayryeng - 谢谢大家。这是因为我不认为用不恰当的问题折磨潜在读者是获得答案的好方法 :) 和 rayryeng - 如果你有其他解决方案,我不介意听到它! @Luis - 让我在 meta 上快速提出这个建议......:D @Dev-iL 你肯定会得到我的投票! :-) @Dev-iL - 这是一个很好的问题,得到了很好的回答 - ***.com/questions/24269516/…。我的最爱之一,由我们的常驻专家 Luis Mendo 回答 【参考方案1】:

这是另一种解决方案,虽然效率极低。此外,输出数组会将所有NaN 值设置为0。假设这对学术研究有好处。以下是我执行的步骤:

    对于第一列中的每个 ID,找到一个唯一列表。 对于其他列,将每一列拆分为一个元胞数组。 创建一个新元胞数组,其中每一列都附加有该元胞数组中每个元素的第一列 为每个包含NaN 值的元胞数组过滤掉那些行 对于过滤结果的每一列,运行 accumarray 并将 mean 作为函数句柄。 使用第 1 步中的 ID,索引每个 accumarray 结果并转换回矩阵

%// Step #1
num = unique(DATA(:,1));

%// Step #2
cells = mat2cell(DATA, size(DATA,1), ones(size(DATA,2),1));

%// Step #3
cellsAppend = cellfun(@(x) [DATA(:,1) x], cells(2:end), 'uni', false);

%// Step #4
cellsNonNaN = cellfun(@(x) x(~isnan(x(:,2)),:), cellsAppend , 'uni', false);

%// Step #5
cellsMean = cellfun(@(x) accumarray(x(:,1), x(:,2), [], @mean), cellsNonNaN, 'uni', false);

%// Step #6
selectCells = cellfun(@(x) x(num), append3, 'uni', false);
RES = [num cell2mat(selectCells)];

结果是:

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 电话的情况下,但我想这仍然是一个学术例子!

【讨论】:

您不应该因为您对特定问题的解决方案对您来说似乎效率低下而气馁......有时有效性比效率更重要。一般来说,既然您花时间回答并看到了其他人的建议 - 您直接受益,将来碰巧停止这个问题的其他人也会受益:-)【参考方案2】:

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

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。 may be faster,并避免了单元格引入的开销:

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];

【讨论】:

+1 - 非常好。不知道nanmean。也是寻找过渡的好方法。我的另一本书。 @Luis - 我喜欢你的第一个解决方案的精髓(事实上它是无循环的),但通过单元格似乎会产生额外的开销,因此感觉就像一个不太可扩展的想法性能 -明智的...你会说我的担心是没有根据的吗? +1 我实际上更喜欢第二种解决方案。请注意,nanmean 是统计工具箱的一部分,但如果您没有它,也可以轻松编写自己的版本。 @Luis - 我重新审视了这个问题,并注意到一些我想提醒您注意的事情:您和我使用的预分配方法的差异 - 您使用 zeros 而我在角落分配零. ||虽然很奇怪,但我这样做的原因是我曾经听过Yair Altman的一个研讨会,总结in this post。长话短说 - 使用zeros 对性能有很大影响(正确到 2012 年)。 (披露:我没有亲自对此进行基准测试) 我只是想我提到,截至 2015a,mean(...,'omitnan') 是标准 MATLAB 的一部分,如果统计工具箱不可用。

以上是关于MATLAB 中忽略 NaN 的累积行值的平均计算的主要内容,如果未能解决你的问题,请参考以下文章

如何在 pandas DataFrame 中忽略滚动平均值计算的 NaN 值?

如何在 Python 的滚动平均值计算中忽略 NaN

Matlab忽略矩阵中的NaN

Matlab矩阵乘法忽略nans?

求助!急!matlab中 NaN会参与运算吗

Matlab使用 MapReduce 计算平均值