Skip to content Skip to sidebar Skip to footer

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?"