Erstellen von Colormaps in Matplotlib#

Matplotlib verfügt über eine Reihe von integrierten Colormaps, die über matplotlib.colormaps zugänglich sind. Es gibt auch externe Bibliotheken wie palettable, die viele zusätzliche Colormaps anbieten.

Wir möchten jedoch möglicherweise auch unsere eigenen Colormaps erstellen oder manipulieren. Dies kann mit der Klasse ListedColormap oder LinearSegmentedColormap erfolgen. Beide Colormap-Klassen ordnen Werte zwischen 0 und 1 Farben zu. Es gibt jedoch Unterschiede, wie unten erläutert.

Bevor wir Colormaps manuell erstellen oder manipulieren, sehen wir uns zunächst an, wie wir Colormaps und ihre Farben aus vorhandenen Colormap-Klassen erhalten können.

Colormaps abrufen und auf deren Werte zugreifen#

Zuerst kann das Abrufen einer benannten Colormap, von denen die meisten in Auswahl von Colormaps in Matplotlib aufgeführt sind, mit matplotlib.colormaps erfolgen, was ein Colormap-Objekt zurückgibt. Die Länge der Liste der intern verwendeten Farben zur Definition der Colormap kann über Colormap.resampled angepasst werden. Unten verwenden wir einen bescheidenen Wert von 8, damit nicht viele Werte zu betrachten sind.

import matplotlib.pyplot as plt
import numpy as np

import matplotlib as mpl
from matplotlib.colors import LinearSegmentedColormap, ListedColormap

viridis = mpl.colormaps['viridis'].resampled(8)

Das Objekt viridis ist aufrufbar, das bei Übergabe eines Float-Werts zwischen 0 und 1 einen RGBA-Wert aus der Colormap zurückgibt.

print(viridis(0.56))
(np.float64(0.122312), np.float64(0.633153), np.float64(0.530398), np.float64(1.0))

ListedColormap#

ListedColormaps speichern ihre Farbwerte in einem Attribut .colors. Die Liste der Farben, aus denen die Colormap besteht, kann direkt über die Eigenschaft colors abgerufen werden, oder sie kann indirekt abgerufen werden, indem viridis mit einem Array von Werten aufgerufen wird, die der Länge der Colormap entsprechen. Beachten Sie, dass die zurückgegebene Liste ein RGBA-(N, 4)-Array ist, wobei N die Länge der Colormap ist.

print('viridis.colors', viridis.colors)
print('viridis(range(8))', viridis(range(8)))
print('viridis(np.linspace(0, 1, 8))', viridis(np.linspace(0, 1, 8)))
viridis.colors [[0.267004 0.004874 0.329415 1.      ]
 [0.275191 0.194905 0.496005 1.      ]
 [0.212395 0.359683 0.55171  1.      ]
 [0.153364 0.497    0.557724 1.      ]
 [0.122312 0.633153 0.530398 1.      ]
 [0.288921 0.758394 0.428426 1.      ]
 [0.626579 0.854645 0.223353 1.      ]
 [0.993248 0.906157 0.143936 1.      ]]
viridis(range(8)) [[0.267004 0.004874 0.329415 1.      ]
 [0.275191 0.194905 0.496005 1.      ]
 [0.212395 0.359683 0.55171  1.      ]
 [0.153364 0.497    0.557724 1.      ]
 [0.122312 0.633153 0.530398 1.      ]
 [0.288921 0.758394 0.428426 1.      ]
 [0.626579 0.854645 0.223353 1.      ]
 [0.993248 0.906157 0.143936 1.      ]]
viridis(np.linspace(0, 1, 8)) [[0.267004 0.004874 0.329415 1.      ]
 [0.275191 0.194905 0.496005 1.      ]
 [0.212395 0.359683 0.55171  1.      ]
 [0.153364 0.497    0.557724 1.      ]
 [0.122312 0.633153 0.530398 1.      ]
 [0.288921 0.758394 0.428426 1.      ]
 [0.626579 0.854645 0.223353 1.      ]
 [0.993248 0.906157 0.143936 1.      ]]

