How To Create An Ax.legend() Method For Contourf Plots That Doesn't Require Passing Of Legend Handles From A User?
Desired feature I would like to be able to call ax.legend() on an axis containing a contourf plot and automatically get the legend (see plot below for an example). More Detail I
Solution 1:
Well, I dappled a bit more and found a solution after all that's surprisingly simple, but I had to dig much deeper into matplotlib.legend
to get the right idea. In _get_legend_handles
it shows how it collects the handles:
for ax in axs:
handles_original += (ax.lines + ax.patches +
ax.collections + ax.containers)
So all I was lacking was to pass the labels to the proxies and the proxies to ax.patches
Example Code with Solution
changes
# pass labels to proxies and place proxies in loop
proxy1 = plt.Rectangle((0, 0), 1, 1, fc=cmap1(0.999), ec=cmap1(0.33),
alpha=0.7, linewidth=3, label='foo')
proxy2 = plt.Rectangle((0, 0), 1, 1, fc=cmap2(0.999), ec=cmap2(0.33),
alpha=0.7, linewidth=3, label='bar')
# pass proxies to ax.patches
ax.patches += [proxy1, proxy2]
#################################### User has access to fig and axes ##################################### no passing of handles and labels anymore
axes[0][-1].legend()
full code
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from matplotlib.colors import LinearSegmentedColormap
######################### not accessed by User #########################defbasic_cmap(color):
return LinearSegmentedColormap.from_list(color, ['#ffffff', color])
cmap1 = basic_cmap('C0')
cmap2 = basic_cmap('C1')
x = np.linspace(0, 10, 50)
mvn1 = stats.multivariate_normal(mean=[4, 4])
mvn2 = stats.multivariate_normal(mean=[6, 7])
X, Y = np.meshgrid(x, x)
Z1 = [[mvn1.pdf([x1, x2]) for x1 in x] for x2 in x]
Z2 = [[mvn2.pdf([x1, x2]) for x1 in x] for x2 in x]
Z1 = Z1 / np.max(Z1)
Z2 = Z2 / np.max(Z2)
fig, axes = plt.subplots(2, 2, sharex='col', sharey='row')
for i, row inenumerate(axes):
for j, ax inenumerate(row):
cont1 = ax.contourf(X, Y, Z1, [0.05, 0.33, 1], cmap=cmap1, alpha=0.7)
cont2 = ax.contourf(X, Y, Z2, [0.05, 0.33, 1], cmap=cmap2, alpha=0.7)
# pass labels to proxies and place proxies in loop
proxy1 = plt.Rectangle((0, 0), 1, 1, fc=cmap1(0.999), ec=cmap1(0.33),
alpha=0.7, linewidth=3, label='foo')
proxy2 = plt.Rectangle((0, 0), 1, 1, fc=cmap2(0.999), ec=cmap2(0.33),
alpha=0.7, linewidth=3, label='bar')
# pass proxies to ax.patches
ax.patches += [proxy1, proxy2]
#################################### User has access to fig and axes ##################################### no passing of handles and labels anymore
axes[0][-1].legend()
plt.savefig("contour_legend_solved.png")
plt.show()
This produces the same image as shown in the question.
Sorry, was able to come up with a solution on my own after all, but maybe this will be helpful for someone else in the future.
Post a Comment for "How To Create An Ax.legend() Method For Contourf Plots That Doesn't Require Passing Of Legend Handles From A User?"