Set Legend Symbol Dekking met Matplotlib?

Ik werk aan een perceel met doorzichtige ‘x’-markers (20% alfa). Hoe maak ik de marker op 100% opaciteit in de legende?

import matplotlib.pyplot as plt
plt.plot_date( x = xaxis, y = yaxis, marker = 'x', color=[1, 0, 0, .2], label='Data Series' )
plt.legend(loc=3, mode="expand", numpoints=1, scatterpoints=1 )

Antwoord 1, Autoriteit 100%

Bijgewerkt:
Er is een gemakkelijkere manier! Wijs eerst uw legende toe aan een variabele wanneer u het maakt:

leg = plt.legend()

Dan:

for lh in leg.legendHandles: 
    lh.set_alpha(1)

of als het bovenstaande niet werkt (u kunt een oudere versie van matplotlib) gebruiken:

for lh in leg.legendHandles: 
    lh._legmarker.set_alpha(1)

Om uw markers opaque te maken voor een plt.plotof een plt.scatter,

Houd er rekening mee dat het gebruik van eenvoudig lh.set_alpha(1)op een plt.plotzal de lijnen in uw legende ondoorzichtig maken in plaats van de markeringen. U moet deze twee mogelijkheden voor de andere plottypen aanpassen.

BRONNEN:
Gesynthetiseerd van sommige goed advies door DRV Over markeringsmetingen. Update is geïnspireerd op nuttige opmerking van Owen .


Antwoord 2, Autoriteit 10%

Opvolgen van het antwoord van Cosmosis, om de “nep” -lijnen te maken voor de legende onzichtbaar op de plot, kunt u nans gebruiken, en ze zullen nog steeds werken voor het genereren van legenda’s:

import numpy as np
import matplotlib.pyplot as plt
# Plot data with alpha=0.2
plt.plot((0,1), (0,1), marker = 'x', color=[1, 0, 0, .2])
# Plot non-displayed NaN line for legend, leave alpha at default of 1.0
legend_line_1 = plt.plot( np.NaN, np.NaN, marker = 'x', color=[1, 0, 0], label='Data Series' )
plt.legend()

Antwoord 3, Autoriteit 3%

Als u iets specifieks in uw legende wilt hebben, is het gemakkelijker om objecten te definiëren die u in de legende plaatst met de juiste tekst. Bijvoorbeeld:

import matplotlib.pyplot as plt
import pylab
plt.plot_date( x = xaxis, y = yaxis, marker = 'x', color=[1, 0, 0, .2], label='Data Series' )
line1 = pylab.Line2D(range(1),range(1),color='white',marker='x',markersize=10, markerfacecolor="red",alpha=1.0)
line2 = pylab.Line2D(range(10),range(10),marker="_",linewidth=3.0,color="dodgerblue",alpha=1.0)
plt.legend((line1,line2),('Text','Other Text'),numpoints=1,loc=1)

Hier definieert Line1 een korte, witte lijn (zo in wezen onzichtbaar) met de marker ‘X’ in rode en volle opaciteit. Als een voorbeeld geeft Line2 u een langere blauwe lijn zonder tellers zichtbaar. Door deze “lijnen” te maken, kunt u hun eigenschappen binnen de legende gemakkelijker regelen.


Antwoord 4, Autoriteit 3%

Andere antwoorden hier geven goede praktische oplossingen door de alfa-waarde in de legende na de oprichting te wijzigen, of het wijzigen van de alfa van de lijn na het scheppen van de legende.

Een oplossing om een ​​andere opaciteit in de legende te bereiken zonder dat daarna iets manipuleert, zou het volgende zijn. Het maakt gebruik van een handler_mapen een updatefunctie.

import matplotlib.pyplot as plt
import numpy as np; np.random.seed(43)
from matplotlib.collections import PathCollection
from matplotlib.legend_handler import HandlerPathCollection, HandlerLine2D
plt.plot(np.linspace(0,1,8), np.random.rand(8), marker="o", markersize=12, label="A line", alpha=0.2)
plt.scatter(np.random.rand(8),np.random.rand(8), s=144,
            c="red", marker=r"$\clubsuit$",  label="A scatter", alpha=0.2)
def update(handle, orig):
    handle.update_from(orig)
    handle.set_alpha(1)
plt.legend(handler_map={PathCollection : HandlerPathCollection(update_func= update),
                        plt.Line2D : HandlerLine2D(update_func = update)})
plt.show()


Antwoord 5, autoriteit 2%

Het lijkt erop dat matplotlib de plotlijnen tekent nadathet alfaniveau naar de legenda is gekopieerd. Dat betekent dat u de plotlijnen kunt maken met het alfaniveau dat u in de legenda wilt hebben, de legenda kunt maken om dat alfaniveau te kopiëren en vervolgens het alfaniveau op de plotlijnen kunt wijzigen.

Hier is een compleet voorbeeld:

import matplotlib.pyplot as plt
x = (0, 1, 2)
y = (0, 2, 1)
line, = plt.plot(x, y, 'ro', label='label')  # Default alpha is 1.0.
plt.legend()  # Copy alpha to legend.
line.set_alpha(0.2)  # Change alpha for data points.
plt.show()

