MatLab: Sorting two row vectors - matlab

I have two row vectors (i_1,i_2,...,i_n) and (b_1,b_2,...,b_n) where i_1 corresponds to b_1.
I need to consider the case where the i elements are not necessarily in order so must be sorted, how do I do this while also making sure the b elements are rearranged so that they still correspond to the correct i element. I have the following sorting code that I need to use but am unsure how to adapt it to do what I need it to.
function [ out ] = myMsort( a )
%MYMSORT returns a sorted version of the input row vector, by merging
% Merge sort: running time O(n log n) with n elements to sort
% But really quite inefficient for small n
n=length(a);
if (n==1)
out=a; return
end
out=myMerge(myMsort(a(1,1:floor(n/2))),myMsort(a(1,floor(n/2)+1:n)));
end
Where the function called within this is as follows
function [ out ] = myMerge( a,b )
% MYMERGE: takes two sorted row vectors and produces the (sorted) merge
% running time linear in output size
out=zeros(1,length(a)+length(b)); % Avoid growing array!
j=1;k=1;l=1;
while (j<=length(a))&&(k<=length(b))
if (a(1,j)<b(1,k))
out(1,l)=a(1,j); j=j+1;l=l+1;
else
out(1,l)=b(1,k); k=k+1;l=l+1;
end
end
while (j<=length(a))
out(1,l)=a(1,j); j=j+1;l=l+1;
end
while (k<=length(b))
out(1,l)=b(1,k); k=k+1;l=l+1;
end
end
Any help would be much appreciated.

You can modify the code to accept multiple rows of data, but still just sort all columns based on the values in the first row. This requires only small changes: first, change every instance of length(a) or length(b) to size(a,2) and size(b,2). Then make sure in the merge assignments you transfer all elements from that column by using the : indexing operator, instead of 1.
function [ out ] = myMsort( a )
n=size(a,2);
if (n==1)
out=a;
return
end
out=myMerge(myMsort(a(:,1:floor(n/2))),myMsort(a(:,floor(n/2)+1:n)));
end
function [ out ] = myMerge( a,b )
out=zeros(size(a,1),size(a,2)+size(b,2)); % Avoid growing array!
j=1;k=1;l=1;
while (j<=size(a,2))&&(k<=size(b,2))
if (a(1,j)<b(1,k))
out(:,l)=a(:,j);
j=j+1;l=l+1;
else
out(:,l)=b(:,k);
k=k+1;l=l+1;
end
end
while (j<=size(a,2))
out(:,l)=a(:,j);
j=j+1;l=l+1;
end
while (k<=size(b,2))
out(:,l)=b(:,k);
k=k+1;l=l+1;
end
end
Now you can run the code like so:
n = 100;
i = rand([1 n]);
b = rand([1 n]);
out = myMsort([i;b]);
And out will be a matrix of two rows: row 1 is the sorted values of i and row 2 is the corresponding values from b.

Just little modification, here my example :
a = [12,4,1,2,8,7];
and into myMsort, I change :
out=myMerge(myMsort(a(1:floor(n/2))),myMsort(a(floor(n/2)+1:n)));
where all line are already sorted.
I don't really understand the part a is two row
EDIT
here the full myMsort function :
function [ out ] = myMsort( a )
%MYMSORT returns a sorted version of the input row vector, by merging
% Merge sort: running time O(n log n) with n elements to sort
% But really quite inefficient for small n
a = reshape(a,1,size(a,1)*size(a,2));
n=length(a);
if (n==1)
out=a; return
end
out=myMerge(myMsort(a(1:floor(n/2))),myMsort(a(floor(n/2)+1:n)));
end
Note that i'm using reshape to make a single row array. You can do it by yourself with another function. the problem is with the recursion your out need to be 2 row vector but it not when myMerge return.

Related

Append rows in matrix

