I often need to indicate the distribution of some data in a concise plot, as in the below figure. I do this by plotting several fill_between
areas, limited by the quantiles of the distribution.
ax.fill_between(x, quantile1, quantile2, alpha=0.2)
In a for loop, I make plots like this by calculating quantiles 1 and 2 (as indicated by the legend) as the 0% to 100% quantiles, then 10% to 90% and so on, each fill_between
plotting on top of the previous "layer".
Here is the output with three layers of transparent colors along with the median line (0.5):
However, the legend colors are not what I would like them to be, since they (naturally) use the color of each individual layer, not taking into account the combined effect of several layers.
ax.legend([0.5]+[['0.0%', '100.0%'], ['10.0%', '90.0%'], ['30.0%', '70.0%']])
What is the best way to overwrite the face color value within the legend command?
I would like to avoid doing this by first plotting 0% to 10% with transparency "0.2", then 10% to 30% with transparency "0.4" and so on, as this will take twice the amount of time to compute and will make the code more complicated.
You can use proxy artists to place in the legend which have the exact same transparency as the resulting overlay from the plot.
As a proxy artist you can use a simple rectangle. The transparency however needs to be calculated as two objects with transparency 0.2
together will appear as a single object with transparency 0.36
(and not 0.4
!).
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.patches
a = np.sort(np.random.rand(6,18), axis=0)
x = np.arange(len(a[0]))
def alpha(i, base=0.2):
l = lambda x: x+base-x*base
ar = [l(0)]
for j in range(i):
ar.append(l(ar[-1]))
return ar[-1]
fig, ax = plt.subplots(figsize=(4,2))
handles = []
labels=[]
for i in range(len(a)/2):
ax.fill_between(x, a[i, :], a[len(a)-1-i, :], color="blue", alpha=0.2)
handle = matplotlib.patches.Rectangle((0,0),1,1,color="blue", alpha=alpha(i, base=0.2))
handles.append(handle)
label = "quant {:.1f} to {:.1f}".format(float(i)/len(a)*100, 100-float(i)/len(a)*100)
labels.append(label)
plt.legend(handles=handles, labels=labels, framealpha=1)
plt.show()
import matplotlib.pyplot as plt
import numpy as np
a = np.sort(np.random.rand(6,18), axis=0)
x = np.arange(len(a[0]))
fig, ax = plt.subplots(figsize=(4,2))
for i in range(len(a)/2):
label = "quant {:.1f} to {:.1f}".format(float(i)/len(a)*100, 100-float(i)/len(a)*100)
c = plt.cm.Blues(0.2+.6*(float(i)/len(a)*2) )
ax.fill_between(x, a[i, :], a[len(a)-1-i, :], color=c, label=label)
plt.legend( framealpha=1)
plt.show()