Make a plot with gradient shaded confidence intervals

53 views (last 30 days)
Dear all,
I would like to fill the red shaded area in the plot below using a gradient fill (ideally with a jet colormap), so that the area between the blue line and the upper red dashed line goes from red to blue and the area between the blue line and the lower red dashed line goes from red to blue.
x = Data(:,1);
y = Data(:,2);
y10 = Data(:,3);
y90 = Data(:,4);
figure
plot(x, y, 'b', 'LineWidth', 2);
hold on;
plot(x, y10, '--r');
plot(x, y90, '--r');
fill([x; flipud(x)], [y10; flipud(y90)], 'r',...
'FaceAlpha', 0.1, 'EdgeColor', 'none');
hold off;
legend('Median', '10th-90th Percentiles', 'Location', 'best');
grid on;
Just to give an idea, something like this (without color steps but with a gradient fill):
I have tried myself, but I can't really find a nice (and fast) solution.
I have attched the Data file.
Any help would be grately appreciated!
  1 Comment
Umar
Umar on 7 Jul 2024
Hi Simone,
To achieve a gradient fill effect in the specified red shaded area of the plot, we can use a combination of surf and colormap functions in Matlab. Here's a step-by-step guide to implement this:
Create a Meshgrid: Generate a meshgrid that covers the area to be filled with a gradient. This meshgrid will define the coordinates for the gradient fill.
[X, Y] = meshgrid(x, linspace(min(y10), max(y90), 100));
Calculate Colors: Determine the colors for the gradient fill based on the jet colormap. You can adjust the colormap to suit your preferences.
colors = jet(100); % Use jet colormap with 100 colors
Plot the Gradient Fill: Use the surf function to plot the gradient fill on the meshgrid.
figure; surf(X, Y, zeros(size(X)), 'CData', colors, 'FaceColor', 'interp', 'EdgeColor', 'none'); view(2); % 2D view for a flat gradient fill
Adjust Plot Settings: Customize the plot appearance to match the original plot.
hold on; plot(x, y, 'b', 'LineWidth', 2); plot(x, y10, '--r'); plot(x, y90, '--r'); hold off; legend('Median', '10th-90th Percentiles', 'Location', 'best'); grid on;
By following these steps, you can create a gradient fill effect in the specified red shaded area of the plot, transitioning smoothly from red to blue based on the jet colormap. Feel free to adjust the colormap, shading, or other parameters to achieve the desired visual effect.

Sign in to comment.

Accepted Answer

DGM
DGM on 8 Jul 2024
Look into how to configure patch() objects:
You could specify a specific color for each vertex with interpolation between:
% some fake data
load data.mat
% split the data array
x = Data(:,1);
y = Data(:,2);
y10 = Data(:,3);
y90 = Data(:,4);
% color endpoints
colorA = [1 0 0];
colorB = [0 0 1];
CData = [repmat(colorA,[numel(x),1]); repmat(colorB,[numel(x),1])];
alph = 0.5;
figure
hold on;
patch([x; flipud(x)], [y; flipud(y90)], permute(CData,[1 3 2]),...
'FaceAlpha', alph, 'EdgeColor', 'none');
patch([x; flipud(x)], [y; flipud(y10)], permute(CData,[1 3 2]),...
'FaceAlpha', alph, 'EdgeColor', 'none');
hp(1) = plot(x, y, 'b', 'LineWidth', 2);
hp(2) = plot(x, y10, '--k', 'LineWidth', 2);
plot(x, y90, '--k', 'LineWidth', 2);
hold off;
legend(hp,'Median', '10th-90th Percentiles', 'Location', 'best');
grid on;
... or you could specify a linear value used as a relative position into the current colormap (with interpolation):
% some fake data
load data.mat
% split the data array
x = Data(:,1);
y = Data(:,2);
y10 = Data(:,3);
y90 = Data(:,4);
% color endpoints
CData = [zeros(numel(x),1); ones(numel(x),1)];
CT = jet(64);
alph = 0.5;
figure
hold on;
patch([x; flipud(x)], [y; flipud(y90)], CData,...
'FaceAlpha', alph, 'EdgeColor', 'none');
patch([x; flipud(x)], [y; flipud(y10)], CData,...
'FaceAlpha', alph, 'EdgeColor', 'none');
hp(1) = plot(x, y, 'b', 'LineWidth', 2);
hp(2) = plot(x, y10, '--k', 'LineWidth', 2);
plot(x, y90, '--k', 'LineWidth', 2);
hold off;
legend(hp,'Median', '10th-90th Percentiles', 'Location', 'best');
grid on;
colormap(CT)
clim([0 1])
  6 Comments
