Multiple shaded spans on a time series plot dependent on a condition for separate time series data
3 views (last 30 days)
Show older comments
Hi, I need to add multiple shaded sections to a plot, dependent on the value of a seperate time series data set (column of values). I believe I can use patch for this, but I have only seen it done where the time intervals are defined (hard coded), not dependent on a condition. The condition is simple: either positive or negative. See the attached image of what I'm trying to create - with shading going to ylims and plot area covered. Thanks
0 Comments
Accepted Answer
Mathieu NOE
on 12 Feb 2024
hi
see demo code below - hope it helps
% dummy data
x = 0:99;
y = 1 + 0.02*sign(sin(1+x/25 + x.^2/250)) ; % y data
y0 = 0.92 + zeros(size(y)); % create a y bottom line used latter on for the patch objects
y_gap = 0.01; % make a y gap between the main curve and the patch objects
cond1 = (y>1.01);
cond2 = (y<0.99);
% find start and end indexes of groups "cond1" and "cond2"
[begin1,ends1] = find_start_end_group(cond1);
[begin2,ends2] = find_start_end_group(cond2);
% loop over this groupes
figure(1)
for k = 1:numel(begin1)
% groups "cond1"
start1 = begin1(k); % start index of k th groups of non zero values
stop1 = ends1(k); % end index of k th groups of non zero values
ind1 = (start1:stop1);
X=[x(ind1),fliplr(x(ind1))]; %#create continuous x value array for plotting
Y=[y0(ind1),fliplr(y(ind1))-y_gap]; %#create y values for out and then back
patch(X,Y,[0.9 0.9 0]); %#plot filled area
hold on
end
for k = 1:numel(begin2)
% groups "cond2"
start2 = begin2(k); % start index of k th groups of non zero values
stop2 = ends2(k); % end index of k th groups of non zero values
ind2 = (start2:stop2);
X=[x(ind2),fliplr(x(ind2))]; %#create continuous x value array for plotting
Y=[y0(ind2),fliplr(y(ind2))-y_gap]; %#create y values for out and then back
patch(X,Y,[0.5 0.6 0.9]); %#plot filled area
end
plot(x,y,'r','linewidth',2)
ylim([0.92 1.02]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [begin,ends] = find_start_end_group(ind)
% This locates the beginning /ending points of data groups
% Important : ind must be a LOGICAL array
D = diff([0;ind(:);0]);
begin = find(D == 1);
ends = find(D == -1) - 1;
end
3 Comments
Mathieu NOE
on 13 Feb 2024
hello
you made a slight mistake in your code
the patch x coordinates are not simply the indexes but x(ind)
your code
qx = [start1 stop1 stop1 start1];
correct code
qx = [x(start1) x(stop1) x(stop1) x(start1)];
same for rx (see full code below)
so this modification avoid the x shift (dx = 1) between the red line and the patch objects as we can see in the picture you posted
now the remaining gap is blank because this is a transition aera, so I don't know what you want to do here
one solution to reduce that gap is to increase the x resolution
here with 100 samples (original code)
![](https://www.mathworks.com/matlabcentral/answers/uploaded_files/1616228/image.png)
with 1000 samples (that seems good enough for my old eyes)
![](https://www.mathworks.com/matlabcentral/answers/uploaded_files/1616233/image.png)
so if your data lacks some resolution , we can fix that by using interp1 and resample the data with finer resolution (my suggestion)
% dummy data
N = 1000; % samples
x = 0:N-1;
y = 1 + 0.02*sign(sin(1+2*pi*x/N + 0.01*x.^2/N)) ; % y data
cond1 = (y>=1.0);
cond2 = (y<1.0);
% find start and end indexes of groups "cond1" and "cond2"
[begin1,ends1] = find_start_end_group(cond1);
[begin2,ends2] = find_start_end_group(cond2);
% loop over this groupes
figure(1)
plot(x,y,'r','linewidth',2)
for k = 1:numel(begin1)
start1 = begin1(k); % start index of k th groups of non zero values
stop1 = ends1(k); % end index of k th groups of non zero values
ind1 = (start1:stop1);
yl = ylim;
qy = [[1 1]*yl(1) [1 1]*yl(2)];
qx = [x(start1) x(stop1) x(stop1) x(start1)];
patch(qx, qy, 'c')
hold on
end
for k = 1:numel(begin2)
start2 = begin2(k); % start index of k th groups of non zero values
stop2 = ends2(k); % end index of k th groups of non zero values
ind2 = (start2:stop2);
yl_1 = ylim;
ry = [[1 1]*yl_1(1) [1 1]*yl_1(2)];
rx = [x(start2) x(stop2) x(stop2) x(start2)];
patch(rx, ry, 'y')
end
plot(x,y,'r','linewidth',2)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [begin,ends] = find_start_end_group(ind)
% This locates the beginning /ending points of data groups
% Important : ind must be a LOGICAL array
D = diff([0;ind(:);0]);
begin = find(D == 1);
ends = find(D == -1) - 1;
end
now we can also force the patches to increase their width on both sides with dx = mean(diff(x)) but according to my second suggestion above , that doesn't seem to be really the best choice , also this is not compatible with data where there would be a "neutral" zone with blank patch
here demo with again N = 100 samples
![](https://www.mathworks.com/matlabcentral/answers/uploaded_files/1616238/image.png)
% dummy data
N = 100; % samples
x = 0:N-1;
y = 1 + 0.02*sign(sin(1+2*pi*x/N + 0.01*x.^2/N)) ; % y data
cond1 = (y>=1.0);
cond2 = (y<1.0);
dx = mean(diff(x));
% find start and end indexes of groups "cond1" and "cond2"
[begin1,ends1] = find_start_end_group(cond1);
[begin2,ends2] = find_start_end_group(cond2);
% loop over this groupes
figure(1)
plot(x,y,'r','linewidth',2)
for k = 1:numel(begin1)
start1 = begin1(k); % start index of k th groups of non zero values
stop1 = ends1(k); % end index of k th groups of non zero values
ind1 = (start1:stop1);
yl = ylim;
qy = [[1 1]*yl(1) [1 1]*yl(2)];
% qx = [x(start1) x(stop1) x(stop1) x(start1)];
qx = [x(start1)-dx/2 x(stop1)+dx/2 x(stop1)+dx/2 x(start1)-dx/2];
patch(qx, qy, 'c')
hold on
end
for k = 1:numel(begin2)
start2 = begin2(k); % start index of k th groups of non zero values
stop2 = ends2(k); % end index of k th groups of non zero values
ind2 = (start2:stop2);
yl_1 = ylim;
ry = [[1 1]*yl_1(1) [1 1]*yl_1(2)];
% rx = [x(start2) x(stop2) x(stop2) x(start2)];
rx = [x(start2)-dx/2 x(stop2)+dx/2 x(stop2)+dx/2 x(start2)-dx/2];
patch(rx, ry, 'y')
end
plot(x,y,'r','linewidth',2)
xlim([min(x) max(x)]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [begin,ends] = find_start_end_group(ind)
% This locates the beginning /ending points of data groups
% Important : ind must be a LOGICAL array
D = diff([0;ind(:);0]);
begin = find(D == 1);
ends = find(D == -1) - 1;
end
Mathieu NOE
on 13 Feb 2024
FYI , this is one other option
with this Fex submission, you can get this result - maybe you can modifiy the function to fill the entire y axis as yous wish
![](https://www.mathworks.com/matlabcentral/answers/uploaded_files/1616268/image.png)
N = 100; % samples
ref = 1;
x = 0:N-1;
y = ref + 0.02*sign(sin(1+2*pi*x/N + 0.01*x.^2/N)) ; % y data
figure
[hlin,href,htop,hbot] = climanomaly(x,y,ref,'top','c','bottom','y',...
'mainline','r-','refline','k--');
hlin.LineWidth = 2;
href.LineWidth = 1;
href.Visible = 'off';
alpha(htop,0.7)
alpha(hbot,0.7)
More Answers (0)
See Also
Categories
Find more on Annotations in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!