MATLAB: Identify maxima and minima and split data accordingly - matlab

I have the following arrays:
x = [1:33];
y = [0 1 2 3 4 5 4 3 2 1 0 1 2 3 4 5 4 3 2 1 0 1 2 1 0 1 2 3 4 3 2 1 0];
I need to separate y into different sections. I need to obtain the ascending parts, the descending parts, and the combined ascending and descending parts.
For example:
Ascending parts = [1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 0 0 1
1 1 1 0 0 0 0];
Descending parts = [0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1
1 1 1 1 0 0 1 1 0 0 0 0 1 1 1 1];
Combined parts = [1 1 1 1 1 1 1 1 1
1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 4 4 4 4 4 4 4 4];
I can, of course, do this manually for these arrays, but I need to do this for arrays with hundreds of thousands of points and I wish to do it automatically. I have been playing around with the findpeaks functions but this isn't straightforward as it sometimes picks up peaks during the descending/ascending parts, rather than at the end points.
Any tips on how I can do this?
version MATLAB 2017/b

For a problem like this, you should consider using Matlab's diff.
For y = [0 1 2 3 4 5 4 3 2 1 0 1 2 3 4 5 4 3 2 1 0 1 2 1 0 1 2 3 4 3 2 1 0];
a=diff(y)
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 1 1 1 -1 -1 -1 -1
b=a, c =a;
b(b<0)=0;
c(c>0)=0;
Will give you:
b = 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 0 0 1 1 1 1 0 0 0 0
c = 0 0 0 0 0 -1 -1 -1 -1 -1 0 0 0 0 0 -1 -1 -1 -1 -1 0 0 -1 -1 0 0 0 0 -1 -1 -1 -1
For the second part, you can do the following:
z=diff(y, 2);
zd=[0 find(z~=0);0 z(find(z~=0))]
0 5 10 15 20 22 24 28 32
0 -2 2 -2 2 -2 2 -2 2
Assuming that this is how your function looks like in general, the above pattern shows convex and concave regions in the sequence. With this assumption, the following should work in your case:
za=[0 zd(1,zd(2,:)>0)];
zad=diff(za);
cell2mat(arrayfun(#(x,y) repelem(x,y), 1:length(zad),zad,'UniformOutput',false))
ans: 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 4 4 4 4 4 4 4 4
which I believe is close to what you want. Hope this helps.

Related

How to use points data plot a colorful area?

I have an array, data, in MATLAB just like following:
data = [0 0 0 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1;
0 0 0 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1;
0 0 0 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1;
0 0 0 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1;
0 0 0 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1;
0 0 0 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1;
0 0 0 0 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1;
0 0 0 0 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1;
0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1;
0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1;
0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1;
0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1;
0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1;
0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1;
0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1;
0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1;
0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1;
0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1;
0 0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1;
0 0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1;
0 0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 1 1 1 1;
0 0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 1 1 1 1;
0 0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 1 1 1];
Numbers in this matrix stand for color. For example:
1 → red
0 → white
it not must be these colors, anyway,
different number → different color
I want to show on a two-dimensional plane.
I use for environment plot points (but these are discrete points, not a region).
If I use surf(data), it will have a 3D plot:
I can rotate this 3D plot and view above, just like:
This image is what I want plot on 2D plane.
Is there any way to plot on a 2D plane directly?
You may want a contour plot. You can get this by:
[X, Y] = meshgrid(1:size(data,2), 1:size(data,1));
contourf(X,Y,data);
For a more square and distinct look you could try:
scatter(reshape(X,[],1), reshape(Y,[],1), 1200, reshape(data,[1],1), 'square', 'filled');
axis equal
However, this solution requires that you to trial-and-error the size (1200) which will be dependent on the screen size of your plot.
Finally, in newer Matlab versions (R2017b+) you may use heatmap for these types of plots (https://se.mathworks.com/help/matlab/ref/heatmap.html) E.g.:
heatmap(data, 'CellLabelColor', 'none')

label over specific cells (index) in imagesc

I have created this map of Jamaica using matrix A. I want to insert text labels on this image at specific indexes for cities. For Example kingston on this map is at point 15, 38, where 15 is the row and 38 the column, this point I would label "kingston". My matrix is below and the image of it generated from imagesc is below as well. I was playing around with get(gca, 'position') but that was not successful.
% Cities
kingston = [15 38];
montegoBay = [4 15];
portRoyal = [18 31];
stThomas = [10 55];
mandeville = [13 21];
ochoRios = [2 29];
A = [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 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 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 3 3 0 0 0 0 3 0 0 0 0 0 0 0 0 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 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 3 0 0 0 0 3 0 3 3 3 0 0 0 0 3 0 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 1 1 1 1 1 0 0 3 3 3 0 3 0 3 3 3 3 0 3 0 3 0 3 3 3 0 3 3 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 3 3 3 0 0 0 3 3 3 3 3 3 0 3 0 3 0 0 0 0 0 0 3 0 0 0 3 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 0 0 3 3 0 0 0 3 3 3 3 3 0 3 3 3 3 0 0 0 0 0 0 0 0 0 3 0 0 0 3 3 3 0 3 3 3 3 0 0 3 0 0 1 1 1 1 1 1 1 1 1 1;
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 3 3 3 0 0 3 3 0 3 0 0 0 0 3 3 3 3 0 0 0 0 0 0 0 0 1 1 1 1 1 1;
1 0 3 0 3 3 0 0 0 3 3 3 0 3 0 3 3 3 3 3 0 3 3 3 0 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 3 3 3 0 0 0 0 0 0 0 0 0 0 1;
1 0 0 0 0 0 3 0 0 0 0 0 0 0 0 3 3 3 0 0 0 0 0 0 0 0 3 3 3 3 3 0 3 0 3 3 0 0 3 3 3 0 0 3 0 0 3 0 3 3 0 3 0 3 0 1;
1 1 1 0 0 0 0 0 0 3 3 3 3 0 0 3 3 3 3 3 3 3 3 0 3 3 3 0 0 0 3 3 3 3 0 3 3 0 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1;
1 1 1 1 1 1 1 1 0 0 0 0 0 0 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 3 3 1 0 0 0 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 3 3 0 3 0 0 3 3 3 3 3 3 0 3 0 0 0 0 0 3 3 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 3 3 3 3 0 0 0 0 0 3 3 3 3 3 3 0 3 0 3 0 3 3 0 0 0 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 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 3 3 3 0 0 0 0 0 0 0 3 0 0 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 0 3 0 1 1 1 1 1 3 0 3 3 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 3 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;
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 1 3 0 3 0 0 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;
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 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 1 1 1 1 1 1];
Cmap = [1 1 1; 0 0 1; 0 1 0];
colormap(Cmap);
imagesc(A);
axis off
You should use the function text like this:
text(38,15,'kingston')
i.e. in opposite order, because your axis is in i-j direction, and not x-y (try typing axis xy to see what I mean).
(I have changed the font size to 15: text(38,15,'kingston','FontSize',15))
If you want to go a step further, define your cities as a cell array:
Cities = {'kingston','montegoBay','portRoyal','stThomas','mandeville','ochoRios'};
and their location in a matrix:
location = [15 38;
4 15;
18 31;
10 55;
13 21;
2 29];
And then all you need is one text command:
text(location(:,2),location(:,1),Cities,'FontSize',12)
to get the final result:
You can use text function to do that as:
text(x,y,'MyText')
See the documentation for more info:
https://www.mathworks.com/help/matlab/ref/text.html
Hope it helps.

MATLAB - Inserting zero rows and columns into matrix

I have written some code that compresses a matrix to remove zero columns and rows, but I can't work out how to reconstruct the original matrix.
Say I have a matrix:
A = [ 0 3 0 2 1 0 6
3 0 0 4 8 0 5
0 0 0 0 0 0 0
2 4 0 0 2 0 1
1 8 0 2 0 0 7
0 0 0 0 0 0 0
6 5 0 1 7 0 0 ]
Here rows/columns 3 and 6 are empty, so my compression function will give the output:
A_dash = [ 0 3 2 1 6
3 0 4 8 5
2 4 0 2 1
1 8 2 0 7
6 5 1 7 0 ]
A_map = [ 1 2 4 5 7]
Where A_map is a vector mapping the indicies of the rows/columns of A_dash to A. This means that if A_map(3) = 4, then row/column 4 of A is the same as row/column 3 of A_dash - ie. a row/column of zeroes must be inserted between columns/rows 2 and 3 in A_dash
What is the easiest way people can suggest for me to recreate matrix A from A_dash, using the information in A_map?
Here is what I have got so far:
% orig_size is original number of columns/rows
c_count = size(A_dash,1);
A = zeros(c_count, orig_size); % c_count rows to avoid dimension mismatch
for ii = 1:c_count
A(:,A_map(ii)) == A_dash(:,ii);
end
This gives me the right result column-wise:
A = [ 0 3 0 2 1 0 6
3 0 0 4 8 0 5
2 4 0 0 2 0 1
1 8 0 2 0 0 7
6 5 0 1 7 0 0 ]
However, I'm not sure how i should go about inserting the rows, i suppose i could copy the first 1:i rows into one matrix, i:end rows to a second matrix and concatenate those with a zero row in between, but that feels like a bit of a
clunky solution, and probably not very efficient for large sized matrices..
Otherwise, is there a better way that people can suggest I store the map information? I was thinking instead of storing the mapping between column/row indices, that I just store the indices of the zero columns/rows and then insert columns/rows of zeros where appropriate. Would this be a better way?
You've got the indices of the valid rows/columns. Now all you've got to do is put them in a new matrix of zeros the same size as A:
B=zeros(size(A));
B(A_map,A_map)=A_dash
B =
0 3 0 2 1 0 6
3 0 0 4 8 0 5
0 0 0 0 0 0 0
2 4 0 0 2 0 1
1 8 0 2 0 0 7
0 0 0 0 0 0 0
6 5 0 1 7 0 0
Just to check...
>> A==B
ans =
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 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
A and B are equal everywhere, so we've reconstructed A.

Convert adjacency matrix to specific edge list in MATLAB

If I have the matrix
1 0 0
0 0 1
0 0 0
and I want this form in MATLAB
1 2 3 1 2 3 1 2 3
1 1 1 2 2 2 3 3 3
1 0 0 0 0 0 0 1 0
also I want the values of third row in result. i.e. ans= [1 0 0 0 0 0 0 1 0]
Here you go -
[X,Y] = ndgrid(1:size(A,1),1:size(A,2));
out = [X(:).' ; Y(:).' ; A(:).']
For the last part of your question, use the last row of out : out(end,:) or A(:).'.
Sample run -
>> A
A =
1 0 0
0 0 1
0 0 0
>> [X,Y] = ndgrid(1:size(A,1),1:size(A,2));
>> out = [X(:).' ; Y(:).' ; A(:).']
out =
1 2 3 1 2 3 1 2 3
1 1 1 2 2 2 3 3 3
1 0 0 0 0 0 0 1 0

Generating a matrix containing 3 numbers

I am trying to create an 8x8 matrix containing 0s, 1s and 2s. Each row and each column should contain two 0s, three 1s and three 2s.
Previously I have used the below to generate an example containing only 1s and 0s.
output = zeros(8, 8);
for i=1:8
tmp = (1:8) + (i);
tmp = rem(tmp, 4);
output(i,:) = tmp;
output(i,:) = tmp > 0;
end
output =
1 1 0 1 1 1 0 1
1 0 1 1 1 0 1 1
0 1 1 1 0 1 1 1
1 1 1 0 1 1 1 0
1 1 0 1 1 1 0 1
1 0 1 1 1 0 1 1
0 1 1 1 0 1 1 1
1 1 1 0 1 1 1 0
However I would now like something similar to the following:
output =
1 1 0 1 2 2 0 2
1 0 1 2 2 0 2 1
0 1 2 2 0 2 1 1
1 2 2 0 2 1 1 0
2 2 0 2 1 1 0 1
2 0 2 1 1 0 1 2
0 2 1 1 0 1 2 2
2 1 1 0 1 2 2 0
Thanks for your help.
What you have in your example is a Hankel matrix so you could use the hankel function
c = [1 1 0 1 2 2 0 2];
k = [2 1 1 0 1 2 2 0];
A = hankel(c,k)
where c is the first column of the output matrix and k is the last row.
Making your output matrix a Hankel matrix is a good idea (based on your requirements) as it will enforce the row and column frequency counts for each value. You would not necessarily get this just by creating rows that are random permutations of a base row (using randperm for example) as duplicate rows would be possible which would break your column requirements.
As an example, if you want random c with fixed numbers of specific elements, you can randomly permute a base vector containing the required values and frequencies - as per your requirement this would be
c = [0 0 1 1 1 2 2 2];
index = randperm(numel(c));
c = c(index);
c =
0 2 0 2 2 1 1 1
To get the square Hankel structure then choose k to be the next cyclic permutation of c
k = circshift(c',1)'
k =
1 0 2 0 2 2 1 1
and just call hankel with these as mentioned above
A = hankel(c,k)
A =
0 2 0 2 2 1 1 1
2 0 2 2 1 1 1 0
0 2 2 1 1 1 0 2
2 2 1 1 1 0 2 0
2 1 1 1 0 2 0 2
1 1 1 0 2 0 2 2
1 1 0 2 0 2 2 1
1 0 2 0 2 2 1 1
The above output is based on what I got on my machine based on the output from randperm.
Any output matrix generated using the above will meet your requirements specified in the question.