Creazione di mappe dei colori in Matplotlib #

Matplotlib ha una serie di mappe di colori integrate accessibili tramite matplotlib.colormaps. Ci sono anche librerie esterne come palettable che hanno molte colormap extra.

Tuttavia, spesso vogliamo creare o manipolare mappe di colori in Matplotlib. Questo può essere fatto usando la classe ListedColormapo LinearSegmentedColormap. Viste dall'esterno, entrambe le classi colormap mappano i valori compresi tra 0 e 1 in un gruppo di colori. Ci sono, tuttavia, lievi differenze, alcune delle quali sono mostrate di seguito.

Prima di creare o manipolare manualmente le mappe dei colori, vediamo prima come possiamo ottenere le mappe dei colori ei loro colori dalle classi di mappe dei colori esistenti.

Ottenere mappe dei colori e accedere ai loro valori #

Innanzitutto, ottenere una mappa di colori denominata, la maggior parte delle quali è elencata in Scelta delle mappe di colore in Matplotlib , può essere eseguita utilizzando matplotlib.colormaps, che restituisce un oggetto mappa di colori. La lunghezza dell'elenco dei colori utilizzati internamente per definire la mappa dei colori può essere regolata tramite Colormap.resampled. Di seguito usiamo un valore modesto di 8, quindi non ci sono molti valori da guardare.

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)

L'oggetto viridisè un callable, che quando viene passato un float tra 0 e 1 restituisce un valore RGBA dalla mappa dei colori:

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

Mappa dei colori elencata #

ListedColormaps memorizzano i loro valori di colore in un .colorsattributo. È possibile accedere direttamente all'elenco dei colori che compongono la mappa dei colori utilizzando la colorsproprietà oppure indirettamente chiamando viridiscon un array di valori corrispondenti alla lunghezza della mappa dei colori. Si noti che l'elenco restituito è sotto forma di un array RGBA Nx4, dove N è la lunghezza della mappa dei colori.

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.      ]]

La mappa dei colori è una tabella di ricerca, quindi il "sovracampionamento" della mappa dei colori restituisce l'interpolazione del vicino più vicino (notare i colori ripetuti nell'elenco seguente)

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 non hanno un .colorsattributo. Tuttavia, si può ancora chiamare la mappa dei colori con un array intero o con un array float compreso tra 0 e 1.

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.        ]]

Creazione di mappe di colori elencate #

La creazione di una mappa di colori è essenzialmente l'operazione inversa di quanto sopra in cui forniamo un elenco o un array di specifiche di colore ListedColormapper creare una nuova mappa di colori.

Prima di continuare con il tutorial, definiamo una funzione di supporto che prende una o più mappe di colori come input, crea alcuni dati casuali e applica le mappe di colori a un grafico dell'immagine di quel set di dati.

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

Nel caso più semplice potremmo digitare un elenco di nomi di colori per creare una mappa di colori da quelli.

cmap = ListedColormap(["darkorange", "gold", "lawngreen", "lightseagreen"])
plot_examples([cmap])
manipolazione della mappa dei colori

In effetti, quell'elenco può contenere qualsiasi specifica di colore Matplotlib valida . Particolarmente utili per creare mappe di colori personalizzate sono gli array numpy Nx4. Perché con la varietà di operazioni intorpidite che possiamo eseguire su un tale array, la falegnameria di nuove mappe di colori da mappe di colori esistenti diventa piuttosto semplice.

Ad esempio, supponiamo di voler rendere rosa le prime 25 voci di una mappa dei colori "viridis" lunga 256 per qualche motivo:

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])
manipolazione della mappa dei colori

Possiamo ridurre la gamma dinamica di una mappa di colori; qui scegliamo la metà centrale della mappa dei colori. Si noti, tuttavia, che poiché viridis è una mappa di colori elencata, ci ritroveremo con 128 valori discreti invece dei 256 valori che erano nella mappa di colori originale. Questo metodo non interpola nello spazio colore per aggiungere nuovi colori.

viridis_big = mpl.colormaps['viridis']
newcmp = ListedColormap(viridis_big(np.linspace(0.25, 0.75, 128)))
plot_examples([viridis, newcmp])
manipolazione della mappa dei colori

e possiamo facilmente concatenare due colormap:

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])
manipolazione della mappa dei colori

Ovviamente non è necessario iniziare da una mappa di colori con nome, è sufficiente creare l'array Nx4 da passare a ListedColormap. Qui creiamo una mappa dei colori che va dal marrone (RGB: 90, 40, 40) al bianco (RGB: 255, 255, 255).

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])
manipolazione della mappa dei colori

Creazione di mappe di colori segmentate lineari #

La LinearSegmentedColormapclasse specifica le mappe di colore utilizzando punti di ancoraggio tra i quali vengono interpolati i valori RGB(A).

Il formato per specificare queste mappe di colori consente discontinuità nei punti di ancoraggio. Ogni punto di ancoraggio è specificato come una riga in una matrice della forma , dove è l'ancora e e sono i valori del colore su entrambi i lati del punto di ancoraggio.[x[i] yleft[i] yright[i]]x[i]yleft[i]yright[i]

Se non ci sono discontinuità, allora :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)
manipolazione della mappa dei colori

Per creare una discontinuità in un punto di ancoraggio, la terza colonna è diversa dalla seconda. La matrice per ciascuno di "rosso", "verde", "blu" e facoltativamente "alfa" è impostata come:

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

e per i valori passati alla mappa colori tra x[i]e x[i+1], l'interpolazione è tra yright[i]e yleft[i+1].

Nell'esempio qui sotto c'è una discontinuità in rosso a 0.5. L'interpolazione tra 0 e 0.5 va da 0.3 a 1, e tra 0.5 e 1 va da 0.9 a 1. Si noti che , e sono entrambi superflui all'interpolazione perché (cioè, ) è il valore a sinistra di 0, e ( cioè, ) è il valore a destra di 1, che si trova al di fuori del dominio di mappatura dei colori.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)
manipolazione della mappa dei colori

Creazione diretta di una mappa colori segmentata da un elenco #

L'approccio sopra descritto è molto versatile, ma certamente un po' macchinoso da implementare. Per alcuni casi di base, l'uso di LinearSegmentedColormap.from_listpotrebbe essere più semplice. Questo crea una mappa di colori segmentata con spaziature uguali da un elenco di colori fornito.

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

Se lo si desidera, i nodi della mappa dei colori possono essere dati come numeri compresi tra 0 e 1. Ad esempio, si potrebbe fare in modo che la parte rossastra occupi più spazio nella mappa dei colori.

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

plot_examples([cmap1, cmap2])
manipolazione della mappa dei colori

Tempo di esecuzione totale dello script: (0 minuti 4,802 secondi)

Galleria generata da Sphinx-Gallery