Solving a Simple Sorting Problem in MATLAB - matlab

I have been given this problem in a MATLAB course I am doing. The instructor's solution provided there is wrong, and I have been struggling with the same problem for hours as I am a beginner who has just started coding (a science student here).
Consider a one-dimensional matrix A such as A = \[5 8 8 8 9 9 6 6 5 5 4 1 2 3 5 3 3 \]. Show the percentage frequencies of unique
elements in the matrix A in descending order.
Hint: Use the functions tabulate and sort.
How do I solve this problem using only tabulate, sort, and find functions (find is for eliminating zero frequency elements in tabulate table, which my instructor did not do)?
I tried first extracting the indices of non-zero elements in the percentage column of tabulating table using the find function, which I succeeded in doing using the following:
A = [5 8 8 8 9 9 6 6 5 5 4 1 2 3 5 3 3 ];
B = tabulate(A);
C = find(B(:,3) > 0)
But I am now struggling to return the values corresponding to the 3rd column of B using indices in C. Please help. Also please give me some alternative syntax where one can easily make a vector out of non-zero elements of a row or column easily by omitting the zeroes in that vector if it exists. Rest of the problem I'll do by myself.

With your find command, you are just finding the indices of the matrix and not the values themselves.
So you either will do something like this:
A = [5 8 8 8 9 9 6 6 5 5 4 1 2 3 5 3 3 ];
B = tabulate(A);
for i = 1:size(B,1)-1
if B(i,3) == 0
B(i,:) = [];
end
end
sortrows(B,3,'descend')
where you remove the 0 value's row.
Or since you have all the numbers with none-zero frequency you can ask for their rows. Like this:
A = [5 8 8 8 9 9 6 6 5 5 4 1 2 3 5 3 3 ];
B = tabulate(A);
C = find(B(:,3) > 0);
sortrows(B(C(:),:),3,'descend')
in a bit more elegant way. B(C(:),:) calls all the rows with first indices the indices of matrix C. Which is exactly what you are asking for. While at the same time you sort your matrix based on row 3 at a descending order.

Related

obtain data using columns data indices

i have data A=( 3,5,3,1,4 ) in a column and
B=[
4 6 9 1 3
2 7 2 5 7
7 3 1 8 2
4 1 6 9 1
2 5 8 3 6 ]
And i want: as in A first element is 3 and for this i want to get first element of column 3 row 1 from B which is 9. The second element of A is 5 and for this i want to get the the 2nd element of column 5 and row 2 from B which is 7 ,and do the process for all other elements . how to do this in matlab? the required elements are bold and underlined. The desired output is [9,7,1,4,3]
Read about linear indexes.
sub2ind will convert from [row col] to index.
Cols=[ 3,5,3,1,4 ];
Rows=1:length(Cols);
B=[
4 6 9 1 3
2 7 2 5 7
7 3 1 8 2
4 1 6 9 1
2 5 8 3 6 ];
Indexes=sub2ind(size(B),Rows,Cols);
Vals=B(Indexes)
if I've read well you want an elements replacement. It's quite simple
A(1)=B(1,3)
A(2)=B(2,5)
So after you have declared your two vectors you can handle them replacing singular components. In general when you have a 1-D vectors you can access to it's component position only declaring the position itself inside the parentheses, as I did for A.
Whe you have to face situation like in B, If you remember linear algebra and matrix in general B(a,b) means the element of matrix B placed in the line a and column b so you have to specify row and column to access that element.

Reshape a matrix by splitting it after k columns in MATLAB

