Pregunta

Estoy usando matplotlib para crear gráficos de línea 2D. A los fines de publicación, me gustaría tener esas tramas en blanco y negro (no escala de grises), y estoy luchando por encontrar una solución no intrusiva para eso.

Gnuplot altera automáticamente los patrones de carrera para diferentes líneas, ¿es algo similar posible con matplotlib?

¿Fue útil?

Solución

A continuación, proporciono funciones para convertir una línea de colores a una línea negra con un estilo único. Mi prueba rápida mostró que después de 7 líneas, los colores se repitieron. Si este no es el caso (y cometí un error), entonces se necesita un ajuste menor para la "constante" COLORMAP en la rutina proporcionada.

Aquí está la rutina y el ejemplo:

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

Esto proporciona las siguientes dos parcelas: primero en color:enter image description hereLuego en blanco y negro:enter image description here

Puede ajustar cómo cada color se convierte en un estilo. Si solo quieres jugar con el estilo de tablero (-. Vs. vs. cualquier patrón que quieras), configure el COLORMAP valor de 'marcador' correspondiente para None y ajustó el patrón 'Dash', o viceversa.

Por ejemplo, el último color en el diccionario es 'K' (para negro); Originalmente solo tenía un patrón discontinuo [1,2,1,10], correspondiente a un píxel que se muestra, dos no, uno mostrado, 10 no, que es un patrón de espacio-espacio de puntos. Luego comenté eso, estableciendo el tablero a (ninguno, ninguno), una forma muy formal de decir una línea continua, y agregué el marcador 'O', para Circle.

También establecí un marcador 'constante', que establecerá el tamaño de cada marcador, porque encontré que el tamaño predeterminado es un poco grande.

Obviamente, esto no maneja el caso cuando sus líneas ya tienen un tablero o un marcador, pero puede usar estas rutinas como punto de partida para construir un convertidor más sofisticado. Por ejemplo, si tu gráfica original tuviera una línea continua roja y una línea punteada roja, ambos se convertirían en líneas negras de dato con estas rutinas. Algo a tener en cuenta cuando los usas.

Otros consejos

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)

Respuesta extendida

Más nuevo matplotlib Los lanzamientos introdujeron un nuevo rcParams, a saber 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'])

Para los estilos precanticos, disponibles por plt.style.use(...) o with plt.style.context(...):, la prop_cycle es equivalente al tradicional y en desuso 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']

pero el cycler El objeto tiene muchas más posibilidades, en particular un complejo cycler puede componerse de las más simples, refiriéndose a diferentes propiedades, utilizando + y *, lo que significa respectivamente creciente y producto cartesiano.

Aquí importamos el cycler Función auxiliar, definimos 3 simples cycler que se refieren a diferentes propiedades y finalmente las componen utilizando el producto 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

Aquí tenemos dos complejos diferentes (?) cycler Y sí, son diferentes porque esta operación no es común, eche un vistazo

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

El ciclo elemental que cambia más rápido es el último en el producto, etc., esto es importante si queremos un cierto orden en el estilo de las líneas.

Cómo usar la composición de cycler¿s? Por los medios de plt.rc, o una forma equivalente de modificar el rcParams de matplotlib. P.ej,

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

Por supuesto, esto es solo un ejemplo, y el OP puede mezclar y combinar diferentes propiedades para lograr la salida visual más agradable.

PD que olvidé mencionar que este enfoque se encarga automáticamente de las muestras de línea en la caja de leyenda,enter image description here

Yo usé mucho el código de Yann, pero hoy leí una respuesta de ¿Puedo recorrer los estilos de línea en matplotlib? Así que ahora haré mis tramas de BW de esta manera:

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

Cosas como plot(x,y,'k-.') producirá el negro ('k') Dot-Dashed ('-.') línea. ¿No es eso lo que buscas?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top