Как залить цвет фона легенды при построении с ДВУМЯ осями?

Проблема

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

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

Код и сюжет

#Plot
x= np.arange(0,3)
fig,ax = plt.subplots(figsize=(6,6))
#DOD
dod1 = ax.plot(x, ctrl_dod,  color='r',       label='CTRL'  )
dod2 = ax.plot(x, mfkc_dod,  color='#e68a00', label='MFKC'  )
dod3 = ax.plot(x, gses_dod,  color='green',   label='GSES'  )
dod4 = ax.plot(x, gses3_dod, color='blue',    label='GSES-3')
dod5 = ax.plot(x, gses4_dod, color='purple',  label='GSES-4')
dod6 = ax.plot(x, mera_dod,  color='brown',   label='MERRA2')
ax.xaxis.grid(True)
ax.set_ylim([0.02,0.044])
ax.set_yticks(np.arange(0.02,0.045,0.004))
ax.set_xlabel('Month')
ax.set_ylabel('Dust Optical Depth (550 nm)')
ax.set_title('Global Mean DOD and DCM')
legend = ax.legend()
legend.get_frame().set_facecolor('white')

#DCM
ax2  = ax.twinx()
dcm1 = ax2.plot(x, ctrl_dcm*1e6,  color='r',       linestyle='--', label='CTRL'  )
dcm2 = ax2.plot(x, mfkc_dcm*1e6,  color='#e68a00', linestyle='--', label='MFKC'  )
dcm3 = ax2.plot(x, gses_dcm*1e6,  color='green',   linestyle='--', label='GSES'  )
dcm4 = ax2.plot(x, gses3_dcm*1e6, color='blue',    linestyle='--', label='GSES-3')
dcm5 = ax2.plot(x, gses4_dcm*1e6, color='purple',  linestyle='--', label='GSES-4')
dcm6 = ax2.plot(x, mera_dcm*1e6,  color='brown',   linestyle='--', label='MERRA2')
ax2.xaxis.grid(True)
ax2.yaxis.grid(True)
ax2.set_xlabel('Month')
ax2.set_ylabel('Dust Column Mass (mg m-2)')

#Limits
axes = plt.gca()
axes.set_xlim([-0.25,2.25])

#Labels
axes.set_xticks(x)
axes.set_xticklabels(['June','July','August'])

#Save
pylab.savefig('dod+dcm.png')

введите описание изображения здесь

Вопрос

Как я могу

а) используйте в клавишах легенды сплошные линии и

б) имеет ли фон для легенды непрозрачный белый цвет?


person ChristineB    schedule 26.07.2017    source источник
comment
в клавишах легенды используются сплошные линии - клавиши легенды на изображении уже представляют собой сплошные линии, не так ли?   -  person Vinícius Figueiredo    schedule 26.07.2017
comment
Я не понимаю, что мне нужно указать линии сетки для обеих осей, иначе они не появятся. Использование только ax.grid () без отображения сетки для ax2 должно напрямую решить проблему. Если нет, нам нужен минимальный воспроизводимый пример проблемы.   -  person ImportanceOfBeingErnest    schedule 26.07.2017
comment
Важность. Хорошо, это действительно работает для этого конкретного примера, потому что оси совпадают. Однако вчера у меня были разные линии сетки для левой и правой осей, поэтому мне понадобился yaxis.grid (True), установленный для ax и ax2. Возможно, кому-то еще будет полезно получить ответ, если сетки не совпадают.   -  person ChristineB    schedule 26.07.2017
comment
Намотку верхних осей нужно сделать прозрачной.   -  person Mad Physicist    schedule 26.07.2017
comment
Я вижу, мой ответ ниже будет не зависеть от наличия сеток. Так что это будет работать в обоих случаях.   -  person ImportanceOfBeingErnest    schedule 26.07.2017
comment
Винисиус: Да, потому что я установил эту легенду для первой оси, но именно поэтому линии сетки просвечивают. Я указал, что в ключах легенды используются сплошные линии, потому что мне нужно, чтобы легенда располагалась после ax2, чтобы линии сетки не отображались, но я не хочу, чтобы в легенде использовались пунктирные линии ax2 в качестве ключей.   -  person ChristineB    schedule 26.07.2017


