How to do this kind of sorting (in MATLAB)? - matlab

I have a square matrix and I want to get a 2 by n matrix that will have indices of matrix in a sorted order. For example, I want to get from this matrix
0 0 0
1 0 0
2 3 0
something like this
[3 2; 3 1; 2 1] ....
(3,2) being the indices of the biggest element in the matrix, (3,1) the second biggest and so on. It would be good if it could ignore zeros (or NaN-s instead of zeros).
Additional information about the matrix: it is positive, but not necessarily 3 by 3, diagonal elements and every element above the diagonal is either 0 or NaN (a side question, which is processed faster, NaNs or 0s?)

This considers only non-zero elements:
[ii, jj, aa] = find(A);
[~, kk] = sort(aa, 'descend');
result = [ii(kk) jj(kk)];

Assuming your matrix is in A, you need to use the ind2sub function,
Edited to remove zero indices
[Ap, i] = sort(A(:), 'descend');
[r,c] = ind2sub(size(A), i);
orderedPairs = [r,c];
orderedPairsSansZeros = orderedPairs(Ap ~= 0, :);

This following should work. The matrix sortix is what you're looking for. I've replaced a zero in your (1,3) element with NaN so you can see that NaN's don't show up in your final ordered matrix.
matrix = [0, 0, NaN;
1, 3, 0;
2, 3, 0];
new_matrix = matrix;
%new_matrix(new_matrix(:)==0) = NaN; % uncomment to get rid of zeros
saveix = 1;
for i=1:length(matrix(:))
[maxVal, maxIndex] = max(new_matrix(:));
allMax = ismember(new_matrix, maxVal);
idx = find(allMax);
for ix=1:length(idx)
[sortix(saveix, 1), sortix(saveix, 2)] = ind2sub(size(matrix), ...
idx(ix));
saveix = saveix + 1;
end
new_matrix(idx) = NaN;
end

Related

extract 3D matrix's columns based on "surface" values - vectorization

I have an NxMxK matrix A
A = [1 2 1; 1 1 2];
A = cat(3, A, [3 3 3; 3 3 3])
A(:,:,1) =
1 2 1
1 1 2
A(:,:,2) =
3 3 3
3 3 3
and I want to create a YxK 2D matrix B where K is the number of elements of A(:,:,1)==2:
k=0;
for ii=1:size(A,1)
for jj=1:size(A,2)
if A(ii,jj)==2
k=k+1;
B(k,:) = A(ii,jj,:);
end
end
end
Is there a way of vectorizing this code?
My attempt was to find the indices of A(:,:,1)==2 and then try to select the whole column but I do not know how to do it:
inds = find(A(:,:,1)==2)
B = A(inds,:) %this line does not make sense
EDIT
Preallocating B helps:
inds=find(A(:,:,1)==2);
B=NaN(numel(inds),size(A,3));
k=0;
for ii=1:size(A,1)
for jj=1:size(A,2)
if A(ii,jj)==2
k=k+1;
B(k,:) = squeeze(A(ii,jj,:));
end
end
end
But still not vectorized.
You can reshape matrix A to a (N*M)xK 2D matrix.
A = [1 2 1; 1 1 2];
A = cat(3, A, [3 3 3; 3 3 3]);
A_ = reshape(A,numel(A(:,:,1)),size(A,3));
B = A_(A_(:,1)==2,:);
Your first attempt at vectorization is almost right. Just don't use find, but use the logical matrix for indexing.
inds = A(:,:,1)==2;
The inds matrix is 2D, not 3D, so we use repmat to repeat its values along the 3rd dimension:
K = size(A,3);
inds = repmat(inds,1,1,K); % or simply cat(3,inds,inds) if K==2
B = A(inds);
The result is a column vector of size Y*K, not a matrix of size YxK, we can use reshape to fix that:
B = reshape(B,[],K);
I guess this answer is similar to Anthony's, except the indexing and the reshaping are reversed. I didn't really notice the similarity until after I wrote it down. I guess also Anthony's is a little shorter. :/

Matlab: How to read data into a matrix

