我有以下数据:
N = 10^3;
x = randn(N,1);
y = randn(N,1);
z = randn(N,1);
f = x.^2+y.^2+z.^2;
现在,我想将此连续的3D空间拆分为nB
个容器。
nB = 20;
[~,~,x_bins] = histcounts(x,nB);
[~,~,y_bins] = histcounts(y,nB);
[~,~,z_bins] = histcounts(z,nB);
然后将每个多维数据集的平均值f
或nan
放入多维数据集中,如果没有观测值:
F = nan(50,50,50);
for iX = 1:20
for iY = 1:20
for iZ = 1:20
idx = (x_bins==iX)&(y_bins==iY)&(z_bins==iZ);
F(iX,iY,iZ) = mean(f(idx));
end
end
end
isosurface(F,0.5)
这段代码可以满足我的要求。我的问题是速度。当N > 10^5
和nB = 100
时,此代码非常慢。
如何加快此代码的速度?
我还尝试了accumarray()
函数:
subs=([x_bins,y_bins,z_bins]);
F2 = accumarray(subs,f,[],@mean);
all(F(:) == F2(:)) % false
但是,此代码会产生不同的结果。
答案 0 :(得分:3)
OP中代码的问题在于,它针对输出数组中的每个元素测试数据的所有元素。输出数组包含nB^3
个元素,数据包含N
个元素,因此算法为O(N*nB^3
)。取而代之的是,可以在输入的N
元素上循环,并在输出数组中设置相应的元素,这是一个操作O({N
)(下面的第二个代码块)。
OP中的accumarray
解决方案需要使用fillvals
参数,并将其设置为NaN
(下面的第三个代码块)。
要比较结果,需要显式测试两个数组在相同位置具有NaN
,并且在其他位置具有相同的非NaN值:
all( ( isnan(F(:)) & isnan(F2(:)) ) | ( F(:) == F2(:) ) )
% \-------same NaN values------/ \--same values--/
这是代码。所有这三个版本产生相同的结果。 Octave 4.4.1中的时序(无JIT),在MATLAB中,循环代码应该更快。 (使用来自OP的输入数据,N=10^3
和nB=20
)。
%% OP's code, O(N*nB^3)
tic
F = nan(nB,nB,nB);
for iX = 1:nB
for iY = 1:nB
for iZ = 1:nB
idx = (x_bins==iX)&(y_bins==iY)&(z_bins==iZ);
F(iX,iY,iZ) = mean(f(idx));
end
end
end
toc
% Elapsed time is 1.61736 seconds.
%% Looping over input, O(N)
tic
s = zeros(nB,nB,nB);
c = zeros(nB,nB,nB);
ind = sub2ind([nB,nB,nB],x_bins,y_bins,z_bins);
for ii=1:N
s(ind(ii)) = s(ind(ii)) + f(ii);
c(ind(ii)) = c(ind(ii)) + 1;
end
F2 = s ./ c;
toc
% Elapsed time is 0.0606539 seconds.
%% Other alternative, using accumarray
tic
ind = sub2ind([nB,nB,nB],x_bins,y_bins,z_bins);
F3 = accumarray(ind,f,[nB,nB,nB],@mean,NaN);
toc
% Elapsed time is 0.14113 seconds.