Umgekehrte Farbkarte in Matplotlib

252

Ich möchte wissen, wie man einfach die Farbreihenfolge einer bestimmten Farbkarte umkehrt, um sie mit plot_surface zu verwenden.

Mermoz
quelle

Antworten:

463

Die Standard-Farbkarten haben auch alle umgekehrte Versionen. Sie haben die gleichen Namen mit _rbis zum Ende angeheftet. ( Dokumentation hier. )

Ptomato
quelle
Dies funktioniert nicht mit "amfhot": "ValueError: Colormap amfhot_r wird nicht erkannt". Ich nehme an, "hot_r" muss ausreichen.
Schockbrenner
Ebenso "ValueError: Colormap red_r wird nicht erkannt."
Alex Willison
18

In matplotlib ist eine Farbkarte keine Liste, sondern enthält die Liste ihrer Farben als colormap.colors. Das Modul matplotlib.colorsbietet eine Funktion ListedColormap()zum Generieren einer Farbkarte aus einer Liste. Sie können also jede Farbkarte umkehren, indem Sie dies tun

colormap_r = ListedColormap(colormap.colors[::-1])
Gilles
quelle
7
+1. Dies wird jedoch keine Farbkarte generisch umkehren. Nur ListedColormaps (dh diskret und nicht interpoliert) haben ein colorsAttribut. Das Umkehren LinearSegmentedColormapsist etwas komplexer. (Sie müssen jeden Punkt im _segmentdataDiktat umkehren .)
Joe Kington
3
In Bezug auf das Umkehren habe LinearSegmentedColormapsich dies nur für einige Farbkarten getan. Hier ist ein IPython-Notizbuch darüber.
Kwinkunks
@ Kwinkunks Ich denke, die Funktion in Ihrem Notizbuch ist nicht richtig, siehe Antwort unten
Mattijn
14

Die Lösung ist ziemlich einfach. Angenommen, Sie möchten das Farbkartenschema "Herbst" verwenden. Die Standardversion:

cmap = matplotlib.cm.autumn

Um das Farbspektrum der Farbkarte umzukehren, verwenden Sie die Funktion get_cmap () und hängen Sie '_r' wie folgt an den Titel der Farbkarte an:

cmap_reversed = matplotlib.cm.get_cmap('autumn_r')
Jm M.
quelle
Können Sie einen Dokumentationslink angeben, von dem Sie den .autumn erhalten haben?
Xitcod13
Dies kann später passieren ... matplotlib.org/3.1.1/gallery/color/colormap_reference.html , aber ich bin sicher, dass jeder Interessierte dies trotzdem durch Suche finden kann.
Jlanger
13

Da a LinearSegmentedColormapsauf einem Wörterbuch aus Rot, Grün und Blau basiert, muss jedes Element umgekehrt werden:

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

Sehen Sie, dass es funktioniert:

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

Geben Sie hier die Bildbeschreibung ein

BEARBEITEN


Ich bekomme den Kommentar von user3445587 nicht. Es funktioniert gut auf der Regenbogen-Farbkarte:

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

Geben Sie hier die Bildbeschreibung ein

Es funktioniert jedoch besonders gut für benutzerdefinierte deklarierte Farbkarten, da es keine Standardeinstellung _rfür benutzerdefinierte deklarierte Farbkarten gibt. Das folgende Beispiel stammt von 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')

Geben Sie hier die Bildbeschreibung ein

Mattijn
quelle
Dieses Beispiel ist nicht vollständig in dem Sinne, dass die Segmentdaten nicht in Listen enthalten sein müssen, sodass sie nicht unbedingt umkehrbar sind (z. B. Standard-Regenbogen-Farbkarte). Ich denke im Prinzip sollten alle LinearSegmentedColormaps im Prinzip mit einer Lambda-Funktion wie in der Regenbogen-Colormap reversibel sein?
Übersee
@ user3445587 Ich füge einige weitere Beispiele hinzu, aber ich denke, es funktioniert gut auf der Standard-Regenbogen-Farbkarte
Mattijn
Da es zu lang war, habe ich eine neue Antwort hinzugefügt, die für alle Arten von LinearSegmentData funktionieren sollte. Das Problem ist, dass für rainbow _segmentdata anders implementiert ist. Ihr Code funktioniert also - zumindest auf meinem Computer - nicht mit der Regenbogen-Farbkarte.
Übersee
12

Ab Matplotlib 2.0 gibt es eine reversed()Methode für ListedColormapund LinearSegmentedColorMapObjekte, sodass Sie dies einfach tun können

cmap_reversed = cmap.reversed()

Hier ist die Dokumentation.

David Stansby
quelle
1

Es gibt zwei Arten von LinearSegmentedColormaps. In einigen Fällen werden die _segmentdata explizit angegeben, z. B. für 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))}

Für den Regenbogen werden _segmentdata wie folgt angegeben:

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

Wir können die Funktionen in der Quelle von matplotlib finden, wo sie als angegeben sind

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

Alles, was Sie wollen, ist bereits in matplotlib erledigt. Rufen Sie einfach cm.revcmap auf, wodurch beide Arten von Segmentdaten umgekehrt werden

cm.revcmap(cm.rainbow._segmentdata)

sollte den Job machen - Sie können einfach ein neues LinearSegmentData daraus erstellen. In revcmap erfolgt die Umkehrung funktionsbasierter SegmentData mit

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

während die anderen Listen wie gewohnt umgekehrt werden

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

Also eigentlich ist das Ganze was du willst

def reverse_colourmap(cmap, name = 'my_cmap_r'):
     return mpl.colors.LinearSegmentedColormap(name, cm.revcmap(cmap._segmentdata)) 
Übersee-
quelle
1

Es gibt (noch) keine integrierte Möglichkeit, beliebige Farbkarten umzukehren, aber eine einfache Lösung besteht darin, die Farbleiste nicht zu ändern, sondern ein invertierendes Normalisierungsobjekt zu erstellen:

from matplotlib.colors import Normalize

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

Sie können dies dann mit plot_surfaceund anderen Matplotlib-Plotfunktionen verwenden, indem Sie z

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

Dies funktioniert mit jeder Matplotlib-Farbkarte.

Astrofrog
quelle
Da ist jetzt! matplotlib.org/api/_as_gen/…
David Stansby