Colormap inversa in matplotlib


252

Vorrei sapere come invertire semplicemente l'ordine dei colori di una determinata mappa colori per usarla con plot_surface.

Risposte:


463

I colormap standard hanno anche tutte le versioni invertite. Hanno gli stessi nomi con l' _radesione fino alla fine. ( Documentazione qui. )


Questo non funziona con "amfhot": "ValueError: Colormap amfhot_r non è riconosciuto". Suppongo che "hot_r" dovrà bastare.
shockburner

Allo stesso modo, "ValueError: Colormap red_r non è riconosciuto."
Alex Willison,

18

In matplotlib una mappa dei colori non è un elenco, ma contiene l'elenco dei suoi colori come colormap.colors. E il modulo matplotlib.colorsfornisce una funzione ListedColormap()per generare una mappa dei colori da un elenco. Quindi puoi invertire qualsiasi mappa dei colori facendo

colormap_r = ListedColormap(colormap.colors[::-1])

7
+1. Tuttavia, ciò non genererà in genere una mappa dei colori. Solo ListedColormaps (cioè discreto, anziché interpolato) hanno un colorsattributo. L'inversione LinearSegmentedColormapsè un po 'più complessa. (È necessario invertire tutti gli elementi del _segmentdatadict.)
Joe Kington,

3
Per quanto riguarda la retromarcia LinearSegmentedColormaps, l'ho appena fatto per alcune colorazioni. Ecco un Notebook IPython al riguardo.
kwinkunks,

@kwinkunks penso che la funzione nel tuo notebook non sia corretta, vedi risposta sotto
Mattijn

14

La soluzione è piuttosto semplice. Supponiamo di voler utilizzare lo schema mappa colori "autunno". La versione standard:

cmap = matplotlib.cm.autumn

Per invertire lo spettro dei colori della mappa colori, utilizzare la funzione get_cmap () e aggiungere '_r' al titolo della mappa colori in questo modo:

cmap_reversed = matplotlib.cm.get_cmap('autumn_r')

potresti fornire il collegamento alla documentazione da cui hai ottenuto l'autunno?
Xitcod13

Questo potrebbe rompersi in seguito ... matplotlib.org/3.1.1/gallery/color/colormap_reference.html , ma sono sicuro che chiunque sia interessato sarà in grado di trovarlo cercando comunque.
Jlanger,

13

Essendo LinearSegmentedColormapsbasato su un dizionario di rosso, verde e blu, è necessario invertire ciascun elemento:

import matplotlib.pyplot as plt
import matplotlib as mpl
def reverse_colourmap(cmap, name = 'my_cmap_r'):
    """
    In: 
    cmap, name 
    Out:
    my_cmap_r

    Explanation:
    t[0] goes from 0 to 1
    row i:   x  y0  y1 -> t[0] t[1] t[2]
                   /
                  /
    row i+1: x  y0  y1 -> t[n] t[1] t[2]

    so the inverse should do the same:
    row i+1: x  y1  y0 -> 1-t[0] t[2] t[1]
                   /
                  /
    row i:   x  y1  y0 -> 1-t[n] t[2] t[1]
    """        
    reverse = []
    k = []   

    for key in cmap._segmentdata:    
        k.append(key)
        channel = cmap._segmentdata[key]
        data = []

        for t in channel:                    
            data.append((1-t[0],t[2],t[1]))            
        reverse.append(sorted(data))    

    LinearL = dict(zip(k,reverse))
    my_cmap_r = mpl.colors.LinearSegmentedColormap(name, LinearL) 
    return my_cmap_r

Guarda che funziona:

my_cmap        
<matplotlib.colors.LinearSegmentedColormap at 0xd5a0518>

my_cmap_r = reverse_colourmap(my_cmap)

fig = plt.figure(figsize=(8, 2))
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15])
norm = mpl.colors.Normalize(vmin=0, vmax=1)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap = my_cmap, norm=norm,orientation='horizontal')
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap = my_cmap_r, norm=norm, orientation='horizontal')

inserisci qui la descrizione dell'immagine

MODIFICARE


Non ricevo il commento dell'utente3445587. Funziona bene sulla mappa dei colori dell'arcobaleno:

cmap = mpl.cm.jet
cmap_r = reverse_colourmap(cmap)

fig = plt.figure(figsize=(8, 2))
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15])
norm = mpl.colors.Normalize(vmin=0, vmax=1)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap = cmap, norm=norm,orientation='horizontal')
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap = cmap_r, norm=norm, orientation='horizontal')

inserisci qui la descrizione dell'immagine

Ma funziona particolarmente bene con le mappe dei colori dichiarate personalizzate, in quanto non esiste un valore predefinito _rper le mappe dei colori dichiarate personalizzate. Esempio seguente tratto da http://matplotlib.org/examples/pylab_examples/custom_cmap.html :