DGM
DGM on 11 Jul 2024
Sorry about not being at the computer. Try this. It's still a kludge on my part, but we can at least see if it's doing what you need. I'm just doing PWL interpolation for the gradient instead of trying to calculate the full distribution. Even with only 9 breakpoints, it's a challenge to notice any banding in the output, and the interpolation should be exact at the values in prc_Stps.
load('xy.mat')
x = xy(:,1);
y = xy(:,2);
windowSize = days(90);
prc_Min = 10;
prc_Max = 90;
% idk if you were going to use this for something else
% but i'm going to use it
prc_Stp = 10;
prc_Stps = prc_Min:prc_Stp:prc_Max;
% Compute moving median and percentiles with variable time step
% don't really need Y_median so long as Y contains 50
npk = numel(prc_Stps);
Y_median = zeros(size(y));
Y = zeros(numel(y),npk);
for i = 1:length(x)
window_start = x(i);
window_end = window_start + windowSize;
idx = x >= window_start & x <= window_end;
if ~isempty(idx)
Y_median(i) = median(y(idx), 'omitnan');
for pk = 1:npk
Y(i,pk) = prctile(y(idx), prc_Stps(pk));
end
else
Y_median(i) = NaN;
Y(i,pk) = NaN;
end
end
figure
plot(x, Y_median, 'b', 'LineWidth', 2);
hold on;
plot(x, Y(:,1), '--r'); % 10th percentile
plot(x, Y(:,end), '--r'); % 90th percentile
fill([x; flipud(x)], [Y(:,1); flipud(Y(:,end))], 'r', 'FaceAlpha', 0.1, 'EdgeColor', 'none'); % Confidence interval fill
hold off;
legend('Median', '10th-90th Percentiles', 'Location', 'best');
grid on;
xlim(imrange(x))
%% Your Code
% idk what keep() is, but i don't have it
%keep x Y_median Y_10 Y_30 Y_70 Y_90
% split the data array
y = Y_median;
% color underlay parameters
CT = jet(64);
alph = 0.5;
reversegradient = false;
sz = [1000 1000]; % size of the raster image
% the image space
xv = 1:sz(2);
yv = (1:sz(1)).';
% just rescale everything into image coordinates
xrange = imrange(x);
yrange = imrange(Y);
xim = rescale(x,xrange,[1 sz(2)+1]).';
Yim = rescale(Y,yrange,[1 sz(1)]); % lower
% interpolate onto the uniform image grid
Yim = interp1(xim,Yim,xv); % lower
% generate the underlay image
% this could probably also be done with interp2()
pctrange = [prc_Min prc_Max];
pctswing = max(abs(50 - pctrange));
z = 1 - abs(prc_Stps - (pctswing + prc_Min))/pctswing;
Z = zeros(sz);
for k = 1:sz(2)
Z(:,k) = interp1(Yim(k,:),z,yv.','linear','extrap');
end
% just plot the lines first so that xlim,ylim are defined
figure
hold on;
hp(1) = plot(x, y, 'b', 'LineWidth', 2);
hp(2) = plot(x, Y(:,1), '--k', 'LineWidth', 2);
plot(x, Y(:,end), '--k', 'LineWidth', 2);
hp(3) = plot(x, Y(:,3), '-k', 'LineWidth', 2);
plot(x, Y(:,7), '-k', 'LineWidth', 2);
legend(hp,'Median', '10th-90th Percentiles','30th-70th Percentiles', 'Location', 'best');
yl = ylim; % cache these
% show the image
% mask off all the external area and set opacity
hi = imagesc(xrange,yrange,Z);
hi.AlphaData = (Z >= 0)*alph;
% final plot setup
hold off;
grid on;
if reversegradient
colormap(CT)
else
colormap(flipud(CT))
end
clim([0 1])
xlim(imrange(x)); ylim(yl);
% purely out of spite for rescale()'s cumbersome syntax
function yy = rescale(xx,inrange,outrange)
scalefactor = diff(outrange)/diff(inrange);
yy = scalefactor*(double(xx) - inrange(1)) + outrange(1);
end
function [minval maxval] = imrange(inpict)
% [min max] = IMRANGE(INPICT)
% returns the global min and max pixel values in INPICT
%
% INPICT can be a vector or array of any dimension or class
x = inpict(:);
minval = double(min(x));
maxval = double(max(x));
if nargout < 2
minval = cat(2,minval,maxval);
end
end
Simone
Simone on 11 Jul 2024
Hi @DGM please don't be sorry. Your help has been essential (and so much appreciated).
Indeed, your code works great and does exactly what I need.
However, the only problem with imagesc is that if I want to plot it with a log scale, it won't work.
To overcome this issue, I came up with another piece of code that can deal with log scales (see code below for future reference).
That said, without your help it would have not been possible to solve my problem.
Thank you very much indeed
clear
close all
clc
load('xy.mat')
x = xy(:,1);
y = xy(:,2);
windowSize = days(90);
prc_Min = 10;
prc_Max = 90;
prc_Stp = 1;
prc_Stps = prc_Min:prc_Stp:prc_Max;
npk = numel(prc_Stps);
Y_Median = zeros(size(y));
YY = zeros(numel(y),npk);
for i = 1:length(x)
window_start = x(i);
window_end = window_start + windowSize;
idx = x >= window_start & x <= window_end;
if ~isempty(idx)
Y_Median(i) = median(y(idx), 'omitnan');
for pk = 1:npk
YY(i,pk) = prctile(y(idx), prc_Stps(pk));
end
else
Y_Median(i) = NaN;
YY(i,pk) = NaN;
end
end
figure
plot(x, Y_Median, 'b', 'LineWidth', 2);
hold on;
plot(x, YY(:,1), '--r'); % 10th percentile
plot(x, YY(:,end), '--r'); % 90th percentile
fill([x; flipud(x)], [YY(:,1); flipud(YY(:,end))], 'r', 'FaceAlpha', 0.1, 'EdgeColor', 'none'); % Confidence interval fill
hold off;
legend('Median', '10th-90th Percentiles', 'Location', 'best');
grid on;
j_Up = find(YY(1,:)>Y_Median(1));
j_Down = find(YY(1,:)<Y_Median(1));
sz = (numel(YY(1,:))-1)/2;
CT_Down = jet(sz);
CT_Up = flipud(jet(sz));
FaceAlpha_Transp = 1;
figure
hold on
Patch_X = [x; flipud(x)];
k = 1;
for i_Up = 1 : numel(j_Up)
Patch_Y_Up = [YY(:, j_Up(i_Up)); flipud(YY(:, j_Up(i_Up)-1))];
fill(Patch_X, Patch_Y_Up, CT_Up(k,:), 'FaceAlpha',...
FaceAlpha_Transp,'EdgeColor','none');
k = k + 1;
end
k = 1;
for i_Down = 1 : numel(j_Down)
Patch_Y_Down = [YY(:, j_Down(i_Down)); flipud(YY(:, j_Down(i_Down)+1))];
fill(Patch_X, Patch_Y_Down, CT_Down(k,:), 'FaceAlpha',...
FaceAlpha_Transp,'EdgeColor','none');
k = k + 1;
end
hp(1) = plot(x, Y_Median, '-k', 'LineWidth', 2); % Median
hp(2) = plot(x, YY(:,21), '-k', 'LineWidth', 1); % 30th Percentile
hp(3) = plot(x, YY(:,61), '-k', 'LineWidth', 1); % 70th Percentile
legend(hp,'Median', '30th-70th Percentiles', 'Location', 'best');
grid on

Sign in to comment.

More Answers (0)

Categories

Find more on Resizing and Reshaping Matrices in Help Center and File Exchange

Products


Release

R2024a

Community Treasure Hunt

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

Start Hunting!