Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding the column index for the 1 in each row of a matrix

Tags:

matrix

matlab

I have the following matrix in Matlab:

M = [0 0 1
     1 0 0
     0 1 0
     1 0 0
     0 0 1];

Each row has exactly one 1. How can I (without looping) determine a column vector so that the first element is a 2 if there is a 1 in the second column, the second element is a 3 for a one in the third column etc.? The above example should turn into:

M = [ 3
      1
      2
      1
      3];
like image 321
machinery Avatar asked Mar 11 '16 22:03

machinery


1 Answers

You can actually solve this with simple matrix multiplication.

result = M * (1:size(M, 2)).';

     3
     1
     2
     1
     3

This works by multiplying your M x 3 matrix with a 3 x 1 array where the elements of the 3x1 are simply [1; 2; 3]. Briefly, for each row of M, element-wise multiplication is performed with the 3 x 1 array. Only the 1's in the row of M will yield anything in the result. Then the result of this element-wise multiplication is summed. Because you only have one "1" per row, the result is going to be the column index where that 1 is located.

So for example for the first row of M.

element_wise_multiplication = [0 0 1] .* [1 2 3]

    [0, 0, 3]

sum(element_wise_multiplication)

    3

Update

Based on the solutions provided by @reyryeng and @Luis below, I decided to run a comparison to see how the performance of the various methods compared.

To setup the test matrix (M) I created a matrix of the form specified in the original question and varied the number of rows. Which column had the 1 was chosen randomly using randi([1 nCols], size(M, 1)). Execution times were analyzed using timeit.

When run using M of type double (MATLAB's default) you get the following execution times.

enter image description here

If M is a logical, then the matrix multiplication takes a hit due to the fact that it has to be converted to a numerical type prior to matrix multiplication, whereas the other two have a bit of a performance improvement.

enter image description here

Here is the test code that I used.

sizes = round(linspace(100, 100000, 100));
times = zeros(numel(sizes), 3);

for k = 1:numel(sizes)
    M = generateM(sizes(k));
    times(k,1) = timeit(@()M * (1:size(M, 2)).');
    M = generateM(sizes(k));
    times(k,2) = timeit(@()max(M, [], 2), 2);
    M = generateM(sizes(k));
    times(k,3) = timeit(@()find(M.'), 2);
end

figure
plot(range, times / 1000);
legend({'Multiplication', 'Max', 'Find'})
xlabel('Number of rows in M')
ylabel('Execution Time (ms)')

function M = generateM(nRows)
    M = zeros(nRows, 3);
    col = randi([1 size(M, 2)], 1, size(M, 1));
    M(sub2ind(size(M), 1:numel(col), col)) = 1;
end
like image 64
Suever Avatar answered Oct 08 '22 02:10

Suever