¿Es posible consultar el estado actual del ciclo de color de matplotlib? En otras palabras, ¿existe una función get_cycle_stateque se comportará de la siguiente manera?
>>> plot(x1, y1)
>>> plot(x2, y2)
>>> state = get_cycle_state()
>>> print state
2
Donde espero que el estado sea el índice del siguiente color que se usará en una gráfica. Alternativamente, si devolvió el siguiente color ("r" para el ciclo predeterminado en el ejemplo anterior), eso también estaría bien.
python
matplotlib
mwaskom
fuente
fuente

Respuestas:
Accediendo al iterador del ciclo de color
No hay un método "de cara al usuario" (también conocido como "público") para acceder al iterador subyacente, pero puede acceder a él a través de métodos "privados" (por convención). Sin embargo, no puede obtener el estado de un
iteratorsin cambiarlo.Configurar el ciclo de color
Un aparte rápido: puede establecer el ciclo de color / propiedad de varias formas (por ejemplo,
ax.set_color_cycleen las versiones <1.5 oax.set_prop_cycleren> = 1.5). Eche un vistazo al ejemplo aquí para la versión 1.5 o superior , o al estilo anterior aquí .Accediendo al iterador subyacente
Sin embargo, aunque no existe un método de cara al público para acceder al iterable, puede acceder a él para un objeto de ejes determinado (
ax) a través de la_get_linesinstancia de la clase auxiliar.ax._get_lineses un toque de nombre confuso, pero es la maquinaria detrás de escena la que permite que elplotcomando procese todas las formas extrañas y variadas queplotse pueden llamar. Entre otras cosas, es lo que realiza un seguimiento de los colores que se asignan automáticamente. Del mismo modo, hayax._get_patches_for_fillque controlar el ciclo a través de los colores de relleno predeterminados y las propiedades del parche.En cualquier caso, el ciclo de color iterable es
ax._get_lines.color_cyclepara líneas yax._get_patches_for_fill.color_cycleparches. En matplotlib> = 1.5, esto ha cambiado para usar lacyclerbiblioteca , y se llama al iterable enprop_cyclerlugar decolor_cycley produce unadictde propiedades en lugar de solo un color.Con todo, harías algo como:
import matplotlib.pyplot as plt fig, ax = plt.subplots() color_cycle = ax._get_lines.color_cycle # or ax._get_lines.prop_cycler on version >= 1.5 # Note that prop_cycler cycles over dicts, so you'll want next(cycle)['color']No puede ver el estado de un
iteratorSin embargo, este objeto es un "desnudo"
iterator. Podemos obtener fácilmente el siguiente elemento (por ejemplonext_color = next(color_cycle), pero eso significa que el siguiente color después de ese es el que se trazará. Por diseño, no hay forma de obtener el estado actual de un iterador sin cambiarlo.En
v1.5o superior, sería bueno obtener elcyclerobjeto que se usa, ya que podríamos inferir su estado actual. Sin embargo, elcyclerobjeto en sí no es accesible (de forma pública o privada) en ninguna parte. En cambio, solo se puede acceder a laitertools.cycleinstancia creada a partir delcyclerobjeto. De cualquier manera, no hay forma de llegar al estado subyacente del ciclador de color / propiedad.En su lugar, haga coincidir el color del elemento trazado anteriormente
En su caso, parece que desea hacer coincidir el color de algo que acaba de trazar. En lugar de intentar determinar cuál será el color / propiedad, establezca el color / etc. de su nuevo elemento en función de las propiedades de lo que se trazó.
Por ejemplo, en el caso que describiste, haría algo como esto:
import matplotlib.pyplot as plt import numpy as np def custom_plot(x, y, **kwargs): ax = kwargs.pop('ax', plt.gca()) base_line, = ax.plot(x, y, **kwargs) ax.fill_between(x, 0.9*y, 1.1*y, facecolor=base_line.get_color(), alpha=0.5) x = np.linspace(0, 1, 10) custom_plot(x, x) custom_plot(x, 2*x) custom_plot(x, -x, color='yellow', lw=3) plt.show()No es la única forma, pero es más limpia que intentar obtener el color de la línea trazada de antemano, en este caso.
fuente
line, = ax.plot(x, y)y luego usarline.get_color()para obtener el color de la línea trazada previamente.ax._get_lines.color_cycleya no existe en 1.5?ax._get_lines.prop_cyclerque puede tener una construcción similarif 'color' in ax._get_lines._prop_keys:seguida denext(ax._get_lines.prop_cycler)['color']para hacer el equivalente de lo que se sugierecolor_cycleen la respuesta. No estoy seguro de si puede obtener una iteración solo de los colores, necesitaría explorar más.Aquí hay una forma que funciona en 1.5 y que, con suerte, estará preparada para el futuro, ya que no depende de métodos precedidos de guiones bajos:
colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]Esto le dará una lista de los colores definidos en orden para el estilo actual.
fuente
Nota: En las últimas versiones de matplotlib (> = 1.5)
_get_linesha cambiado. Ahora necesita usarnext(ax._get_lines.prop_cycler)['color']en Python 2 o 3 (oax._get_lines.prop_cycler.next()['color']en Python 2 ) para obtener el siguiente color del ciclo de color.Siempre que sea posible, use el enfoque más directo que se muestra en la parte inferior de la respuesta de @ joe-kington. Como
_get_linesno está orientado a API, podría cambiar nuevamente de una manera no compatible con versiones anteriores en el futuro.fuente
Seguro, esto lo hará.
#rainbow import matplotlib.pyplot as plt import numpy as np x = np.linspace(0,2*np.pi) ax= plt.subplot(1,1,1) ax.plot(np.sin(x)) ax.plot(np.cos(x)) rainbow = ax._get_lines.color_cycle print rainbow for i, color in enumerate(rainbow): if i<10: print color,Da:
<itertools.cycle object at 0x034CB288> r c m y k b g r c mAquí está la función itertools que matplotlib usa itertools.cycle
Editar: Gracias por el comentario, parece que no es posible copiar un iterador. Una idea sería volcar un ciclo completo y realizar un seguimiento del valor que está utilizando, déjeme volver a eso.
Edit2: Muy bien, esto le dará el siguiente color y creará un nuevo iterador que se comporta como si el siguiente no fuera llamado. Esto no preserva el orden de coloración, solo el siguiente valor de color, eso se lo dejo a usted.
Esto da la siguiente salida, observe que la inclinación en el gráfico corresponde al índice, por ejemplo, primero g es el gráfico más bajo y así sucesivamente.
#rainbow import matplotlib.pyplot as plt import numpy as np import collections import itertools x = np.linspace(0,2*np.pi) ax= plt.subplot(1,1,1) def create_rainbow(): rainbow = [ax._get_lines.color_cycle.next()] while True: nextval = ax._get_lines.color_cycle.next() if nextval not in rainbow: rainbow.append(nextval) else: return rainbow def next_color(axis_handle=ax): rainbow = create_rainbow() double_rainbow = collections.deque(rainbow) nextval = ax._get_lines.color_cycle.next() double_rainbow.rotate(-1) return nextval, itertools.cycle(double_rainbow) for i in range(1,10): nextval, ax._get_lines.color_cycle = next_color(ax) print "Next color is: ", nextval ax.plot(i*(x)) plt.savefig("SO_rotate_color.png") plt.show()Consola
Next color is: g Next color is: c Next color is: y Next color is: b Next color is: r Next color is: m Next color is: k Next color is: g Next color is: cfuente
Solo quiero agregar a lo que @Andi dijo anteriormente. Dado que
color_cycleestá obsoleto en matplotlib 1.5, debe usarprop_cycler, sin embargo, la solución de Andi (ax._get_lines.prop_cycler.next()['color']) me devolvió este error:El código que funcionó para mí fue:
next(ax._get_lines.prop_cycler)que en realidad no está muy lejos de la respuesta original de @ joe-kington.Personalmente, me encontré con este problema al hacer un eje twinx (), que reinicia el ciclador de color. Necesitaba una forma de hacer que los colores se ciclaran correctamente porque estaba usando
style.use('ggplot'). Puede haber una manera mejor o más fácil de hacer esto, así que no dudes en corregirme.fuente
next(ax._get_lines.prop_cycler)es una posible alternativa.next(ax._get_lines.prop_cycler)cambie realmente el estado del ciclador de color?prop_cycler. Si lee la respuesta aceptada a esta pregunta, verá que no hay forma de acceder al estado deprop_cyclersin cambiar su estado porque es iterable.Dado que matplotlib usa
itertools.cycle, podemos ver todo el ciclo de color y luego restaurar el iterador a su estado anterior:def list_from_cycle(cycle): first = next(cycle) result = [first] for current in cycle: if current == first: break result.append(current) # Reset iterator state: for current in cycle: if current == result[-1]: break return resultEsto debería devolver la lista sin cambiar el estado del iterador.
Úselo con matplotlib> = 1.5:
>>> list_from_cycle(ax._get_lines.prop_cycler) [{'color': 'r'}, {'color': 'g'}, {'color': 'b'}]o con matplotlib <1.5:
>>> list_from_cycle(ax._get_lines.color_cycle) ['r', 'g', 'b']fuente
La forma más sencilla posible que pude encontrar sin hacer todo el ciclo a través del ciclador es
ax1.lines[-1].get_color().fuente
En matplotlib versión 2.2.3 hay un
get_next_color()método en la_get_linespropiedad:import from matplotlib import pyplot as plt fig, ax = plt.subplots() next_color = ax._get_lines.get_next_color()get_next_color()devuelve una cadena de color html y avanza el iterador del ciclo de color.fuente
¿Cómo acceder al ciclo de color (y estilo completo)?
El estado actual se almacena en
ax._get_lines.prop_cycler. No hay métodos incorporados para exponer la "lista base" para un genéricoitertools.cycle, y en particular paraax._get_lines.prop_cycler(ver más abajo).He publicado aquí algunas funciones para obtener información sobre un archivo
itertools.cycle. Entonces uno podría usarstyle_cycle = ax._get_lines.prop_cycler curr_style = get_cycle_state(style_cycle) # <-- my (non-builtin) function curr_color = curr_style['color']para obtener el color actual sin cambiar el estado del ciclo .
TL; DR
¿Dónde se almacena el ciclo de color (y estilo completo)?
El ciclo de estilo se almacena en dos lugares diferentes, uno para el predeterminado y otro para los ejes actuales (asumiendo que
import matplotlib.pyplot as pltyaxes un controlador de eje):default_prop_cycler = plt.rcParams['axes.prop_cycle'] current_prop_cycle = ax._get_lines.prop_cyclerTenga en cuenta que estos tienen diferentes clases. El valor predeterminado es una "configuración de ciclo base" y no conoce ningún estado actual para ningún eje, mientras que la corriente conoce el ciclo a seguir y su estado actual:
print('type(default_prop_cycler) =', type(default_prop_cycler)) print('type(current_prop_cycle) =', type(current_prop_cycle)) []: type(default_prop_cycler) = <class 'cycler.Cycler'> []: type(current_prop_cycle) = <class 'itertools.cycle'>El ciclo predeterminado puede tener varias teclas (propiedades) para realizar el ciclo, y solo se pueden obtener los colores:
print('default_prop_cycler.keys =', default_prop_cycler.keys) default_prop_cycler2 = plt.rcParams['axes.prop_cycle'].by_key() print(default_prop_cycler2) print('colors =', default_prop_cycler2['color']) []: default_prop_cycler.keys = {'color', 'linestyle'} []: {'color': ['r', 'g', 'b', 'y'], 'linestyle': ['-', '--', ':', '-.']} []: colors = ['r', 'g', 'b', 'y']Incluso se podría cambiar el
cycleruso para un determinadoaxes, después de definir esocustom_prop_cycler, conPero no hay métodos integrados para exponer la "lista base" para un genérico
itertools.cycle, y en particular paraax._get_lines.prop_cycler.fuente