Nota
Fare clic qui per scaricare il codice di esempio completo
Scelta delle mappe dei colori in Matplotlib #
Matplotlib ha una serie di mappe di colori integrate accessibili tramite
matplotlib.colormaps
. Esistono anche librerie esterne che hanno molte mappe di colori extra, che possono essere visualizzate nella
sezione Mappe di colori di terze parti della documentazione di Matplotlib. Qui discutiamo brevemente come scegliere tra le molte opzioni. Per assistenza sulla creazione delle proprie mappe di colori, vedere
Creazione di mappe di colori in Matplotlib .
Panoramica n.
L'idea alla base della scelta di una buona mappa di colori è trovare una buona rappresentazione nello spazio colore 3D per il tuo set di dati. La migliore mappa dei colori per un determinato set di dati dipende da molte cose, tra cui:
Sia che rappresenti forma o dati metrici ( [Ware] )
La tua conoscenza del set di dati ( ad esempio , c'è un valore critico da cui gli altri valori si discostano?)
Se esiste uno schema di colori intuitivo per il parametro che stai tracciando
Se c'è uno standard nel campo, il pubblico potrebbe aspettarselo
Per molte applicazioni, una mappa colori percettivamente uniforme è la scelta migliore; cioè una mappa di colori in cui passi uguali nei dati sono percepiti come passi uguali nello spazio colore. I ricercatori hanno scoperto che il cervello umano percepisce i cambiamenti nel parametro di luminosità come cambiamenti nei dati molto meglio rispetto, ad esempio, ai cambiamenti di tonalità. Pertanto, le mappe dei colori che hanno una luminosità crescente in modo monotono attraverso la mappa dei colori saranno interpretate meglio dallo spettatore. Splendidi esempi di colormap percettivamente uniformi si possono trovare anche nella sezione colormap di terze parti .
Il colore può essere rappresentato nello spazio 3D in vari modi. Un modo per rappresentare il colore è usare CIELAB. In CIELAB, lo spazio colore è rappresentato dalla luminosità, \(L^*\); rosso verde,\(a^*\); e giallo-blu,\(b^*\). Il parametro di leggerezza\(L^*\)può quindi essere utilizzato per saperne di più su come le mappe dei colori matplotlib saranno percepite dagli spettatori.
Un'ottima risorsa di partenza per conoscere la percezione umana delle mappe dei colori proviene da [IBM] .
Classi di colormap #
Le mappe dei colori sono spesso suddivise in diverse categorie in base alla loro funzione (vedi, ad esempio , [Moreland] ):
Sequenziale: modifica della luminosità e spesso della saturazione del colore in modo incrementale, spesso utilizzando un'unica tonalità; dovrebbe essere usato per rappresentare informazioni che hanno un ordinamento.
Divergente: cambiamento di luminosità ed eventualmente saturazione di due colori diversi che si incontrano nel mezzo in un colore insaturo; dovrebbe essere utilizzato quando le informazioni che vengono tracciate hanno un valore medio critico, come la topografia o quando i dati deviano intorno allo zero.
Ciclico: cambio di luminosità di due colori diversi che si incontrano a metà e inizio/fine a un colore insaturo; dovrebbe essere utilizzato per i valori che si avvolgono nei punti finali, come l'angolo di fase, la direzione del vento o l'ora del giorno.
Qualitativo: spesso sono colori vari; dovrebbe essere usato per rappresentare informazioni che non hanno ordini o relazioni.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from colorspacious import cspace_converter
Innanzitutto, mostreremo l'intervallo di ciascuna mappa di colori. Si noti che alcuni sembrano cambiare più "rapidamente" di altri.
cmaps = {}
gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))
def plot_color_gradients(category, cmap_list):
# Create figure and adjust figure height to number of colormaps
nrows = len(cmap_list)
figh = 0.35 + 0.15 + (nrows + (nrows - 1) * 0.1) * 0.22
fig, axs = plt.subplots(nrows=nrows + 1, figsize=(6.4, figh))
fig.subplots_adjust(top=1 - 0.35 / figh, bottom=0.15 / figh,
left=0.2, right=0.99)
axs[0].set_title(f'{category} colormaps', fontsize=14)
for ax, name in zip(axs, cmap_list):
ax.imshow(gradient, aspect='auto', cmap=mpl.colormaps[name])
ax.text(-0.01, 0.5, name, va='center', ha='right', fontsize=10,
transform=ax.transAxes)
# Turn off *all* ticks & spines, not just the ones with colormaps.
for ax in axs:
ax.set_axis_off()
# Save colormap list for later.
cmaps[category] = cmap_list
# sequenziale
Per i grafici sequenziali, il valore di luminosità aumenta in modo monotono attraverso le mappe dei colori. Questo è buono. Alcuni dei\(L^*\)i valori nelle mappe dei colori vanno da 0 a 100 (binario e l'altra scala di grigi) e altri iniziano intorno \(L^*=20\). Quelli che hanno una gamma più piccola di\(L^*\)avrà di conseguenza una gamma percettiva più piccola. Si noti inoltre che il\(L^*\)la funzione varia tra le mappe di colori: alcune sono approssimativamente lineari\(L^*\)e altri sono più curvi.
plot_color_gradients('Perceptually Uniform Sequential',
['viridis', 'plasma', 'inferno', 'magma', 'cividis'])
plot_color_gradients('Sequential',
['Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn'])
Sequenziale2 #
Molti dei\(L^*\)I valori delle trame sequenziali2 sono monotonicamente in aumento, ma alcuni altopiani (autunno, fresco, primavera e inverno) o addirittura vanno su e giù\(L^*\)spazio. Altri (afmhot, copper, gist_heat e hot) hanno dei nodi nel file\(L^*\)funzioni. I dati che vengono rappresentati in una regione della mappa dei colori che si trova su un plateau o un nodo porteranno a una percezione di bande dei dati in quei valori nella mappa dei colori (vedi [mycarta-banding] per un eccellente esempio di questo).
plot_color_gradients('Sequential (2)',
['binary', 'gist_yarg', 'gist_gray', 'gray', 'bone',
'pink', 'spring', 'summer', 'autumn', 'winter', 'cool',
'Wistia', 'hot', 'afmhot', 'gist_heat', 'copper'])
Divergente #
Per le mappe divergenti, vogliamo avere un aumento monotono\(L^*\) valori fino a un massimo, che dovrebbe essere vicino a\(L^*=100\), seguito da una diminuzione monotona\(L^*\)i valori. Stiamo cercando un minimo approssimativamente uguale\(L^*\)Valori alle estremità opposte del Colormap. Con queste misure, BRBG e RDBU sono buone opzioni. coolwarm è una buona opzione, ma non dura una vasta gamma di\(L^*\)Valori (vedere la sezione della scala di grigi di seguito).
plot_color_gradients('Diverging',
['PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu', 'RdYlBu',
'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic'])
Ciclico #
Per le mappe cicliche, vogliamo iniziare e finire sullo stesso colore e incontrare un punto centrale simmetrico nel mezzo.\(L^*\)dovrebbe cambiare in modo monotono dall'inizio alla metà e inversamente dalla metà alla fine. Dovrebbe essere simmetrico sul lato crescente e decrescente e differire solo nella tonalità. Alle estremità e al centro,\(L^*\)invertirà la direzione, che dovrebbe essere smussata \(L^*\)spazio per ridurre gli artefatti. Vedi [Kovesi-Colormaps] per ulteriori informazioni sulla progettazione di mappe cicliche.
La mappa di colori HSV usata spesso è inclusa in questo insieme di mappe di colori, sebbene non sia simmetrica rispetto a un punto centrale. Inoltre, il\(L^*\)i valori variano ampiamente in tutta la mappa dei colori, rendendola una scelta sbagliata per rappresentare i dati che gli spettatori possono vedere percettivamente. Vedi un'estensione su questa idea su [mycarta-jet] .
plot_color_gradients('Cyclic', ['twilight', 'twilight_shifted', 'hsv'])
Qualitativo #
Le mappe di colore qualitative non mirano ad essere mappe percettive, ma guardare il parametro di luminosità può verificarlo per noi. Il\(L^*\)i valori si spostano ovunque nella mappa dei colori e chiaramente non aumentano in modo monotono. Queste non sarebbero buone opzioni da usare come mappe di colori percettive.
plot_color_gradients('Qualitative',
['Pastel1', 'Pastel2', 'Paired', 'Accent', 'Dark2',
'Set1', 'Set2', 'Set3', 'tab10', 'tab20', 'tab20b',
'tab20c'])
Varie #
Alcune delle varie colormap hanno usi particolari per i quali sono state create. Ad esempio, gist_earth, ocean e terrain sembrano tutti creati per tracciare insieme topografia (verde/marrone) e profondità dell'acqua (blu). Ci aspetteremmo di vedere una divergenza in queste mappe di colori, quindi, ma più nodi potrebbero non essere l'ideale, come in gist_earth e terrain. CMRmap è stato creato per convertire bene in scala di grigi, anche se sembra avere alcuni piccoli nodi \(L^*\). cubehelix è stato creato per variare uniformemente sia la luminosità che la tonalità, ma sembra avere una piccola gobba nell'area della tonalità verde. turbo è stato creato per visualizzare i dati di profondità e disparità.
La mappa di colori jet usata spesso è inclusa in questo set di mappe di colori. Possiamo vedere che il\(L^*\)i valori variano ampiamente in tutta la mappa dei colori, rendendola una scelta sbagliata per rappresentare i dati che gli spettatori possono vedere percettivamente. Guarda un'estensione di questa idea su [mycarta-jet] e [turbo] .
plot_color_gradients('Miscellaneous',
['flag', 'prism', 'ocean', 'gist_earth', 'terrain',
'gist_stern', 'gnuplot', 'gnuplot2', 'CMRmap',
'cubehelix', 'brg', 'gist_rainbow', 'rainbow', 'jet',
'turbo', 'nipy_spectral', 'gist_ncar'])
plt.show()
Leggerezza delle mappe dei colori di Matplotlib #
Qui esaminiamo i valori di leggerezza dei matplotlib colormaps. Si noti che è disponibile alcune documentazioni sui Colormaps ( [List-Colmaps] ).
mpl.rcParams.update({'font.size': 12})
# Number of colormap per subplot for particular cmap categories
_DSUBS = {'Perceptually Uniform Sequential': 5, 'Sequential': 6,
'Sequential (2)': 6, 'Diverging': 6, 'Cyclic': 3,
'Qualitative': 4, 'Miscellaneous': 6}
# Spacing between the colormaps of a subplot
_DC = {'Perceptually Uniform Sequential': 1.4, 'Sequential': 0.7,
'Sequential (2)': 1.4, 'Diverging': 1.4, 'Cyclic': 1.4,
'Qualitative': 1.4, 'Miscellaneous': 1.4}
# Indices to step through colormap
x = np.linspace(0.0, 1.0, 100)
# Do plot
for cmap_category, cmap_list in cmaps.items():
# Do subplots so that colormaps have enough space.
# Default is 6 colormaps per subplot.
dsub = _DSUBS.get(cmap_category, 6)
nsubplots = int(np.ceil(len(cmap_list) / dsub))
# squeeze=False to handle similarly the case of a single subplot
fig, axs = plt.subplots(nrows=nsubplots, squeeze=False,
figsize=(7, 2.6*nsubplots))
for i, ax in enumerate(axs.flat):
locs = [] # locations for text labels
for j, cmap in enumerate(cmap_list[i*dsub:(i+1)*dsub]):
# Get RGB values for colormap and convert the colormap in
# CAM02-UCS colorspace. lab[0, :, 0] is the lightness.
rgb = mpl.colormaps[cmap](x)[np.newaxis, :, :3]
lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb)
# Plot colormap L values. Do separately for each category
# so each plot can be pretty. To make scatter markers change
# color along plot:
# https://stackoverflow.com/q/8202605/
if cmap_category == 'Sequential':
# These colormaps all start at high lightness but we want them
# reversed to look nice in the plot, so reverse the order.
y_ = lab[0, ::-1, 0]
c_ = x[::-1]
else:
y_ = lab[0, :, 0]
c_ = x
dc = _DC.get(cmap_category, 1.4) # cmaps horizontal spacing
ax.scatter(x + j*dc, y_, c=c_, cmap=cmap, s=300, linewidths=0.0)
# Store locations for colormap labels
if cmap_category in ('Perceptually Uniform Sequential',
'Sequential'):
locs.append(x[-1] + j*dc)
elif cmap_category in ('Diverging', 'Qualitative', 'Cyclic',
'Miscellaneous', 'Sequential (2)'):
locs.append(x[int(x.size/2.)] + j*dc)
# Set up the axis limits:
# * the 1st subplot is used as a reference for the x-axis limits
# * lightness values goes from 0 to 100 (y-axis limits)
ax.set_xlim(axs[0, 0].get_xlim())
ax.set_ylim(0.0, 100.0)
# Set up labels for colormaps
ax.xaxis.set_ticks_position('top')
ticker = mpl.ticker.FixedLocator(locs)
ax.xaxis.set_major_locator(ticker)
formatter = mpl.ticker.FixedFormatter(cmap_list[i*dsub:(i+1)*dsub])
ax.xaxis.set_major_formatter(formatter)
ax.xaxis.set_tick_params(rotation=50)
ax.set_ylabel('Lightness $L^*$', fontsize=12)
ax.set_xlabel(cmap_category + ' colormaps', fontsize=14)
fig.tight_layout(h_pad=0.0, pad=1.5)
plt.show()
Conversione in scala di grigi #
È importante prestare attenzione alla conversione in scala di grigi per le trame a colori, poiché possono essere stampati su stampanti in bianco e nero. Se non attentamente considerati, i lettori possono finire con grafici indeciflabili perché la scala di grigi cambia in modo imprevedibile attraverso il Colormap.
La conversione in scala di grigi viene eseguita in molti modi diversi [bw] . Alcuni dei migliori utilizzano una combinazione lineare dei valori rgb di un pixel, ma pesati in base a come percepiamo l'intensità del colore. Un metodo non lineare di conversione in scala di grigi consiste nell'utilizzare il file\(L^*\)valori dei pixel. In generale, principi simili si applicano a questa domanda come fanno per presentare le proprie informazioni percettivamente; cioè, se viene scelto un colamap che sta aumentando monotonicamente\(L^*\)Valori, verrà stampato in modo ragionevole per la scala di grigi.
Con questo in mente, vediamo che le mappe di colori sequenziali hanno rappresentazioni ragionevoli in scala di grigi. Alcune delle colormap di Sequential2 hanno rappresentazioni in scala di grigi abbastanza decenti, anche se alcune (autunno, primavera, estate, inverno) hanno variazioni minime in scala di grigi. Se una mappa di colori come questa è stata utilizzata in un grafico e poi il grafico è stato stampato in scala di grigi, molte informazioni potrebbero essere associate agli stessi valori di grigio. Le colormap divergenti variano principalmente dal grigio più scuro sui bordi esterni al bianco al centro. Alcuni (PuOr e sismico) hanno un grigio notevolmente più scuro da un lato rispetto all'altro e quindi sono poco simmetrici. coolwarm ha una piccola gamma di scala di grigi e stamperebbe su una trama più uniforme, perdendo molti dettagli. Si noti che i contorni sovrapposti ed etichettati potrebbero aiutare a distinguere tra un lato della mappa dei colori e un altro. l'altro poiché il colore non può essere usato una volta che una trama è stampata su scala di grigi. Molte delle colormap qualitative e varie, come Accent, hsv, jet e turbo, cambiano da più scure a più chiare e tornano a un grigio più scuro in tutta la colormap. Ciò renderebbe impossibile per uno spettatore interpretare le informazioni in una trama una volta stampati in scala di grigi.
mpl.rcParams.update({'font.size': 14})
# Indices to step through colormap.
x = np.linspace(0.0, 1.0, 100)
gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))
def plot_color_gradients(cmap_category, cmap_list):
fig, axs = plt.subplots(nrows=len(cmap_list), ncols=2)
fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99,
wspace=0.05)
fig.suptitle(cmap_category + ' colormaps', fontsize=14, y=1.0, x=0.6)
for ax, name in zip(axs, cmap_list):
# Get RGB values for colormap.
rgb = mpl.colormaps[name](x)[np.newaxis, :, :3]
# Get colormap in CAM02-UCS colorspace. We want the lightness.
lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb)
L = lab[0, :, 0]
L = np.float32(np.vstack((L, L, L)))
ax[0].imshow(gradient, aspect='auto', cmap=mpl.colormaps[name])
ax[1].imshow(L, aspect='auto', cmap='binary_r', vmin=0., vmax=100.)
pos = list(ax[0].get_position().bounds)
x_text = pos[0] - 0.01
y_text = pos[1] + pos[3]/2.
fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10)
# Turn off *all* ticks & spines, not just the ones with colormaps.
for ax in axs.flat:
ax.set_axis_off()
plt.show()
for cmap_category, cmap_list in cmaps.items():
plot_color_gradients(cmap_category, cmap_list)
Deficit della visione dei colori #
Sono disponibili molte informazioni sul daltonismo ( ad es . [daltonismo] ). Inoltre, sono disponibili strumenti per convertire le immagini nel modo in cui cercano diversi tipi di carenze nella visione dei colori.
La forma più comune di deficit della visione dei colori comporta la differenziazione tra rosso e verde. Pertanto, evitare le mappe dei colori sia con il rosso che con il verde eviterà molti problemi in generale.
Riferimenti #
https://mycarta.wordpress.com/2012/10/06/the-rainbow-is-deadlong-live-the-rainbow-part-3/
Tempo di esecuzione totale dello script: (0 minuti 14,251 secondi)