I have a data file matrix.txt, it has three columns. The first column stores the row index, the second column stores the column index, the third column stores the value. How do I read these into a matrix called mat. To be explicit, suppose our mat is a n*n square matrix, let n=2 for instance. In the text file, it has:
0 0 10
1 1 -10
The element in mat not specified is 0. Thus mat is supposed to be:
mat = 10 0
0 -10
How do I achieve this?
This should work for the generic 2-D case.
% Read in matrix specification
fID = fopen('matrix.txt');
tmp = fscanf(fID, '%u%u%f', [3 inf])';
fclose(fID);
% Use the maximum row and column subscripts to obtain the matrix size
tmp(:, 1:2) = tmp(:, 1:2) + 1; % MATLAB doesn't use 0-based indexing
matsize = [max(tmp(:,1)), max(tmp(:,2))];
% Convert subscripts to linear indices
lidx = sub2ind(matsize, tmp(:,1), tmp(:,2));
mat = zeros(matsize); % Initialize matrix
mat(lidx) = tmp(:,3); % Assign data
Using a sample matrix.txt:
0 0 10
1 1 -10
1 2 20
We receive:
>> mat
mat =
10 0 0
0 -10 20
Since in MATLAB, indices begin with 1 (not zero), we should add 1 to our indices in code.
r and c stand for row and column.
Alsom and n is for m by n zero matrix
A = importdata('matrix.txt');
r = A(:, 1)';
c = A(:, 2)';
m = max(r);
n = max(c);
B = zeros(m + 1, n + 1);
for k = 1:size(A,1);
B(r(k) + 1, c(k) + 1) = A(k, 3);
end
Result:
B =
10 0
0 -10
I see I am too slow, but I decided post my answer anyway...
I initialized matrix A as a vector, and used reshape:
%Load all file to matrix at once
%You may consider using fopen and fscanf, in case Matrix.txt is not ordered perfectly.
row_column_val = load('Matrix.txt', '-ascii');
R = row_column_val(:, 1) + 1; %Get vector of row indexes (add 1 - convert to Matalb indeces).
C = row_column_val(:, 2) + 1; %Get vector of column indexes (add 1 - convert to Matalb indeces).
V = row_column_val(:, 3); %Get vector of values.
nrows = max(R); %Number of rows in matrix.
ncols = max(C); %Number of columns in matrix.
A = zeros(nrows*ncols, 1); %Initialize A as a vector instead of a matrix (length of A is nrows*ncols).
%Put value v in place c*ncols + r for all elements of V, C and R.
%The formula is used for column major matrix (Matlab stored matrices in column major format).
A((C-1)*nrows + R) = V;
A = reshape(A, [nrows, ncols]);

How to get the indices of max values in a matrix,and map them to the indices of another same sized matrix?

I have two 16x12 matrices, In matrix A, I should sort in descending order and find the first 10 max values. But I should know the max values' indices before being sorted.
Finally, I should give those indices to the second matrix and find the values in that matrix.
I tried with for-loop but it doesn't give me accurate answer.
This should work:
[~,I] = sort(A(:), 'descend');
vals = B(I(1:10));
For example:
>> A = [ 4 2; 1 5];
>> B = [ 7 8; 0 NaN];
>> [~,I] = sort( A(:), 'descend' );
>> vals = B(I(1:2))
vals =
NaN
7

Convert vector into logical matrix?

