pythonmatplotlibconfidence-intervalerrorbar

What is the best way to plot confidence intervals for close curves


I have to plot a figure that contains three curves that are close to each other. When I plot the confidence intervals in each curve, I get an ugly figure that contains confidence intervals that are overlapped.

Here is an example.

from matplotlib import pyplot as plt
import numpy as np


y1 = [2, 3.9, 5.8, 7.2]
y2 = [2, 3.7, 5.6, 7.1]
y3 = [1.9, 3.6, 5.4, 6.8]
x = [2, 4, 6, 8]

ci1 = 1.96 * np.std(y1)/np.sqrt(len(x))
ci2 = 1.96 * np.std(y2)/np.sqrt(len(x))
ci3 = 1.96 * np.std(y3)/np.sqrt(len(x))

fig, ax = plt.subplots()
ax.plot(x, y1)
ax.fill_between(x, (y1-ci1), (y1+ci1), color='b', alpha=.1)

ax.plot(x, y2)
ax.fill_between(x, (y2-ci2), (y2+ci2), color='b', alpha=.1)

ax.plot(x, y3)
ax.fill_between(x, (y3-ci3), (y3+ci3), color='b', alpha=.1)

plt.show()

enter image description here Do you know of any better way to show the curves with the confidence intervals? I tried with error bars but it is even uglier. Is it a good alternative to show the confidence intervals in the legend or some where in the x- or y-axis?


Solution

  • I'm not sure about your confidence interval being constant along the x-axis.

    Your data should have multiple points for each x values, then you have a confidence interval for each x value, something like this:

    import numpy as np
    
    x = [2, 4, 6, 8]
    num_x = len(x)
    num_y = 20
    
    y1 = np.array([2, 3.9, 5.8, 7.2])
    y2 = np.array([2, 3.7, 5.6, 7.1])
    y3 = np.array([1.9, 3.6, 5.4, 6.8])
    
    # Multiple points for each x values
    y1 = np.stack([y1 for _ in range(num_y)], axis=0)
    y2 = np.stack([y2 for _ in range(num_y)], axis=0)
    y3 = np.stack([y3 for _ in range(num_y)], axis=0)
    
    # Some randomness for CI
    y1 = y1 + np.random.randn(*y1.shape)
    y2 = y2 + np.random.randn(*y2.shape)
    y3 = y3 + np.random.randn(*y3.shape)
    

    For your actual question, maybe you can plot individual boxes for each point of each curve:

    from matplotlib import pyplot as plt
    
    fig, ax = plt.subplots()
    
    # Tweaks the position so no overlapping
    num_curves = 3
    pos1 = np.arange(1, (num_curves + 1) * num_x + 1, num_curves + 1)
    pos2 = np.arange(2, (num_curves + 1) * num_x + 2, num_curves + 1)
    pos3 = np.arange(3, (num_curves + 1) * num_x + 3, num_curves + 1)
    
    # But then fix the ticks
    xticks = [2, 6, 10, 14]
    xtickslabels = [1, 2, 3, 4]
    
    bplot1 = ax.boxplot(y1, positions=pos1, patch_artist=True)
    bplot2 = ax.boxplot(y2, positions=pos2, patch_artist=True)
    bplot3 = ax.boxplot(y3, positions=pos3, patch_artist=True)
    
    ax.set_xticks(xticks)
    ax.set_xticklabels(xtickslabels)
    
    bplots = (bplot1, bplot2, bplot3)
    colors = ["pink", "lightblue", "lightgreen"]
    for bplot, clr in zip(bplots, colors):
        for patch in bplot["boxes"]:
            patch.set_facecolor(clr)
    
    plt.show()
    

    boxplot