matlab - 复制/索引 3D 矩阵可变部分的最快方法
问题描述
我有大量 3D 数据,其中包含在 2D 空间中获取的 1D 信号。处理这些数据的第一步是对所有信号进行阈值处理,以找到一个高振幅脉冲的到达。该脉冲存在于所有信号中,并且在不同时间到达。阈值处理后,应重新排序 3D 数据集,以便每个信号在脉冲到达时开始,而之前的信号被丢弃(信号的结尾并不重要,因为现在我将零连接到所有的结尾信号,因此数据保持相同的大小)。
现在,我通过以下方式实现了这一点:
首先,我首先计算所有信号中第一个超过阈值的样本的样本数
M = randn(1000,500,500); % example matrix of realistic size
threshold = 0.25*max(M(:,1,1)); % 25% of the maximum in the first signal as threshold
[~,index] = max(M>threshold); % indices of first sample exceeding threshold in all signals
接下来,我希望对所有信号进行移位,以便它们都以脉冲开始。现在,我已经以这种方式实现了它:
outM = zeros(size(M)); % preallocation for speed
for i = 1:size(M,2)
for j = 1:size(M,3)
outM(1:size(M,1)+1-index(1,i,j),i,j) = M(index(1,i,j):end,i,j);
end
end
这很好用,而且我知道 for 循环不再那么慢了,但是对于我机器上的数据集来说,这很容易花费几秒钟。for 循环的单次迭代大约需要 0.05-0.1 秒,这对我来说似乎很慢,因为我只是复制了一个包含 500-2000 个双精度值的向量。
因此,我已经研究了解决这个问题的最佳方法,但目前我还没有找到更好的方法。我尝试了几件事:3D 蒙版、线性索引和并行循环 (parfor)。
对于 3D 蒙版,我检查了是否有任何改进的可能。因此,我首先构造一个逻辑掩码,然后将逻辑掩码索引/复制的速度与双嵌套 for 循环进行比较。
%% set up for logical mask copying
AA = logical(ones(500,1)); % only copy the first 500 values after the threshold value
Mask = logical(zeros(size(M)));
Jepla = zeros(500,size(M,2),size(M,3));
for i = 1:size(M,2)
for j = 1:size(M,3)
Mask(index(1,i,j):index(1,i,j)+499,i,j) = AA;
end
end
%% speed comparison
tic
Jepla = M(Mask);
toc
tic
for i = 1:size(M,2)
for j = 1:size(M,3)
outM(1:size(M,1)+1-index(1,i,j),i,j) = M(index(1,i,j):end,i,j);
end
end
toc
for 循环每次都更快,即使复制的内容更多。
接下来,线性索引。
%% setup for linear index copying
%put all indices in 1 long column
LongIndex = reshape(index,numel(index),1);
% convert to linear indices and store in new variable
linearIndices = sub2ind(size(M),LongIndex,repmat(1:size(M,2),1,size(M,3))',repelem(1:size(M,3),size(M,2))');
% extend linear indices with those of all values to copy
k = zeros(numel(M),1);
count = 1;
for i = 1:numel(LongIndex)
values = linearIndices(i):size(M,1)*i;
k(count:count+length(values)-1) = values;
count = count + length(values);
end
k = k(1:count-1);
% get linear indices of locations in new matrix
l = zeros(length(k),1);
count = 1;
for i = 1:numel(LongIndex)
values = repelem(LongIndex(i)-1,size(M,1)-LongIndex(i)+1);
l(count:count+length(values)-1) = values;
count = count + length(values);
end
l = k-l;
% create new matrix
outM = zeros(size(M));
%% speed comparison
tic
outM(l) = M(k);
toc
tic
for i = 1:size(M,2)
for j = 1:size(M,3)
outM(1:size(M,1)+1-index(1,i,j),i,j) = M(index(1,i,j):end,i,j);
end
end
toc
同样,另一种方法,线性索引,(很多)慢。失败后,我了解了并行化,尽管这肯定会加快我的代码速度。通过阅读 parfor 周围的一些文档并尝试一下,我将代码更改为以下内容:
gcp;
outM = zeros(size(M));
inM = mat2cell(M,size(M,1),ones(size(M,2),1),size(M,3));
tic
parfor i = 1:500
for j = 1:500
outM(:,i,j) = [inM{i}(index(1,i,j):end,1,j);zeros(index(1,i,j)-1,1)];
end
end
end
toc
我改变了它,以便“outM”和“inM”都是切片变量,因为我读到这是最好的。这仍然很慢,比原来的 for 循环慢很多。
那么现在的问题是,我应该放弃尝试提高此操作的速度吗?还是有另一种方法可以做到这一点?我已经搜索了很多,现在不知道如何加快速度。很抱歉这个问题很长,但我想展示我尝试过的东西。先感谢您!
解决方案
不确定您的情况是否有一个选项,但看起来单元数组实际上在这里更快:
outM2 = cell(size(M,2),size(M,3));
tic;
for i = 1:size(M,2)
for j = 1:size(M,3)
outM2{i,j} = M(index(1,i,j):end,i,j);
end
end
toc
第二个想法也更快出来,批处理所有必须移动相同值的数据:
tic;
for i = 1:unique(index).'
outM(1:size(M,1)+1-i,index==i) = M(i:end,index==i);
end
toc
如果这种方法实际上更快,这完全取决于您的数据。 是的,整数值和逻辑索引可以混合使用
推荐阅读
- bootstrap-table - wenzhixin 将记录追加到现有的引导表
- python - Matplotlib 图例中出现相同的符号
- postgresql - 如何连接到 OpenMapTiles Docker Postgres DB
- swift - 如何区分位于 2 个不同类中的表视图
- python - “user_id”列中的空值违反非空约束细节:失败行包含(34、30、空)
- couchdb - PouchDB 复制与 CouchDB 的版本兼容性
- liferay-7 - 我想在 Liferay 7.x 中禁用浏览器缓存
- r - 无法在 R 中导入 JSON 文件
- android - Wear OS:通知不会显示
- c++ - 如何“传递”命名空间作为参数?