Colormaps in Matplotlib erstellen #

Matplotlib verfügt über eine Reihe integrierter Farbkarten, auf die über matplotlib.colormaps. Es gibt auch externe Bibliotheken wie palettable , die viele zusätzliche Farbkarten haben.

Wir möchten jedoch häufig Colormaps in Matplotlib erstellen oder manipulieren. Dies kann mit der Klasse ListedColormapoder erfolgen LinearSegmentedColormap. Von außen betrachtet bilden beide Colormap-Klassen Werte zwischen 0 und 1 auf eine Reihe von Farben ab. Es gibt jedoch geringfügige Unterschiede, von denen einige im Folgenden dargestellt werden.

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

Colormaps abrufen und auf ihre Werte zugreifen #

Erstens kann das Abrufen einer benannten Farbtabelle, von denen die meisten unter Auswählen von Farbtabellen in Matplotlib aufgelistet sind , mit erfolgen matplotlib.colormaps, was ein Farbtabellenobjekt zurückgibt. Die Länge der Liste der Farben, die intern zur Definition der Farbtabelle verwendet werden, kann über angepasst werden Colormap.resampled. Unten verwenden wir einen bescheidenen Wert von 8, so dass nicht viele Werte betrachtet werden müssen.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib.colors import ListedColormap, LinearSegmentedColormap

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

Das Objekt viridisist ein aufrufbares Objekt, das bei Übergabe eines Floats zwischen 0 und 1 einen RGBA-Wert aus der Farbtabelle zurückgibt:

print(viridis(0.56))
(0.122312, 0.633153, 0.530398, 1.0)

ListedColormap #

ListedColormaps speichern ihre Farbwerte in einem .colorsAttribut. Auf die Liste der Farben, aus denen die Farbtabelle besteht, kann direkt über die colorsEigenschaft oder indirekt durch Aufrufen viridismit einem Array von Werten zugegriffen werden, die der Länge der Farbtabelle entsprechen. Beachten Sie, dass die zurückgegebene Liste die Form eines RGBA Nx4-Arrays hat, wobei N die Länge der Farbtabelle 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 Farbtabelle ist eine Nachschlagetabelle, daher gibt das "Oversampling" der Farbtabelle die Interpolation des nächsten Nachbarn zurück (beachten Sie die wiederholten Farben in der Liste unten).

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 .colorsAttribut. Man kann die Colormap jedoch immer noch mit einem Integer-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.        ]]

Aufgelistete Colormaps erstellen #

Das Erstellen einer Farbtabelle ist im Wesentlichen die umgekehrte Operation der obigen Vorgehensweise, bei der wir eine Liste oder ein Array von ListedColormapFarbspezifikationen bereitstellen, um eine neue Farbtabelle zu erstellen.

Bevor wir mit dem Lernprogramm fortfahren, definieren wir eine Hilfsfunktion, die eine oder mehrere Farbtabellen als Eingabe verwendet, einige zufällige Daten erstellt und die Farbtabelle(n) auf einen Bildplot dieses Datensatzes 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),
                            constrained_layout=True, 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 mit Farbnamen eingeben, um daraus eine Farbkarte 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 zum Erstellen benutzerdefinierter Farbkarten sind Nx4-Numpy-Arrays. Denn mit der Vielzahl von numpy-Operationen, die wir auf einem solchen Array ausführen können, wird die Zimmerei von neuen Colormaps aus bestehenden Colormaps ziemlich einfach.

Angenommen, wir möchten die ersten 25 Einträge einer 256-langen "viridis"-Colormap aus irgendeinem Grund rosa 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 dynamischen Bereich einer Farbtabelle reduzieren; Hier wählen wir die mittlere Hälfte der Farbkarte. Beachten Sie jedoch, dass wir, da viridis eine aufgelistete Farbtabelle ist, am Ende 128 diskrete Werte anstelle der 256 Werte in der ursprünglichen Farbtabelle erhalten. 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 Farbkarten einfach 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 Farbtabelle ausgehen, wir müssen nur das Nx4-Array erstellen, um es an zu übergeben ListedColormap. Hier erstellen wir eine Farbkarte, die von Braun (RGB: 90, 40, 40) bis Weiß (RGB: 255, 255, 255) reicht.

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

Linear segmentierte Farbkarten erstellen #

Die LinearSegmentedColormapKlasse spezifiziert Colormaps mit Ankerpunkten, zwischen denen RGB(A)-Werte interpoliert werden.

Das Format zum Spezifizieren dieser Farbabbildungen lässt Diskontinuitäten an den Ankerpunkten zu. Jeder Ankerpunkt wird als Zeile in einer Matrix der Form angegeben , wobei der Anker und und die Werte der Farbe auf beiden Seiten des Ankerpunkts sind.[x[i] yleft[i] yright[i]]x[i]yleft[i]yright[i]

Wenn es keine Diskontinuitäten gibt, dann :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), constrained_layout=True)
    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 an einem Ankerpunkt eine Diskontinuität herzustellen, unterscheidet sich die dritte Spalte von der zweiten. Die Matrix für „Rot“, „Grün“, „Blau“ und optional „Alpha“ ist wie folgt eingerichtet:

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

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

Im Beispiel unten 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 geht sie von 0,9 bis 1. Beachten Sie, dass , und beide für die Interpolation überflüssig sind, weil (dh ) der Wert links von 0 ist und ( dh ) ist der Wert rechts von 1, der außerhalb des Farbzuordnungsbereichs liegt.red[0, 1]red[2, 2]red[0, 1]yleft[0]red[2, 2]yright[2]

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 Farbkarte aus einer Liste #

Der oben beschriebene Ansatz ist sehr vielseitig, aber zugegebenermaßen etwas umständlich in der Umsetzung. Für einige grundlegende Fälle kann die Verwendung von LinearSegmentedColormap.from_listeinfacher sein. Dadurch wird aus einer bereitgestellten Farbliste eine segmentierte Farbkarte mit gleichen Abständen erstellt.

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

Falls gewünscht, können die Knoten der Farbkarte als Zahlen zwischen 0 und 1 angegeben werden. Beispielsweise könnte man den rötlichen Teil in der Farbkarte mehr Platz einnehmen lassen.

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

plot_examples([cmap1, cmap2])
Colormap-Manipulation

Gesamtlaufzeit des Skripts: ( 0 Minuten 4.802 Sekunden)

Galerie generiert von Sphinx-Gallery