簡體   English   中英

Python Matplotlib 多色圖例條目

[英]Python Matplotlib Multi-color Legend Entry

我想讓 matplotlib 中的圖例條目看起來像這樣:

在此處輸入圖片說明

對於給定的圖例項,它有多種顏色。 代碼如下所示,它輸出一個紅色矩形。 我想知道我需要做什么才能將一種顏色疊加在另一種顏色上? 或者有更好的解決方案嗎?

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt

red_patch = mpatches.Patch(color='red', label='Foo')
plt.legend(handles=[red_patch])

plt.show()

我提出的解決方案是將兩個不同的代理藝術家組合為一個條目圖例,如下所述: 為圖例組合兩個 Pyplot 補丁

然后策略是將第一個正方形標記的fillstyle設置為left而另一個設置為right (參見http://matplotlib.org/1.3.0/examples/pylab_examples/filledmarker_demo.html )。 然后可以將兩種不同的顏色歸因於每個標記,以生成所需的雙色圖例條目。

下面的代碼顯示了如何做到這一點。 注意, numpoints=1在參數plt.legend是重要的,以便僅顯示一個為每個條目標記。

import matplotlib.pyplot as plt

plt.close('all')

#---- Generate a Figure ----

fig = plt.figure(figsize=(4, 4))
ax = fig.add_axes([0.15, 0.15, 0.75, 0.75])
ax.axis([0, 1, 0, 1])

#---- Define First Legend Entry ----

m1, = ax.plot([], [], c='red' , marker='s', markersize=20,
              fillstyle='left', linestyle='none')

m2, = ax.plot([], [], c='blue' , marker='s', markersize=20,
              fillstyle='right', linestyle='none')

#---- Define Second Legend Entry ----

m3, = ax.plot([], [], c='cyan' , marker='s', markersize=20,
              fillstyle='left', linestyle='none')

m4, = ax.plot([], [], c='magenta' , marker='s', markersize=20,
              fillstyle='right', linestyle='none')

#---- Plot Legend ----

ax.legend(((m2, m1), (m3, m4)), ('Foo', 'Foo2'), numpoints=1, labelspacing=2,
          loc='center', fontsize=16)

plt.show(block=False)

結果是:

在此處輸入圖片說明

免責聲明:這僅適用於雙色圖例條目。 如果需要兩種以上的顏色,除了@jwinterm( Python Matplotlib Multi-color Legend Entry )描述的方法之外,我想不出任何其他方法來做到這一點

也許是另一種處理兩個以上補丁的黑客。 確保根據列數對手柄/標簽進行排序:

from matplotlib.patches import Patch
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

pa1 = Patch(facecolor='red', edgecolor='black')
pa2 = Patch(facecolor='blue', edgecolor='black')
pa3 = Patch(facecolor='green', edgecolor='black')
#
pb1 = Patch(facecolor='pink', edgecolor='black')
pb2 = Patch(facecolor='orange', edgecolor='black')
pb3 = Patch(facecolor='purple', edgecolor='black')

ax.legend(handles=[pa1, pb1, pa2, pb2, pa3, pb3],
          labels=['', '', '', '', 'First', 'Second'],
          ncol=3, handletextpad=0.5, handlelength=1.0, columnspacing=-0.5,
          loc='center', fontsize=16)

plt.show()

這導致:

可能不完全是您要查找的內容,但是您可以(非常)手動地通過在情節上放置補丁和文本來完成。 例如:

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

red_patch = mpatches.Patch(color='red', label='Foo')
plt.legend(handles=[red_patch])

r1 = mpatches.Rectangle((0.1, 0.1), 0.18, 0.1, fill=False)
r2 = mpatches.Rectangle((0.12, 0.12), 0.03, 0.06, fill=True, color='red')
r3 = mpatches.Rectangle((0.15, 0.12), 0.03, 0.06, fill=True, color='blue')
ax.add_patch(r1)
ax.add_patch(r2)
ax.add_patch(r3)
ax.annotate('Foo', (0.2, 0.13), fontsize='x-large')

plt.show()

實際上有一種正確的方法可以通過實現自定義圖例處理程序來執行此操作,如 matplotlib-doc 中“實現自定義圖例處理程序” (此處)下所述

import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection

# define an object that will be used by the legend
class MulticolorPatch(object):
    def __init__(self, colors):
        self.colors = colors
        
# define a handler for the MulticolorPatch object
class MulticolorPatchHandler(object):
    def legend_artist(self, legend, orig_handle, fontsize, handlebox):
        width, height = handlebox.width, handlebox.height
        patches = []
        for i, c in enumerate(orig_handle.colors):
            patches.append(plt.Rectangle([width/len(orig_handle.colors) * i - handlebox.xdescent, 
                                          -handlebox.ydescent],
                           width / len(orig_handle.colors),
                           height, 
                           facecolor=c, 
                           edgecolor='none'))

        patch = PatchCollection(patches,match_original=True)

        handlebox.add_artist(patch)
        return patch


# ------ choose some colors
colors1 = ['g', 'b', 'c', 'm', 'y']
colors2 = ['k', 'r', 'k', 'r', 'k', 'r']

# ------ create a dummy-plot (just to show that it works)
f, ax = plt.subplots()
ax.plot([1,2,3,4,5], [1,4.5,2,5.5,3], c='g', lw=0.5, ls='--',
        label='... just a line')
ax.scatter(range(len(colors1)), range(len(colors1)), c=colors1)
ax.scatter([range(len(colors2))], [.5]*len(colors2), c=colors2, s=50)

# ------ get the legend-entries that are already attached to the axis
h, l = ax.get_legend_handles_labels()

# ------ append the multicolor legend patches
h.append(MulticolorPatch(colors1))
l.append("a nice multicolor legend patch")

h.append(MulticolorPatch(colors2))
l.append("and another one")

# ------ create the legend
f.legend(h, l, loc='upper left', 
         handler_map={MulticolorPatch: MulticolorPatchHandler()}, 
         bbox_to_anchor=(.125,.875))

在此處輸入圖片說明

我非常喜歡@raphael 的回答。 這是一個帶圓圈的版本。 此外,我對代碼進行了重構和修剪,使其更加模塊化。

import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

class MulticolorCircles:
    """
    For different shapes, override the ``get_patch`` method, and add the new
    class to the handler map, e.g. via

    ax_r.legend(ax_r_handles, ax_r_labels, handlelength=CONF.LEGEND_ICON_SIZE,
            borderpad=1.2, labelspacing=1.2,
            handler_map={MulticolorCircles: MulticolorHandler})
    """

    def __init__(self, face_colors, edge_colors=None, face_alpha=1,
                 radius_factor=1):
        """
        """
        assert 0 <= face_alpha <= 1, f"Invalid face_alpha: {face_alpha}"
        assert radius_factor > 0, "radius_factor must be positive"
        self.rad_factor = radius_factor
        self.fc = [mcolors.colorConverter.to_rgba(fc, alpha=face_alpha)
                   for fc in face_colors]
        self.ec = edge_colors
        if edge_colors is None:
            self.ec = ["none" for _ in self.fc]
        self.N = len(self.fc)

    def get_patch(self, width, height, idx, fc, ec):
        """
        """
        w_chunk = width / self.N
        radius = min(w_chunk / 2, height) * self.rad_factor
        xy = (w_chunk * idx + radius, radius)
        patch = plt.Circle(xy, radius, facecolor=fc, edgecolor=ec)
        return patch

    def __call__(self, width, height):
        """
        """
        patches = []
        for i, (fc, ec) in enumerate(zip(self.fc, self.ec)):
            patch = self.get_patch(width, height, i, fc, ec)
            patches.append(patch)
        result = PatchCollection(patches, match_original=True)
        #
        return result


class MulticolorHandler:
    """
    """
    @staticmethod
    def legend_artist(legend, orig_handle, fontsize, handlebox):
        """
        """
        width, height = handlebox.width, handlebox.height
        patch = orig_handle(width, height)
        handlebox.add_artist(patch)
        return patch

示例用法和圖像,請注意,某些圖例句柄具有radius_factor=0.5因為真實大小太小。

ax_handles, ax_labels = ax.get_legend_handles_labels()
ax_labels.append(AUDIOSET_LABEL)
ax_handles.append(MulticolorCircles([AUDIOSET_COLOR],
                                    face_alpha=LEGEND_SHADOW_ALPHA))
ax_labels.append(FRAUNHOFER_LABEL)
ax_handles.append(MulticolorCircles([FRAUNHOFER_COLOR],
                                    face_alpha=LEGEND_SHADOW_ALPHA))
ax_labels.append(TRAIN_SOURCE_NORMAL_LABEL)
ax_handles.append(MulticolorCircles(SHADOW_COLORS["source"],
                                    face_alpha=LEGEND_SHADOW_ALPHA))
ax_labels.append(TRAIN_TARGET_NORMAL_LABEL)
ax_handles.append(MulticolorCircles(SHADOW_COLORS["target"],
                                    face_alpha=LEGEND_SHADOW_ALPHA))
ax_labels.append(TEST_SOURCE_ANOMALY_LABEL)
ax_handles.append(MulticolorCircles(DOT_COLORS["anomaly_source"],
                                    radius_factor=LEGEND_DOT_RATIO))
ax_labels.append(TEST_TARGET_ANOMALY_LABEL)
ax_handles.append(MulticolorCircles(DOT_COLORS["anomaly_target"],
                                    radius_factor=LEGEND_DOT_RATIO))
#
ax.legend(ax_handles, ax_labels, handlelength=LEGEND_ICON_SIZE,
            borderpad=1.1, labelspacing=1.1,
            handler_map={MulticolorCircles: MulticolorHandler})

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM