103 votes

Obtenir l'état du cycle de couleur de matplotlib

Est-il possible d'interroger l'état actuel du cycle de couleurs de matplotlib ? En d'autres termes, existe-t-il une fonction get_cycle_state qui se comportera de la manière suivante ?

>>> plot(x1, y1)
>>> plot(x2, y2)
>>> state = get_cycle_state()
>>> print state
2

Où je m'attends à ce que l'état soit l'indice de la prochaine couleur qui sera utilisée dans un tracé. Sinon, s'il retournait la couleur suivante ("r" pour le cycle par défaut dans l'exemple ci-dessus), ce serait bien aussi.

127voto

Joe Kington Points 68089

Accès à l'itérateur du cycle des couleurs

Il n'y a pas de méthode "face à l'utilisateur" (c'est-à-dire "publique") pour accéder à l'itérateur sous-jacent, mais vous pouvez y accéder par des méthodes "privées" (par convention). Cependant, vous ne pouvez pas obtenir l'état d'un objet iterator sans le modifier.

Réglage du cycle des couleurs

Une parenthèse rapide : Vous pouvez définir le cycle couleur/propriété de plusieurs façons (par exemple ax.set_color_cycle dans les versions <1.5 ou ax.set_prop_cycler dans >=1.5). Jetez un coup d'œil à la exemple ici pour la version 1.5 ou supérieure ou le style précédent ici .

Accès à l'itérateur sous-jacent

Cependant, bien qu'il n'y ait pas de méthode publique pour accéder à l'itérable, vous pouvez y accéder pour un objet d'axes donné ( ax ) par l'intermédiaire du _get_lines instance de classe d'aide. ax._get_lines est un peu confus dans son nom, mais il s'agit de la machinerie en coulisse qui permet à l'équipe de la plot pour traiter toutes les manières bizarres et variées dont plot peut être appelé. C'est, entre autres, ce qui permet de savoir quelles couleurs attribuer automatiquement. De même, il y a ax._get_patches_for_fill pour contrôler le cycle des couleurs de remplissage par défaut et des propriétés du patch.

En tout cas, l'itérable du cycle de couleur est ax._get_lines.color_cycle pour les lignes et ax._get_patches_for_fill.color_cycle pour les correctifs. Sur matplotlib >=1.5, cela a changé en utiliser le cycler bibliothèque et l'itérable s'appelle prop_cycler au lieu de color_cycle et donne un dict de propriétés au lieu d'une seule couleur.

En somme, vous feriez quelque chose comme :

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']

Vous ne pouvez pas voir l'état d'une iterator

Cependant, cet objet est un "nu" iterator . Nous pouvons facilement obtenir l'élément suivant (par ex. next_color = next(color_cycle) mais cela signifie que la couleur suivante après cela est ce qui sera tracé. Par conception, il n'y a aucun moyen d'obtenir l'état actuel d'un itérateur sans le modifier.

Sur v1.5 ou plus, il serait bon d'obtenir la cycler qui est utilisé, car nous pourrions déduire son état actuel. Cependant, l'objet cycler L'objet lui-même n'est accessible (publiquement ou en privé) nulle part. Au lieu de cela, seul le itertools.cycle créée à partir de l'instance cycler est accessible. Dans tous les cas, il n'y a aucun moyen d'accéder à l'état sous-jacent du cycleur de couleurs/propriétés.

Faites correspondre la couleur de l'élément précédemment tracé à la place.

Dans votre cas, il semble que vous vouliez faire correspondre la couleur d'un élément qui vient d'être tracé. Au lieu d'essayer de déterminer la couleur/la propriété, définissez la couleur/etc de votre nouvel élément en fonction des propriétés de ce qui est tracé.

Par exemple, dans le cas que vous avez décrit, je ferais quelque chose comme ceci :

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

Ce n'est pas le seul moyen, mais il est plus propre que d'essayer d'obtenir la couleur de la ligne tracée à l'avance, dans ce cas.

41voto

Mike DePalatis Points 521

Voici une méthode qui fonctionne dans la version 1.5 et qui, espérons-le, sera à l'épreuve du temps puisqu'elle ne repose pas sur des méthodes précédées d'underscores :

colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]

Vous obtiendrez ainsi une liste des couleurs définies dans l'ordre pour le style actuel.

18voto

Andi Points 868

Remarque : dans les dernières versions de matplotlib (>= 1.5) _get_lines a changé. Vous devez maintenant utiliser next(ax._get_lines.prop_cycler)['color'] dans Python 2 ou 3 (ou ax._get_lines.prop_cycler.next()['color'] en Python 2 ) pour obtenir la couleur suivante du cycle des couleurs.

Dans la mesure du possible, utilisez l'approche plus directe présentée dans la partie inférieure de la réponse de @joe-kington. Comme _get_lines n'est pas orienté vers l'API, il pourrait être modifié à l'avenir d'une manière non rétrocompatible.

6voto

arynaq Points 2819

Bien sûr, ça va le faire.

#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,

Donne :

<itertools.cycle object at 0x034CB288>
r c m y k b g r c m

Voici la fonction itertools que matplotlib utilise itertools.cycle

Edit : Merci pour le commentaire, il semble qu'il ne soit pas possible de copier un itérateur. Une idée serait de vider un cycle complet et de garder la trace de la valeur que vous utilisez, laissez-moi revenir sur ce point.

Edit2 : Très bien, cela vous donnera la couleur next et créera un nouvel itérateur qui se comportera comme si next n'avait pas été appelé. Cela ne préserve pas l'ordre de la coloration, juste la valeur de la couleur suivante, je vous laisse faire.

Cela donne le résultat suivant, remarquez que la pente du graphique correspond à l'indice, par exemple le premier g est le graphique le plus bas et ainsi de suite.

#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()

Console

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

5voto

Carson Points 139

Je veux juste ajouter à ce que @Andi a dit plus haut. Depuis color_cycle est déprécié dans matplotlib 1.5, vous devez utiliser prop_cycler Cependant, la solution d'Andi ( ax._get_lines.prop_cycler.next()['color'] ) m'a renvoyé cette erreur :

AttributeError : L'objet 'itertools.cycle' ne possède pas d'attribut 'next'.

Le code qui a fonctionné pour moi était : next(ax._get_lines.prop_cycler) ce qui n'est pas très éloigné de la réponse originale de @joe-kington.

Personnellement, j'ai rencontré ce problème en faisant un axe twinx(), qui réinitialise le cycleur de couleurs. J'avais besoin d'un moyen de faire en sorte que le cycle des couleurs soit correct, car j'utilisais la méthode style.use('ggplot') . Il existe peut-être un moyen plus simple ou plus efficace de procéder, alors n'hésitez pas à me corriger.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X