Frage Get Matplotlib Farbzyklusstatus


Ist es möglich, den aktuellen Zustand des Matplotlib-Farbzyklus abzufragen? Mit anderen Worten, gibt es eine Funktion get_cycle_state das wird sich so verhalten?

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

Wo ich erwarte, dass der Zustand der Index der nächsten Farbe ist, die in einer Handlung verwendet wird. Alternativ, wenn es die nächste Farbe zurückgibt ("r" für den Standardzyklus im obigen Beispiel), wäre das auch in Ordnung.


76
2017-12-12 01:50


Ursprung


Antworten:


Zugriff auf den Farbzyklus-Iterator

Es gibt keine "Benutzer-zugewandte" (a.k.a. "public") Methode, um auf den zugrundeliegenden Iterator zuzugreifen, aber Sie können darauf durch "private" (durch Konvention) Methoden zugreifen. Sie können jedoch nicht den Status eines iterator ohne es zu ändern.

Einstellen des Farbzyklus

Kurz zur Seite: Sie können den Farb- / Eigenschaftszyklus auf verschiedene Arten einstellen (z. ax.set_color_cycle in Versionen <1.5 oder ax.set_prop_cycler in> = 1,5). Sieh dir die. An Beispiel hier für Version 1.5 oder höher, oder der vorheriger Stil hier.

Zugriff auf den zugrunde liegenden Iterator

Es gibt zwar keine öffentlich zugängliche Methode für den Zugriff auf das iterierbare Objekt, Sie können jedoch für ein bestimmtes Achsenobjekt darauf zugreifen (ax) durch das _get_lines Hilfsklasseninstanz. ax._get_lines ist eine Berührung, die verwirrenderweise genannt wird, aber es ist die hinter den Kulissen Maschinerie, die das erlaubt plot Befehl, um alle die ungeraden und verschiedenen Möglichkeiten zu verarbeiten, die plot kann angerufen werden. Unter anderem ist es der Überblick darüber, welche Farben automatisch zugewiesen werden. Genauso ist es ax._get_patches_for_fill Steuern des Durchlaufs durch Standardfüllfarben und Patch-Eigenschaften.

Auf jeden Fall ist der Farbzyklus iterierbar ax._get_lines.color_cycle für Linien und ax._get_patches_for_fill.color_cycle für Patches. Auf matplotlib> = 1.5 hat sich dies geändert in benutze die cycler Bibliothekund das iterable wird aufgerufen prop_cycler Anstatt von color_cycle und ergibt a dict von Eigenschaften anstelle von nur einer Farbe.

Alles in allem würden Sie etwas tun wie:

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

Sie können den Status eines nicht anzeigen iterator

