Matlab: Find values of a matrix between desired bounds - matlab

I have a matrix sorted in ascending order.
S = 25;
RT = zeros(S,2);
for i = 1:S;
for j = 1:i;
R = i *j;
T = R + j;
RT(j,:) = [R T];
end
end
sortRT = sortrows(RT, [1 2]);
disp(sortRT);
I want to find the sortRT elements which values is lower than 500 (for R) and 490 (for T) per column and place these values inside a matrix. Is it possible?

Just use find:
idx = find(sortRT(:,1)<500 & sortRT(:,2)<490)
idx' =
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
These are the rows where both R<500 and T<490. You can of course separate these two:
idxR500 = find(sortRT(:,1)<500);
idxT490 = find(sortRT(:,2)<490);
If you're just going to copy elements or rows, then find isn't even necessary and you can use logical indexing:
R500 = sortRT(find(sortRT(:,1)<500) , 1);
is the same as
R500 = sortRT(sortRT(:,1)<500 , 1);
This copies elements of the first column, if you want to copy the whole row, use the colon operator:
R500 = sortRT(sortRT(:,1)<500 , :);

Related

Generate cell with random pairs without repetitions

How to generate a sequence of random pairs without repeating pairs?
The following code already generates the pairs, but does not avoid repetitions:
for k=1:8
Comb=[randi([-15,15]) ; randi([-15,15])];
T{1,k}=Comb;
end
When running I got:
T= [-3;10] [5;2] [1;-5] [10;9] [-4;-9] [-5;-9] [3;1] [-3;10]
The pair [-3,10] is repeated, which cannot happen.
PS : The entries can be positive or negative.
Is there any built in function for this? Any sugestion to solve this?
If you have the Statistics Toolbox, you can use randsample to sample 8 numbers from 1 to 31^2 (where 31 is the population size), without replacement, and then "unpack" each obtained number into the two components of a pair:
s = -15:15; % population
M = 8; % desired number of samples
N = numel(s); % population size
y = randsample(N^2, M); % sample without replacement
result = s([ceil(y/N) mod(y-1, N)+1]); % unpack pair and index into population
Example run:
result =
14 1
-5 7
13 -8
15 4
-6 -7
-6 15
2 3
9 6
You can use ind2sub:
n = 15;
m = 8;
[x y]=ind2sub([n n],randperm(n*n,m));
Two possibilities:
1.
M = nchoosek(1:15, 2);
T = datasample(M, 8, 'replace', false);
2.
T = zeros(8,2);
k = 1;
while (k <= 8)
t = randi(15, [1,2]);
b1 = (T(:,1) == t(1));
b2 = (T(:,2) == t(2));
if ~any(b1 & b2)
T(k,:) = t;
k = k + 1;
end
end
The first method is probably faster but takes up more memory and may not be practicable for very large numbers (ex: if instead of 15, the max was 50000), in which case you have to go with 2.

Get the first four minimum values in a matrix

