xkcd pyplot color python matplotlib

python - pyplot - Obtenga el estado del ciclo de color matplotlib



rgba colors python (6)

¿Es posible consultar el estado actual del ciclo de color matplotlib? En otras palabras, ¿hay una función get_cycle_state que se comporte 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 un diagrama. Alternativamente, si devuelve el siguiente color ("r" para el ciclo predeterminado en el ejemplo anterior), eso también estaría bien.


Accediendo al iterador del ciclo de color

No hay un método "orientado 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 podría obtener el estado de un iterator sin cambiarlo.

Configurando el ciclo de color

Dejando a un lado ax.set_color_cycle : Puede configurar el ciclo de color / propiedad de varias maneras (por ejemplo, ax.set_color_cycle en las versiones <1.5 o ax.set_prop_cycler in> = 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 público para acceder al iterable, puede acceder a él para un objeto de ejes dado ( ax ) a través de la instancia de la clase de ayuda _get_lines . ax._get_lines tiene un toque confuso, pero es la maquinaria detrás de escena la que permite que el comando plot todas las formas raras y variadas en que se puede llamar la plot . Entre otras cosas, es lo que hace un seguimiento de qué colores asignar automáticamente. Del mismo modo, hay ax._get_patches_for_fill para controlar el ciclo a través de colores de relleno predeterminados y propiedades de parche.

En cualquier caso, el ciclo de color iterable es ax._get_lines.color_cycle para líneas y ax._get_patches_for_fill.color_cycle para parches. En matplotlib> = 1.5, esto ha cambiado para usar la biblioteca del cycler , y el iterable se llama prop_cycler lugar de color_cycle y produce un dict de propiedades en lugar de solo un color.

En general, 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 puedes ver el estado de un iterator

Sin embargo, este objeto es un iterator "vacío". Podemos obtener fácilmente el siguiente elemento (por ejemplo, next_color = next(color_cycle) , pero eso significa que el siguiente color después de eso es lo 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 el objeto cycler que se utiliza, ya que podríamos inferir su estado actual. Sin embargo, el objeto cycler sí no es accesible (pública o privadamente) en ninguna parte. En cambio, solo se puede itertools.cycle instancia de itertools.cycle creada desde el objeto de cycler . De cualquier manera, no hay forma de llegar al estado subyacente del ciclador de color / propiedad.

Haga coincidir el color del elemento previamente trazado en su lugar

En tu caso, parece que quieres combinar el color de algo que se tramó. En lugar de tratar de determinar cuál será el color / propiedad, configure el color / etc de su nuevo artículo 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 manera, pero es más limpio que tratar de obtener el color de la línea trazada de antemano, en este caso.


Claro, 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 descargar un ciclo completo y realizar un seguimiento del valor que está utilizando, déjenme volver sobre eso.

Edit2: Bien, esto le dará el siguiente color y creará un nuevo iterador que se comporta como si no se hubiera llamado al siguiente. Esto no conserva el orden de coloración, solo el siguiente valor de color, te lo dejo a ti.

Esto da la siguiente salida, observe que la inclinación en la gráfica corresponde al índice, por ejemplo, la primera g es la gráfica más baja 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


Como matplotlib usa itertools.cycle , podemos mirar 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'']


Esta es una forma que funciona en 1.5, 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 para el estilo actual.


Nota: En las últimas versiones de matplotlib (> = 1.5) _get_lines ha cambiado. Ahora necesita usar next(ax._get_lines.prop_cycler)[''color''] en Python 2 o 3 (o ax._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 la API, podría cambiar nuevamente de una manera que no sea compatible con versiones anteriores en el futuro.


Solo quiero agregar lo que @Andi dijo anteriormente. Como color_cycle está en desuso en matplotlib 1.5, debe usar prop_cycler ; sin embargo, la solución de Andi ( ax._get_lines.prop_cycler.next()[''color''] ) me devolvió este error:

AttributeError: el objeto ''itertools.cycle'' no tiene ningún atributo ''siguiente''

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 funcionaran correctamente porque estaba usando style.use(''ggplot'') . Puede haber una manera más fácil / mejor de hacer esto, así que siéntete libre de corregirme.