Assigning Values to the neighbors same value in MATLAB - matlab

I am having a small issue but I am clueless where I am at fault. Can someone please guide me the right way? Thanks in advance.
What I have done.
My codes finds local maxima’s.
Bring down from local maxima to a certain point.
Assign the neighbors that are greater than the downsized value, the value of downsized point.
Small Example
X = [1 0 1 4.3 4.5 5 4.3 4.2 0 0 0 2 6.2 6.3 7 6.2 7.4 8 7.2 1 2 3 4 2];
Local maxima’s are 5, 7, 8, and 4
Go down to certain point. Like 4, 6, 7, 3.
Assign neighbors that have values greater than 4,6,7,3 same values.
Output will be like
X = [1 0 1 4 4 4 4 4 0 0 0 2 6 6 6 6 6 6 6 1 2 3 3 2];
As this was a simple example so did not have much difference when I applied my code on it.
I have provided another example with my code. Please have a look at that.
t = 50;
X = sin(2*pi*10*(1:t)/t) + 0.5*sin(2*pi*5*(1:50)/t) - linspace(0,3,50);
% plot(X)
A= X;
% A1 = [1 0 1 4.3 4.5 5 4.3 4.2 0 0 6 6 6 6 6 6 6 6 6 1 2 3 4 2];
[pks,locs] = findpeaks(A);
idx = A(locs)-0.3 ; % will bring local maxima to the point where we want
for b = 1: numel(idx) % loop for checking idx
for c = 1:numel(A) % loop for checking neighbourhoods
if (A(locs(1,b)-c)> idx(1,b) && A(locs(1,b)+c)> idx(1,b))
else
d= c-1;
z(b)= d;
break
end
end
end
for i = 1:numel(idx)
for n = 1:z(1,i)
idx_1 = A(locs(i) - n); % will find what is in neighbourhood of local maxima
idx_2 = A(locs(i) + n);
% idx_1 = A(locs - n); % will find what is in neighbourhood of local maxima
% idx_2 = A(locs + n);
if (idx_1>idx(1,i) && (idx_2>idx(1,i))) % Check and assign the same value to neighbourhood of local maxima
A(locs(1,i)) = idx(i);
A(locs(1,i)-n) = idx(i);
A(locs(1,i)+n) = idx(i);
% A(i)) = A(idx(i))
else if (idx_1 < idx(1,i) && (idx_2>idx(1,i)))
A(locs(1,i)) = idx(i);
A(locs(1,i)+n) = idx(i);
else if (idx_1 > idx(1,i) && (idx_2<idx(1,i)))
A(locs(1,i)) = idx(i);
A(locs(1,i)-n) = idx(i);
else
A(locs(1,i)) = idx(i);
A(locs(1,i)-n) = idx_1(i);
A(locs(1,i)+n) = idx_1(i);
end
end
end
end
A(locs(1,i)) = idx(i);
end
figure(1)
hold on
plot(X,'r')
plot(A,'b')
hold off
Expected Output be like Black line in the picture not blue lines that are formed from code

Related

smart way of element accessing in an array to perform certain actions in MATLAB

