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?

È stato utile?

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:enter image description hereQuindi in bianco e nero:enter image description here

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 cyclerS? 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();

enter image description here

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,enter image description here

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

enter image description here

Cose come plot(x,y,'k-.') Produrrà il nero ('k') DOT-DASHED ('-.') linea. Non è quello che stai cercando?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top