向量化插值 MATLAB

4
我有一组N个测量值,记录于S个时刻(对于不同的测量值,时刻是不同的)。我有两个矩阵:
V - 一个NxS的矩阵,表示测量值
T - 一个NxS的矩阵,表示测量时间
我想生成一个VI矩阵,来代表在时刻TI的线性插值测量值。
以下是非向量化版本的代码:
tic;
VI = zeros([size(V,1), size(TI,2)]);
for j = 1:size(V,1)
    VI(j,:) = interp1(T(j,:),V(j,:),TI);
end
toc;

我希望重写这段代码,消除for循环,改用矩阵运算和函数实现。是否可以向量化?


请提供一些数据来改进您的解决方案。该配置文件将帮助我们了解瓶颈所在。 - Andrey Rubshtein
@Andrey - 抱歉,这个问题的重点不是“我如何加快代码的速度?” 而是 - “是否可以使用矩阵操作/函数来消除 for 循环?” 我将编辑问题以删除与执行时间有关的参考,这是一个红鲱鱼。 - Marc
你看,循环并不是邪恶的。你说性能不是你的主要关注点。你想向量化的原因是什么?你想为读者提供更清晰的代码吗?也许是为了节省内存空间? - Andrey Rubshtein
  1. 代码的清晰易懂度;
  2. 正确使用 MATLAB 语言和内置函数;
  3. 如果存在向量化内置函数,则可能实现相当大(数量级)的性能提升。
- Marc
(1) - 我认为你的代码非常优秀清晰。 (2) - 这是非常主观的。 (3) - 也许你可以在不进行向量化的情况下提高性能,值得一试! - Andrey Rubshtein
4个回答

2
没有数据和运行分析工具很难做出任何判断,但如果您的数据已经排序,可以使用interp1q代替interp,这样就不需要对数据进行任何检查。
引用Matlab帮助文档:
为使interp1q正常工作,x必须是单调递增的列向量。Y必须是长度为x行数的列向量或矩阵。xi必须是列向量。

1
感谢您的建议;使用interp1q函数替代后,执行时间减少了15%。 - Marc

1
Matlab将数据按列而不是行打包,因此我怀疑只需将循环从行更改为列,您就会看到速度有所提高:
[N, S] = size(V);

VT = V';                             % Value series in columns
TT = T';                             % Time series in columns
VIT = zeros(length(TI), N);          % Interpolated value series in columns

for j = 1:N
    VIT(:, j) = interp1(VT(:, j), TT(:, j), TI);
end

VI = VIT';                           % Interpolated value series in rows

很遗憾,我认为无法进一步改善此代码的性能,因为不同的值系列没有相关的时间序列。如果它们有相同的时间,以便我们可以将T折叠成一个向量,使length(T) = S,那么这将很容易实现:

VIT = interp1(VT, T, TI);            % (see VIT and VT from above)

尝试过了,但并没有加速执行。不过还是谢谢你的建议! - Marc

1

如果您所说的所有测量都有不同的测量时间(T是矩阵而不是向量),您可以通过一次对arrayfun的调用来实现所需操作,如下所示:

VI = arrayfun(@(x)(interp1(T(x,:),V(x,:),TI)), 1:size(V, 1), 'UniformOutput', false);
VI = cell2mat(VI');

arrayfun类似于循环,但由于它是一个内部的matlab函数,所以它可能更快。它返回一个向量的单元格,因此第二行确保您有一个矩阵作为输出。您可能不需要它-这取决于您稍后对数据要做什么。

另一方面,如果在不同的N值下同时进行了测量(T是大小为S的向量,而不是矩阵,或者换句话说,T的所有行都相等),则可以在一次调用interp1中进行插值。

VI = interp1(T(1,:), V', TI)

在interp1内插列时,您必须转置V。这是因为MATLAB按列存储矩阵(列在内存中是连续的)。如果将V作为SxN矩阵传递,则可能允许更有效的interp1并行化,因为所有CPU可以更有效地访问内存。因此,我建议您在整个代码中转置矩阵,除非您在某些其他地方依赖于此确切的数据布局以提高性能。

编辑由于矩阵的列布局,您的原始代码可以通过转置矩阵并按列处理来改进。对于N = 1000,S = 10000和TI为10000个元素,以下版本在我的计算机上快约20%。由于更有效的内存带宽利用,它可能会随着系统大小而增长。

tic;
VI = zeros(size(TI,2), size(V,2));
for j = 1:size(V,2)
    VI(:,j) = interp1(T(:,j),V(:,j),TI);
end
toc;

尝试了数组函数,与for循环相比并没有加快执行速度。感谢您的建议! - Marc
@Marc,你可以在你的问题中表明速度是目标。请查看更新的答案。 - angainor

0
我现在在工作,没有时间熟悉interp1(我以前从未使用过),如果下面的回答不当,请提前道歉。
话虽如此,由于您的循环迭代之间没有依赖关系,因此应该可以向量化处理。我觉得您可以使用mat2cellcellfun来消除显式循环。
以下是我的一个简单示例:
NumRow = 4;
NumCol = 3;
V = randn(NumRow, NumCol);
VCell = mat2cell(V, ones(NumRow, 1), NumCol);
A = cellfun(@sum, VCell);

当然,我做的事情等同于sum(V,2)。但我认为我使用的方法也适用于你的情况。 mat2cell函数将V转换为一个单元列向量,其中每个单元格包含V的一行。然后对VCell中的每个单元格应用sum函数,并将结果返回到A中的cellfun调用。您可以尝试简单地将@sum替换为@interp1,并当然适当地调整cellfun的输入。

如果您无法使其正常工作,就请告诉我,我会在回家后尽力为您提供更明确的解决方案。如果您成功了,但速度并没有显著提高,我会非常感兴趣知道这一点,请在此处发布结果。


使用cellfun是否真的比for循环更好?MATLAB是否会在cellfun中进行一些聪明且并行的操作? - Marc
@Marc 先生,这是一个值得探讨的问题!我不知道。这就是为什么我说如果使用它可以加速事情的话,我会很感兴趣知道 :-) 所以我变得好奇并进行了一些基本测试。结果很有趣。我将把它们发布为新的SO问题。 - Colin T Bowers
@Marc 我在这个话题上的新SO问题在这里 - Colin T Bowers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接