Matlab将向量转换为二进制矩阵

4
我有一个大小为(m,1)的向量v,其元素是从1到n选择的整数。我想创建一个大小为(m,n)的矩阵M,其元素M(i,j)在v(i)=j时为1,在其他情况下为0。我不想使用循环,并希望仅通过简单的向量-矩阵操作实现此目标。
因此,我首先考虑创建一个具有重复元素的矩阵。
 M = v * ones(1,n) % this is a (m,n) matrix of repeated v

例如,v=[1,1,3,2]',其中m=4,n=3。
M =
     1     1     1
     1     1     1
     3     3     3
     2     2     2

接下来我需要创建一个大小为(1,n)的比较向量c。

c = 1:n
1 2 3

然后我需要执行一系列的逻辑比较。

M(1,:)==c % this results in [1,0,0]
.
M(4,:)==c % this results in [0,1,0]

然而,我认为可以通过紧凑的矩阵符号执行遍历每一行的最后一步骤,但是我被卡住了,对索引不够了解。

最终结果应该是:

M =
     1     0     0
     1     0     0
     0     0     1
     0     1     0
3个回答

5
一个非常简单的bsxfun函数调用就可以完成这个技巧:
>> n = 3;
>> v = [1,1,3,2].';
>> M = bsxfun(@eq, v, 1:n)

M =

     1     0     0
     1     0     0
     0     0     1
     0     1     0

代码的工作原理其实非常简单。 bsxfun 是所谓的二元单例扩展函数。它的作用是提供两个任意大小的数组/矩阵,只要它们是可广播的即可。这意味着它们需要能够扩展到相同的大小。在这种情况下,v 是您感兴趣的向量,是第一个参数 - 请注意它已经被转置了。第二个参数是从1到n的向量。现在会发生的是,列向量v会被复制/ 扩展,直到有与n一样多的值,第二个向量会被复制,直到有与v中的行数一样多。然后我们在这两个数组之间进行eq /等于运算符。 这个扩展矩阵实际上在第一列中有所有的1,在第二列中有所有的2,一直到n。通过在这两个矩阵之间执行eq,您实际上正在确定v中哪些值等于相应的列索引。

这是一个详细的时间测试和每个函数的分解。我将每个实现放入单独的函数中,并且我还让n=max(v),以便Luis的第一段代码可以工作。我使用timeit来计时每个函数:

function timing_binary

n = 10000;
v = randi(1000,n,1);
m = numel(v);

    function luis_func()
    M1 = full(sparse(1:m,v,1));       
    end

    function luis_func2()
    %m = numel(v);
    %n = 3; %// or compute n automatically as n = max(v);
    M2 = zeros(m, n);
    M2((1:m).' + (v-1)*m) = 1;      
    end

    function ray_func()
    M3 = bsxfun(@eq, v, 1:n);
    end

    function op_func()
    M4= ones(1,m)'*[1:n] == v * ones(1,n);
    end

t1 = timeit(@luis_func);
t2 = timeit(@luis_func2);
t3 = timeit(@ray_func);
t4 = timeit(@op_func);

fprintf('Luis Mendo - Sparse: %f\n', t1);
fprintf('Luis Mendo - Indexing: %f\n', t2);
fprintf('rayryeng - bsxfun: %f\n', t3);
fprintf('OP: %f\n', t4);


end

这个测试假设n = 10000,向量v是一个10000 x 1的随机分布整数向量,范围从1到1000。顺便说一句,我不得不修改Luis的第二个函数,以使索引工作,因为加法需要具有兼容维度的向量。运行此代码,我们得到:
>> timing_binary
Luis Mendo - Sparse: 0.015086
Luis Mendo - Indexing: 0.327993
rayryeng - bsxfun: 0.040672
OP: 0.841827

路易斯·门多的代码sparse获胜(正如我所预期的那样),其次是bsxfun,其次是索引,然后是使用矩阵操作的您提出的方法。时间以秒为单位。

哦,糟糕,我正在写这个完全相同的答案哈哈,我肯定打字太慢了! - Benoit_11
@LuisMendo - 谢谢!:D 我不得不稍微改一下,因为我读题太快了。 - rayryeng
1
@LuisMendo 会不同意你的看法。 .' 是执行转置操作,无论数组是实数还是复数。 单独使用 ' 将执行复杂转置。 我使用点号是因为我明确想要表达这一点。 虽然这更多是一种风格选择。 在你的情况下,你没有虚部,所以你可以使用任何一个,但我习惯使用 .'。 不使用它已经让我吃过几次亏...所以我习惯使用它。 - rayryeng
我将运行一些时间测试,看看哪个更快。 - rayryeng
1
@gciriani 我总是建议:当你只想转置时,请使用.'!以下是两个使用'而意思应该是.'的错误示例:https://dev59.com/Tn3aa4cB1Zd3GeqPcFok http://stackoverflow.com/questions/23509241/whats-the-difference-between-two-way-to-input-matlab-complex-matrices - Luis Mendo
显示剩余4条评论

3
假设 n 等于 max(v),你可以使用sparse
v = [1,1,3,2];
M = full(sparse(1:numel(v),v,1));

sparse 的作用是使用第一个参数作为行索引,第二个参数作为列索引,第三个参数作为矩阵的值来构建稀疏矩阵。然后将其转换为完整矩阵,使用full


另一种方法是定义一个最初包含零的矩阵,然后使用线性索引来填充其中的“1”:
v = [1,1,3,2];
m = numel(v);
n = 3; %// or compute n automatically as n = max(v);
M = zeros(m, n);
M((1:m) + (v-1)*m) = 1;

1

我认为我也找到了一种方法,希望有人能告诉我在处理非常大的向量和矩阵时哪种方法更快。我想到的额外方法如下:

M= ones(1,m)'*[1:n] == v * ones(1,n)

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