cdict1 = {'red':   ((0.0, 0.0, 0.0),
                   (0.5, 0.0, 0.1),
                   (1.0, 1.0, 1.0)),

         'green': ((0.0, 0.0, 0.0),
                   (1.0, 0.0, 0.0)),

         'blue':  ((0.0, 0.0, 1.0),
                   (0.5, 0.1, 0.0),
                   (1.0, 0.0, 0.0))
         }

blue_red1 = mpl.colors.LinearSegmentedColormap('BlueRed1', cdict1)
blue_red1_r = reverse_colourmap(blue_red1)

fig = plt.figure(figsize=(8, 2))
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15])

norm = mpl.colors.Normalize(vmin=0, vmax=1)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap = blue_red1, norm=norm,orientation='horizontal')
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap = blue_red1_r, norm=norm, orientation='horizontal')

inserisci qui la descrizione dell'immagine


Questo esempio non è completo, nel senso che i segmentdata non devono essere presenti nelle liste, quindi non è necessariamente reversibile (es. Mappa arcobaleno standard). Penso che in linea di principio tutte le LinearSegmentedColormaps dovrebbero in principio essere reversibili usando una funzione lambda come nella mappa dei colori arcobaleno?
oltre

@ user3445587 Aggiungo altri esempi, ma penso che funzioni perfettamente sulla mappa colori arcobaleno standard
Mattijn

Dato che era troppo lungo, ho aggiunto una nuova risposta, che dovrebbe funzionare per tutti i tipi di LinearSegmentData. Il problema è che per arcobaleno, _segmentdata è implementato in modo diverso. Quindi il tuo codice - almeno sulla mia macchina - non funziona con la mappa dei colori dell'arcobaleno.
oltre

12

A partire da Matplotlib 2.0, esiste un reversed()metodo per ListedColormape LinearSegmentedColorMapoggetti, quindi puoi semplicemente farlo

cmap_reversed = cmap.reversed()

Ecco la documentazione


1

Esistono due tipi di LinearSegmentedColormaps. In alcuni, _segmentdata è dato esplicitamente, ad esempio, per jet:

>>> cm.jet._segmentdata
{'blue': ((0.0, 0.5, 0.5), (0.11, 1, 1), (0.34, 1, 1), (0.65, 0, 0), (1, 0, 0)), 'red': ((0.0, 0, 0), (0.35, 0, 0), (0.66, 1, 1), (0.89, 1, 1), (1, 0.5, 0.5)), 'green': ((0.0, 0, 0), (0.125, 0, 0), (0.375, 1, 1), (0.64, 1, 1), (0.91, 0, 0), (1, 0, 0))}

Per arcobaleno, _segmentdata è dato come segue:

>>> cm.rainbow._segmentdata
{'blue': <function <lambda> at 0x7fac32ac2b70>, 'red': <function <lambda> at 0x7fac32ac7840>, 'green': <function <lambda> at 0x7fac32ac2d08>}

Possiamo trovare le funzioni nella fonte di matplotlib, dove sono indicate come

_rainbow_data = {
        'red': gfunc[33],   # 33: lambda x: np.abs(2 * x - 0.5),
        'green': gfunc[13], # 13: lambda x: np.sin(x * np.pi),
        'blue': gfunc[10],  # 10: lambda x: np.cos(x * np.pi / 2)
}

Tutto ciò che desideri è già fatto in matplotlib, basta chiamare cm.revcmap, che inverte entrambi i tipi di segmentata, quindi

cm.revcmap(cm.rainbow._segmentdata)

dovrebbe fare il lavoro - puoi semplicemente creare un nuovo LinearSegmentData da quello. In revcmap, viene eseguita l'inversione di SegmentData basata su funzioni

def _reverser(f):
    def freversed(x):
        return f(1 - x)
    return freversed

mentre gli altri elenchi sono invertiti come al solito

valnew = [(1.0 - x, y1, y0) for x, y0, y1 in reversed(val)] 

Quindi in realtà è tutto ciò che vuoi

def reverse_colourmap(cmap, name = 'my_cmap_r'):
     return mpl.colors.LinearSegmentedColormap(name, cm.revcmap(cmap._segmentdata)) 

1

Non esiste (ancora) un modo incorporato per invertire le mappe di colore arbitrarie, ma una soluzione semplice è in realtà non modificare la barra dei colori ma creare un oggetto Normalizza invertito:

from matplotlib.colors import Normalize

class InvertedNormalize(Normalize):
    def __call__(self, *args, **kwargs):
        return 1 - super(InvertedNormalize, self).__call__(*args, **kwargs)

È quindi possibile utilizzare questo con plot_surfacee altre funzioni di tracciamento di Matplotlib facendo ad es

inverted_norm = InvertedNormalize(vmin=10, vmax=100)
ax.plot_surface(..., cmap=<your colormap>, norm=inverted_norm)

Funzionerà con qualsiasi mappa dei colori Matplotlib.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.