Position from reordering in ascending order in Matlab? - matlab

I have a matrix in Matlab of dimension mxn, e.g.
A= [ 1 1 1;
1 1 1;
2 2 2;
0 0 1]
I want to order the rows of A in ascending order and get the position of each row within this order. If I use
[~,~,jj] = unique(A,'rows');
I get
jj=[2;2;3;1]
What I want to get is jj=[2;3;4;1] (or jj=[3;2;4;1]), i.e. even if the first two rows of A are equivalent they should not be associated to the same position jj.

Check sortrows. This sorts your array row-based and gives you an array index that tells you where each row was initially.
[B,index] = sortrows(A);
B =
0 0 1
1 1 1
1 1 1
2 2 2
index =
4
1
2
3
And, as #Divakar pointed out:
[~,out] = intersect(index,1:4);
out =
2 3 4 1

If the elements are integers only, this could be another way -
[~,idx] = sort(A*[0:size(A,2)-1].'*(max(A(:))+1),1) %//'
[~,out] = sort(idx) %//'
Sample run -
>> A
A =
1 1 1
1 1 1
2 2 2
0 0 1
>> [~,idx] = sort(A*[0:size(A,2)-1].'*(max(A(:))+1),1);
[~,out] = sort(idx)
out =
2
3
4
1

Related

How to create all permutations of a 2-column cell-array?

I created a cell array of shape m x 2, each element of which is a matrix of shape d x d.
For example like this:
A = cell(8, 2);
for row = 1:8
for col = 1:2
A{row, col} = rand(3, 3);
end
end
More generally, I can represent A as follows:
where each A_{ij} is a matrix.
Now, I need to randomly pick a matrix from each row of A, because A has m rows in total, so eventually I will pick out m matrices, which we call a combination.
Obviously, since there are only two picks for each row, there are a total of 2^m possible combinations.
My question is, how to get these 2^m combinations quickly?
It can be seen that the above problem is actually finding the Cartesian product of the following sets:
2^m is actually a binary number, so we can use those to create linear indices. You'll get an array containing 1s and 0s, something like [1 1 0 0 1 0 1 0 1], which we can treat as column "indices", using a 0 to indicate the first column and a 1 to indicate the second.
m = size(A, 1);
% Build all binary numbers and create a logical matrix
bin_idx = dec2bin(0:(2^m -1)) == '1';
row = 3; % Loop here over size(bin_idx,1) for all possible permutations
linear_idx = [find(~bin_idx(row,:)) find(bin_idx(row,:))+m];
A{linear_idx} % the combination as specified by the permutation in out(row)
On my R2007b version this runs virtually instant for m = 20.
NB: this will take m * 2^m bytes of memory to store bin_idx. Where that's just 20 MB for m = 20, that's already 30 GB for m = 30, i.e. you'll be running out of memory fairly quickly, and that's for just storing permutations as booleans! If m is large in your case, you can't store all of your possibilities anyway, so I'd just select a random one:
bin_idx = rand(m, 1); % Generate m random numbers
bin_idx(bin_idx > 0.5) = 1; % Set half to 1
bin_idx(bin_idx < 0.5) = 0; % and half to 0
Old, slow answer for large m
perms()1 gives you all possible permutations of a given set. However, it does not take duplicate entries into account, so you'll need to call unique() to get the unique rows.
unique(perms([1,1,2,2]), 'rows')
ans =
1 1 2 2
1 2 1 2
1 2 2 1
2 1 1 2
2 1 2 1
2 2 1 1
The only thing left now is to somehow do this over all possible amounts of 1s and 2s. I suggest using a simple loop:
m = 5;
out = [];
for ii = 1:m
my_tmp = ones(m,1);
my_tmp(ii:end) = 2;
out = [out; unique(perms(my_tmp),'rows')];
end
out = [out; ones(1,m)]; % Tack on the missing all-ones row
out =
2 2 2 2 2
1 2 2 2 2
2 1 2 2 2
2 2 1 2 2
2 2 2 1 2
2 2 2 2 1
1 1 2 2 2
1 2 1 2 2
1 2 2 1 2
1 2 2 2 1
2 1 1 2 2
2 1 2 1 2
2 1 2 2 1
2 2 1 1 2
2 2 1 2 1
2 2 2 1 1
1 1 1 2 2
1 1 2 1 2
1 1 2 2 1
1 2 1 1 2
1 2 1 2 1
1 2 2 1 1
2 1 1 1 2
2 1 1 2 1
2 1 2 1 1
2 2 1 1 1
1 1 1 1 2
1 1 1 2 1
1 1 2 1 1
1 2 1 1 1
2 1 1 1 1
1 1 1 1 1
NB: I've not initialised out, which will be slow especially for large m. Of course out = zeros(2^m, m) will be its final size, but you'll need to juggle the indices within the for loop to account for the changing sizes of the unique permutations.
You can create linear indices from out using find()
linear_idx = [find(out(row,:)==1);find(out(row,:)==2)+size(A,1)];
A{linear_idx} % the combination as specified by the permutation in out(row)
Linear indices are row-major in MATLAB, thus whenever you need the matrix in column 1, simply use its row number and whenever you need the second column, use the row number + size(A,1), i.e. the total number of rows.
Combining everything together:
A = cell(8, 2);
for row = 1:8
for col = 1:2
A{row, col} = rand(3, 3);
end
end
m = size(A,1);
out = [];
for ii = 1:m
my_tmp = ones(m,1);
my_tmp(ii:end) = 2;
out = [out; unique(perms(my_tmp),'rows')];
end
out = [out; ones(1,m)];
row = 3; % Loop here over size(out,1) for all possible permutations
linear_idx = [find(out(row,:)==1).';find(out(row,:)==2).'+m];
A{linear_idx} % the combination as specified by the permutation in out(row)
1 There's a note in the documentation:
perms(v) is practical when length(v) is less than about 10.

Transform a matrix to a stacked vector where all zeroes after the last non-zero value per row are removed

I have a matrix with some zero values I want to erase.
a=[ 1 2 3 0 0; 1 0 1 3 2; 0 1 2 5 0]
>>a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
However, I want to erase only the ones after the last non-zero value of each line.
This means that I want to retain 1 2 3 from the first line, 1 0 1 3 2 from the second and 0 1 2 5 from the third.
I want to then store the remaining values in a vector. In the case of the example this would result in the vector
b=[1 2 3 1 0 1 3 2 0 1 2 5]
The only way I figured out involves a for loop that I would like to avoid:
b=[];
for ii=1:size(a,1)
l=max(find(a(ii,:)));
b=[b a(ii,1:l)];
end
Is there a way to vectorize this code?
There are many possible ways to do this, here is my approach:
arotate = a' %//rotate the matrix a by 90 degrees
b=flipud(arotate) %//flips the matrix up and down
c= flipud(cumsum(b,1)) %//cumulative sum the matrix rows -and then flip it back.
arotate(c==0)=[]
arotate =
1 2 3 1 0 1 3 2 0 1 2 5
=========================EDIT=====================
just realized cumsum can have direction parameter so this should do:
arotate = a'
b = cumsum(arotate,1,'reverse')
arotate(b==0)=[]
This direction parameter was not available on my 2010b version, but should be there for you if you are using 2013a or above.
Here's an approach using bsxfun's masking capability -
M = size(a,2); %// Save size parameter
at = a.'; %// Transpose input array, to be used for masked extraction
%// Index IDs of last non-zero for each row when looking from right side
[~,idx] = max(fliplr(a~=0),[],2);
%// Create a mask of elements that are to be picked up in a
%// transposed version of the input array using BSXFUN's broadcasting
out = at(bsxfun(#le,(1:M)',M+1-idx'))
Sample run (to showcase mask usage) -
>> a
a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
>> M = size(a,2);
>> at = a.';
>> [~,idx] = max(fliplr(a~=0),[],2);
>> bsxfun(#le,(1:M)',M+1-idx') %// mask to be used on transposed version
ans =
1 1 1
1 1 1
1 1 1
0 1 1
0 1 0
>> at(bsxfun(#le,(1:M)',M+1-idx')).'
ans =
1 2 3 1 0 1 3 2 0 1 2 5

Modify parts of a matrix based on linear equation on row and column numbers

For example:
>> tmp = ones(5,5)
tmp =
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
I want a command like:
tmp(colNum - 2*rowNum > 0) = 0
that modifies entries of tmp when the column number is more than twice the row number e.g. it should produce:
tmp =
1 1 0 0 0
1 1 1 1 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
As a second example, tmp(colNum - rowNum == 0) = 0 should set the diagonal elements of tmp to be zero.
A possibly more efficient solution is to use bsxfun like so
nRows = 5;
nCols = 5;
bsxfun(#(col,row)~(col - 2*row > 0), 1:nCols, (1:nRows)')
You can generalize this to just accept a function so it becomes
bsxfun(#(col,row)~f(col,row), 1:nCols, (1:nRows)')
And now just replace f with exactly the way you specify the equation in your question i.e.
f = #(colNum, rowNum)(colNum - 2*rowNum > 0)
or
f = #(colNum, rowNum)(colNum - rowNum == 0)
of course it might make more sense to specify your function to accept (row,col) instead of (col,row) as that's how MATLAB indexes
You can use meshgrid to generate a grid of 2D coordinates, then use this to impose any condition you wish. The variant you seek outputs 2 2D matrices where the first matrix gives you the column locations and the second matrix outputs the row locations.
For example, given your situation above:
>> [X,Y] = meshgrid(1:5, 1:5)
X =
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
Y =
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5
You can see that each unique spatial location shared between X and Y give you the desired 2D location as if you were envisioning a 2D grid.
Therefore, you would do something like this for your first situation:
[X,Y] = meshgrid(1:5,1:5); % Generate 2D coordinates
tmp = ones(5); % Generate desired matrix
tmp(X > 2*Y) = 0; % Set desired locations to 0
We get:
>> tmp
tmp =
1 1 0 0 0
1 1 1 1 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
Finally for your second example:
[X,Y] = meshgrid(1:5,1:5); % Generate 2D coordinates
tmp = ones(5); % Generate desired matrix
tmp(X == Y) = 0; % Set desired locations to 0
We get:
>> tmp
tmp =
0 1 1 1 1
1 0 1 1 1
1 1 0 1 1
1 1 1 0 1
1 1 1 1 0
Simply put, generate a grid of 2D coordinates, then use those directly to index into your desired matrix using logical / Boolean conditions to set the desired locations to 0.

MATLAB what lines are different between matrices

I am trying to find the number of the lines where the values of two matrices are not the same
I found only a way to know the indexs on the not same items by:
find(a~=b)
where a is N*N and b is N*N
How can I know the rows numbers of the not same items
ps
looking for nicer way then
dint the find and then having some vector in a loop filling with
ind2sub(size(A), 6)
You can use max on the logical array of such matches or mis-mistaches in this case along a certain dimension, alongwith find.
If you are looking to find unique row IDs for mismatches, do this -
find(max(a~=b,[],2))
For unique column IDs, just change the dimension specifier -
find(max(a~=b,[],1))
Sample run -
>> a
a =
1 2 2 2 1
1 2 1 1 1
2 2 2 2 1
1 1 2 1 1
>> b
b =
1 2 1 1 2
1 2 1 2 1
2 2 2 2 1
1 1 2 2 2
>> a~=b
ans =
0 0 1 1 1
0 0 0 1 0
0 0 0 0 0
0 0 0 1 1
>> find(max(a~=b,[],2)) %// unique row IDs
ans =
1
2
4
>> find(max(a~=b,[],1)) %// unique col IDs
ans =
3 4 5
here I found an easy way if any one will need it
indexs=find(a~=b)
[~,rows]=ind2sub(size(a),indexs)
rows=unique( sort( rows ) )
now rows are only the different rows
NotSame = 0;
for ii = 1:size(a,1)
if a(ii,:) ~= b(ii,:)
NotSame = NotSame+1;
end
end
This checks it row by row and when a row in a is not the same as the row in b this will increase the count of NotSame. Not the fastest way, I'm sure someone can produce a solution using bsxfun, but I'm not an expert in that.
You can also use the double output of find
[row, col] = find(a~=b)
myrows = unique(row);
You can also have the columns where a & b have different values
mycols = unique(col);

Replace specific matrix position with array value without using for loop in MATLAB

can I know how can I replace values in specific matrix position without using for loop in MATLAB? I initialize matrix a that I would like to replace its value on specified row and column for each no. This has to be done a few time within num for loop. The num for loop is important here because I would want the update the value in the original code.
The real code is more complicated, I am simplifying the code for this question.
I have the code as follow:
a = zeros(2,10,15);
for num = 1:10
b = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
c = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
d = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
for no = 1:15
a(b(no),d(no),no) = c(1,no,:)
end
end
A sample output for, say no 13 is as follows:
a(:,:,13) =
Columns 1 through 8
0 0 0 0 0 7.3160 0 0
0 0 0 0 0 0 0 0
Columns 9 through 10
0 0
0 0
Thank you so much for any help I could get.
It can be done using sub2ind, which casts the subs to a linear index.
Following your vague variable names, it would look like this (omitting the useless loop over num):
a = zeros(2,10,15);
b = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
d = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
c = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
% // we vectorize the loop over no:
no = 1:15;
a(sub2ind(size(a), b, d, no)) = c;
Apart from the sub2ind based approach as suggested in Nras's solution, you can use a "raw version" of sub2ind to reduce a function call if performance is very critical. The related benchmarks comparing sub2ind and it's raw version is listed in another solution. Here's the implementation to solve your case -
no = 1:15
a = zeros(2,10,15);
[m,n,r] = size(a)
a((no-1)*m*n + (d-1)*m + b) = c
Also for pre-allocation, you can use a much faster approach as listed in Undocumented MATLAB blog post on Preallocation performance with -
a(2,10,15) = 0;
The function sub2ind is your friend here:
a = zeros(2,10,15);
x = [2 2 1 1 2 2 2 1 2 2 2 2 1 2 2];
y = [1 1 1 2 1 1 1 1 1 1 3 1 6 1 1];
z = 1:15;
dat = [8.0268 5.5218 2.9893 5.7105 7.5969 7.5825 7.0740 4.6471 ...
6.3481 14.7424 13.5594 10.6562 7.3160 -4.4648 30.6280];
inds = sub2ind(size(a), x, y, z);
a(inds) = dat;
Matlab provides a function 'sub2ind' may do what you expected.
with variable as the same you posted:
idx = sub2ind(size(a),b,d,[1:15]); % return the index of row a column b and page [1:15]
a(idx) = c;