Clear Filters
Clear Filters

Multiple shaded spans on a time series plot dependent on a condition for separate time series data

3 views (last 30 days)
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

Accepted Answer

Mathieu NOE
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
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)
with 1000 samples (that seems good enough for my old eyes)
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
% 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
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
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)

Sign in to comment.

More Answers (0)

Products


Release

R2019b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!