Filling an area between max and minimum lines on a plot

Hi,
I am trying to fill the area between the max at any given x and the min at any given x in the attached plot. I want it to be one colour.
This is the code that I am using to make the current plot:
low_thick = 0.0;
high_thick = 4.0;
incre_thick = 0.0001;
increments = low_thick:incre_thick:high_thick;
%%Plot figure and grab matrix
figure(); hold on
cmap = colormap(parula(71))
for idx = 5:14:71
month_data = data(:,idx);
total_n = sum(~isnan(month_data));
m = zeros(numel(increments),1);
for n = 1:numel(increments)
y = (sum(month_data < increments(n))/total_n)*100;
m(n,:) = y;
end
plot(m,increments,'color',cmap(idx,:))
clearvars month_data total_n
end
It is basically plotting how much of each series of data is under a threshold (y), and I want to have the area shaded and then the lines on top as well.
I've done a fair amount of searching and playing with fill() and area() with no luck.
Any assistance would be greatly received.
Thanks

 Réponse acceptée

dpb
dpb le 30 Août 2018
Modifié(e) : dpb le 30 Août 2018
See if Answer_332770 will give enough clues.
Basically, must build the lines of maxima/minima between the curves. That shouldn't be too bad if you've got the data as an array; I didn't try to read the code.
I didn't have time-enough to read the code to figure out the storage to save the data or the min/max while going through the loop, the easy way may be to "cheat" and just retrieve it from the plot when done...
hL(idx)=plot(plot(m,increments,'color',cmap(idx,:));
and then you can retrieve each after the plot is complete by
X=get(hL(1),'XData');
Y=cell2mat(get(hL,'YData'));
and then
Ymin=min(Y); YMax=max(Y);
ADDENDUM/ERRATUM
I forgot the linked-to Answer is an area plot because that poster wanted area to baseline; area can't do variable baseline; you need patch.
String the y min, max data out in long vector and call patch.
hA=patch([X fliplr(X)].',[YMin fliplr(yMax)].','r');
I did a real simple-minded simulation here to illustrate the idea--
subplot(2,1,1), hold on
for i=1:5,hL(i)=plot(rand(100,1));end % make a bunch of lines on the plot
X=get(hL(1),'XData'); % retrieve the x axis data (presume same)
Y=cell2mat(get(hL,'YData')); % and all the y data
yMn=min(Y); yMx=max(Y); % min, max of the y of all plot lines
subplot(2,1,2)
patch([X fliplr(X)].',[yMn fliplr(yMx)].','r')
results in
The second you can see is the bounded area of the first by visual comparison. NB: the reversal of the data for the second portion; patch needs an enclosed area so this draws minimum array from left to right; then sticks the RH end of the maximum array onto the end in reversed order to go back to the origin.
I did assume that the x- vector is the same for each line here; if that isn't so, you'll have to retrieve those positions that go with the min and max values by saving the indices in the optional second output argument of min,max and select the corresponding X.

19 commentaires

I don't have the data as an array because I build the data in the loop to plot it. I am currently trying to work out how to do this as I thought this would be what I needed but I've only been using matlab for a few days.
dpb
dpb le 30 Août 2018
Modifié(e) : dpb le 30 Août 2018
I've got meetings in town right now so have to run...the easy way then may be to "cheat" and retrieve the data from the plot itself...
When you plot, save the line handles...
hL(idx)=plot(plot(m,increments,'color',cmap(idx,:));
and then you can retrieve each after the plot is complete by
X=cell2mat(get(hL(1),'XData'));
Y=cell2mat(get(hL,'YData'));
and then
Ymin=min(Y); YMax=max(Y);
ADDENDUM
I forgot the linked-to Answer is an area plot because that poster wanted area to baseline; area can't do variable baseline; you need patch.
String the y min, max data out in long vector and call patch.
hA=patch([X fliplr(X)].',[YMin fliplr(yMax)].','r');
Gotta' run; will check back in when get back to house...
Andrew Jolly
Andrew Jolly le 12 Sep 2018
Modifié(e) : dpb le 12 Sep 2018
Hi DPB,
Sorry for delay, I have been away from the office and also working on something else, I came back to this today. Many many thanks for the effort you have put into helping me so far.
I have made some progress and now successfully have the correct max for both ys as seen plotted on the curve below:
It is hard to see on the plot but the lines do cross over somewhat between 1 and 1.5 on x and the black dots that plot the min and max are correct.
I went about this in a different way to you as I actually also needed a table of results so I simply pulled the mins/maxs from the results row wise.
However when I come to plotting the fill, it doesn't work how I am looking for as seen in the plot below in red:
It seems that it isn't filling between the two lines but it is linked to the max of each line which both reach 100 at about 2.5 and 1.3, whilst x goes all the way to 4. Other data sets that I need to analyse will always max out at 100 but the point along x at which this happens will vary. My x vector goes from 0 to 4 because of this.
Please find below the relevant bits of my code. I know I could combine some of this onto one line but as I am very much still learning I have really broken it out so it helps me understand why I'm doing what.
Creating my data:
%%create array of data
output = []; %pre-define output array
for idx_1 = 1:5
month_data = novs(:,idx_1); %novs is a 10495x5 double
total_n = sum(~isnan(month_data)); %total number of non-NaN results
month_out=nan(numel(increments),1);%pre-define output array
for idx_2 = 1:numel(increments)
month_per=sum(month_data<increments(idx_2)/total_n)*100; %number below threshold
month_out(idx_2,:)=month_per; %build output array for that month
end
output = [output month_out]; %build final output array
end
Plotting the lines:
%%Create data for plotting mean and individuals & plotting
mean_output = mean(output,2); %mean of each row of output
min_output = max(output,[],2); %min of each row of output
max_output = min(output,[],2); %max of each row of output
hold on
figure(1)
plot(increments,output,'r--') %plot each month with dashed red line
plot(increments,mean_output,'r-','lineWidth',2) %plot mean output solid red line
plot(increments, min_output,'ko','markerSize',2) %plot min for verification
plot(increments, max_output,'ko','markerSize',2) %plot max for verification
Attempt at filling the lines:
%%plotting fill between two lines
%using patch() to work up one line and back down the other
figure(2)
x = increments'; %transpose increments so it is same dimension as output (got error)
x_flip = fliplr(x); %flip x so can work back down x
y1 = min_output; %define y1 as min at each x (for clarity)
y2 = max_output; %define y2 as max at each x (for clarity)
y2_flip = fliplr(y2); %flip y2 so can work back down y2
x_fill = [x, x_flip]; %define x-fill vector
y_fill = [y1, y2_flip]; %define y-fill vector
patch(x_fill,y_fill,'r')
I have tried manually only plotting each line until they reach 100 but that doesn't work either, and I didn't want to spend any time fixing something problematically before knowing what the problem was.
Thanks again for your help.
dpb
dpb le 12 Sep 2018
Modifié(e) : dpb le 12 Sep 2018
The 45-degree line indicates your vector is connecting from the end back to the beginning from the last to the first directly instead of connecting from the RH end of the lower up to the RH end of the upper and THEN back to the origin.
I don't have time right now to try to decipher the code but remember patch encloses a closed area traced by the x,y coordinates; you need the data to follow the path of the global minimum from origin to its RH end point, then at that same RH x-value the y-value needs last (RH) max and then return to origin. Somewhere you've introduced a break and are going from the RH max y to the origin x in one step; that results in the straight lower boundary.
OH! Took one more glance before having to head to town...
min_output = max(output,[],2); %min of each row of output
max_output = min(output,[],2); %max of each row of output
Maybe there's your problem; you've mixed metaphors here and named max() with min and vice versa; I'm guessing there's the problem.
I follow the use of separate lines to understand what individual steps are; however when you throw away the min/max in the cast back to y1/y2 that makes understanding the code more difficult rather than simpler as have lost the connection as to "who's who in the zoo!" by having meaningful variable name. But, it looks like those are consistent with a correct definition of min/max so if you just fix the above, it probably will work.
Again - thanks for getting back to me quickly.
Ah, I'd actually spotted that mistake earlier and fixed it, but it doesn't result in what I'm looking for.
I have combined the code into one line so you can see the meaningful variable name, and then also plotted it again in my stepped out manner. Both are identical.
At the bottom of the plot is the min and max plotted in blue and red respectively.
I am still presuming that it has something to do with both lines going to x=4, y=100 but in my attempts to chop them at a point manually haven't resulted in correct results either.
See code for the plot above:
%%plotting fill between two lines
%using patch() to work up one line and back down the other
subplot(3,1,1)
%first all in one (plotted in cyan)
patch([increments',fliplr(increments')],[min_output,fliplr(max_output)],'c')
%now seperated out (plotted in red)
x = increments'; %transpose increments so it is same dimension as output (got error)
x_flip = fliplr(x); %flip x so can work back down x
y1 = min_output; %define y1 as min at each x (for clarity)
y2 = max_output; %define y2 as max at each x (for clarity)
y2_flip = fliplr(y2); %flip y2 so can work back down y2
x_fill = [x, x_flip]; %define x-fill vector
y_fill = [y1, y2_flip]; %define y-fill vector
subplot(3,1,2)
patch(x_fill,y_fill,'r')
subplot(3,1,3)
hold on
plot(increments,y1,'b-') %minimum in blue
plot(increments,y2,'r-') %maximum in red
hold off
Thanks again for the time you have put in to help me out.
Hmmm...well, somehow then you've got the first symptom.
How about attaching a .mat file with your data to play with rather than trying to simulate?
Hi - yes ok, thanks. I will have to strip out some of the information in the files as some of it isn't ok to share but will do this tomorrow.
Thanks again.
Just
save ajolly novs increments
and attach ajolly.mat should do it. Nothing else is needed to build the plots.
ah great, thanks. I don't have access to it remotely so it will need to wait til the morning in any case but will do that.
dpb
dpb le 12 Sep 2018
Modifié(e) : dpb le 12 Sep 2018
OK...it's annual auction this weekend for the local community college foundation of which I'm prez so I'll likely be tied up a fair amount of the day helping with the set up getting ready but I'll try to look in when get a chance...shouldn't take long once have some actual data to see what's actually going wrong.
You could just check and make sure I didn't overlook anything that if you just load that file that the code snippets above work to produce the bum figure; they're not needing something else to actually run (albeit with less than satisfactory result, but that's what we're wanting). That would save a round if I did miss something.
jonas
jonas le 12 Sep 2018
Modifié(e) : jonas le 12 Sep 2018
Just wanted to chime in and say that the error could be related to the dimensions of x and y. In the following lines x_fill should be a vector, not a matrix.
x_fill = [x, x_flip]; %define x-fill vector
y_fill = [y1, y2_flip]; %define y-fill vector
However, there is a risk that you have made a 2-column matrix by mistake, which would explain the two patches. Check the size of your vectors!
Perhaps a stupid comment, didn't read the entire correspondance :)

Hi Jonas - thanks for chiming in. I'm not sure I fully understand, but I have gone back and checked and everything is a 401 x 1 vector.

As dpb pointed out it might be a bit confusing to reassign things to x & y (although I originally did it to make sure I understood what was going on) I have gone back to:

patch([increments' fliplr(increments')],[min_output fliplr(max_output)],'c')

