Create a rectangular patch with upper and lower edge in matplotlib

Viewed 202

I have the following plot in matplotlib:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Patch

x=np.linspace(0,2.,100)
y1=np.power(x,2)*2.+x
y2=np.power(x,2)*2-0.2

plt.plot(x,y1,color="k")
plt.plot(x,y2,color="k")

plt.fill_between(x,y1,y2,facecolor=(0,0,0,0.3),lw=0)

and I want to add a legend similar to this:

legend_elements = [Patch(facecolor=(0,0,0,0.4), edgecolor='k',
                         label='Filled area')]
plt.gca().legend(handles=legend_elements)

Which produces this:

Plot with filled area

However, I would like to remove the lateral edges from the patch in the legend, to match what I see in the plot (i.e., I only have the upper and lower edge).

I searched how to draw only some edges of a matplotlib patch but did not find anything. Is something like this possible at all?

2 Answers

You can make your own legend handler by extending the built-in HandlerPolyCollection which is used for fill_between:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

x = np.linspace(0, 2., 100)
y1 = np.power(x, 2) * 2 + x
y2 = np.power(x, 2) * 2 - 0.2

plt.plot(x, y1, color="k")
plt.plot(x, y2, color="k")

plt.fill_between(x, y1, y2, fc=(0, 0, 0, 0.3), lw=0, label="Filled area")

class HandlerFilledBetween(mpl.legend_handler.HandlerPolyCollection):
    def create_artists(self, legend, orig_handle, xdescent, ydescent, width, height, fontsize, trans):
        p = super().create_artists(legend, orig_handle, xdescent, ydescent, width, height, fontsize, trans)[0]
        x0, y0 = p.get_x(), p.get_y()
        x1 = x0 + p.get_width()
        y1 = y0 + p.get_height()
        line_upper = mpl.lines.Line2D([x0, x1], [y1, y1], color='k')
        line_lower = mpl.lines.Line2D([x0, x1], [y0, y0], color='k')
        return [p, line_upper, line_lower]
    
plt.gca().legend(handler_map={mpl.collections.PolyCollection: HandlerFilledBetween()})

enter image description here

I am not sure it's possible to have different edgecolor values for the different edges in your patch. One possible workaround to get the result you want would be to set the edgecolor argument in the Patch to None and just plot two horizontal lines where your legend is. Below is an example of that:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Patch

x=np.linspace(0,2.,100)
y1=np.power(x,2)*2.+x
y2=np.power(x,2)*2-0.2

plt.plot(x,y1,color="k")
plt.plot(x,y2,color="k")


plt.fill_between(x,y1,y2,facecolor=(0,0,0,0.3),lw=0)
legend_elements = [Patch(facecolor=(0,0,0,0.4), edgecolor=None,
                         label='Filled area')]

plt.gca().legend(handles=legend_elements,loc='upper center',frameon=False)
plt.hlines(y=10.1,xmin=0.735,xmax=0.87,color='k',lw=1.5,alpha=1)
plt.hlines(y=9.7,xmin=0.735,xmax=0.87,color='k',lw=1.5,alpha=1)
plt.show()

And the output gives:

enter image description here

Related