Ответы (2)


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

h, l = ax.get_legend_handles_labels()
legend = ax2.legend(h,l, facecolor="white")
person ImportanceOfBeingErnest    schedule 26.07.2017
comment
Я получаю сообщение об ошибке для этой второй строки - TypeError: __init __ () получил неожиданный аргумент ключевого слова 'facecolor'. Но если я уберу этот бит цвета лица и добавлю legend.get_frame (). Set_facecolor ('white') в новую строку, это сработает! Измените свой ответ, и я его приму. - person ChristineB; 26.07.2017
comment
facecolor - это задокументированный аргумент легенды. Если вы используете старую версию matplotlib, вы, конечно, можете использовать другой способ установки цвета лица. - person ImportanceOfBeingErnest; 26.07.2017
comment
Это вполне возможно, поскольку я использую общесетевую установку Python и не знаю, какая это версия. Я приму твой ответ, потому что ручка - это то, что мне действительно нужно. - person ChristineB; 26.07.2017
comment
Это не сработает, если я хочу легенду для топора и вторую легенду для ax2. При создании одного из двух этим методом второй исчезает. - person Bastian; 17.02.2021
comment
... но в этом случае метод, предоставленный @ImportanceOfBeingErnest на github.com / matplotlib / matplotlib / issues / помогает - person Bastian; 17.02.2021

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

Принятый ответ, предоставленный @ImportanceOfBeingErnest, больше не работает, потому что он позволяет прикрепить только одну легенду к ax2.

В этом случае есть альтернативное решение, также предоставленное @ImportanceOfBeingErnest по адресу https://github.com/matplotlib/matplotlib/issues/3706#issuecomment-378407795.

Я объединил их в одну функцию, которую я обычно использую в этом случае, и подумал, что было бы полезно предоставить ее здесь:

def legend_to_ax( ax, ax_placein=None, method=2, **kwargs ):
    """ Wrapper around ax.legend(**kwargs) which permits to have the legend
    placed in the axis ax_placein.
    This is useful when drawing legends for multiple axes, e.g.produced with
    ax2 = ax1.twinx(), in order to have all legends on top of all axes.
    In this case provide the uppermost axis, e.g. ax2 here, as ax_placein.
    Args:
     - ax         : axis for which the legend is created
     - ax_placein : axis which the legend should be placed in (optional)
     - method     : method by which to handle the placement of the legend in
                    ax_placein.
                    method=1 ... based on 
                                 https://stackoverflow.com/a/45336414/7042795
                                 This method fails when multiple legends should
                                 be added to ax_placein.
                    method=2 ... based on
                                 https://github.com/matplotlib/matplotlib/issues/3706#issuecomment-378407795
                                 This adds the legend as an artist to ax_placein
                                 making it no longer appear via ax.get_legend().
                                 Instead parse ax_placein.artists()
     - **kwargs   : kwargs of ax.legend()
    Returns:
     - leg        : legend handle
    """

    if ax_placein is None:
        leg = ax.legend(**kwargs)
    
    elif method==1:
        # based on https://stackoverflow.com/a/45336414/7042795
        h, l = ax.get_legend_handles_labels()
        if ax_placein is not None:
            ax = ax_placein
        leg =  ax.legend( h, l, **kwargs)
    
    elif method==2:
        # based on https://github.com/matplotlib/matplotlib/issues/3706#issuecomment-378407795
        leg = ax.legend(**kwargs)
        if ax_placein is not None:
            leg.remove()
            ax_placein.add_artist(leg)
    
    return leg

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

legend = legend_to_ax( ax, ax_placein=ax2, method=1, facecolor="white")

or

legend = legend_to_ax( ax, ax_placein=ax2, method=2, facecolor="white")

Если вам нужны две легенды, это должно сработать:

legend1 = legend_to_ax( ax,  ax_placein=ax2, method=2, facecolor="white", loc="upper left")
legend2 = legend_to_ax( ax2, ax_placein=ax2, method=2, facecolor="white", loc="upper right")

Я надеюсь, что это подходящее место, чтобы поделиться этой информацией. Однако кредиты причитаются @ImportanceOfBeingErnest. Я только слил их решение в эту функцию.

person Bastian    schedule 18.02.2021