Dieses Objekt ist jedoch ein "blankes" iterator. Wir können leicht den nächsten Gegenstand (z.B. next_color = next(color_cycle), aber das bedeutet, dass die nächste Farbe danach wird was geplottet werden. Es ist nicht möglich, den aktuellen Status eines Iterators zu erhalten, ohne ihn zu ändern.

Im v1.5 oder größer wäre es schön, das zu bekommen cycler Objekt, das verwendet wird, wie wir seinen aktuellen Zustand ableiten könnten. Aber die cycler Das Objekt selbst ist nirgendwo (öffentlich oder privat) zugänglich. Stattdessen nur die itertools.cycle Instanz erstellt von der cycler Objekt ist zugänglich. In jedem Fall gibt es keine Möglichkeit, zum zugrunde liegenden Status des Farb- / Eigenschaftscyclers zu gelangen.

Passen Sie stattdessen die Farbe des zuvor gezeichneten Elements an

In Ihrem Fall klingt es so, als wollten Sie die Farbe von etwas, das gerade gezeichnet wurde, anpassen. Anstatt zu versuchen, die Farbe / Eigenschaft zu bestimmen, legen Sie die Farbe / etc Ihres neuen Elements basierend auf den Eigenschaften dessen, was geplottet wird, fest.

Zum Beispiel würde ich in dem Fall, den du beschrieben hast, so etwas tun:

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

Es ist nicht der einzige Weg, aber es ist sauberer, als in diesem Fall vorher die Farbe der gezeichneten Linie zu bekommen.


86
2017-12-12 02:24



Hinweis: In den neuesten Versionen von Matplotlib (> = 1.5) _get_lines hat sich verändert. Sie müssen jetzt verwenden next(ax._get_lines.prop_cycler)['color'] in Python 2 oder 3 (oder ax._get_lines.prop_cycler.next()['color'] im Python 2) um die nächste Farbe aus dem Farbzyklus zu erhalten.

Wo immer möglich, verwenden Sie den direkteren Ansatz, der im unteren Teil von @ Joe-Kingtons Antwort gezeigt wird. Wie _get_lines ist nicht API-zugewandt, könnte es sich in Zukunft nicht wieder rückwärtskompatibel ändern.


13
2017-11-09 11:56



Hier ist ein Weg, der in 1.5 funktioniert, der hoffentlich zukunftssicher sein wird, da er nicht auf Methoden basiert, die mit Unterstrichen versehen sind:

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

Dadurch erhalten Sie eine Liste der Farben, die für den aktuellen Stil definiert wurden.


9
2017-07-23 06:47



Sicher, das wird es tun.

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

Gibt:

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

Hier ist die Itirtools-Funktion, die Matplotlib verwendet itertools.cycle

Edit: Danke für den Kommentar, es scheint, dass es nicht möglich ist, einen Iterator zu kopieren. Eine Idee wäre, einen vollen Zyklus abzulegen und zu verfolgen, welchen Wert Sie verwenden, lassen Sie mich darauf zurückkommen.

Edit2: Alles klar, das wird Ihnen die nächste Farbe geben und einen neuen Iterator erstellen, der sich so verhält, als ob next nicht aufgerufen würde. Dies bewahrt nicht die Reihenfolge der Färbung, nur der nächste Farbwert, das überlasse ich Ihnen.

Dies ergibt die folgende Ausgabe. Beachten Sie, dass die Steilheit in der Grafik dem Index entspricht, z. B. das erste g ist das Bottomest-Diagramm und so weiter.

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

Konsole

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



Ich möchte nur hinzufügen, was @Andi oben gesagt hat. Schon seit color_cycle ist in Matplotlib 1.5 veraltet, müssen Sie verwenden prop_cyclerjedoch Andi Lösung (ax._get_lines.prop_cycler.next()['color']) hat diesen Fehler für mich zurückgegeben:

AttributeError: 'itertools.cycle' Objekt hat kein Attribut 'next'

Der Code, der für mich funktionierte, war: next(ax._get_lines.prop_cycler), was tatsächlich nicht weit von @ joe-kingtons ursprünglicher Antwort entfernt ist.

Persönlich stieß ich auf dieses Problem, wenn ich eine Twinx () Achse machte, die den Farbcycler zurücksetzte. Ich brauchte einen Weg, um die Farben richtig zu machen, weil ich sie benutzte style.use('ggplot'). Es könnte einen einfacheren / besseren Weg geben, um dies zu tun, also fühlen Sie sich frei, mich zu korrigieren.


3
2017-12-15 17:20



Da Matplotlib verwendet itertools.cycle Wir können tatsächlich den gesamten Farbzyklus durchsehen und dann den vorherigen Zustand des Iterators wiederherstellen:

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

Dies sollte die Liste zurückgeben, ohne den Status des Iterators zu ändern.

Benutze es mit Matplotlib> = 1.5:

>>> list_from_cycle(ax._get_lines.prop_cycler)
[{'color': 'r'}, {'color': 'g'}, {'color': 'b'}]

oder mit Matplotlib <1.5:

>>> list_from_cycle(ax._get_lines.color_cycle)
['r', 'g', 'b']

1
2017-09-22 08:38