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.