Matlab 矢量化代码的性能:从索引向量创建一个稀疏矩阵,每行1个

Matlab 矢量化代码的性能:从索引向量创建一个稀疏矩阵,每行1个,matlab,for-loop,vectorization,sparse-matrix,Matlab,For Loop,Vectorization,Sparse Matrix,我有一个大的列向量y,包含从1到10的整数值。我想把它转换成一个矩阵,其中每一行都是0,除了在y的相应行的值所给出的索引处的1之外 这个例子应该更清楚: y = [3; 4; 1; 10; 9; 9; 4; 2; ...] % gets converted to: Y = [ 0 0 1 0 0 0 0 0 0 0; 0 0 0 1 0 0 0 0 0 0; 1 0 0 0 0 0 0 0 0 0; 0 0 0 0 0 0 0 0 0 1; 0 0 0

我有一个大的列向量
y
,包含从1到10的整数值。我想把它转换成一个矩阵,其中每一行都是0,除了在
y
的相应行的值所给出的索引处的1之外

这个例子应该更清楚:

y = [3; 4; 1; 10; 9; 9; 4; 2; ...]

% gets converted to:

Y = [
    0 0 1 0 0 0 0 0 0 0;
    0 0 0 1 0 0 0 0 0 0;
    1 0 0 0 0 0 0 0 0 0;
    0 0 0 0 0 0 0 0 0 1;
    0 0 0 0 0 0 0 0 1 0;
    0 0 0 0 0 0 0 0 1 0;
    0 0 0 1 0 0 0 0 0 0;
    0 1 0 0 0 0 0 0 0 0;
    ...
    ]
我已经为此编写了以下代码(它可以工作):

我知道有很多方法可以删除代码中的for循环(矢量化)。包含以下内容,包括:

Y = full(sparse(1:length(y), y, ones(length(y),1)));
但是我必须将
y
转换为double才能使用它,结果实际上比我的“for”方法慢3倍,使用10.000.000作为
y
的长度

  • 对于非常大的
    y
    ,进行这种矢量化是否可能会带来更好的性能?我已经读过很多次,矢量化计算可以带来更好的性能(不仅仅是在MATLAB中),但这种解决方案似乎可以带来更多的计算

  • 在本例中,有没有一种方法可以比for方法实际提高性能?也许这里的问题很简单,对double而不是int进行操作并不是比较的最佳选择,但是我找不到一种方法来使用
    sparse


  • 这样的事情对你不管用吗

    tic;
    N = 1e6;
    y = randperm( N );
    Y = spalloc( N, N, N );
    inds = sub2ind( size(Y), y(:), (1:N)' );
    Y = sparse( 1:N, y, 1, N, N, N );
    toc
    
    上述产出

    运行时间为0.144683秒


    似乎您正在寻找完整的数字矩阵
    Y
    作为输出。因此,您可以尝试这种方法-

    m = numel(y);
    Y1(m,10) = 0; %// Faster way to pre-allocate zeros than using function call `zeros`
      %// Source - http://undocumentedmatlab.com/blog/preallocation-performance
    linear_idx = (y-1)*m+(1:m)'; %//'# since y is mentioned as a column vector, 
                                  %// so directly y can be used instead of y(:)
    Y1(linear_idx)=1; %// Y1 would be the desired output
    

    标杆管理 使用并稍微增加数据量-

    y = randi([1 10], [1.5e6 1], 'double');
    
    最后使用
    Y(m,10)=0执行前面提到的更快的预分配方案而不是
    Y=0(m,10),我在我的系统上得到了这些结果-

    >> testIndicatorMatrix
    ans =
        0.1798
        0.4651
        0.1693
        0.1457
    

    这就是这里提到的
    矢量化方法
    (基准测试套件中的最后一种)比
    for loop
    代码(基准测试套件中的第一种)的性能提高了15%以上。因此,如果您使用大数据量并打算获得稀疏矩阵的完整版本,这种方法将是有意义的(在我个人看来)。

    下面是一个测试:

    function [t,v] = testIndicatorMatrix()
        y = randi([1 10], [1e6 1], 'double');
        funcs = {
            @() func1(y);
            @() func2(y);
            @() func3(y);
            @() func4(y);
        };
    
        t = cellfun(@timeit, funcs, 'Uniform',true);
        v = cellfun(@feval, funcs, 'Uniform',false);
        assert(isequal(v{:}))
    end
    
    我得到:

    >> testIndicatorMatrix
    ans =
        0.0388
        0.1712
        0.0490
        0.0430
    

    这样一个简单的for循环可以在运行时进行动态JIT编译,并且运行速度非常快(甚至比矢量化代码稍微快一点)

    如果您将数组名称从
    y
    y
    更改为不同的名称,如
    x
    y
    ,可能会有所帮助。有一次我用
    ECG
    作为一个名字,它让我的代码运行得很慢,直到我意识到
    ECG
    是一个MATLAB函数。这是一个很好的建议;)也许读者也有点困惑。现在我不能更改它,或者所有的问题都需要修改,但我下次会记住。很好的基准测试+1.你能用
    Y(m,10)=0
    而不是最后两个
    funcs
    ,因为这必须加快它的速度;我怀疑创建指标矩阵的任务是代码中的瓶颈!对于一个包含数百万个元素的向量,它们都在几分之一秒内运行:)是的,但我想在这个问题的背景下,看看到目前为止提到的方法是如何公平的,我想这是公平的。该死,太多的“公平”用在那里:)对。我想我们应该提一提这个问题:事实上,最初的来源是这个,我想-我的意思是我首先在那里发现了这个。
    function Y = func1(y)
        m = numel(y);
        Y = zeros(m, 10);
        for i = 1:m
            Y(i, y(i)) = 1;
        end
    end
    
    function Y = func2(y)
        m = numel(y);
        Y = full(sparse(1:m, y, 1, m, 10, m));
    end
    
    function Y = func3(y)
        m = numel(y);
        Y = zeros(m,10);
        Y(sub2ind([m,10], (1:m).', y)) = 1;
    end
    
    function Y = func4(y)
        m = numel(y);
        Y = zeros(m,10);
        Y((y-1).*m + (1:m).') = 1;
    end
    
    >> testIndicatorMatrix
    ans =
        0.0388
        0.1712
        0.0490
        0.0430