M = sortrows(M,6);
IDX = diff(M(:,6));
tmp = find(IDX);
tmp = [0 ;tmp;size(M,1)];
for iSub= 2:numel(tmp)
avM2(iSub-1,:) = [nanmean(M(tmp(iSub-1)+1:tmp(iSub),1:5),1) tmp(iSub)-tmp(iSub-1)];tmp(iSub-1)];
end
这比您在我的计算机上的原始代码快60倍。加速主要来自对数据进行预排序,然后找到所有下标更改的位置。这样,每次不必遍历整个数组以找到正确的下标,而是仅在每次迭代中检查必要的内容。因此,您可以在约100行上计算平均值,而无需首先检查在该迭代中是否需要每行1,000,000行。
因此:在原始代码中,您检查numel(uniqueSubs)
,在这种情况下为10,000,看所有N
,在这里为1,000,000,数字是否属于某个类别,这导致了10^12次检查。建议的代码对行进行排序(排序是NlogN
,因此在此处为6,000,000),然后循环一次完整的数组而不进行额外的检查。
为了完成,这里是原始代码和我的版本,两者是相同的:
N = 10^6;
K = 10^4;
subs = randi([1 K],N,1);
M = [randn(N,5) subs];
M(M<-1.2) = nan;
uniqueSubs = unique(M(:,6));
avM = nan(numel(uniqueSubs),7);
tic
uniqueSubs = unique(M(:,6));
for iSub = 1:numel(uniqueSubs)
tmpM = M(M(:,6)==uniqueSubs(iSub),1:5);
avM(iSub,:) = [nanmean(tmpM,1) size(tmpM,1) uniqueSubs(iSub)];
end
toc
avM = sortrows(avM,7);
avM2 = nan(numel(uniqueSubs),6);
tic
M = sortrows(M,6);
IDX = diff(M(:,6));
tmp = find(IDX);
tmp = [0 ;tmp;size(M,1)];
for iSub = 2:numel(tmp)
avM2(iSub-1,:) = [nanmean(M(tmp(iSub-1)+1:tmp(iSub),1:5),1) tmp(iSub)-tmp(iSub-1)];
end
toc
all(avM(:,1:6) == avM2)
Elapsed time is 58.561347 seconds.
Elapsed time is 0.843124 seconds.
ans =
1×6 logical array
1 1 1 1 1 1
mean
函数的'omitnan'
版本,因为它存在于基础 MATLAB 中并且执行完全相同的操作。 - Edricfindgroups
和/或splitapply
在底层进行了检查,而我在循环中省略了吗? - Adriaanfindgroups
和splitapply
都是MATLAB代码,在我的机器上,两者都没有明显的浪费。我认为findgroups
和splitapply
更易读,并且(现在我已经学会了它们)我认为更多的人应该利用它们。 - Edric