Suppose that I have a matrix , let's call it A, as follows:
1 2 3 4 5 1 2 3 4 5
0 2 4 6 8 1 3 5 7 9
And I want to reshape it into a matrix like this:
1 2 3 4 5
0 2 4 6 8
1 2 3 4 5
1 3 5 7 9
So, basically, what I want to be done is that MATLAB first reads a block of size (2,5) and then splits the remaining matrix to the next row and then repeats this so on so forth until we get something like in my example.
I tried to do this using MATLAB's reshape command in several ways but I failed. Any help is appreciated. In case that it matters, my original data is larger. It's (2,1080). Thanks.
I don't believe you can do this in a single command, but perhaps someone will correct me. If speed isn't a huge concern a for loop should work fine.
Alternatively you can get your results by reshaping each row of A and then placing the results into every other row of a new matrix. This will also work with your larger data.
A = [1 2 3 4 5 1 2 3 4 5
0 2 4 6 8 1 3 5 7 9];
An = zeros(numel(A)/5, 5); % Set up new, empty matrix
An(1:2:end,:) = reshape(A(1,:), 5, [])'; % Write the first row of A to every other row of An
An(2:2:end,:) = reshape(A(2,:), 5, [])' % Write second row of A to remaining rows
An =
1 2 3 4 5
0 2 4 6 8
1 2 3 4 5
1 3 5 7 9
You may need to read more about indexing in the Matlab's documentation.
For your example, it is easy to do the following
A=[1 2 3 4 5 1 2 3 4 5; 0 2 4 6 8 1 3 5 7 9]
a1=A(:,1:5); % extract all rows, and columns from 1 to 5
a2=A(:,6:end); % extract all rows, and columns from 6 to end
B=[a1;a2] % construct a new matrix.
It is not difficult to build some sort of loops to extract the rest.
Here's a way you can do it in one line using the reshape and permute commands:
B = reshape(permute(reshape(A,2,5,[]), [1,3,2]), [], 5);
The reshape(A,2,5,[]) command reshapes your A matrix into a three-dimensional tensor of dimension 2 x 5 x nblocks, where nblocks is the number of blocks in A in the horizontal direction. The permute command then swaps the 2nd and 3rd dimensions of this 3D tensor, so that it becomes a 2 x nblocks x 5 tensor. The final reshape command then transforms the 3D tensor into a matrix of dimension (2*nblocks) x 5.
Looking at the results at each stage may give you a better idea of what's happening.

How can I perfect out my zigzag matlab function?