Die Colormap ist eine Lookup-Tabelle, sodass das "Übersampeln" der Colormap eine Nearest-Neighbor-Interpolation zurückgibt (beachten Sie die wiederholten Farben in der folgenden Liste).

print('viridis(np.linspace(0, 1, 12))', viridis(np.linspace(0, 1, 12)))
viridis(np.linspace(0, 1, 12)) [[0.267004 0.004874 0.329415 1.      ]
 [0.267004 0.004874 0.329415 1.      ]
 [0.275191 0.194905 0.496005 1.      ]
 [0.212395 0.359683 0.55171  1.      ]
 [0.212395 0.359683 0.55171  1.      ]
 [0.153364 0.497    0.557724 1.      ]
 [0.122312 0.633153 0.530398 1.      ]
 [0.288921 0.758394 0.428426 1.      ]
 [0.288921 0.758394 0.428426 1.      ]
 [0.626579 0.854645 0.223353 1.      ]
 [0.993248 0.906157 0.143936 1.      ]
 [0.993248 0.906157 0.143936 1.      ]]

LinearSegmentedColormap#

LinearSegmentedColormaps haben kein Attribut .colors. Man kann die Colormap jedoch immer noch mit einem ganzzahligen Array oder mit einem Float-Array zwischen 0 und 1 aufrufen.

copper = mpl.colormaps['copper'].resampled(8)

print('copper(range(8))', copper(range(8)))
print('copper(np.linspace(0, 1, 8))', copper(np.linspace(0, 1, 8)))
copper(range(8)) [[0.         0.         0.         1.        ]
 [0.17647055 0.1116     0.07107143 1.        ]
 [0.35294109 0.2232     0.14214286 1.        ]
 [0.52941164 0.3348     0.21321429 1.        ]
 [0.70588219 0.4464     0.28428571 1.        ]
 [0.88235273 0.558      0.35535714 1.        ]
 [1.         0.6696     0.42642857 1.        ]
 [1.         0.7812     0.4975     1.        ]]
copper(np.linspace(0, 1, 8)) [[0.         0.         0.         1.        ]
 [0.17647055 0.1116     0.07107143 1.        ]
 [0.35294109 0.2232     0.14214286 1.        ]
 [0.52941164 0.3348     0.21321429 1.        ]
 [0.70588219 0.4464     0.28428571 1.        ]
 [0.88235273 0.558      0.35535714 1.        ]
 [1.         0.6696     0.42642857 1.        ]
 [1.         0.7812     0.4975     1.        ]]

Erstellen von gelisteten Colormaps#

Das Erstellen einer Colormap ist im Wesentlichen die umgekehrte Operation zum obigen, bei der wir eine Liste oder ein Array von Farbspezifikationen an ListedColormap übergeben, um eine neue Colormap zu erstellen.

Bevor wir mit dem Tutorial fortfahren, definieren wir eine Hilfsfunktion, die eine oder mehrere Colormaps als Eingabe nimmt, einige Zufallsdaten erstellt und die Colormap(s) auf eine Bilddarstellung dieser Daten anwendet.

def plot_examples(colormaps):
    """
    Helper function to plot data with associated colormap.
    """
    np.random.seed(19680801)
    data = np.random.randn(30, 30)
    n = len(colormaps)
    fig, axs = plt.subplots(1, n, figsize=(n * 2 + 2, 3),
                            layout='constrained', squeeze=False)
    for [ax, cmap] in zip(axs.flat, colormaps):
        psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4)
        fig.colorbar(psm, ax=ax)
    plt.show()

Im einfachsten Fall könnten wir eine Liste von Farbnamen eingeben, um eine Colormap daraus zu erstellen.

cmap = ListedColormap(["darkorange", "gold", "lawngreen", "lightseagreen"])
plot_examples([cmap])
colormap manipulation

Tatsächlich kann diese Liste jede gültige Matplotlib-Farbspezifikation enthalten. Besonders nützlich für die Erstellung benutzerdefinierter Colormaps sind Arrays der Form (N, 4). Da wir mit der Vielfalt von NumPy-Operationen, die wir auf ein solches Array anwenden können, die Erstellung neuer Colormaps aus vorhandenen Colormaps recht einfach gestalten können.