for i = 1:6
if data(i,1) == 1
disp(i)
m(i,:) = data(i,:)
end
end
The code above returns a matrix m, with rows of data from the data file.
However, data(i,1) == 1 is true 4 times for the particular data, however m has 6 rows. 2 of the rows of m are just full of 0's, but the if statement is only true 4 times.
Why is that happening?
In answer to "why is that happening", it is because your matrices are the same size, but you only assign values to the rows which satisfy a condition. Therefore leaving other rows as 0s.
You either need a way to build m row by row (see end of this post) or create it in another way (my answer).
You can do this with logical indexing
% For creating m
m = data(data(:, 1) == 1, :);
% For displaying which indices satisfy your condition, one call to disp
disp( find(data(:, 1) == 1) )
Breaking this down, m is assigned to the values of data, where the column 1 of data is equal to 1, and all of the columns.
find returns the index of any non-zero element. The logical indexing returns an array of 0s and 1s, so all elements which satisfy the condition (and are 1) will be indexed by find.
You could also create the logical index and use it twice, better for maintenance at a later date if your condition changes:
% create logical index
idx = ( data(:,1) == 1 );
% same as above but using idx
m = data(idx, :);
disp( find(idx) )
Documentation
Logical indexing - https://uk.mathworks.com/help/matlab/matlab_prog/find-array-elements-that-meet-a-condition.html
find - https://uk.mathworks.com/help/matlab/ref/find.html
#Ander's suggestion to append only certain rows will work, and demonstrates well how to build a matrix. However, in this case you do not need your loop and if condition at all.
This is standard MATLAB.
Lets assume data(1,1) and data(3,1) are 1.
Then m(1,:)=data(1,:) and later m(3,:)=data(3,:). But what about m(2,:) It has to exist, because you filled m(3,:). There is no 3 without 2!
If you want m to have only the ones where data(i,1) == 1 then do:
m=[]; %create empty matrix
for i = 1:6
if data(i,1) == 1
disp(i)
m= [m; data(i,:)]; % "append" to m
end
end

Matrix as input and output of Matlab function

For example I want to have a function which let me delete rows of my matrix where the highest value is 1. Thus I wrote:
% A is an input matrix
% pict2 suppose to be output cleared matrix
function pict2 = clear_(A)
B=A
n = size(A,1)
for i=1:n
if(max(B(i,:))==1)
B(i,:)=[]
end
end
But after I call:
pict2=clear_(pict) the Matlab resposes:
"warning: clear_: some elements in list of return values are undefined
warning: called from
clear_ at line 5 column 1 pict2 = "
I'm not sure which elements were left undefined?
The variable name of your output argument must match the variable that you want to be returned. So you'll want to change the first line to the following so that your modifications to B are saved and returned.
function B = clear_(A)
As far as your algorithm is concerned, it's not going to work because you are modifying B while trying to loop through it. Instead, you can replace your entire function with the following expression which computes the maximum value of each row, then determines if this value is equal to 1 and removes the rows where this is the case.
B(max(B, [], 2) == 1, :) == [];
I believe that, alternatively to the suggestions you already recieved, you might want to try the following. Using logicals is probably one of the best options for such a problem, since you don't need to use for-loops:
function out = clear_matr(A)
% ind is true for all the rows of A, where the highest value is not equal to 1
ind = ~(max(A, [], 2) == 1);
% filter A accordingly
out = A(ind, :);
end

storing values from a matrix to another one with an if operation using matlab