I have a vector y of length n. y(i) is an integer in 1..m. Is there a simpler way to convert y into an n x m logical matrix yy, where yy(i, j) = 1 if y(i) = j, but 0 otherwise? Here's how I've been doing it:
% If m is known (m = 3 here), you could write it out all at once
yy = [y == 1; y== 2; y == 3];
yy = reshape(yy, n, 3);
or
% if m is not known ahead of time
yy = [ y == 1 ];
for i = 2:m;
yy = [ yy; y == i ];
end
yy = reshape(yy, n, m);
You can use bsxfun for this
yy = bsxfun(#eq,y(:),[1,2,3])
y is transformed (if necessary) to a column-vector, while the other vector is a row vector. bsxfun implicitly expands the m-by-1 and 1-by-n arrays so that the result becomes m-by-n.
If n*m is sufficiently large (and m is, by itself, sufficiently large), it is a good idea to create yy as a sparse matrix. Your y vector is really a special type of sparse matrix format, but we can translate it into the built-in sparse matrix format by doing the following.
yy = sparse(1:length(y), y, 1);
This will keep your storage to O(n). It is not going to be doing you a lot of favors if you are using yy for a lot of indexing. If that is the case you are better off using your original sparse structure (i.e., y).
A slight modification to your method:
% A n-dimensional vector y, with values in some range 1..m
m = 4;
n = 7;
y = randi([1 m], n, 1);
% Preallocating a n by m matrix of zeros
nXm = zeros(n, m);
% In each pass of this loop a single column of nXm is updated, where
% for each column index j in nXm, if y(i) = j then nXm(i,j) = 1
for j = 1:m;
nXm(:,j) = (y == j);
end
From Machine Learning on Coursera:
yy = eye(m)(y, :)
This requires that the list be a range 1:m (as OP stated). For an irregular list, like [2 3 5], do this
yy = eye(m)(:, [2 3 5])(y, :)
Note: not tested on MATLAB.
In octave you can write:
yy = y' == (1:m); % or y == (1:m)' for transposed
[1 2 1 3 2] == [1 2 3]' % = [1 0 1 0 0; 0 1 0 0 1; 0 0 0 1 0]

Sample specific rows in matlab

I have a scenario in which there is a Label matrix of dimension N x 1.
The example entries in the label matrix is given below
Label = [1; 3; 5; ....... 6]
I would like to random sample 'm1' records of label1, 'm2' records of label2 etc. so that the output LabelIndicatorMatrix (N x 1 dimension) look something like
LabelIndicatorMatrix = [1; 1; 0;.....1]
1 represent record has been chosen, 0 represent record not chosen during sampling. The output matrix satisfies the following condition
Sum(LabelIndicatorMatrix) = m1+m2...m6
One possible solution:
Label = randi([1 6], [100 1]); %# random Nx1 vector of labels
m = [2 3 1 0 1 2]; %# number of records to sample from each category
LabelIndicatorMatrix = false(size(Label)); %# marks selected records
uniqL = unique(Label); %# unique labels: 1,2,3,4,5,6
for i=1:numel(uniqL)
idx = find(Label == uniqL(i)); %# indices where label==k
ord = randperm(length(idx)); %# random permutation
ord = ord(1:min(m(i),end)); %# pick first m_k
LabelIndicatorMatrix( idx(ord) ) = true; %# mark them as selected
end
To make sure we satisfy the requirements, we check:
>> sum(LabelIndicatorMatrix) == sum(m)
ans =
1
Here is my attempt at a vectorized solution:
Label = randi([1 6], [100 1]); %# random Nx1 vector of labels
m = [2 3 1 0 1 2]; %# number of records to sample from each category
%# some helper functions
firstN = #(V,n) V(1:min(n,end)); %# first n elements from vector
pickN = #(V,n) firstN(V(randperm(length(V))), n); %# pick n elements from vector
%# randomly sample labels, and get indices
idx = bsxfun(#eq, Label, unique(Label)'); %'# idx(:,k) indicates where label==k
[r c] = find(idx); %# row/column indices
idx = arrayfun(#(k) pickN(r(c==k),m(k)), 1:size(idx,2), ...
'UniformOutput',false); %# sample m(k) from labels==k
%# mark selected records
LabelIndicatorMatrix = false(size(Label));
LabelIndicatorMatrix( vertcat(idx{:}) ) = true;
%# check results are correct
assert( sum(LabelIndicatorMatrix)==sum(m) )
you could start with this little sample of code, it selects random samples of your label vector and find which values of your label vector have been selected at least once:
Label = [1; 3; 5; ....... 6];
index = randi(N,m1,1);
index = unique(index);
LabelIndicatorMatrix = zeros(N,1);
LabelIndicatorMatrix(index)=1;
That said I am not sure I understand the final condition on the LabelIndicatorMatrix.