Angenommen, wir möchten beispielsweise die ersten 25 Einträge einer "viridis"-Colormap mit 256 Einträgen aus irgendeinem Grund pink machen.

viridis = mpl.colormaps['viridis'].resampled(256)
newcolors = viridis(np.linspace(0, 1, 256))
pink = np.array([248/256, 24/256, 148/256, 1])
newcolors[:25, :] = pink
newcmp = ListedColormap(newcolors)

plot_examples([viridis, newcmp])
colormap manipulation

Wir können den Dynamikbereich einer Colormap reduzieren; hier wählen wir die mittlere Hälfte der Colormap. Beachten Sie jedoch, dass wir, da viridis eine gelistete Colormap ist, am Ende 128 diskrete Werte anstelle der 256 Werte erhalten, die in der ursprünglichen Colormap vorhanden waren. Diese Methode interpoliert nicht im Farbraum, um neue Farben hinzuzufügen.

viridis_big = mpl.colormaps['viridis']
newcmp = ListedColormap(viridis_big(np.linspace(0.25, 0.75, 128)))
plot_examples([viridis, newcmp])
colormap manipulation

und wir können zwei Colormaps leicht verketten.

top = mpl.colormaps['Oranges_r'].resampled(128)
bottom = mpl.colormaps['Blues'].resampled(128)

newcolors = np.vstack((top(np.linspace(0, 1, 128)),
                       bottom(np.linspace(0, 1, 128))))
newcmp = ListedColormap(newcolors, name='OrangeBlue')
plot_examples([viridis, newcmp])
colormap manipulation

Natürlich müssen wir nicht von einer benannten Colormap ausgehen, wir müssen nur das (N, 4)-Array erstellen, das an ListedColormap übergeben wird. Hier erstellen wir eine Colormap, die von Braun (RGB: 90, 40, 40) zu Weiß (RGB: 255, 255, 255) geht.

N = 256
vals = np.ones((N, 4))
vals[:, 0] = np.linspace(90/256, 1, N)
vals[:, 1] = np.linspace(40/256, 1, N)
vals[:, 2] = np.linspace(40/256, 1, N)
newcmp = ListedColormap(vals)
plot_examples([viridis, newcmp])
colormap manipulation

Erstellen von linearen segmentierten Colormaps#

Die Klasse LinearSegmentedColormap spezifiziert Colormaps mithilfe von Ankerpunkten, zwischen denen RGB(A)-Werte interpoliert werden.

Das Format zur Spezifikation dieser Colormaps ermöglicht Diskontinuitäten an den Ankerpunkten. Jeder Ankerpunkt wird als Zeile in einer Matrix der Form [x[i] yleft[i] yright[i]] angegeben, wobei x[i] der Anker ist und yleft[i] und yright[i] die Werte der Farbe auf beiden Seiten des Ankerpunkts sind.

Wenn keine Diskontinuitäten vorhanden sind, dann gilt yleft[i] == yright[i].

cdict = {'red':   [[0.0,  0.0, 0.0],
                   [0.5,  1.0, 1.0],
                   [1.0,  1.0, 1.0]],
         'green': [[0.0,  0.0, 0.0],
                   [0.25, 0.0, 0.0],
                   [0.75, 1.0, 1.0],
                   [1.0,  1.0, 1.0]],
         'blue':  [[0.0,  0.0, 0.0],
                   [0.5,  0.0, 0.0],
                   [1.0,  1.0, 1.0]]}


def plot_linearmap(cdict):
    newcmp = LinearSegmentedColormap('testCmap', segmentdata=cdict, N=256)
    rgba = newcmp(np.linspace(0, 1, 256))
    fig, ax = plt.subplots(figsize=(4, 3), layout='constrained')
    col = ['r', 'g', 'b']
    for xx in [0.25, 0.5, 0.75]:
        ax.axvline(xx, color='0.7', linestyle='--')
    for i in range(3):
        ax.plot(np.arange(256)/256, rgba[:, i], color=col[i])
    ax.set_xlabel('index')
    ax.set_ylabel('RGB')
    plt.show()