I have a "x1" matrix, and I want to extract some particular elements with respect to a logical condition:
for z=1:length(x1)
if (x1(z+1)-x1(z))>=20)
extract=
end
end
How can I obtain the "extract" matrix and the indices of these values in x1?
The index of the elements in x1 is obviously z itself: the z-th element satisfies that condition. In extract I suppose you want to put x1(z), so that's the value. But extract may have less elements than x1 so we need a proper index to run through extract, let's call it k.
k=1
for z=1:length(x1)-1
if (x1(z+1)-x1(z))>=20)
extract(k)=x1(z); %if you want to extract the value
k=k+1;
end
end
or, if you want to save the index of x1 and not the value
k=1
for z=1:length(x1)-1
if (x1(z+1)-x1(z))>=20)
extract(k)=z;
k=k+1;
end
end
At the end of this loop, extract will be an array containing all the values (first loop) or the indices (second loop) of the elements in x1 that satisfy your condition.
For the sake of completeness, to obtain both, we must engage two arrays (extract for the values and indices for the indices):
k=1
for z=1:length(x1)-1
if (x1(z+1)-x1(z))>=20)
extract(k)=x1(z); % store the value
indices(k)=z; % store the index
k=k+1;
end
end
You can use this code to extract the indices
x1Diff = diff(x1)
x1DiffTh = zeros(size(x1Diff))
x1DiffTh[x1Diff>20] = 1
indx = find(x1DiffTh)
Or compacted in 1 line
indx = find(diff(x1)>20)
For loops are very ineficient in Matlab since the code is not compiled.

expand matrix in matlab?

I would like to expand matrix row by row under a conditional statement without initializing the matrix. In C++, simply I use std::vector and push_back method without initializing the size of the vector in C++. However, I want to do same scenario in Matlab. This is my pseudo code
for i = 1:lengt(data)
if ( condition )
K = [data(1) data(2) i]
end
K
Let us assume some working code to resemble your pseudo-code.
%// Original code
for i = 1:10
if rand(1)>0.5
data1 = rand(2,1)
K = [data1(1) data1(2) i]
end
end
Changes for "pushing data without initialization/pre-allocation":
To save data at each iteration we keeping on "stacking" data along a chosen dimension. This could be thought of as pushing data. For a 2D case, use either a "row vector push" or a "column vector push". For this case we are assuming a former case.
We don't index into K using the original iterator, but use a custom one,
that only increments when the condition is satisfied.
The code below must make it clear.
%// Modified code
count = 1; %// Custom iterator; initialize it for the iteration when condition would be satisfied for the first time
for i = 1:10
if rand(1)>0.5
data1 = rand(2,1)
K(count,:) = [data1(1) data1(2) i] %// Row indexing to save data at each iteration
count = count +1; %// We need to manually increment our custom iterator
end
end
If we assume from the above that data is an Nx2 matrix, and that you only want to save the rows that satisfy some condition, then you almost have the correct code to update your K matrix without having to initialize it to some size:
K = []; % initialize to an empty matrix
for i=1:size(data,1) % iterate over the rows of data
if (condition)
% condition is satisfied so update K
K = [K ; data(i,:) i];
end
end
K % contains all rows of data and the row number (i) that satisfied condition
Note that to get all elements from a row, we use the colon to say get all column elements from row i.

Matlab loop inside loop

I have an Nx2 matrix say D(k1,k2).I have to compare k1 and k2 from each row and switch accordingly. There is another vector d(i) which has M values.
if k1 and k2 is any one value of d(i) I have to switch.
if D(k1,1)==d(i)&&D(k1,2)==d(i)....
Is there any method to compare all the d(i) elements in the if loop without using a for loop for i?
You can use the ismember function for checking if the vector d contains certain values:
D_in_d = ismember(D,d);
and then you still have to loop to perform the flipping operation on specific rows:
for i=1:size(D,1)
if all(D_in_d(i,:))
D(i,:)=fliplr(D(i,:));
end
end
This is relatively easy to accomplish with matlab's vectorizion without any loops at all.
% A swap logical vector ( 1 if you need to swap that row, 0 otherwise)
swap_logical = ( ismember(D(:,1),d) | ismember(D(:,2),d) );
% Vectorized swapping based on the swap boolian.
Dnew = swap_logical.*D(:,2:-1:1) + ~swap_logical.*D;