Sorry for asking question again but it is different from my previously asked question just the arrays are same but query is different.
I have an original array
A = [1 0 1 4.3 4.5 5 4.3 3 0 0 0 2 6.2 6.3 7 6.2 7.4 8 7.2 1 2 3 4 2];
Expected Output from code:
A = [1 1 1 4 4 4 1 1 1 0 0 2 6 6 6 6.2 7.4 8 2 2 2 3 3 2];
I have found Positive Local maxima’s and negative Local maxima’s in an array A and after that I applied some limitations and removed some maxima’s and minima’s and the formed an array with local maximas and minimas in order .
Array Containing Positive and negative local maxima’s in order along with location.
Yloc = [2 6 9 15 20 23];
Ymaximaminimanew = [0 5 0 7 1 4];
Now I separated locations of positive and negative local maximas from Yloc.
Yposlocfiltered = [ 6 15 23];
Yneglocfiltered = [2 9 20];
What I want:
I want to make a loop that checks the values in Ymaximaminimanew if negative local maximas comes first then do certain action that is linked with negative local maxima and if the value belongs to positive local maxima then do certain action that belongs to positive local maxima.
Example
Like in Ymaximaminimanew first is 0 which belongs to negative local maxima
then do the action. Please have a look at attached code it will go to elseif
and perform the task.
2nd value is 5 which is positive local maxima so it will go to if and
perform the task.
The logic I have used is provided in the code. If someone suggest me better logic to make my code smarter then it will be nice.
I have some issue with the code I am not getting the desired output what I am expecting. Just a small error may be in formatting of loop.
I have some issue with loop 4 when t=4 its starts misbehaving also If someone let me know how to do this smartly in simple and minimal steps then it will be really nice.
Thanks a lot in advance for your time, expertise and help.
Code:
A = [1 0 1 4.3 4.5 5 4.3 3 0 0 0 2 6.2 6.3 7 6.2 7.4 8 7.2 1 2 3 4 2];
Yposlocfiltered = [ 6 15 23];
Yneglocfiltered = [2 9 20];
Yloc = [2 6 9 15 20 23];
Ymaximaminimanew = [0 5 0 7 1 4];
sA = numel(A);
n=3;
r = 1;
s = 1;
for t = 1:numel(Yloc)
if (ismember(A(Yloc(1,t)),A(Yposlocfiltered)))
x = A(Yposlocfiltered);
x = x(1,r);
poslocations=x ;
idx = poslocations-1;
sI = numel(idx);
f = Yposlocfiltered(1,r) - [1:sA];
g = Yposlocfiltered(1,r) + [1:sA];
f(f<1) = [];
g(g>sA) = [];
backward_window(t) = f(find(A(f) <= idx, 1)) + 1;
forward_window(t) = g(find(A(g) <= idx, 1)) - 1;
r = r+1;
range = backward_window(t) : forward_window(t);
if n <= numel(range)
A(range(1:n)) = idx;
else
A(range) = idx;
end
elseif (ismember(A(Yloc(1,t)),A(Yneglocfiltered)))
c = A( Yneglocfiltered);
c = c(1,s);
neglocations=c;
idx_neg = neglocations+1;
sU = numel(idx_neg );
h = Yneglocfiltered(1,s) - [1:sA];
p = Yneglocfiltered(1,s) + [1:sA];
h(h<1) = [];
p(p>sA) = [];
backward_window_neg(t) = h(find(A(h) <= idx_neg, 1)) + 1;
forward_window_neg(t) = p(find(A(p) <= idx_neg, 1)) - 1;
range = backward_window_neg(t) : forward_window_neg(t);
s = s + 1;
if n <= numel(range)
A(range(1:n)) = idx_neg;
else
A(range) = idx_neg;
end
end
end

Dynamic window forming in efficient way in MATLAB