plot_linearmap(cdict)
colormap manipulation

Um eine Diskontinuität an einem Ankerpunkt zu erzeugen, unterscheidet sich die dritte Spalte von der zweiten. Die Matrix für "rot", "grün", "blau" und optional "alpha" wird wie folgt aufgebaut:

cdict['red'] = [...
                [x[i]      yleft[i]     yright[i]],
                [x[i+1]    yleft[i+1]   yright[i+1]],
               ...]

und für Werte, die der Colormap zwischen x[i] und x[i+1] übergeben werden, erfolgt die Interpolation zwischen yright[i] und yleft[i+1].

Im folgenden Beispiel gibt es eine Diskontinuität in Rot bei 0,5. Die Interpolation zwischen 0 und 0,5 geht von 0,3 bis 1 und zwischen 0,5 und 1 von 0,9 bis 1. Beachten Sie, dass red[0, 1] und red[2, 2] für die Interpolation beide überflüssig sind, da red[0, 1] (d. h. yleft[0]) der Wert links von 0 ist und red[2, 2] (d. h. yright[2]) der Wert rechts von 1 ist, die außerhalb des Farbbereichs liegen.

cdict['red'] = [[0.0,  0.0, 0.3],
                [0.5,  1.0, 0.9],
                [1.0,  1.0, 1.0]]
plot_linearmap(cdict)
colormap manipulation

Direktes Erstellen einer segmentierten Colormap aus einer Liste#

Der oben beschriebene Ansatz ist sehr vielseitig, aber zugegebenermaßen etwas umständlich zu implementieren. Für einige grundlegende Fälle kann die Verwendung von LinearSegmentedColormap.from_list einfacher sein. Dies erstellt eine segmentierte Colormap mit gleichen Abständen aus einer bereitgestellten Liste von Farben.

colors = ["darkorange", "gold", "lawngreen", "lightseagreen"]
cmap1 = LinearSegmentedColormap.from_list("mycmap", colors)

Wenn gewünscht, können die Knotenpunkte der Colormap als Zahlen zwischen 0 und 1 angegeben werden. Beispielsweise könnte der rötliche Teil mehr Platz in der Colormap einnehmen.

nodes = [0.0, 0.4, 0.8, 1.0]
cmap2 = LinearSegmentedColormap.from_list("mycmap", list(zip(nodes, colors)))

plot_examples([cmap1, cmap2])
colormap manipulation

Umkehren einer Colormap#

Colormap.reversed erstellt eine neue Colormap, die eine umgekehrte Version der ursprünglichen Colormap ist.

colors = ["#ffffcc", "#a1dab4", "#41b6c4", "#2c7fb8", "#253494"]
my_cmap = ListedColormap(colors, name="my_cmap")

my_cmap_r = my_cmap.reversed()

plot_examples([my_cmap, my_cmap_r])
colormap manipulation

Wenn kein Name übergeben wird, benennt .reversed die Kopie auch, indem '_r' angehängt wird an den Namen der ursprünglichen Colormap.

Registrieren einer Colormap#

Colormaps können der Liste der benannten Colormaps von matplotlib.colormaps hinzugefügt werden. Dies ermöglicht den Zugriff auf die Colormaps über ihren Namen in Plot-Funktionen.

# my_cmap, my_cmap_r from reversing a colormap
mpl.colormaps.register(cmap=my_cmap)
mpl.colormaps.register(cmap=my_cmap_r)

data = [[1, 2, 3, 4, 5]]

fig, (ax1, ax2) = plt.subplots(nrows=2)

ax1.imshow(data, cmap='my_cmap')
ax2.imshow(data, cmap='my_cmap_r')

plt.show()
colormap manipulation

Gesamtlaufzeit des Skripts: (0 Minuten 5,023 Sekunden)

Galerie generiert von Sphinx-Gallery