如何在matplotlib图例中放置多个颜色图补丁? [英] How to put multiple colormap patches in a matplotlib legend?

查看:113
本文介绍了如何在matplotlib图例中放置多个颜色图补丁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当前情况:

我有多组线,其中同一组中的线根据某些特定于组的参数而变化.我根据此参数为每个组使用不同的颜色图,根据该参数为同一组中的每条线分配颜色图中的颜色.

现在,我想在图上添加图例,每组线有一个条目. stackoverflow问题:如何在matplotlib图例中放置多个色图补丁?

<p> <strong>仅一组行的解决方案:</strong> </p>

<p>如果我只有一组线,则最好的标记方法是按照答案中的建议添加色条:<a href= Matplotlib :将颜色条添加到不可映射的对象.

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

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

最低工作示例:

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

 import numpy as np
import matplotlib.pyplot as plt

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))
plt.show()
 

这将产生上面显示的图.


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

解决方案

我将ImportanceOfBeingErnest的答案的解决方案修改为在这种情况下,matplotlib会为图形图例添加双色矩形矩形.如此处所链接,在实现自定义图例的部分中的说明 matplotlib图例指南中的处理程序特别有用.

结果:

stackoverflow答案:如何在matplotlib图例中放置多个色图补丁?

<p> <strong>解决方案:</strong> </p>

<p>我为传奇处理程序<a href=创建了从matplotlib的基类派生的类HandlerColormap HandlerBase . HandlerColormap将颜色图和许多条纹作为参数.

对于参数cmap,应使用matplotlib colormap 实例被给予.

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

按照HandlerBase中的指示,我使用给定的尺寸覆盖了它的create_artist方法,因此应按字体大小自动(自动)缩放代码.在这个新的create_artist方法中,我创建了多个条纹(细长的matplotlib 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()

Situation at hand:

I have multiple groups of lines, where the lines within the same group vary according to some group specific parameter. I assign each of these lines within the same group a color from a colormap according to this parameter using a different colormap for each group.

Now, I would like to add a legend to the plot with one entry per group of lines.

Solution for only one set of lines:

If I had only one group of lines the best way of labelling would be to add a colorbar as suggested in the answer to: Matplotlib: Add colorbar to non-mappable object.

How best to do this for multiple sets of lines?

As I have multiple such groups of lines, I do not want to add a colorbar for each new parameter. Instead, I would rather put patches filled with the corresponding colormaps in the legend (as a sort of mini colorbar).

Minimal working example:

In the following you can find a minimal working example of the situation at hand. Note, though, that I heavily simplified the calculation of the lines which hides the parameter dependence. Thus, my "parameter" param here is just the index I am iterating over. My actual code calculates the x and y values depending on a model parameter with more complicated functions. Accordingly the maximum param_max here is the same for each group of lines, though actually it would not be.

import numpy as np
import matplotlib.pyplot as plt

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))
plt.show()

This produces the plot shown above.


Since I could not find anything directly answering this on stackoverflow, I tried finding a solution myself which you can find in the answers section. If there is a more direct/proper way of doing this I would be happy to know.

解决方案

I adapted the solution of the answer by ImportanceOfBeingErnest to "Create a matplotlib mpatches with a rectangle bi-colored for figure legend" to this case. As linked there, the instructions in the section on Implementing a custom legend handler in the matplotlib legend guide were particularly helpful.

Result:

Solution:

I created the class HandlerColormap derived from matplotlib's base class for legend handlers HandlerBase. HandlerColormap takes a colormap and a number of stripes as arguments.

For the argument cmap a matplotlib colormap instance should be given.

The argument num_stripes determines how (non-)continuous the color gradient in the legend patch will be.

As instructed in HandlerBase I override its create_artist method using the given dimension such that the code should be (automatically) scaled by fontsize. In this new create_artist method I create multiple stripes (slim matplotlib Rectangles) colored according to the input colormap.

Code:

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()

这篇关于如何在matplotlib图例中放置多个颜色图补丁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