如何在matplotlib图例中放置多个色图补丁?

时间:2019-04-03 18:24:21

标签: python matplotlib legend colormap

当前情况:

我有多组线,其中同一组中的线根据某些特定于组的参数而变化。我根据此参数为同一组内的每行分配一种颜色表中的颜色,并对每个组使用不同的颜色表。

现在,我想在图上添加图例,每组线有一个条目。 stackoverflow question: How to put multiple colormap patches in a matplotlib legend?

仅一组行的解决方案:

如果我只有一组线,最好的标记方法是按照答案Matplotlib: Add colorbar to non-mappable object中的建议添加色条。

如何最好地对多组线执行此操作?

由于我有多组这样的线,所以我不想为每个新参数添加一个颜色条。相反,我宁愿在图例中放置填充有对应色图的色块(作为一种迷你色条)。

最低工作示例:

在下面,您可以找到当前情况的最小工作示例。但是请注意,我在很大程度上简化了隐藏参数依赖性的行的计算。因此,这里的“参数” $this->就是我要遍历的索引。我的实际代码根据具有更复杂功能的模型参数来计算x和y值。因此,每行的最大param相同,尽管实际上并非如此。

param_max

这将产生上面显示的图。


由于在stackoverflow上找不到直接回答此问题的任何内容,因此我尝试自己找到一种解决方案,您可以在答案部分中找到它。如果有更直接/合适的方法,我将很高兴知道。

1 个答案:

答案 0 :(得分:2)

针对这种情况,我将ImportanceOfBeingErnest的答案的解决方案调整为"Create a matplotlib mpatches with a rectangle bi-colored for figure legend"。如此处所链接,Implementing a custom legend handlermatplotlib legend guide一节中的说明特别有用。

结果:

stackoverflow answer: How to put multiple colormap patches in a matplotlib legend?

解决方案:

我创建了一个类HandlerColormap,该类是从matplotlib的基类派生的,用于图例处理程序HandlerBaseHandlerColormap以一个颜色图和一些条纹作为参数。

对于参数cmap,应提供一个matplotlib colormap实例。

参数num_stripes确定图例补丁中颜色渐变的(非)连续性。

按照HandlerBase中的指示,我使用给定的维度覆盖了其create_artist方法,因此应按字体大小自动(自动)缩放代码。在这个新的create_artist方法中,我创建了多个条纹(细长的matplotlib Rectangles),它们根据输入的颜色图着色。

代码:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from matplotlib.legend_handler import HandlerBase

class HandlerColormap(HandlerBase):
    def __init__(self, cmap, num_stripes=8, **kw):
        HandlerBase.__init__(self, **kw)
        self.cmap = cmap
        self.num_stripes = num_stripes
    def create_artists(self, legend, orig_handle, 
                       xdescent, ydescent, width, height, fontsize, trans):
        stripes = []
        for i in range(self.num_stripes):
            s = Rectangle([xdescent + i * width / self.num_stripes, ydescent], 
                          width / self.num_stripes, 
                          height, 
                          fc=self.cmap((2 * i + 1) / (2 * self.num_stripes)), 
                          transform=trans)
            stripes.append(s)
        return stripes

x_array = np.linspace(1, 10, 10)
y_array = x_array
param_max = x_array.size
cmaps = [plt.cm.spring, plt.cm.winter]  # set of colormaps 
                                        # (as many as there are groups of lines)
plt.figure()
for param, (x, y) in enumerate(zip(x_array, y_array)):  
    x_line1 = np.linspace(x, 1.5 * x, 10)
    y_line1 = np.linspace(y**2, y**2 - x, 10)
    x_line2 = np.linspace(1.2 * x, 1.5 * x, 10)
    y_line2 = np.linspace(2 * y, 2 * y - x, 10)
    # plot lines with color depending on param using different colormaps:
    plt.plot(x_line1, y_line1, c=cmaps[0](param / param_max))
    plt.plot(x_line2, y_line2, c=cmaps[1](param / param_max))

cmap_labels = ["parameter 1 $\in$ [0, 10]", "parameter 2 $\in$ [-1, 1]"]
# create proxy artists as handles:
cmap_handles = [Rectangle((0, 0), 1, 1) for _ in cmaps]
handler_map = dict(zip(cmap_handles, 
                       [HandlerColormap(cm, num_stripes=8) for cm in cmaps]))
plt.legend(handles=cmap_handles, 
           labels=cmap_labels, 
           handler_map=handler_map, 
           fontsize=12)
plt.show()