Matlab, Integer vector to binary matrix without loop [duplicate] - matlab

This question already has answers here:
Creating Indicator Matrix
(6 answers)
Closed 7 years ago.
I have a vector with N elements, all integers 1-M. I want to convert this to a NxM matrix with each row containing only zeros except for the i:th element set to one, i being the integer in the vector.
For example:
[1 1 3] => [1 0 0; 1 0 0; 0 0 1]
I currently do this in a loop, like this:
y_vec = zeros(m, num_labels);
for i = 1:m
y_vec(i, y(i)) = 1;
end
Is there a way to do this without a loop?

Yes, there is:
y = [1 1 3];
m = length(y);
num_labels = max(y);
%# initialize y_vec
y_vec = zeros(m,num_labels);
%# create a linear index from {row,y}
idx = sub2ind(size(y_vec),1:m,y);
%# set the proper elements of y_vec to 1
y_vec(idx) = 1;

If you have access to Statistics Toolbox, the command dummyvar does exactly this.
>> dummyvar([1 1 3])
ans =
1 0 0
1 0 0
0 0 1

(This has been asked in Creating Indicator Matrix and Matlab/Octave 1-of-K representation.)
My favorite answer is woodchips' sparse(1:n,labels,1,n,m);.

Related

How to generate label matrix from a label vector? [duplicate]

This question already has answers here:
Replace specific columns in a matrix with a constant column vector
(4 answers)
Closed 8 years ago.
I've a label vector as follows:
y=[3 1 5 3 4 2];
Is there any efficient way to generate the following label matrix?
[0 0 1 0 0;
1 0 0 0 0;
0 0 0 0 1;
0 0 1 0 0;
0 0 0 1 0;
0 1 0 0 0;]
UPDATED:
This post and this post are both good answers. Using the scripts provided by #Nras, the following is to handle the missing labels:
Y=[3 1 5 3 4 2];
labels=unique(Y);
[~,indexes]=ismember(Y,labels);
rows = 1:length(Y); %// row indx
T = zeros(length(Y),length(unique(indexes))); %// A matrix full of zeros
T(sub2ind(size(T),rows ,indexes)) = 1; %// Ones at the desired row/column combinations
Use sub2ind for this problem. Your y determines the columns to use,
the rows are simply always incremented by 1. By using sub2ind the desired row-column-combinations get transformed to a linear index and can then all be adressed in a vectorized way.
y = [3 1 5 3 4 2]; %// column indx
rows = 1:length(y); %// row indx
M = zeros(length(y), max(y)); %// A matrix full of zeros
M(sub2ind(size(M),rows ,y)) = 1; %// Ones at the desired row/column combinations

Moving rows from a matrix to a cell array using matlab: Trouble indexing cells