Dat plot ziet er zo uit als ik het uitvoer met matplotlib 2.2.3 op Python 2.7.15:


Antwoord 6

Ik heb ontdekt dat de functie .set_alpha()werkt op veel legenda-objecten, maar helaas hebben veel legenda-objecten meerdere stukken (zoals de uitvoer van errorbar()) en de .set_alpha()-aanroep heeft slechts invloed op een van hen.

Je kunt .get_legend_handles_labels()gebruiken en vervolgens delen van de handvatten en .set_alpha()doorlopen, maar helaas, copy.deepcopy()lijkt niet te werken op de lijst met handvatten, dus de plot zelf zal worden beïnvloed. De beste oplossing die ik kon vinden was om de originele alfa’s, .set_alpha()op te slaan naar wat ik wilde, de legenda te maken en vervolgens de plot-alfa’s terug te zetten naar hun oorspronkelijke waarden. Het zou veel schoner zijn als ik handleszou kunnen deepcopy (ik zou geen alfa-waarden hoeven op te slaan of ze opnieuw in te stellen), maar ik zou dit niet kunnen doen in python2.7 (misschien hangt dit af van welke objecten zijn in de legende).

f,ax=plt.subplots(1)
ax.plot(  ...  )
def legend_alpha(ax,newalpha=1.0):
    #sets alpha of legends to some value
    #this would be easier if deepcopy worked on handles, but it doesn't 
    handles,labels=ax.get_legend_handles_labels()
    alphass=[None]*len(handles) #make a list to hold lists of saved alpha values
    for k,handle in enumerate(handles): #loop through the legend entries
        alphas=[None]*len(handle) #make a list to hold the alphas of the pieces of this legend entry
        for i,h in enumerate(handle): #loop through the pieces of this legend entry (there could be a line and a marker, for example)
            try: #if handle was a simple list of parts, then this will work
                alphas[i]=h.get_alpha()
                h.set_alpha(newalpha)
            except: #if handle was a list of parts which themselves were made up of smaller subcomponents, then we must go one level deeper still.
                #this was needed for the output of errorbar() and may not be needed for simpler plot objects
                alph=[None]*len(h) 
                for j,hh in enumerate(h):
                    alph[j]=hh.get_alpha() #read the alpha values of the sub-components of the piece of this legend entry
                    hh.set_alpha(newalpha)
                alphas[i]=alph #save the list of alpha values for the subcomponents of this piece of this legend entry
        alphass[k]=alphas #save the list of alpha values for the pieces of this legend entry
    leg=ax.legend(handles,labels) #create the legend while handles has updated alpha values
    for k,handle in enumerate(handles): #loop through legend items to restore origina alphas on the plot
        for i,h in enumerate(handle): #loop through pieces of this legend item to restore alpha values on the plot
            try: 
                h.set_alpha(alphass[k][i])
            except:
                for j,hh in enumerate(h): #loop through sub-components of this piece of this legend item to restore alpha values
                    hh.set_alpha(alphass[k][i][j])
    return leg
leg=legend_alpha(ax)
leg.draggable()

Antwoord 7

In mijn geval heeft set_alpha(1)ook de edgecolorsaangepast, wat ik niet wilde: ik had “onzichtbare” randen en het instellen van alpha op ondoorzichtig gemaakt ze zichtbaar in de legende. Het volgende fragment (OOP) verandert de dekking van het gezicht zonder de randkleur te veranderen:

leg = ax.legend()
for lh in leg.legendHandles:
    fc_arr = lh.get_fc().copy()
    fc_arr[:, -1] = 1  # set opacity here
    lh.set_fc(fc_arr)

Let op de aanroep van .copy(), als we dit niet doen, wordt de dekking voor de hele plot gewijzigd. Kopiëren betekent dat we alleen de gezichtskleur wijzigen in het legendavak.

Als alternatief kunt u deze functie aan uw bibliotheek toevoegen:

def opaque_legend(ax):
    """
    Calls legend, and sets all the legend colors opacity to 100%.
    Returns the legend handle.
    """
    leg = ax.legend()
    for lh in leg.legendHandles:
        fc_arr = lh.get_fc().copy()
        fc_arr[:, -1] = 1
        lh.set_fc(fc_arr)
    return leg

En vervang dan eenvoudig leg = ax.legend()door leg = opaque_legend(ax). Ik hoop dat dit helpt!

Andrés


Antwoord 8

In plaats van de ondoorzichtigheid van de legende te verknoeien, vond ik een andere manier. Eerst maak ik een plotlijn met de stijl die ik wil dat de legende is. Dan verander ik de stijl van de plotlijn en, wonderbaarlijk, blijft de legendastijl intact. MWE:

plt.plot(x, y, 'ro', label='label')
for lh in plt.gca().get_legend_handles_labels():
    lh[0].set_alpha(new_alpha)

Ik zou graag willen uitleggen waarom het werkt, maar ik kan het niet. Ik weet ook niet zeker dat het voor alle backends werkt.


En ja, ik weet dat de vraag oud is. Omdat het nog steeds in Google verschijnt, zal ik het later vinden en mijn toekomstige zelf helpen.

Other episodes