matplotlib - in bianco e nero colormap (con trattini, punti ecc.)
-
28-10-2019 - |
Domanda
Sto usando matplotlib per creare punti di linea 2D. Ai fini della pubblicazione, vorrei avere quelle trame in bianco e nero (non Grayscale), e sto lottando per trovare una soluzione non intrusiva per questo.
Gnuplot altera automaticamente i motivi che si affrettano per diverse righe, è qualcosa di simile possibile con Matplotlib?
Soluzione
Di seguito fornisco funzioni per convertire una linea colorata in una linea nera con stile unico. Il mio test rapido ha mostrato che dopo 7 righe, i colori si sono ripetuti. Se questo non è così (e ho fatto un errore), è necessario un adeguamento minore per la "costante" COLORMAP
Nella routine fornita.
Ecco la routine e l'esempio:
import matplotlib.pyplot as plt
import numpy as np
def setAxLinesBW(ax):
"""
Take each Line2D in the axes, ax, and convert the line style to be
suitable for black and white viewing.
"""
MARKERSIZE = 3
COLORMAP = {
'b': {'marker': None, 'dash': (None,None)},
'g': {'marker': None, 'dash': [5,5]},
'r': {'marker': None, 'dash': [5,3,1,3]},
'c': {'marker': None, 'dash': [1,3]},
'm': {'marker': None, 'dash': [5,2,5,2,5,10]},
'y': {'marker': None, 'dash': [5,3,1,2,1,10]},
'k': {'marker': 'o', 'dash': (None,None)} #[1,2,1,10]}
}
lines_to_adjust = ax.get_lines()
try:
lines_to_adjust += ax.get_legend().get_lines()
except AttributeError:
pass
for line in lines_to_adjust:
origColor = line.get_color()
line.set_color('black')
line.set_dashes(COLORMAP[origColor]['dash'])
line.set_marker(COLORMAP[origColor]['marker'])
line.set_markersize(MARKERSIZE)
def setFigLinesBW(fig):
"""
Take each axes in the figure, and for each line in the axes, make the
line viewable in black and white.
"""
for ax in fig.get_axes():
setAxLinesBW(ax)
xval = np.arange(100)*.01
fig = plt.figure()
ax = fig.add_subplot(211)
ax.plot(xval,np.cos(2*np.pi*xval))
ax.plot(xval,np.cos(3*np.pi*xval))
ax.plot(xval,np.cos(4*np.pi*xval))
ax.plot(xval,np.cos(5*np.pi*xval))
ax.plot(xval,np.cos(6*np.pi*xval))
ax.plot(xval,np.cos(7*np.pi*xval))
ax.plot(xval,np.cos(8*np.pi*xval))
ax = fig.add_subplot(212)
ax.plot(xval,np.cos(2*np.pi*xval))
ax.plot(xval,np.cos(3*np.pi*xval))
ax.plot(xval,np.cos(4*np.pi*xval))
ax.plot(xval,np.cos(5*np.pi*xval))
ax.plot(xval,np.cos(6*np.pi*xval))
ax.plot(xval,np.cos(7*np.pi*xval))
ax.plot(xval,np.cos(8*np.pi*xval))
fig.savefig("colorDemo.png")
setFigLinesBW(fig)
fig.savefig("bwDemo.png")
Questo fornisce le seguenti due trame: prima a colori:Quindi in bianco e nero:
Puoi regolare il modo in cui ogni colore viene convertito in uno stile. Se vuoi solo giocare con lo stile Dash (-. Vs.-vs. qualunque schema tu voglia), imposta il COLORMAP
Valore "marcatore" corrispondente a None
e regolato il modello "Dash" o viceversa.
Ad esempio, l'ultimo colore nel dizionario è 'k' (per il nero); Inizialmente avevo solo uno schema tratteggiato [1,2,1,10]
, corrispondente a un pixel mostrato, due no, uno mostrato, 10 no, che è un modello di spazio punto punto. Poi ho commentato che, impostando il cruscotto su (nessuno, nessuno), un modo molto formale di dire una linea solida e ho aggiunto il marcatore "O", per il cerchio.
Ho anche impostato una marcatura "costante", che imposterà le dimensioni di ciascun marcatore, perché ho trovato la dimensione predefinita un po 'grande.
Questo ovviamente non gestisce il caso in cui le tue linee hanno già un trattino o un motch di marker, ma puoi usare queste routine come punto di partenza per costruire un convertitore più sofisticato. Ad esempio, se la trama originale avesse una linea solida rossa e una linea rossa tratteggiata, entrambi si trasformerebbero in linee nere con queste routine. Qualcosa da tenere a mente quando li usi.
Altri suggerimenti
Tl; dr
import matplotlib.pyplot as plt
from cycler import cycler
monochrome = (cycler('color', ['k']) * cycler('marker', ['', '.']) *
cycler('linestyle', ['-', '--', ':', '=.']))
plt.rc('axes', prop_cycle=monochrome)
Risposta estesa
Più nuovo matplotlib
le versioni hanno introdotto un nuovo rcParams
, vale a dire axes.prop_cycle
In [1]: import matplotlib.pyplot as plt
In [2]: plt.rcParams['axes.prop_cycle']
Out[2]: cycler('color', ['b', 'g', 'r', 'c', 'm', 'y', 'k'])
Per gli stili precoce, disponibili da plt.style.use(...)
o with plt.style.context(...):
, il prop_cycle
è equivalente al tradizionale e deprecato axes.color_cycle
In [3]: plt.rcParams['axes.color_cycle']
/.../__init__.py:892: UserWarning: axes.color_cycle is deprecated and replaced with axes.prop_cycle; please use the latter.
warnings.warn(self.msg_depr % (key, alt_key))
Out[3]: ['b', 'g', 'r', 'c', 'm', 'y', 'k']
ma il cycler
L'oggetto ha molte più possibilità, in particolare un complesso cycler
può essere composto da quelli più semplici, facendo riferimento a proprietà diverse, usando +
e *
, significato rispettivamente zipping e prodotto cartesiano.
Qui importa il cycler
Funzione di supporto, definiamo 3 semplici cycler
che si riferiscono a proprietà diverse e infine li compongono usando il prodotto cartesiano
In [4]: from cycler import cycler
In [5]: color_c = cycler('color', ['k'])
In [6]: style_c = cycler('linestyle', ['-', '--', ':', '-.'])
In [7]: markr_c = cycler('marker', ['', '.', 'o'])
In [8]: c_cms = color_c * markr_c * style_c
In [9]: c_csm = color_c * style_c * markr_c
Qui abbiamo due diversi (?) Complesso cycler
E sì, sono diversi perché questa operazione non è commessa, dai un'occhiata
In [10]: for d in c_csm: print('\t'.join(d[k] for k in d))
- k
- . k
- o k
-- k
-- . k
-- o k
: k
: . k
: o k
-. k
-. . k
-. o k
In [11]: for d in c_cms: print('\t'.join(d[k] for k in d))
- k
-- k
: k
-. k
- . k
-- . k
: . k
-. . k
- o k
-- o k
: o k
-. o k
Il ciclo elementare che cambia più velocemente è l'ultimo nel prodotto, ecc., Questo è importante se vogliamo un certo ordine nello stile delle linee.
Come usare la composizione di cycler
S? Per mezzo di plt.rc
, o un modo equivalente per modificare il rcParams
di matplotlib
. Per esempio,
In [12]: %matplotlib
Using matplotlib backend: Qt4Agg
In [13]: import numpy as np
In [14]: x = np.linspace(0, 8, 101)
In [15]: y = np.cos(np.arange(7)+x[:,None])
In [16]: plt.rc('axes', prop_cycle=c_cms)
In [17]: plt.plot(x, y);
In [18]: plt.grid();
Naturalmente questo è solo un esempio e l'OP può mescolare e abbinare proprietà diverse per ottenere l'output visivo più piacevole.
PS Ho dimenticato di menzionare che questo approccio si occupa automaticamente dei campioni di linea nella scatola della legenda,
Ho usato pesantemente il codice di Yann, ma oggi ho letto una risposta da Posso scorrere gli stili di linea in matplotlib Quindi ora farò le mie trame BW in questo modo:
import pylab as plt
from itertools import cycle
lines = ["k-","k--","k-.","k:"]
linecycler = cycle(lines)
plt.figure()
for i in range(4):
x = range(i,i+10)
plt.plot(range(10),x,next(linecycler))
plt.show()
Cose come plot(x,y,'k-.')
Produrrà il nero ('k'
) DOT-DASHED ('-.'
) linea. Non è quello che stai cercando?