2

我正在使用一个 30*26000 大小的矩阵,该矩阵的开头和结尾都有 NaN。NaN 也散布在每一行中。我可以用线性插值填充 NaN,但这会将 NaN 留在每行的开头和结尾。推断在末端替换这些 NaN 对我的数据集来说并不理想。

我只想修剪矩阵。以 3 x 6 矩阵为例:

NaN NaN 1 2  3  NaN
NaN  1  2 3 NaN NaN
 1  NaN 2 3  4   5

切断最左边和最右边的列,使任何行都以 NaN 开头结尾。

1 2
2 3
2 3

所以我们留下了一个 3 x 2 矩阵。

我怎样才能在 Matlab 中做到这一点?(速度优化;我需要将其应用于一百万大小的矩阵)

谢谢!

4

3 回答 3

7

对于您的示例,您可以执行以下操作:

让您的矩阵带有 NaN 和数值。

ind1 = sum(isnan(a),1); % count the NaN values along columns

s = find(ind1 == 0, 1, 'first'); % find the first column without any NaN

e = find(ind1 == 0, 1, 'last'); % find the last column without any NaN

所以现在只需将这部分矩阵从第 s 列保留到第 e 列:

b = a(:,s:e);

如果没有一列不包含 NaN,则可能需要进行额外检查。

于 2012-11-21T23:46:17.947 回答
2

早期提出的两种解决方案都很棒,为了完整起见,我发布了这个单行:

A(:,isfinite(sum(A)))

ans =

 1     2
 2     3
 2     3

它通过首先计算行总和然后调用isfinite. 我还删除了find调用 - 它们不是必需的,因为您可以使用逻辑索引。

我这里没有我的电脑,所以我省略了性能测试。

于 2012-11-22T11:52:19.670 回答
2

首先,argyris 的矢量化解决方案可以很好地工作 (+1)。我发布这个只是因为你强调你想要一个速度优化的解决方案。好吧,argyris 解决方案的缺点是sumandisnan操作是在整个矩阵上执行的。如果您必须在任一侧走很长一段路才能找到第一个非 NaN 列,这将是最佳选择。但如果你不这样做呢?一个基于循环的解决方案利用您可能只需要进入几列这一事实可能会做得更好(特别是考虑到 JIT 加速器在快速执行单个循环方面有多好)。我整理了一个速度测试,其中包括 argyris 和我的解决方案:

%#Set up an example case using the matrix size you indicated in the question
T = 30;
N = 26000;
X = rand(T, N);
TrueL = 8;
TrueR = N - 8;
X(:, 1:TrueL) = NaN;
X(:, TrueR:end) = NaN;

%#argyris solution
tic
I1 = sum(isnan(X));
argL = find(I1 == 0, 1, 'first');
argR = find(I1 == 0, 1, 'last');
Soln1 = X(:, argL:argR);
toc

%#My loop based solution (faster if TrueL and TrueR are small)
tic
for n = 1:N
    if ~any(isnan(X(:, n)))
        break
    end
end
ColinL = n;
for n = N:-1:1
    if ~any(isnan(X(:, n)))
        break
    end
end
ColinR = n;
Soln2 = X(:, ColinL:ColinR);
toc

在上面的例子中,解决方案需要去掉前 8 列和后 8 列。速度测试的结果?

Elapsed time is 0.002919 seconds. %#argyris solution
Elapsed time is 0.001007 seconds. %#My solution

基于循环的解决方案几乎快 3 倍。好的,现在让我们将两边需要删除的列数增加到 100:

Elapsed time is 0.002769 seconds. %#argyris solution
Elapsed time is 0.001999 seconds. %#My solution

还在前面。两边各有 1000 列呢?

Elapsed time is 0.003597 seconds. %#argyris solution
Elapsed time is 0.003719 seconds. %#My solution

所以我们找到了我们的临界点(至少在我的机器上——四核 i7、Linux Mint v12、Matlab R2012b)。一旦我们需要在任一侧输入大约 1000 列,我们最好使用矢量化解决方案。

最后一点注意事项:如果例程发生在另一个(可能不相关的)循环中,则应重新进行速度比较。这是因为我的解决方案现在将涉及双循环。即使循环是不相关的,JIT 加速器对双循环也不是很好。我在我的机器上做了一些快速测试,对于小型 TrueL 和 TrueR(即小于 100),我的解决方案仍然领先,但优势不如没有外部循环时那么大。

无论如何,希望这对您或其他任何来阅读的人有用。

干杯!

编辑:我做了一些速度测试,结合了 angainor 非常简洁的单线 (+1)。当要删除的列数很少时,它的性能几乎与我的基于循环的解决方案一样好。令人惊讶的是,与 argyris 的解决方案不同,当要删除的列数很大时,它的扩展性并不好。不过,这可能与我现在使用的计算机有关:工作 Windows 机器 - 我从来没有真正完全信任它 :-)

于 2012-11-22T02:01:14.807 回答