¿Es posible consultar el estado actual del ciclo de color de matplotlib? En otras palabras, ¿existe una función get_cycle_state
que 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
iterator
sin 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_cycle
en las versiones <1.5 oax.set_prop_cycler
en> = 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_lines
instancia de la clase auxiliar.ax._get_lines
es un toque de nombre confuso, pero es la maquinaria detrás de escena la que permite que elplot
comando procese todas las formas extrañas y variadas queplot
se 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_fill
que 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_cycle
para líneas yax._get_patches_for_fill.color_cycle
parches. En matplotlib> = 1.5, esto ha cambiado para usar lacycler
biblioteca , y se llama al iterable enprop_cycler
lugar decolor_cycle
y produce unadict
de 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
iterator
Sin 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.5
o superior, sería bueno obtener elcycler
objeto que se usa, ya que podríamos inferir su estado actual. Sin embargo, elcycler
objeto en sí no es accesible (de forma pública o privada) en ninguna parte. En cambio, solo se puede acceder a laitertools.cycle
instancia creada a partir delcycler
objeto. 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_cycle
ya no existe en 1.5?ax._get_lines.prop_cycler
que 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_cycle
en 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_lines
ha 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_lines
no 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 m
Aquí 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: c
fuente
Solo quiero agregar a lo que @Andi dijo anteriormente. Dado que
color_cycle
está 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_cycler
sin 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 result
Esto 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_lines
propiedad: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 plt
yax
es un controlador de eje):default_prop_cycler = plt.rcParams['axes.prop_cycle'] current_prop_cycle = ax._get_lines.prop_cycler
Tenga 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
cycler
uso 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