Multiple shaded spans on a time series plot dependent on a condition for separate time series data
6 次查看(过去 30 天)
显示 更早的评论
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 个评论
采纳的回答
Mathieu NOE
2024-2-12
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 个评论
Mathieu NOE
2024-2-13
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
2024-2-13
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)
更多回答(0 个)
另请参阅
类别
在 Help Center 和 File Exchange 中查找有关 Logical 的更多信息
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!