Can someone help me to provide an efficient way or help me to perform the provide code to do make same results in minimal possible steps. I shall be grateful to you.
I have an Original Array:
A = [1 1 1 4.3 4.5 4 4.3 3 1 0 0 2 6.2 6.3 6 6.2 7.4 8 7.2 2 2 3 3 2];
Output Looks like:
A = [1 1 1 4 4 4 4 3 1 0 0 2 6 6 6 6 6 7 7 2 2 3 3 2];
I apply some restrictions and removed some values from array of local maxima’s after that I received some new arrays.
Yposlocfiltered = [6 15 23];
idx = [4 6 3];
Yneglocfiltered = [2 9 20];
idx_neg = [1 1 2];
Where I will find idx(local maxima value) I will check if values behind and ahead are greater make a window.
Example
If I will find 4 and 4.5, 4.3 is greater than 4 include it in backward window
4.3 is greater than 4 include it in forward window.
I need to find range of values behind local maxima and ahead local maxima.
I have tried to write a code that’s works fine but issue is that it’s too long.
Can someone please provide me an idea to perform this action in minimal steps and in faster ways?
I have only provided code for positive local maxima’s as for negative local maxima code Is just replica of this.
Code:only for positive local maximas
clc
clear all
A = [1 0 1 4.3 4.5 5 4.3 3 0 0 0 2 6.2 6.3 7 6.2 7.4 8 7.2 1 2 3 4 2];
Yposlocfiltered = [ 6 15 23];
idx = [4 6 3];
Yneglocfiltered = [2 9 20];
idx_neg = [1 1 2];
for b = 1: numel(idx)
for c = 1:numel(A)
f = Yposlocfiltered(1,b)-c ;
g = Yposlocfiltered(1,b)+c ;
% if (f > 0 && g <= numel(X))
if(f > 0)
if (A(Yposlocfiltered(1,b)-c))> idx(1,b)
else
d= f+1;
z(b)= d;
backward_window = z;
break
end
end
end
end
for r = 1: numel(idx)
for s = 1:numel(A)
u = Yposlocfiltered(1,r)-s ;
v = Yposlocfiltered(1,r)+s ;
% if (f > 0 && g <= numel(X))
if(v <=numel(A))
if (A(Yposlocfiltered(1,r)+s))> idx(1,r)
else
w= v-1;
y(r)= w;
forward_window = y;
break
end
end
end
end
n=4
for i=1:length(backward_window)
range = backward_window(i): forward_window(i);
p = range
if n <= numel(p)
p = range(1:n)
A( p) = idx(i);
else
% if (size(range)<= 3)
A( p) = idx(i);
end
end
From the first look at your code, I believe you can combine your first two for loops into one.
sA = numel(A);
sI = numel(idx);
for i = 1:sI
f = Yposlocfiltered(i) - [1:sA];
g = Yposlocfiltered(i) + [1:sA];
f(f<1) = [];
g(g>sA) = [];
backward_window(i) = f(find(A(f) <= idx(i), 1)) + 1;
forward_window(i) = g(find(A(g) <= idx(i), 1)) - 1;
end
Here, you can use find to locate the element of an array matching the specified condition, i.e. g <= numel(X) or A(f) <= idx(i).
Your last loop which modifies A can also be integrated into the same loop, so you can have:
sA = numel(A);
sI = numel(idx);
n=4;
for i = 1:sI
f = Yposlocfiltered(i) - [1:sA];
g = Yposlocfiltered(i) + [1:sA];
f(f<1) = [];
g(g>sA) = [];
backward_window(i) = f(find(A(f) <= idx(i), 1)) + 1;
forward_window(i) = g(find(A(g) <= idx(i), 1)) - 1;
range = backward_window(i) : forward_window(i);
if n <= numel(range)
A(range(1:n)) = idx(i);
else
A(range) = idx(i);
end
end

How to save indices and values from Matrix in Matlab?

I have a 3x3 Matrix and want to save the indices and values into a new 9x3 matrix. For example A = [1 2 3 ; 4 5 6 ; 7 8 9] so that I will get a matrix x = [1 1 1; 1 2 2; 1 3 3; 2 1 4; 2 2 5; ...] With my code I only be able to store the last values x = [3 3 9].
A = [1 2 3 ; 4 5 6 ; 7 8 9];
x=[];
for i = 1:size(A)
for j = 1:size(A)
x =[i j A(i,j)]
end
end
Thanks for your help
Vectorized approach
Here's one way to do it that avoids loops:
A = [1 2 3 ; 4 5 6 ; 7 8 9];
[ii, jj] = ndgrid(1:size(A,1), 1:size(A,2)); % row and column indices
vv = A.'; % values. Transpose because column changes first in the result, then row
x = [jj(:) ii(:) vv(:)]; % result
Using your code
You're only missing concatenation with previous x:
A = [1 2 3 ; 4 5 6 ; 7 8 9];
x = [];
for i = 1:size(A)
for j = 1:size(A)
x = [x; i j A(i,j)]; % concatenate new row to previous x
end
end
Two additional suggestions:
Don't use i and j as variable names, because that shadows the imaginary unit.
Preallocate x instead of having it grow in each iteration, to increase speed.
The modified code is:
A = [1 2 3 ; 4 5 6 ; 7 8 9];
x = NaN(numel(A),3); % preallocate
n = 0;
for ii = 1:size(A)
for jj = 1:size(A)
n = n + 1; % update row counter
x(n,:) = [ii jj A(ii,jj)]; % fill row n
end
end
I developed a solution that works much faster. Here is the code:
% Generate subscripts from linear index
[i, j] = ind2sub(size(A),1:numel(A));
% Just concatenate subscripts and values
x = [i' j' A(:)];
Try it out and let me know ;)

