【发布时间】:2012-12-29 03:38:09
【问题描述】:
我有一个包含随机数矩阵的单元格,比如a = {[300*20],....,[300*20]};。我有另一个相同格式的单元格,称为b,其中包含nan 术语在a 中的位置逻辑。
我想使用cellfun 循环遍历单元格,基本上让nan 等于0 即a(b)=0。
谢谢, j
【问题讨论】:
标签: matlab indexing cell octave matrix-indexing
我有一个包含随机数矩阵的单元格,比如a = {[300*20],....,[300*20]};。我有另一个相同格式的单元格,称为b,其中包含nan 术语在a 中的位置逻辑。
我想使用cellfun 循环遍历单元格,基本上让nan 等于0 即a(b)=0。
谢谢, j
【问题讨论】:
标签: matlab indexing cell octave matrix-indexing
您可以定义一个将任何 NaN 替换为零的函数。
function a = nan2zero(a)
a(isnan(a)) = 0;
然后您可以使用cellfun 将此函数应用于您的元胞数组。
a0 = cellfun(@nan2zero, a, 'UniformOutput', 0)
这样,您甚至不需要任何矩阵b。
【讨论】:
b 中的矩阵已经存在,将它们用作函数的第二个输入会更有效。诚然,速度差异会很小,除非a 和b 真的 很大:-) 但是是的,+1
b 快得多。
b 应该更快,只要在函数内部没有分配给b。 Matlab 将处理 b "By Ref" 只要你不改变它。
cellfun 处理两个单元阵列的方法,并且将a 和b 中的每个矩阵组合起来会更糟。让我知道你得到了什么。如果 MATLAB 将 a(isnan(a)) = 0 作为一个整体进行优化,我敢打赌,b 看起来会很伤心。
首先,您可能应该给@s.bandara打勾,因为这是第一个正确答案,它使用了cellfun(根据您的要求)。不要给这个答案。这个答案的目的是提供一些额外的分析。
我想我会研究解决这个问题的一些可能方法的效率。
第一种方法是@s.bandara 提倡的方法。
第二种方法与@s.bandara 提倡的方法类似,但它使用b 将nan 转换为0,而不是使用isnan。理论上,这种方法可能更快,因为函数内部没有分配任何东西给b,所以应该“按引用”处理。
第三种方法使用循环来绕过使用cellfun,因为cellfun is often slower than an explicit loop
快速测速的结果是:
Elapsed time is 3.882972 seconds. %# First approach (a, isnan, and cellfun, eg @s.bandara)
Elapsed time is 3.391190 seconds. %# Second approach (a, b, and cellfun)
Elapsed time is 3.041992 seconds. %# Third approach (loop-based solution)
换句话说,通过传递b 而不是使用isnan 可以节省(少量)费用。使用循环而不是cellfun 可以进一步节省(少量)。但我不会因此而失眠。请记住,任何模拟的结果都特定于指定的输入。
注意,这些结果在多次运行中是一致的,我使用 tic 和 toc 来执行此操作,尽管每种方法都有许多循环。如果我想真正彻底,我应该使用 FEX 的timeit。如果有人有兴趣,这三种方法的代码如下:
%# Build some example matrices
T = 1000; N = 100; Q = 50; M = 100;
a = cell(1, Q); b = cell(1, Q);
for q = 1:Q
a{q} = randn(T, N);
b{q} = logical(randi(2, T, N) - 1);
a{q}(b{q}) = nan;
end
%# Solution using a, isnan, and cellfun (@s.bandara solution)
tic
for m = 1:M
Soln2 = cellfun(@f1, a, 'UniformOutput', 0);
end
toc
%# Solution using a, b, and cellfun
tic
for m = 1:M
Soln1 = cellfun(@f2, a, b, 'UniformOutput', 0);
end
toc
%# Solution using a loop to avoid cellfun
tic
for m = 1:M
Soln3 = cell(1, Q);
for q = 1:Q
Soln3{q} = a{q};
Soln3{q}(b{q}) = 0;
end
end
toc
%# Solution proposed by @EitanT
[K, N] = size(a{1});
tic
for m = 1:M
a0 = [a{:}]; %// Concatenate matrices along the 2nd dimension
a0(isnan(a0)) = 0; %// Replace NaNs with zeroes
Soln4 = mat2cell(a0, K, N * ones(size(a)));
end
toc
地点:
function x1 = f1(x1)
x1(isnan(x1)) = 0;
和:
function x1 = f2(x1, x2)
x1(x2) = 0;
更新:@EitanT 提出了第四种方法。这种方法将矩阵元胞数组连接成一个大矩阵,对大矩阵执行运算,然后可选择将其转换回元胞数组。我已将此过程的代码添加到上面的测试例程中。对于我的测试代码中指定的输入,即T = 1000、N = 100、Q = 50和M = 100,定时运行如下:
Elapsed time is 3.916690 seconds. %# @s.bandara
Elapsed time is 3.362319 seconds. %# a, b, and cellfun
Elapsed time is 2.906029 seconds. %# loop-based solution
Elapsed time is 4.986837 seconds. %# @EitanT
我对此感到有些惊讶,因为我认为@EitanT 的方法会产生最好的结果。在纸面上,这似乎非常明智。请注意,我们当然可以乱用输入参数来找到有利于不同解决方案的特定设置。例如,如果矩阵很小,但数量很大,那么@EitanT 的方法效果很好,例如T = 10、N = 5、Q = 500 和M = 100 产生:
Elapsed time is 0.362377 seconds. %# @s.bandara
Elapsed time is 0.299595 seconds. %# a, b, and cellfun
Elapsed time is 0.352112 seconds. %# loop-based solution
Elapsed time is 0.030150 seconds. %# @EitanT
这里@EitanT 的方法占主导地位。
对于 OP 指出的问题规模,我发现基于循环的解决方案通常具有最佳性能。但是,对于某些Q,例如Q = 5,@EitanT 的解决方案却成功地领先。
【讨论】:
M 来控制每种方法的迭代次数。但是您的代码(我在没有仔细检查的情况下粘贴了该代码)使用M 来测量矩阵维度之一的长度。我已经修复它,重新运行模拟,并调整了我的答案。干杯!
嗯。
鉴于元胞数组内容的性质,可能存在一个更更快的解决方案:您可以将元胞数据转换为单个矩阵并使用向量索引替换所有NaN 值立即进入其中,无需cellfun 或循环:
a0 = [a{:}]; %// Concatenate matrices along the 2nd dimension
a0(isnan(a0)) = 0; %// Replace NaNs with zeroes
如果你想将它转换回元胞数组,那很好:
[M, N] = size(a{1});
mat2cell(a0, M, N * ones(size(a)))
附言
如果可能,使用 3-D 矩阵而不是元胞数组。在 MATLAB 中,向量化运算通常要快得多。
【讨论】: