Search code examples
matlabplotmatlab-figurematlab-hg2

Nicer errorbars when multiple data is shown


So I need to plot some errobar plots in a figure. Specifically I need 4 errorbar plots in each figure, the problem is that the figure gets a bit unreadable when several data is plotted.

Example:

clear all
close all
clc



x = 0:pi/10:pi;
y = sin(x);
y2=cos(x);
y3=atan(x);
e = std(y)*ones(size(x));
e2 = std(y2)*ones(size(x));
e3 = std(y3)*ones(size(x));

figure
hold on
errorbar(x,y,e)
errorbar(x,y2,e2)
errorbar(x,y3,e3)

enter image description here

My idea to solve the problem is to fill the area that the corners of the errorbars delimit with the same color of the plot and low alpha, so the overlapping of the areas is visible.

The problem is that the only way I can imagine of doing this is to create a mesh in the area delimited by the errorbar corners and then fill them with patch. This is indeed possible, but quite annoying, as a plot will not have a convex hull, therefore I will need to iteratively go creating the triangles one by one. So the question is : Is there a more elegant way of doing this?

Additionally, I am open to suggestions of a better way of visualizing this data, if anyone has.


Solution

  • Approach 1

    Plot the graphs normally, and then plot the errorbars manually using patches. The data for the patches (coordinates and color) is taken from the plotted graphs, and the alpha of the patch can be set to any desired value.

    clear all
    close all
    clc
    
    error_alpha = .4;
    error_width_factor = .01;
    
    x = 0:pi/10:pi;
    y = sin(x);
    y2 = cos(x);
    y3 = atan(x);
    e = std(y)*ones(size(x));
    e2 = std(y2)*ones(size(x));
    e3 = std(y3)*ones(size(x));
    ee = [e; e2; e3];
    
    figure
    hold on
    hp(1) = plot(x,y);
    hp(2) = plot(x,y2);
    hp(3) = plot(x,y3);
    
    w = diff(xlim)*error_width_factor;
    for m = 1:numel(hp)
        for n = 1:numel(hp(m).XData)
            patch(hp(m).XData(n)+[-1 1 1 -1]*w, hp(m).YData(n)+[-1 -1 1 1]*ee(m,n), 'w',...
               'FaceColor', hp(m).Color, 'FaceAlpha', error_alpha, 'EdgeColor', 'none');
        end
    end
    

    enter image description here

    Approach 2

    Similar as before, but use narrower patches and plot them with a graph-dependent horizontal shift (as suggested by @Patrik). Applying an alpha value helps keep the figure lighter.

    The code is a modified version of that of approach 1. The example shown here contains 101 data values, and is still rather visible.

    clear all
    close all
    clc
    
    error_alpha = .4;
    error_width_factor = .003;
    
    x = 0:pi/50:pi;
    y = sin(x);
    y2 = cos(x);
    y3 = atan(x);
    e = std(y)*ones(size(x));
    e2 = std(y2)*ones(size(x));
    e3 = std(y3)*ones(size(x));
    ee = [e; e2; e3];
    
    figure
    hold on
    hp(1) = plot(x,y);
    hp(2) = plot(x,y2);
    hp(3) = plot(x,y3);
    
    w = diff(xlim)*error_width_factor;
    m0 = (numel(hp)+1)/2;
    for m = 1:numel(hp)
        for n = 1:numel(hp(m).XData)
            patch(hp(m).XData(n)+[-1 1 1 -1]*w+w*(m-m0),...
            hp(m).YData(n)+[-1 -1 1 1]*ee(m,n), 'w', 'FaceColor', hp(m).Color, ...
            'FaceAlpha', error_alpha, 'EdgeColor', 'none');
        end
    end
    

    enter image description here