Pregunta Obtenga el estado del ciclo de color matplotlib


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


76
2017-12-12 01:50


origen


Respuestas:


Accediendo al iterador del ciclo de color

No existe un método "orientado al usuario" (a.k.a. "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 rápidamente: puede establecer el ciclo de color / propiedad de varias maneras (p. ax.set_color_cycle en versiones <1.5 o ax.set_prop_cycler en> = 1.5). Eche un vistazo al ejemplo aquí para la versión 1.5 o superior, o el 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 _get_lines instancia de clase auxiliar. ax._get_lines es un toque confusamente llamado, pero es la maquinaria detrás de escena que permite plot comando para procesar todas las formas raras y variadas que plot puede ser llamado. 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 a utilizar el cycler biblioteca, y el iterable se llama prop_cycler en 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 "desnudo" iterator. Podemos obtener fácilmente el siguiente artículo (p. next_color = next(color_cycle), pero eso significa que el próximo color después de esto es lo que se tramará. Por diseño, no hay forma de obtener el estado actual de un iterador sin cambiarlo.

En v1.5 o más, sería bueno obtener el cycler objeto que se utiliza, ya que podríamos inferir su estado actual. sin embargo, el cycler el objeto mismo no es accesible (pública o privadamente) en ninguna parte. En cambio, solo el itertools.cycle instancia creada a partir de cycler objeto es accesible. 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()

enter image description here

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.


86
2017-12-12 02:24



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 API, podría cambiar nuevamente de una manera que no sea compatible con versiones anteriores en el futuro.


13
2017-11-09 11:56



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.


9
2017-07-23 06:47



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 usa matplotlib 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

Rotate color


6
2017-12-12 02:20



Solo quiero agregar lo que @Andi dijo anteriormente. Ya que color_cycle está en desuso en matplotlib 1.5, tienes que 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 corrieran 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.


3
2017-12-15 17:20



Dado que matplotlib usa itertools.cycle en realidad podemos mirar a través de 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']

1
2017-09-22 08:38