How to create a random path between two known points in matrix in MATLAB

If there is a matrix and two known points, how to create one random path (no need to be the shortest) between these two points with:
a path that can have a level of deviation
a path can be totally random (but not necessary)
the next step can be only from 4 neighbors
ex.
5x5 matrix with two known points: (2,1) and (5,5)
After input: pt1 = [2,1]; pt2 = [5,5];.
How could I get the pattern such as follows with the path recorded in the parameter, such aspath = [2,1;2,2-;3,2;4,2;4,3;4,4;4,5;5,5].
X X X X X
o o X X X
X o X X X
X o o o o
X X X X o
PART A - Aim is to find coordinates of a line/path connecting two points on a 2D domain such that no two neighboring coordinates are diagonal to each other i.e. that is left/right/top/bottom only.
Function codes
function pts_array = points_array(pt1,pt2)
if pt1(1)==pt2(1)
if pt2(2)>pt1(2)
pts_array = [repmat(pt1(1),(pt2(2)-pt1(2)+1),1) (pt1(2):pt2(2))'];
elseif pt2(2)<pt1(2)
pts_array = flipud([repmat(pt1(1),(pt1(2)-pt2(2)+1),1) (pt2(2):pt1(2))']);
else
pts_array = pt1;
end
elseif pt1(2)==pt2(2)
if pt2(1)>pt1(1)
pts_array = [(pt1(1):pt2(1))' repmat(pt1(2),(pt2(1)-pt1(1)+1),1)];
elseif pt2(1)<pt1(1)
pts_array = flipud([(pt2(1):pt1(1))' repmat(pt1(2),(pt1(1)-pt2(1)+1),1)]);
else
pts_array = pt1;
end
else
gslope1_org = (pt2(2)-pt1(2))/(pt2(1)-pt1(1));
if gslope1_org <1
pt1 = fliplr(pt1);
pt2 = fliplr(pt2);
end
gslope1 = (pt2(2)-pt1(2))/(pt2(1)-pt1(1));
off1 = 1;
pts_array = [pt1];
gpt1 = pt1;
while 1
slope1 = (pt2(2)-gpt1(2))/(pt2(1)-gpt1(1));
if (slope1<gslope1)
gpt1 = [gpt1(1)+off1 gpt1(2)];
pts_array = [pts_array; gpt1];
else
new_y = floor(gpt1(2)+slope1);
range_y = (gpt1(2)+1 : floor(gpt1(2)+slope1))';
gpt1 = [gpt1(1) new_y];
pts_array = [pts_array ; [repmat(gpt1(1),[numel(range_y) 1]) range_y]];
end
if isequal(gpt1,pt2)
break;
end
end
if gslope1_org <1
pts_array = fliplr(pts_array);
end
end
function pts_array = points_array_wrap(pt1,pt2) %%// Please remember that this needs points_array.m
x1 = pt1(1);
y1 = pt1(2);
x2 = pt2(1);
y2 = pt2(2);
quad4 = y2<y1 & x2>x1; %% when pt2 is a lower height than pt1 on -slope
quad3 = y2<y1 & x2<x1; %% when pt2 is a lower height than pt1 on +slope
quad2 = y2>y1 & x2<x1; %% when pt2 is a higher height than pt1 on -slope
if quad4
y2 = y2+ 2*(y1 - y2);
end
if quad2
y2 = y2 - 2*(y2 - y1);
t1 = x1;t2 = y1;
x1 = x2;y1 = y2;
x2 = t1;y2 = t2;
end
if quad3
t1 = x1;t2 = y1;
x1 = x2;y1 = y2;
x2 = t1;y2 = t2;
end
pts_array = points_array([x1 y1],[x2 y2]);
if quad4
offset_mat = 2.*(pts_array(:,2)-pt1(2));
pts_array(:,2) = pts_array(:,2) - offset_mat;
end
if quad3
pts_array = flipud(pts_array);
end
if quad2
offset_mat = 2.*(pt1(2)-pts_array(:,2));
pts_array(:,2) = pts_array(:,2) + offset_mat;
pts_array = flipud(pts_array);
end
return;
Script
pt1 = [2 1];
pt2 = [5 5];
pts_array = points_array_wrap(pt1,pt2);
plot(pts_array(:,1),pts_array(:,2),'o'), grid on, axis equal
for k = 1:size(pts_array,1)
text(pts_array(k,1),pts_array(k,2),strcat('[',num2str(pts_array(k,1)),',',num2str(pts_array(k,2)),']'),'FontSize',16)
end
Output
pts_array =
2 1
2 2
3 2
3 3
4 3
4 4
4 5
5 5
Plot
PART B - Aim is to find coordinates of a line/path connecting two points on a 2D domain through given spaces.
In this special case, we are assuming that there are some spaces and only through which the path is to be connected. This is not asked by OP, but I thought it could interesting to share. So, for this, the spaces would be the o's as shown in OP's question.
Code
function your_path = path_calc(mat1,starting_pt,final_pt)
[x1,y1] = find(mat1);
pt1 = [x1 y1];
d1 = pdist2(pt1,final_pt,'euclidean');
[~,ind1] = sort(d1,'descend');
path1 = pt1(ind1,:);
your_path = path1(find(ismember(path1,starting_pt,'rows')):end,:);
return;
Run - 1
%%// Data
mat1 = zeros(5,5);
mat1(2,1:2) = 1;
mat1(3,2) = 1;
mat1(4,2:5) = 1;
mat1(5,5) = 1;
starting_pt = [2 1];
final_pt = [5 5];
%%// Path traces
path = path_calc(mat1,starting_pt,final_pt);
Gives -
mat1 =
0 0 0 0 0
1 1 0 0 0
0 1 0 0 0
0 1 1 1 1
0 0 0 0 1
path =
2 1
2 2
3 2
4 2
4 3
4 4
4 5
5 5
Run - 2
%%// Data
mat1 = zeros(5,5);
mat1(2,1:2) = 1;
mat1(3,2) = 1;
mat1(4,2:5) = 1;
mat1(5,5) = 1;
mat1 = fliplr(mat1');
%%// Notice it starts not from the farthest point this time
starting_pt = [2 3];
final_pt = [5 1];
%%// Path traces
path = path_calc(mat1,starting_pt,final_pt);
Gives
mat1 =
0 0 0 1 0
0 1 1 1 0
0 1 0 0 0
0 1 0 0 0
1 1 0 0 0
path =
2 3
2 2
3 2
4 2
5 2
5 1
To find a purely random path from the start to the goal, this function selects a random direction, checks that there is a valid neighbor in that direction and if there is, moves to that new neighbor and adds it to the path.
Directions can be invalid if, for instance, we're in the leftmost column and try to move left. We could check beforehand and only select randomized directions that lead to valid neighbors, but that would complicate the code and the chances of selecting a valid neighbor are at worst 50/50.
function path = random_path(start, goal, board_size)
m = board_size(1);
n = board_size(2);
isInBounds = #(x) x(1) >= 1 && x(1) <= m && x(2) >= 1 && x(2) <= n;
neighbor_offset = [ 0, -1; % Neighbor indices:
-1, 0; % 2
0, 1; % 1 x 3
1, 0]; % 4
% Edit: get the actual size of our neighbor list
[possible_moves, ~] = size(neighbor_offset);
current_position = start;
path = current_position;
while sum(current_position ~= goal) > 0
valid = false;
while ~valid
% Edit: "magic numbers" are bad; fixed below
% move = randi(4);
move = randi(possible_moves);
candidate = current_position + neighbor_offset(move, :);
valid = isInBounds(candidate);
end
current_position = candidate;
path = [path; current_position];
end
end
The while condition:
sum(current_position ~= goal) > 0
continues while at least one of the coordinates of sum and goal are different. I'm sure this could be written more concisely, so if there are any suggestions as to how to improve this I'd be grateful.
Likewise, the isInBounds anonymous function also seems a bit clunky, so any suggestions there would be appreciated as well.
At any rate, here's a sample of the output. Since the paths are completely random, some of them can get quite long:
random_path([2,1], [5,5], [5,5])
ans =
2 1
3 1
2 1
3 1
3 2
4 2
4 1
5 1
4 1
4 2
3 2
3 1
2 1
1 1
2 1
3 1
3 2
4 2
4 3
4 4
4 3
4 2
4 3
5 3
4 3
3 3
4 3
4 2
4 1
4 2
4 1
4 2
4 3
4 2
5 2
5 3
5 2
4 2
3 2
3 3
3 4
3 5
3 4
2 4
3 4
4 4
5 4
5 3
4 3
3 3
3 2
4 2
4 3
4 4
5 4
5 5

How to cleanup/fix my mapping of cell-array MATLAB code so I do not hit a recursive limit?

I have to solve a problem where I have to take a vector for ex [1 2 3] and map it using a table such that 1 = [1 1], 2 = [4 6 8], 3 = [6 9 12 15] (a shorter example of actual problem)
So my function ideally would be:
convert([1 2 3]) = [1 1 4 6 8 6 9 12 15]
My thinking process is that I have to make the vector a cell array first so that I can replace the values to others that different dimensions, and then convert cell array back to vector/matrix.
This is what I have so far
y = num2cell(x);
for n = 1:length(x)
if y{n} == 0
y{n} = [0 2];
elseif y{n} == 1
y{n}= [1 1];
elseif y{n} == 2
y{n} = [4 6 8];
elseif y{n} == 3
y{n} = [6 9 12 15];
elseif y{n} < 0
y{n} = 1 - convert(-(x+1));
end
output = cell2mat(y);
end
end
Everything works fine if my input has the positive values of 0, 1 , 2, or 3 in the initial vector. However, I need to have this condition where if the vector has a negative entry (x), the negative entry gets entered such that y{n} = 1 - convert(-(x+1)) However, when I do this, I get an error of hitting recursive limit.
Is there a way to fix my code such that it will work with negative values and not produce an error? Also, is there a way to do it without using a for or while loop? Thanks
You don't needs cells at all but to answer your question I suggest you deal with negatives before you loop to avoid any recursion which is also unnecessary So something along the lines of this assuming I've followed your code correctly:
negs = x < 0;
x(negs) = -(x(negs) + 1)
y = num2cell(x);
for n = 1:length(x)
if y{n} == 0
y{n} = [0 2];
elseif y{n} == 1
y{n}= [1 1];
elseif y{n} == 2
y{n} = [4 6 8];
elseif y{n} == 3
y{n} = [6 9 12 15];
end
if negs(n)
y{n} = 1 - x;
end
output = cell2mat(y);
end
end
BTW I have a feeling that this line y{n} = 1 - x; should actually be y{n} = 1 - x(n); (i.e. your original code should have read y{n} = 1 - convert(-(x(n)+1));