I was given the following Question:
Write a function call zigzag that takes in a 2-dimensional array A and return a 1- dimensional array created by traverse through A in zigzag way starting at position (1,1).
Example:
A =[1 2 3 4 5 6
7 8 9 1 3 4
3 4 5 6 3 1
3 4 5 6 7 8]
zigzag(A) should return:
[1 2 3 4 5 6 4 3 1 9 8 7 3 4 5 6 3 1 8 7 6 5 4 3]
The way I solved it, I am not sure if this is a correct method to do it. I would be glad to know if this is perfect and how I could improve my answer:
function B=zigzag(A)
[r,c]=size(A);
B= reshape(A’,1,:);
m=0
n=0
For r>m+2
m=m+2;
n=n+1;
For i=1:c
B(nc+i)=B(2cn-i+1);
End
End
disp(B)
If it gives you the right output, then you're certainly doing something right. However, what I would have done was access the even rows of your matrix, reverse the directions so that they're displayed in reverse order, transpose your matrix then unravel it.
The reason why we transpose it is because when we unravel a matrix in MATLAB, this means that the columns of the matrix are stacked on top of each other so that one single vector is produced. We want the rows to be stacked on top of each other and making the even rows in reverse order will allow you to do the zigzag that you expect. If you want the rows to be stacked on top of each other, you need to transpose the matrix first so that rows become columns, and when you unravel this matrix, you'll stack the rows on top of each other instead to create a single vector.
Something like this:
B = A; %// Make a copy
B(2:2:end,:) = fliplr(B(2:2:end,:)); %// Flip even rows
B = reshape(B.', 1, []); %// Unravel
With your example, I get:
B =
Columns 1 through 13
1 2 3 4 5 6 4 3 1 9 8 7 3
Columns 14 through 24
4 5 6 3 1 8 7 6 5 4 3

How to change the value of the diagonal column of the matrix?

How do I change the list of value to all 1? I need the top right to bottom left also end up with 1.
rc = input('Please enter a value for rc: ');
mat = ones(rc,rc);
for i = 1:rc
for j = 1:rc
mat(i,j) = (i-1)+(j-1);
end
end
final = mat
final(diag(final)) = 1 % this won't work?
Code for the original problem -
final(1:size(final,1)+1:end)=1
Explanation: As an example consider a 5x5 final matrix, the diagonal elements would have indices as (1,1), (2,2) .. (5,5). Convert these to linear indices - 1, 7 and so on till the very last element, which is exactly what 1:size(final,1)+1:end gets us.
Edit : If you would like to set the diagonal(from top right to bottom left elements) as 1, one approach would be -
final(fliplr(eye(size(final)))==1)=1
Explanation: In this case as well we can use linear indexing, but just for more readability and maybe a little fun, we can use logical indexing with a proper mask, which is being created with fliplr(eye(size(final)))==1.
But, if you care about performance, you can use linear indexing here as well, like this -
final(sub2ind(size(final),1:size(final,1),size(final,2):-1:1))=1
Explanation: Here we are creating the linear indices with the rows and columns indices of the elements to be set. The rows here would be - 1:size(final,1) and columns are size(final,2):-1:1. We feed these two to sub2ind to get us the linear indices that we can use to index into final and set them to 1.
If you would to squeeze out the max performance here, go with this raw version of sub2ind -
final([size(final,2)-1:-1:0]*size(final,1) + [1:size(final,1)])=1
All of the approaches specified so far are great methods for doing what you're asking.
However, I'd like to provide another viewpoint and something that I've noticed in your code, as well as an interesting property of this matrix that may or may not have been noticed. All of the anti-diagonal values in your matrix have values equal to rc - 1.
As such, if you want to set all of the anti-diagonal values to 1, you can cheat and simply find those values equal to rc-1 and set these to 1. In other words:
final(final == rc-1) = 1;
Minor note on efficiency
As a means of efficiency, you can do the same thing your two for loops are doing when constructing mat by using the hankel command:
mat = hankel(0:rc-1,rc-1:2*(rc-1))
How hankel works in this case is that the first row of the matrix is specified by the vector of 0:rc-1. After, each row that follows incrementally shifts values to the left and adds an increasing value of 1 to the right. This keeps going until you encounter the vector seen in the second argument, and at this point we stop. In other words, if we did:
mat = hankel(0:3,3:6)
This is what we get:
mat =
0 1 2 3
1 2 3 4
2 3 4 5
3 4 5 6
Therefore, by specifying rc = 5, this is the matrix I get with hankel, which is identical to what your code produces (before setting the anti-diagonal to 1):
mat =
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
Tying it all together
With hankel and the cheat that I mentioned, we can compute what you are asking in three lines of code - with the first line of code asking for the dimensions of the matrix:
rc = input('Please enter a value for rc: ');
mat = hankel(0:rc-1, rc-1:2*(rc-1));
mat(mat == rc-1) = 1;
mat contains your final matrix. Therefore, with rc = 5, this is the matrix I get:
mat =
0 1 2 3 1
1 2 3 1 5
2 3 1 5 6
3 1 5 6 7
1 5 6 7 8
Here's a simple method where I just add/subtract the appropriate matrices to end up with the right thing:
final=mat-diag(diag(mat-1))+fliplr(diag([2-rc zeros(1,rc-2) 2-rc]))
Here is one way to do it:
Say we have a the square matrix:
a = ones(5, 5)*5
a =
5 5 5 5 5
5 5 5 5 5
5 5 5 5 5
5 5 5 5 5
5 5 5 5 5
You can remove the diagonal, then create a diagonal list of ones to replace it:
a = a - fliplr(diag(diag(fliplr(a)))) + fliplr(diag(ones(length(a), 1)))
a =
5 5 5 5 1
5 5 5 1 5
5 5 1 5 5
5 1 5 5 5
1 5 5 5 5
The diag(ones(length(a), 1)) can be any vector, ie. 1->5:
a = a - fliplr(diag(diag(fliplr(a)))) + fliplr(diag(1:length(a)))
a =
5 5 5 5 1
5 5 5 2 5
5 5 3 5 5
5 4 5 5 5
5 5 5 5 5

Find the increasing and decreasing trend in a curve MATLAB

a=[2 3 6 7 2 1 0.01 6 8 10 12 15 18 9 6 5 4 2].
Here is an array i need to extract the exact values where the increasing and decreasing trend starts.
the output for the array a will be [2(first element) 2 6 9]
a=[2 3 6 7 2 1 0.01 6 8 10 12 15 18 9 6 5 4 2].
^ ^ ^ ^
| | | |
Kindly help me to get the result in MATLAB for any similar type of array..
You just have to find where the sign of the difference between consecutive numbers changes.
With some common sense and the functions diff, sign and find, you get this solution:
a = [2 3 6 7 2 1 0.01 6 8 10 12 15 18 9 6 5 4 2];
sda = sign(diff(a));
idx = [1 find(sda(1:end-1)~=sda(2:end))+2 ];
result = a(idx);
EDIT:
The sign function messes things up when there are two consecutive numbers which are the same, because sign(0) = 0, which is falsely identified as a trend change. You'd have to filter these out. You can do this by first removing the consecutive duplicates from the original data. Since you only want the values where the trend change starts, and not the position where it actually starts, this is easiest:
a(diff(a)==0) = [];
This is a great place to use the diff function.
Your first step will be to do the following:
B = [0 diff(a)]
The reason we add the 0 there is to keep the matrix the same length because of the way the diff function works. It will start with the first element in the matrix and then report the difference between that and the next element. There's no leading element before the first one so is just truncates the matrix by one element. We add a zero because there is no change there as it's the starting element.
If you look at the results in B now it is quite obvious where the inflection points are (where you go from positive to negative numbers).
To pull this out programatically there are a number of things you can do. I tend to use a little multiplication and the find command.
Result = find(B(1:end-1).*B(2:end)<0)
This will return the index where you are on the cusp of the inflection. In this case it will be:
ans =
4 7 13