I have a matrix:
X =
0 81 13 15 100 2
11 0 6 10 200 8
19 22 0 20 300 23
I want to get the first four minimal values in the whole array X with the indices of each value in the array. For example I should get vector v = [2 6 8 10] and the index of each value in X.
Also, I want to ignore the zero values when the row number equals the column number.
I have tried to use the min and sort functions, but I am not sure how to do it.
I would suggest the following
X2 = X;
X2(~~eye(size(X2))) = inf; %// or X2(logical(eye(size(X2)))) = inf
[val, idx] = sort(X2(:));
result = val(1:4);
[idxRow, idxCol] = ind2sub(size(X), idx(1:4));
Use:
vals = sort(X(~eye(size(X)))); %takes non diagonal values and sort the result
res = vals(1:4) %finds the first 4 elements (which are the smallest)
[row, col] = find(ismember(X,res)); %gets the indices
result:
res = [2; 6; 8; 10]
By The way, if you don't want to ignore all the diagonal values, only the zero ones, use:
vals = sort(X(~eye(size(X)) | (eye(size(X)) & X~=0)));
Sort all but the ones on the diagonal and then find the indices of the ones which are smaller than or equal to the 4th element of sorted array and not on the diagonal:
T=sort(X(~eye(size(X))));
v = T(1:4);
[I,J] = find(X <= v(end) & ~eye(size(X)));
Just want to add to drorco's perfect answer how to find indexes of this first elements:
indexes = arrayfun( #(a) find(X==a), res);
or if you want to get numbers of rows and columns:
[r,c] = arrayfun( #(a) find(X==a), res);
P.S. it works perfectly if all elements except zeros in X are unique.

Sort elements of rows in a matrix with another matrix

I have a matrix D of distances between 3 places and 4 persons
example D(2,3) = 10 means person 3 is far away from place 2 of 10 units.
D=[23 54 67 32
32 5 10 2
3 11 13 5]
another matrix A with the same number of rows (3 places) and where A(i,:) correspond to the persons that picked place i
example for place 1, persons 1 and 3 picked it
no one picked place 2
and persons 2 and 4 picked place 3
A=[1 3 0
0 0 0
2 4 0]
I want to reorder each row of A by the persons who are closest to the place it represents.
In this example, for place 1, person 1 is closer to it than person 3 based on D so nothing to do.
nothing to do for place 2
and there is a change for place 3 since person 4 is closer than 2 to place 3 D(3,2)>D(3,4)
The result should be
A=[1 3
0 0
4 2 ]
each row(place) in A can have 0 or many non zeros elements in it (persons that picked it)
Basically, I want to reorder elements in each row of A based on the rows of D (the closest to the location comes first), something like this but here A and D are not of the same size (number of columns).
[SortedD,Ind] = sort(D,2)
for r = 1:size(A,1)
A(r,:) = A(r,Ind(r,:));
end
There is another Matlab function sortrows(C,colummn_index) that can do the trick. It can sort rows based on the elements in a particular column. So if you transpose your matrix A (C = A') and extend the result by adding to the end the proper column, according to which you want to sort a required row, then you will get what you want.
To be more specific, you can do something like this:
clear all
D=[23 54 67 32;
32 5 10 2;
3 11 13 5];
A=[1 0;
3 0;
4 2 ];
% Sort elements in each row of the matrix A,
% because indices of elements in each row of the matrix D are always
% ascending.
A_sorted = sort(A,2);
% shifting all zeros in each row to the end
for i = 1:length(A_sorted(:,1))
num_zeros = sum(A_sorted(i,:)==0);
if num_zeros < length(A_sorted(i,:))
z = zeros(1,num_zeros);
A_sorted(i,:) = [A_sorted(i,num_zeros+1:length(A_sorted(i,:))) z];
end;
end;
% Prelocate in memory an associated array of the corresponding elements in
% D. The matrix Dr is just a reduced derivation from the matrix D.
Dr = zeros(length(A_sorted(:,1)),length(A_sorted(1,:)));
% Create a matrix Dr of elements in D corresponding to the matrix A_sorted.
for i = 1:length(A_sorted(:,1)) % i = 1:3
for j = 1:length(A_sorted(1,:)) % j = 1:2
if A_sorted(i,j) == 0
Dr(i,j) = 0;
else
Dr(i,j) = D(i,A_sorted(i,j));
end;
end;
end;
% We don't need the matrix A_sorted anymore
clear A_sorted
% In order to use the function SORTROWS, we need to transpose matrices
A = A';
Dr = Dr';
% The actual sorting procedure starts here.
for i = 1:length(A(1,:)) % i = 1:3
C = zeros(length(A(:,1)),2); % buffer matrix
C(:,1) = A(:,i);
C(:,2) = Dr(:,i);
C = sortrows(C,2);
A(:,i) = C(:,1);
% shifting all zeros in each column to the end
num_zeros = sum(A(:,i)==0);
if num_zeros < length(A(:,i))
z = zeros(1,num_zeros);
A(:,i) = [A(num_zeros+1:length(A(:,i)),i) z]';
end;
end;
% Transpose the matrix A back
A = A';
clear C Dr z

filling sparse matrices efficiently matlab

I am working with a sparse matrix of very large size:
U = sparse(a,b) % a and b are very large
On the hand, there exists the cell Ind which has 'a' rows. In each row, there exists a 'variate' number of elements, e.g. :
Ind{1} = [1 3 5 19 1000 1340]
Ind{2} = [9 100 1500 1600 8000 b]
...
Ind{a} = [3 5 6 90 1000 4300 5712 9480]
as could be seen the maximum index number in Ind{i} can be 'b'. For each of these index vector also exists a content matrix like 'c' :
c = [2 3 1 6 3 5 1 3 4 1 2 ... 5]
Here is the question, for each element in Ind{i}, I want to fill the 'row = i' and the 'col=Ind{i}' with c(Ind{i}), i.e.
for i = 1 : a
U(i,Ind{i}) = c(Ind{i}) ;
end
the problem is 'a' is very large and the loop takes long time to be computed. Any idea to avoid looping?
I'm not sure if there is a way to avoid the loop, but I do get a factor of 2-to-20 speed increase (I ranged a from 3 to 5,000 with b fixed at 10,000) by building three large vectors (two for row and column indices and one for values) and building the sparse matrix after the loop:
strides = cellfun(#numel,Ind);
n = sum(strides);
I(n,1) = 0;
J(n,1) = 0;
S(n,1) = 0;
bot = 1;
for k = 1:a
top = bot + strides(k) - 1 ;
mask = bot:top ;
%
I(mask) = k ;
J(mask) = Ind{k} ;
S(mask) = c(Ind{k}) ;
%
bot = top + 1;
end
U = sparse(I,J,S,a,b);
This is the recommend usage of sparse because assignments to a sparse matrix are more costly than regular arrays.

Different unique rows

I have a function which finds the ROWS in an array having repeating number and replaces them by the a unique row.
Here is the function:
NVAR= number of parameter in the unique row
PVBu= upper bound of the numbers in the row
lower bound is 1 by default
function [A]=nonrepeatuniquerow(A,NVAR,PVBu)
HM = zeros(1,NVAR);
p = zeros(1,PVBu);
for j = 1
p(1:PVBu) = 1:PVBu;
for k = PVBu:-1:PVBu-NVAR+1
q = ceil(k*rand);
HM(j,PVBu-k+1) = p(q);
p(q:k-1) = p(q+1:k);
end
end
b = any(~diff(sort(A,2),1,2),2) | any(A==0,2);
A(b,:) = repmat(HM,sum(b),1);
end
For example,
Assume [9 1 7] is the unique randomly generated row.
Matrix A is passed to the function 'nonrepeatuniquerow'.
A= [2 3 3
2 5 2
1 5 9
9 7 6]
And output is:
A= [9 1 7
9 1 7
1 5 9
9 7 6]
My question is how to modify the function so that it replaces the ROWS having a repeating number by DIFFERENT unique rows ?
For example the output could be as follows:
A= [7 2 3
2 1 8
1 5 9
9 7 6]
The function below only generates unique rows every time it is called.
NVAR=number of parameter in the unique row
PVBu= upper bound of the numbers in the row
lower bound is 1 by default
function [HM]=generateunique(NVAR,PVBu)
HM = zeros(1,NVAR);
p = zeros(1,PVBu);
for j = 1
p(1:PVBu) = 1:PVBu;
for k = PVBu:-1:PVBu-NVAR+1
q = ceil(k*rand);
HM(j,PVBu-k+1) = p(q);
p(q:k-1) = p(q+1:k);
end
end
end
I would greatly appreciate any help you can give me in working this problem?
In the function nonrepeatuniquerow, the first part is just for creating the random sequence HM. You could run through all non-unique rows in a for loop and create a separate HM for each. That would look like this:
function [A]=nonrepeatuniquerow(A,NVAR,PVBu)
b = any(~diff(sort(A,2),1,2),2) | any(A==0,2);
for ii=find(b==1).'
HM = zeros(1,NVAR);
p = zeros(1,PVBu);
for j = 1
p(1:PVBu) = 1:PVBu;
for k = PVBu:-1:PVBu-NVAR+1
q = ceil(k*rand);
HM(j,PVBu-k+1) = p(q);
p(q:k-1) = p(q+1:k);
end
end
A(ii,:) = HM;
end
end
You could also replace the generateunique function or the part which does the same in your code by the built-in randperm function. You can create a unique random sequence by
HM = randperm(PVBu,NVAR);
that would give you the following code
function A = nonrepeatuniquerow(A,NVAR,PVBu)
b = any(~diff(sort(A,2),1,2),2) | any(A==0,2);
for ii=find(b==1).'
A(ii,:) = randperm(PVBu,NVAR);
end
end