Notiz
Klicken Sie hier , um den vollständigen Beispielcode herunterzuladen
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 ListedColormap
oder
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 viridis
ist 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 #
ListedColormap
s speichern ihre Farbwerte in einem .colors
Attribut. Auf die Liste der Farben, aus denen die Farbtabelle besteht, kann direkt über die colors
Eigenschaft oder indirekt durch Aufrufen viridis
mit 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 #
LinearSegmentedColormap
s haben kein .colors
Attribut. 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 ListedColormap
Farbspezifikationen 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])
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])
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])
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])
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])
Linear segmentierte Farbkarten erstellen #
Die LinearSegmentedColormap
Klasse 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)
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]
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_list
einfacher 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.
Verweise
In diesem Beispiel wird die Verwendung der folgenden Funktionen, Methoden, Klassen und Module gezeigt:
Gesamtlaufzeit des Skripts: ( 0 Minuten 4.802 Sekunden)