Using matlab, I am attempting to move rows from one matrix (RF) to a cell array using equivalent values in the matrix and the array. For each value i in the first column of matrix RF I would like to find which cell contains the same value. I would then like to move the row containing i (i,:) to the first row of zeros in that cell.
At the end I will delete all remaining rows of zeros in M.
I currently have the following code:
RF = [1 7; 4 8; 3 9; 7 10]
M = cell(3,1);
for k = 1:3
M{k} = zeros(10,2);
end
matsize2 = length((RF(:)));
halfmatsize2 = 0.5 * matsize2;
for i = 1:halfmatsize2 % Values in the first column of RF
J = RF(i);
JK = RF(i,:);
L = find(cell2mat(M)==J); % I wanted this line to give me the index of the cell containing J
H = find(M{L}==0, 1, 'first'); % This returns an error
M{L}(H,:)= JK; % Puts JK into the first row of zeros (my goal)
end
This outputs the error "index exceeds matrix dimensions" for the line H = find(M{L}==0, 1, 'first'); This is because while I wanted L to return the index of the cell containing J, it is giving me the location with all cells combined into one. What's the best way to find the cell containing J and then move JK to the first row of zeros in the aforementioned cell?
For example, I have the matrix RF = [1 7; 4 8; 3 9; 7 10] and the cell array M = cell(3,1), where M{1}=[1 4; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0], M{2} = [2 5; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0] and M{3} = [3 6; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0].
I would like to end with M{1} = [1 4; 1 7; 4 8; 7 10], M{2} = [2 5] and M{3} = [3 6; 3 9].`
The overarching goal here is to automate the process for taking a matrix containing parent-offspring relationships for species and turn it into a phylogeny. The example provided is the simple one I've been using to test other aspects of the larger code.
Help would be much appreciated! After hours of Google searching and trial/error I remain stuck. I would also appreciate advice on how to streamline my rather clumsy code.
Thanks in advance!
Based on what I have understood, I think this would work for you and should be more efficient too -
%// Get matches between each element of col1 of RF against each in its col2
RF12 = bsxfun(#eq,RF(:,1),RF(:,2)'); %//'
%// Cut each cell of M into cells that have only non-zero rows
M_cut = arrayfun(#(n) M{n}(any(M{n},2),:),1:numel(M),'Uni',0)
M_out = cell(size(M)); %// storage for output
for n = 1:numel(M) %// we need to iterate through each cell
%// Matches for all elements in col1 of RF against all elements in M
M_RF1 = any(bsxfun(#eq,M{n}(:),RF(:,1)')); %//'
%// Matching row indices for all elements in col1 of RF against all
%// matches in its col2 and all elements in M
idx = any(bsxfun(#and,RF12,M_RF1),2) | M_RF1(:); %//'
%// Logically index into RF for the matches and vertically concatenate
%// with the M_cut cells to give us the desired output in each cell
M_out{n} = [M_cut{n} ; RF(idx,:)];
end
celldisp(M_out) %// display output
Output -
M_out{1} =
1 4
1 7
4 8
7 10
M_out{2} =
2 5
M_out{3} =
3 6
3 9

Matrix to Diagonal Matrix in MATLAB [duplicate]

This question already has answers here:
How to vectorize row-wise diagonalization of a matrix
(4 answers)
Closed 9 years ago.
Let's say I have a matrix in MATLAB like
A = [1 2 3;
4 5 6;
7 8 9]
and I would like to obtain a matrix of the form
B = [1 0 0;
0 4 0;
0 0 7;
2 0 0;
0 5 0;
0 0 8;
3 0 0;
0 6 0;
0 0 9]
i.e. a matrix that is a concatenation of three diagonal matrices, with each having the columns of matrix A at their diagonals. I know how to do this using a for loop over the columns of A and then concatenating all the results but I am looking for a shorter way to do this. Please share your ideas.
B(repmat(eye(3),3,1)==1) = A;
reshape(B, [], 3)
Here's a way using linear indexing:
B(sub2ind([9 3], 1:9, mod(0:8,3)+1))=A;
reshape(B,9,3)
If you want this to be generic, realize that each column of the original becomes a diagonal. Therefore, the number of rows in the original becomes the number of columns in the output, and 3 rows x cols becomes the number of rows. The rest of the answer doesn't change at all:
c = size(A,1);
r = size(A,1) * size(A,2); #% or prod(size(A));
B(sub2ind([r c], 1:r, mod(0:(r-1),c)+1)) = A;
B = sparse( 1:numel(A), repmat( 1:size(A,2), [1 size(A,1)] ),...
A(:), numel(A), size(A,2));
should do the trick.
You can B = full(B); if you want a full matrix

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.

How to vectorize row-wise diagonalization of a matrix

I have an n-by-m matrix that I want to convert to a mn-by-m matrix, with each m-by-m block of the result containing the diagonal of each row.
For example, if the input is:
[1 2; 3 4; 5 6]
the output should be:
[1 0; 0 2; 3 0; 0 4; 5 0; 0 6]
Of course, I don't want to assemble the matrix step by step myself with a for loop.
Is there a vectorized and simple way to achieve this?
For a vectorized way to do this, create the linear indices of the diagonal elements into the resulting matrix, and assign directly.
%# create some input data
inArray = [10 11;12 13;14 15];
%# make the index array
[nr,nc]=size(inArray);
idxArray = reshape(1:nr*nc,nc,nr)';
idxArray = bsxfun(#plus,idxArray,0:nr*nc:nr*nc^2-1);
%# create output
out = zeros(nr*nc,nc);
out(idxArray) = inArray(:);
out =
10 0
0 11
12 0
0 13
14 0
0 15
Here's a simple vectorized solution, assuming X is the input matrix:
Y = repmat(eye(size(X, 2)), size(X, 1), 1);
Y(find(Y)) = X;
Another alternative is to use sparse, and this can be written as a neat one-liner:
Y = full(sparse(1:numel(X), repmat(1:size(X, 2), 1, size(X, 1)), X'));
The easiest way I see to do this is actually quite simple, using simple index referencing and the reshape function:
I = [1 2; 3 4; 5 6];
J(:,[1,4]) = I;
K = reshape(J',2,6)';
If you examine J, it looks like this:
J =
1 0 0 2
3 0 0 4
5 0 0 6
Matrix K is just what wanted:
K =
1 0
0 2
3 0
0 4
5 0
0 6
As Eitan T has noted in the comments, the above is specific to the example, and doesn't cover the general solution. So below is the general solution, with m and n as described in the question.
J(:,1:(m+1):m^2) = I;
K=reshape(J',m,m*n)';
If you want to test it to see it working, just use
I=reshape(1:(m*n),m,n)';
Note: if J already exists, this can cause problems. In this case, you need to also use
J=zeros(n,m^2);
It may not be the most computationally efficient solution, but here's a 1-liner using kron:
A = [1 2; 3 4; 5 6];
B = diag(reshape(A', 6, 1) * kron(ones(3, 1), eye(2))
% B =
% 1 0
% 0 2
% 3 0
% 0 4
% 5 0
% 0 6
This can be generalized if A is n x m:
diag(reshape(A.', n*m, 1)) * kron(ones(n,1), eye(m))