如何为不需要从用户传递图例句柄的 contourf 图创建 ax.legend() 方法?
How to create an ax.legend() method for contourf plots that doesn't require passing of legend handles from a user?
想要的功能
我希望能够打电话
ax.legend()
在包含 contourf
绘图的轴上,自动 获取图例(示例见下图)。
更多详情
我知道如何使用代理为 contourf
图创建图例条目,请参阅下面的代码并且已经讨论过 in this Q&A。但是,我会对最终调用 axes[0][-1].legend()
不需要传递任何句柄的解决方案感兴趣。
绘图生成(比本例中的绘图更复杂)发生在一个包中,用户将可以访问 fig
和 axes
并且根据绘图可能更喜欢某些轴其他人绘制图例。如果对 ax.legend()
的调用可以简单并且不需要使用代理和显式传递句柄,那就太好了。这适用于普通图、散点图、历史等,但是 contourf
不接受 label
作为 kwarg 并且没有自己的句柄所以我需要创建一个代理(矩形补丁在这种情况下)。
但是我怎么 attach/attribute/... 代理旁边的 contourf
图或 axes
的标签使得 ax.legend()
可以像其他类型的绘图一样自动访问它们吗?
示例图片
示例代码
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 #
########################
def basic_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 in enumerate(axes):
for j, ax in enumerate(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)
###################################
# User has access to fig and axes #
###################################
proxy1 = plt.Rectangle((0, 0), 1, 1, fc=cmap1(0.999), ec=cmap1(0.33), alpha=0.7, linewidth=3)
proxy2 = plt.Rectangle((0, 0), 1, 1, fc=cmap2(0.999), ec=cmap2(0.33), alpha=0.7, linewidth=3)
# would like this without passing of handles and labels
axes[0][-1].legend(handles=[proxy1, proxy2], labels=['foo', 'bar'])
plt.savefig("contour_legend.png")
plt.show()
好吧,我花了点功夫,终于找到了一个解决方案,它非常简单,但我必须更深入地研究 matplotlib.legend
才能找到正确的想法。在 _get_legend_handles
中,它显示了如何收集句柄:
for ax in axs:
handles_original += (ax.lines + ax.patches +
ax.collections + ax.containers)
所以我所缺少的就是将标签传递给代理并将代理传递给 ax.patches
带有解决方案的示例代码
变化
# 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()
完整代码
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 #
########################
def basic_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 in enumerate(axes):
for j, ax in enumerate(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()
这会生成与问题中所示相同的图像。
抱歉,毕竟我自己想出了一个解决方案,但也许这对以后的其他人有帮助。
想要的功能
我希望能够打电话
ax.legend()
在包含 contourf
绘图的轴上,自动 获取图例(示例见下图)。
更多详情
我知道如何使用代理为 contourf
图创建图例条目,请参阅下面的代码并且已经讨论过 in this Q&A。但是,我会对最终调用 axes[0][-1].legend()
不需要传递任何句柄的解决方案感兴趣。
绘图生成(比本例中的绘图更复杂)发生在一个包中,用户将可以访问 fig
和 axes
并且根据绘图可能更喜欢某些轴其他人绘制图例。如果对 ax.legend()
的调用可以简单并且不需要使用代理和显式传递句柄,那就太好了。这适用于普通图、散点图、历史等,但是 contourf
不接受 label
作为 kwarg 并且没有自己的句柄所以我需要创建一个代理(矩形补丁在这种情况下)。
但是我怎么 attach/attribute/... 代理旁边的 contourf
图或 axes
的标签使得 ax.legend()
可以像其他类型的绘图一样自动访问它们吗?
示例图片
示例代码
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 #
########################
def basic_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 in enumerate(axes):
for j, ax in enumerate(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)
###################################
# User has access to fig and axes #
###################################
proxy1 = plt.Rectangle((0, 0), 1, 1, fc=cmap1(0.999), ec=cmap1(0.33), alpha=0.7, linewidth=3)
proxy2 = plt.Rectangle((0, 0), 1, 1, fc=cmap2(0.999), ec=cmap2(0.33), alpha=0.7, linewidth=3)
# would like this without passing of handles and labels
axes[0][-1].legend(handles=[proxy1, proxy2], labels=['foo', 'bar'])
plt.savefig("contour_legend.png")
plt.show()
好吧,我花了点功夫,终于找到了一个解决方案,它非常简单,但我必须更深入地研究 matplotlib.legend
才能找到正确的想法。在 _get_legend_handles
中,它显示了如何收集句柄:
for ax in axs:
handles_original += (ax.lines + ax.patches +
ax.collections + ax.containers)
所以我所缺少的就是将标签传递给代理并将代理传递给 ax.patches
带有解决方案的示例代码
变化
# 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()
完整代码
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 #
########################
def basic_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 in enumerate(axes):
for j, ax in enumerate(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()
这会生成与问题中所示相同的图像。
抱歉,毕竟我自己想出了一个解决方案,但也许这对以后的其他人有帮助。