4

I have this plot in which some areas between curves are being filled by definition. Is there any way to include them in legend? Especially where those filled areas are overlapped and as well as that a new and different color is being appeared.

Or there is possibility to define an arbitrary legend regardless of the curves' data? enter image description here

Shivid
  • 1,295
  • 1
  • 22
  • 36
  • 1
    Have a look [at this thread](http://stackoverflow.com/questions/13303928/how-to-make-custom-legend-in-matplotlib). The answer describes the creation of custom legend entries, which might be what you want. – Ian Oct 14 '16 at 11:43
  • 2
    See http://matplotlib.org/users/legend_guide.html#creating-artists-specifically-for-adding-to-the-legend-aka-proxy-artists which is an example of exactly this use case :) – tacaswell Oct 14 '16 at 13:55

2 Answers2

2

Using fill_bettween to plot your data will automatically include the filled area in the legend.

To include the areas where the two datasets overlap, you can combine the legend handles from both dataset into a single legend handle.

As pointed out in the comments, you can also define any arbitrary legend handle with a proxy.

Finally, you can define exactly what handles and labels you want to appear in the legend, regardless of the data plotted in your graph.

See the MWE below that illustrates the points stated above:

import matplotlib.pyplot as plt
import numpy as np

plt.close('all')

# Gererate some datas:
x = np.random.rand(50)
y = np.arange(len(x))

# Plot data:
fig, ax = plt.subplots(figsize=(11, 4))
fillA = ax.fill_between(y, x-0.25, 0.5, color='darkolivegreen', alpha=0.65, lw=0)
fillB = ax.fill_between(y, x, 0.5, color='indianred', alpha=0.75, lw=0)

linec, = ax.plot(y, np.zeros(len(y))+0.5, color='blue', lw=1.5)
linea, = ax.plot(y, x, color='orange', lw=1.5)
lineb, = ax.plot(y, x-0.25, color='black', lw=1.5)

# Define an arbitrary legend handle with a proxy:
rec1 = plt.Rectangle((0, 0), 1, 1, fc='blue', lw=0, alpha=0.25)

# Generate the legend:
handles = [linea, lineb, linec, fillA, fillB, (fillA, fillB),
           rec1, (fillA, fillB, rec1)]
labels = ['a', 'b', 'c', 'A', 'B', 'A+B', 'C', 'A+B+C']
ax.legend(handles, labels, loc=2, ncol=4)

ax.axis(ymin=-1, ymax=2)

plt.show()

enter image description here

Jean-Sébastien
  • 2,649
  • 1
  • 16
  • 21
1

Yes, you are absolutely right ian_itor, tacaswell and Jean-Sébastien, user defined legend seems to be the unique solution, in addition I made different linewidth for those area to be distinguishable from the curves, and playing with alpha got the right color.

handles, labels = ax.get_legend_handles_labels()
display = (0,1,2,3,4)

overlap_1 = plt.Line2D((0,1),(0,0), color='firebrick', linestyle='-',linewidth=15, alpha = 0.85)
overlap_2= plt.Line2D((0,1),(0,0), color='darkolivegreen',linestyle='-',linewidth=15, alpha = 0.65)
over_lo_3= plt.Line2D((0,1),(0,0), color='indianred',linestyle='-',linewidth=15, alpha = 0.75) 

ax.legend([handle for i,handle in enumerate(handles) if i in display]+[overlap_1 , overlap_2 , overlap_3 ],
      [label for i,label in enumerate(labels) if i in display]+['D','F','G'])

enter image description here

Shivid
  • 1,295
  • 1
  • 22
  • 36
  • Instead of playing to find the right color for `over_lo_3`, it would have been possible also to simply combine the `overlap_1` and `overlap_2` handle by simply using `(overlap_1, overlap_2)` instead of `over_lo_3` when you define the legend. But your solution works good as well, so good job. – Jean-Sébastien Oct 16 '16 at 13:38