我有一个小的MATLAB脚本(包含在下面),用于处理从具有两列和数十万行的CSV文件读取的数据。每个条目都是一个自然数,零只出现在第二列中。这段代码花费了相当多的时间(小时)来运行在最多几秒钟内应该可以实现的内容。分析器确定大约100%的运行时间花在写入零矩阵上,其大小根据输入而变化,但在所有使用中都小于1000x1000。
代码如下
function [data] = DataHandler(D)
n = size(D,1);
s = max(D,1);
data = zeros(s,s);
for i = 1:n
data(D(i,1),D(i,2)+1) = data(D(i,1),D(i,2)+1) + 1;
end
data = zeros(s,s);
行需要大约100%的运行时间。我可以通过更换1000行中的s来快速运行代码,这是一个足够的上限,以确保它不会遇到任何我正在查看的数据的错误。
显然有更好的方法可以做到这一点,但是我只是将代码混合在一起以快速格式化一些我并不太关心的数据。正如我所说的那样,为了我的目的,我只是用1000替换s来修复它,但是我很困惑为什么写这个矩阵会让MATLAB陷入困境几个小时。新代码即时运行。
如果有人之前见过这种行为,或者我知道为什么会发生这种行为,我会非常感兴趣。它有点令人不安,能够自信地在不杀死MATLAB的情况下自由地初始化矩阵将是一件好事。
答案 0 :(得分:4)
您对zeros
的来电不正确。查看代码,D
看起来像D x 2
数组。但是,您对s = max(D,1)
的调用实际上会生成另一个D x 2
数组。通过查阅max
的文档,当您以您使用的方式致电max
时会发生这种情况:
C = max(A,B)
返回与A
和B
大小相同的数组,其中最大元素取自A
或B
。A
和B
的维度相同,或者可以是标量。
因此,由于您使用了max(D,1)
,因此您实际上是将D
中的每个值与值1进行比较,因此您实际获得的只是D
的副本结束。使用此作为zeros
的输入具有相当不确定的行为。实际发生的是,对于s
的每一行,它将分配该大小的临时zeros
矩阵并抛出临时结果。只记录s
的最后一行的维度。因为你有一个非常大的矩阵D
,这可能就是为什么分析器在这里以100%的利用率挂起。因此,zeros
的每个参数都必须标量,但您生成s
的调用会生成矩阵。
我认为你的意图应该是:
s = max(D(:));
通过将D
展开到单个向量并找到总体最大值,可以找到矩阵D
的总体最大值。如果这样做,您的代码应该运行得更快。
作为旁注,这篇文章可能会让您感兴趣:
Faster way to initialize arrays via empty matrix multiplication? (Matlab)
在这篇文章中显示,执行zeros(n,n)
实际上很慢,并且有几个巧妙的技巧来初始化一个零数组。一种方法是通过空矩阵乘法来实现这一点:
data = zeros(n,0)*zeros(0,n);
我个人最喜欢的一个是,如果您认为data
未声明/初始化,您可以这样做:
data(n,n) = 0;
如果我也可以发表评论,for
循环效率很低。您正在做的是计算2D直方图/数据累积。您可以使用效率更高的accumarray
来代替for
循环。这也可以避免分配zeros
和accumarray
数组,为您做好准备。
因此,您的代码基本上会变成这样:
function [data] = DataHandler(D)
data = accumarray([D(:,1) D(:,2)+1], 1);
在这种情况下, accumarray
将采用D(i,1)
和D(i,2) + 1
存储在i = 1, 2, ..., size(D,1)
中的所有行和列坐标对,并将所有匹配的行和列放在一起坐标到一个单独的二维bin中,然后我们将所有出现加起来,此2D二进制位置的输出为您提供此二维区域中有多少值的总计数,这对应于映射到该位置的感兴趣的行和列坐标。