Vectorize columns replacement based on a vector - MATLAB - matlab

I want to replace value of n-th column of a matrix, based on a vector values, in a vectorized way.
Input:
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
Vector:
[2]
[4]
[1]
[3]
Expected output:
[0 1 0 0]
[0 0 0 1]
[1 0 0 0]
[0 0 1 0]
Matlab code with for-loop:
A = zeros(4,4);
b = [2; 4; 1; 3];
for row=1:4
A(row, b(row)) = 1;
endfor
Matlab code with sub2ind:
A = zeros(4,4);
b = [2; 4; 1; 3];
c = [[1:length(b)]' b];
A(sub2ind(size(A), c(:,1), c(:,2))) = 1;
Is there more vectorized way in Matlab? Thank you.

You can use the raw version of sub2ind to solve it in a vectorized manner -
A( (b(:)-1)*size(A,1) + [1:numel(b)]' ) = 1;
How it works: Since elements of b are the column indices and MATLAB follows column-major indexing, so we need to multiply each such column index with the number of rows in A to get us the number of elements before starting that column, which would be (b(:)-1)*size(A,1). Then, add the corresponding row indices i.e. [1:numel(b)]' to give us the final linear indices, same as the ones produced by sub2ind. Finally, index into A with those linear indices and set them to all 1's as per the question's requirement.

Related

Matlab onehot to integers

I want to convert a onehot array to an array of integer values in MATLAB. Given:
Y = 1 0 0
0 1 0
0 1 0
I want to return:
new_y = 1
2
2
You could use find and return only the column indices like so
Y = [1 0 0; 0 1 0; 0 1 0];
[~, new_y] = find(Y); % output: [1; 2; 2] is the col indices of your 1s
Similarly you can return the row indices if your input was the transpose
[new_y, ~] = find(Y); % output: [1; 2; 3] is the row indices of your 1s
The Neural Network toolbox of MATLAB has built-in functions for converting between one-hot vectors and indices: ind2vec() to create a one-hot matrix, and vec2ind() to convert the one-hot matrix back to a vector of indices.
Note: ind2vec returns a sparse matrix. To convert it to a full matrix, you have to use the full() function.
>> Y = full(ind2vec([1, 2, 3]))
Y =
1 0 0
0 1 0
0 0 1
>> new_y = vec2ind(Y)
new_y =
1 2 3

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

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];
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.
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.
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
You can also abuse find and observe the row positions of the transpose of M. You have to transpose the matrix first as find operates in column major 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 though.
Yet another approach: use the second output of max:
[~, result] = max(M.', [], 1);
Or, as suggested by #rayryeng, use max along 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.

How to make a 4X6 matrix from a 6X6 diagonal matrix in MATLAB

I am having a diagonal matrix A = diag(a,b,c,d,e,f) which is a 6x6 matrix. From this I want to make 4x6 matrix specified by:
B(1, :) = [a b 0 0 0 0]
B(2, :) = [0 0 c 0 0 0]
B(3, :) = [0 0 0 d e 0]
B(4, :) = [0 0 0 0 0 f]
My logic is like this: add the two rows whose index is not divided by 3 and place it on the first row of these two... and then delete the all rows with even indices.
A simple for-loop would do that:
diagvalues = [10:15];
A = diag(diagvalues);
B=[];
for i = 1:length(diagvalues)
if mod(i,3) == 1
B(size(B,1)+1,:) = A(i,:) + A(i+1,:);
elseif mod(i,3) == 0
B(size(B,1)+1,:) = A(i,:);
end
end
You can change the diag-vector as you want.

MATLAB: Fastest Way to Count Unique # of 2 Number Combinations in a Vector of Integers

Given a vector of integers such as:
X = [1 2 3 4 5 1 2]
I would like to find a really fast way to count the number of unique combinations with 2-elements.
In this case the two-number combinations are:
[1 2] (occurs twice)
[2 3] (occurs once)
[3 4] (occurs once)
[4 5] (occurs once)
[5 1] (occurs once)
As it stands, I am currently doing this in MATLAB as follows
X = [1 2 3 4 5 1 2];
N = length(X)
X_max = max(X);
COUNTS = nan(X_max); %store as a X_max x X_max matrix
for i = 1:X_max
first_number_indices = find(X==1)
second_number_indices = first_number_indices + 1;
second_number_indices(second_number_indices>N) = [] %just in case last entry = 1
second_number_vals = X(second_number_indices);
for j = 1:X_max
COUNTS(i,j) = sum(second_number_vals==j)
end
end
Is there a faster/smarter way of doing this?
Here is a super fast way:
>> counts = sparse(x(1:end-1),x(2:end),1)
counts =
(5,1) 1
(1,2) 2
(2,3) 1
(3,4) 1
(4,5) 1
You could convert to a full matrix simply as: full(counts)
Here is an equivalent solution using accumarray:
>> counts = accumarray([x(1:end-1);x(2:end)]', 1)
counts =
0 2 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
1 0 0 0 0
EDIT: #Amro has provided a much better solution (well, better in the vast majority of cases, I suspect my method would work better if MaxX is very large and X contains zeros - this is because the presence of zeros will rule out the use of sparse while a large MaxX will slow down the accumarray approach as it creates a matrix of size MaxX by MaxX).
EDIT: Thanks to #EitanT for pointing out an improvement that can be made using accumarray.
Here is how I would solve it:
%Generate some random data
T = 20;
MaxX = 3;
X = randi(MaxX, T, 1);
%Get the unique combinations and an index. Note, I am assuming X is a column vector.
[UniqueComb, ~, Ind] = unique([X(1:end-1), X(2:end)], 'rows');
NumComb = size(UniqueComb, 1);
%Count the number of occurrences of each combination
Count = accumarray(Ind, 1);
All unique sequential two element combinations are now stored in UniqueComb, while the corresponding counts for each unique combination are stored in Count.

Generate boolean matrix by predicate on row and column

I have the following vector:
y = [1; 3; 2; 3; 1];
All its values are between 1 and n (in this case, 3) and denote different options.
I want to create a matrix of size size(y, 1) x n whose rows correpond to y values:
1 0 0 % because y(1) = 1
0 0 1 % because y(2) = 3
0 1 0 % because y(3) = 2
0 0 1
1 0 0
One way to do this would be
Y = zeros(size(y, 1), num_labels);
for i = 1:m
Y(i, y(i)) = 1;
end
Is there a better way to do this, maybe in a single expression?
Basically, what I need is to generate a matrix with boolean predicate (i, j) => j == y(i).
You can try this if a is a column vector
a = [1; 3; 2; 3; 1];
bsxfun(#eq, a, [1:max(a)])
and this if it is a row vector
a = [1; 3; 2; 3; 1]';
bsxfun(#eq, a', [1:max(a)])
If you have access to Statistics Toolbox, the command dummyvar does exactly what you need.
>> y = [1; 3; 2; 3; 1];
>> dummyvar(y)
ans =
1 0 0
0 0 1
0 1 0
0 0 1
1 0 0
You can use sub2ind after initializing the matrix as follows:
y = [1; 3; 2; 3; 1];
m = length(y);
n = max(y);
Y = zeros(m, n);
Y(sub2ind(size(Y), 1:m, y')) = 1
Y =
1 0 0
0 0 1
0 1 0
0 0 1
1 0 0
The trick here is to know that the corresponding rows of y go from 1 to m one by one.
accumarray([(1:length(y)).' y], 1)
As suggested by Dmitri Bouianov on Coursera discussion forum, this also works:
Y = eye(num_labels)(y, :);
This solution uses elements of y to as indices to select rows from an identity matrix.
In Octave (at least as of 3.6.3, not sure when it was introduced), you can use broadcasting to do this extremely easily. It works like this:
Y = y==1:3
(if y is a row matrix, you need to transpose it first - if you want to have Y transposed instead, use y==(1:3)')