Where:

each of the elements, increments', fliplr(increments'), min_output and fliplr(max_output) are all size 401 x 1.

jonas
jonas le 13 Sep 2018
Modifié(e) : jonas le 13 Sep 2018

If increments' is in fact 401 x 1 (single column) then fliplr (flip left right) does nothing for you. Use flipud (flip up down) in that case.

Again, if increments' is single column, then

[increments' flipud(increments')]

is double column, which strengthens my suspicion. Possibly, this could work for you:

patch([increments'; flipud(increments')],[min_output;flipud(max_output)],'c')

...or you could just keep the original code and just remove the transpose (').

Just make sure the input to patch (everything within the [ ]) are vectors and you should be fine

Hi both,
I have it solved!
I have no idea why it works, and on reading the documentation I'm not really any more clear but I simply changed fliplr to flip and it works fine.
Many thanks for your help on this.
Andrew
@jonas I didn't see your most recent comment before posting my thanks. I guess I could go back and fix increments so it is created in the same shape as the other arrays as that's probably where my confusion is coming from.
It seems so obvious now that a 401 x 1 array can't by definition be flipped lr...
Nice!

Yes, and your initial code was based on row vectors, not column which is why used fliplr. I tend to forget flip which is a (relatively) late arrival will do whichever is "the right thing" on a vector of either orientation instead of just operating on the specific orientation of the specific (original) versions.

Yes, I think that my lesson from all of this is to think carefully about dimensions of matrices when writing code. I've only been doing this for 2 weeks alongside other things so I'm still very new at it.
Thank you for all your help.
Yes, it is important; particularly as you become more adept conformance to the rules of matrix algebra for manipulations can lead to great simplification of algorithms by fully vectorizing otherwise complex looping constructs.
Similarly, concatenation only works for conformant sizes and mismatches in orientation can cause unexpected results even if not actual syntax error as was the case here.
I really should have caught the orientation problem; I noticed the colon on first dimension but didn't take the time to dig into the rest that carefully.

Connectez-vous pour commenter.

Plus de réponses (0)

Produits

Version

R2018a

Community Treasure Hunt

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

Start Hunting!

Translated by