Column index search for 1 in each row of the matrix

Matlab has the following matrix:

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 a loop) define a column vector so that the first element is 2 if there are 1 in the second column, the second element is 3 for one in the third column, etc.? The above example should look like this:

 M = [ 3 1 2 1 3]; 
+5
source share
3 answers

In fact, you can solve this with simple matrix multiplication.

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

This works by multiplying the M x 3 matrix by a 3 x 1 array, where 3x1 elements are simple [1; 2; 3] [1; 2; 3] [1; 2; 3] . In short, for each row of M multiplication by elements is performed using an array of 3 x 1. Only 1 in row M will produce the result. Then the result of this multiplication by elements is summed up. Since you only have one “1” per row, the result will be the index of the column that contains this number.

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 make a comparison to see how the characteristics of different methods are compared.

To set up the test matrix ( M ), I created the form matrix asked in the original question and changed the number of rows. Which column had 1 was randomly selected using randi([1 nCols], size(M, 1)) . Runtime was parsed using timeit .

When you start using M type double (default is MATLAB) you get the following runtime.

enter image description here

If M is logical , then matrix multiplication takes a hit because it must be converted to a number type before matrix multiplication, while the other two improve performance a bit.

enter image description here

Here is the test code 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 
+5
source

You can also abuse find and observe the position of the transpose line of M You must first migrate the matrix since find works in the main column order:

 M = [0 0 1 1 0 0 0 1 0 1 0 0 0 0 1]; [out,~] = find(M.'); 

Not sure if this is faster than matrix multiplication.

+2
source

Another approach: use the second max output:

 [~, result] = max(M.', [], 1); 

Or, as suggested by @rayryeng , use max on the second dimension instead of transposing M :

 [~, result] = max(M, [], 2); 

For

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

this gives

 result = 3 1 2 1 3 

If M contains more than one 1 in a given row, this will give the index of the first such 1 .

+2
source

Source: https://habr.com/ru/post/1244922/


All Articles