Как изменить условные обозначения на точечной диаграмме для отображения разных форматов для однотипных маркеров? - PullRequest
1 голос
/ 06 марта 2020

Я пытаюсь изменить легенду о фигуре, которая содержит два наложенных точечных графика. В частности, я хочу два маркера легенды и метки: первый маркер будет содержать несколько точек (каждый из которых окрашен по-разному), а другой маркер состоит из одной точки.

Согласно этому связанному вопросу , я могу изменить дескриптор легенды, чтобы отображать несколько точек, каждая из которых имеет свой цвет.

Согласно этому похожему вопросу , я знаю, что могу изменить количество точек, показанных указанным дескриптором. Тем не менее, это изменение применяется ко всем маркерам в легенде. Можно ли применять его только к одному дескриптору?

Моя цель - объединить оба подхода. Есть ли способ сделать это?

В случае, если это не ясно, я хотел бы изменить встроенную фигуру (см. Ниже) так, чтобы ручка Z vs X показала только одну точку рядом с соответствующий ярлык легенды, оставляя маркер Y vs X без изменений.

Моя неудачная попытка создать такую ​​фигуру приведена ниже:

Attempt at desired figure

Чтобы воспроизвести эту цифру, можно запустить следующий код:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerTuple, HandlerRegularPolyCollection

class ScatterHandler(HandlerRegularPolyCollection):

    def update_prop(self, legend_handle, orig_handle, legend):
        """ """
        legend._set_artist_props(legend_handle)
        legend_handle.set_clip_box(None)
        legend_handle.set_clip_path(None)

    def create_collection(self, orig_handle, sizes, offsets, transOffset):
        """ """
        p = type(orig_handle)([orig_handle.get_paths()[0]], sizes=sizes, offsets=offsets, transOffset=transOffset, cmap=orig_handle.get_cmap(), norm=orig_handle.norm)
        a = orig_handle.get_array()
        if type(a) != type(None):
            p.set_array(np.linspace(a.min(), a.max(), len(offsets)))
        else:
            self._update_prop(p, orig_handle)
        return p

x = np.arange(10)
y = np.sin(x)
z = np.cos(x)

fig, ax = plt.subplots()
hy = ax.scatter(x, y, cmap='plasma', c=y, label='Y vs X')
hz = ax.scatter(x, z, color='k', label='Z vs X')
ax.grid(color='k', linestyle=':', alpha=0.3)
fig.subplots_adjust(bottom=0.2)
handler_map = {type(hz) : ScatterHandler()}
fig.legend(mode='expand', ncol=2, loc='lower center', handler_map=handler_map, scatterpoints=5)

plt.show()
plt.close(fig)

Одно из решений, которое мне не нравится, - создать две легенды - одну для Z vs X и одну для Y vs X , Но мой фактический вариант использования включает в себя необязательное количество маркеров (которое может превышать два), и я предпочел бы не рассчитывать оптимальную ширину / высоту каждого блока легенды. Как еще можно решить эту проблему?

1 Ответ

1 голос
/ 06 марта 2020

Это грязный трюк и не элегантное решение, но вы можете установить размеры других точек для легенды ZX равными 0. Просто измените две последние строки на следующие.

leg = fig.legend(mode='expand', ncol=2, loc='lower center', handler_map=handler_map, scatterpoints=5)
# The third dot of the second legend stays the same size, others are set to 0
leg.legendHandles[1].set_sizes([0,0,leg.legendHandles[1].get_sizes()[2],0,0])

Результат как показано.

enter image description here

...