Odwróć mapę kolorów w matplotlib

252

Chciałbym wiedzieć, jak po prostu odwrócić kolejność kolorów danej mapy kolorów, aby używać jej z powierzchnią plot_surface.

Mermoz
źródło

Odpowiedzi:

463

Wszystkie standardowe mapy kolorów również mają wersje odwrócone. Mają te same nazwy z _rprzypiętym do końca. ( Dokumentacja tutaj. )

ptomato
źródło
To nie działa z „amfhot”: „Błąd wartości: Colormap amfhot_r nie jest rozpoznawany”. Przypuszczam, że wystarczy „hot_r”.
Shockburner
Podobnie „nie można rozpoznać błędu ValueError: Colormap red_r.”
Alex Willison
18

W matplotlib mapa kolorów nie jest listą, ale zawiera listę jej kolorów jako colormap.colors. Moduł matplotlib.colorszapewnia funkcję ListedColormap()generowania mapy kolorów z listy. Dzięki temu możesz odwrócić dowolną mapę kolorów

colormap_r = ListedColormap(colormap.colors[::-1])
Gilles
źródło
7
+1. Nie spowoduje to jednak generalnie odwrócenia żadnej mapy kolorów. Tylko ListedColormaps (tzn. Dyskretne, a nie interpolowane) mają colorsatrybut. Cofanie LinearSegmentedColormapsjest nieco bardziej skomplikowane. (Musisz odwrócić każdy element w _segmentdatanagraniu.)
Joe Kington,
3
Jeśli chodzi o cofanie LinearSegmentedColormaps, właśnie to zrobiłem dla niektórych map kolorów. Oto o tym notatnik IPython.
kwinkunks
@kwinkunks Myślę, że funkcja w twoim notatniku jest nieprawidłowa, patrz odpowiedź poniżej
Mattijn
14

Rozwiązanie jest dość proste. Załóżmy, że chcesz użyć schematu kolorów „jesiennych”. Wersja standardowa:

cmap = matplotlib.cm.autumn

Aby odwrócić spektrum kolorów mapy kolorów, użyj funkcji get_cmap () i dodaj „_r” do tytułu mapy kolorów w następujący sposób:

cmap_reversed = matplotlib.cm.get_cmap('autumn_r')
Jm M.
źródło
czy możesz podać link do dokumentacji, z której pochodzi .autumn?
Xitcod13
Może się to później zepsuć ... matplotlib.org/3.1.1/gallery/color/colormap_reference.html , ale jestem pewien, że każdy zainteresowany będzie w stanie znaleźć to przez wyszukiwanie.
Jlanger
13

Ponieważ a LinearSegmentedColormapsjest oparte na słowniku czerwieni, zieleni i niebieskiego, konieczne jest odwrócenie każdego elementu:

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

Sprawdź, czy to działa:

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')

wprowadź opis zdjęcia tutaj

EDYTOWAĆ


Nie otrzymuję komentarza użytkownika 3445587. Działa dobrze na tęczowej colormap:

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')

wprowadź opis zdjęcia tutaj

Ale działa to szczególnie dobrze w przypadku niestandardowych deklarowanych map kolorów, ponieważ nie ma wartości domyślnej _rdla niestandardowych deklarowanych map kolorów. Poniższy przykład pochodzi z 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')

wprowadź opis zdjęcia tutaj

Mattijn
źródło
Ten przykład nie jest kompletny w tym sensie, że danych segmentu nie ma na listach, więc nie jest koniecznie odwracalny (np. Standardowa tęcza kolorów). Myślę, że w zasadzie wszystkie LinearSegmentedColormaps powinny zasadniczo być odwracalne za pomocą funkcji lambda, jak w tęczowej kolorymie?
za granicą
@ user3445587 Dodaję więcej przykładów, ale myślę, że działa dobrze na standardowej tęczowej
kolorystyce
Ponieważ było za długo, dodałem nową odpowiedź, która powinna działać dla wszystkich rodzajów danych LinearSegmentData. Problem polega na tym, że w przypadku tęczy funkcja _segmentdata jest implementowana inaczej. Twój kod - przynajmniej na moim komputerze - nie działa z tęczową kolormapą.
za granicą
12

Od Matplotlib 2.0 istnieje reversed()metoda ListedColormapi LinearSegmentedColorMapobiekty, więc możesz to zrobić

cmap_reversed = cmap.reversed()

Oto dokumentacja.

David Stansby
źródło
1

Istnieją dwa rodzaje LinearSegmentedColormaps. W niektórych przypadkach dane _segment są podane jawnie, np. Dla 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))}

W przypadku tęczy _segmentdata podano w następujący sposób:

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

Możemy znaleźć funkcje w źródle matplotlib, gdzie podano je jako

_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)
}

Wszystko, czego chcesz, jest już zrobione w matplotlib, wystarczy wywołać cm.revcmap, który odwraca oba typy danych segmentu, więc

cm.revcmap(cm.rainbow._segmentdata)

powinien wykonać zadanie - możesz po prostu utworzyć nowy LinearSegmentData z tego. W revcmap następuje odwrócenie danych segmentowych opartych na funkcjach

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

podczas gdy inne listy są odwracane jak zwykle

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

Więc właściwie to wszystko, czego chcesz, to

def reverse_colourmap(cmap, name = 'my_cmap_r'):
     return mpl.colors.LinearSegmentedColormap(name, cm.revcmap(cmap._segmentdata)) 
za granicą
źródło
1

Nie ma (jeszcze) wbudowanego sposobu odwracania dowolnych mapowań kolorów, ale jednym prostym rozwiązaniem jest nie modyfikowanie paska kolorów, ale utworzenie odwracalnego obiektu Normalizuj:

from matplotlib.colors import Normalize

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

Następnie możesz użyć tego z plot_surfaceinnymi funkcjami drukowania Matplotlib, wykonując np

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

Będzie to działać z każdą mapą kolorów Matplotlib.

astrofrog
źródło
Jest teraz! matplotlib.org/api/_as_gen/…
David Stansby