Uzyskaj stan cyklu kolorów matplotlib

Czy można sprawdzić aktualny stan cyklu kolorów matplotlib? Innymi słowy, czy istnieje funkcja get_cycle_state, która będzie zachowywać się w następujący sposób?

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

Gdzie spodziewam się, że stan będzie indeksem następnego koloru, który zostanie użyty na wykresie. Alternatywnie, jeśli zwróci następny kolor ("r" dla domyślnego cyklu w powyższym przykładzie), to również będzie dobrze.

Author: mwaskom, 2012-12-12

6 answers

Dostęp do iteratora cyklu kolorów

Nie ma metody "user-facing" (znanej jako "public"), która umożliwiałaby dostęp do podstawowego iteratora, ale można uzyskać do niego dostęp za pomocą metod "private" (według konwencji). Jednak nie można uzyskać stanu iterator bez jego zmiany.

Ustawianie cyklu kolorów

Na bok: możesz ustawić cykl kolorów / właściwości na wiele sposobów (np. ax.set_color_cycle w wersjach ax.set_prop_cycler w >=1.5). Spójrz na przykład tutaj dla wersji 1.5 lub większy , lub poprzedni styl tutaj .

Dostęp do podstawowego iteratora

Jednakże, podczas gdy nie ma ogólnodostępnej metody dostępu do iterable, możesz uzyskać do niej dostęp dla danego obiektu axes (ax) poprzez instancję klasy pomocniczej _get_lines. ax._get_lines jest myląco nazwany, ale jest to maszyneria za kulisami, która pozwala plot na przetwarzanie wszystkich dziwnych i różnych sposobów, które można wywołać plot. Między innymi to, co utrzymuje śledź, jakie kolory mają być automatycznie przypisane. Podobnie, istnieje ax._get_patches_for_fill do kontrolowania przełączania domyślnych kolorów wypełnienia i właściwości łat.

W każdym razie powtarzalny cykl kolorów to ax._get_lines.color_cycle dla linii i ax._get_patches_for_fill.color_cycle dla łat. W matplotlib > = 1.5, to zmieniło się na użyj biblioteki cycler , a iterable nazywa się prop_cycler zamiast color_cycle i daje dict właściwości zamiast tylko koloru.

W sumie zrobiłbyś coś takiego:

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

Ty nie można wyświetlić stanu iterator

Jednak Obiekt ten jest "gołym" iterator. Możemy łatwo uzyskać następny element (np. next_color = next(color_cycle), ale to oznacza, że następny kolor po tym jest tym, co zostanie wykreślone. Z założenia nie ma możliwości uzyskania aktualnego stanu iteratora bez jego zmiany.

W v1.5 lub większym, byłoby miło uzyskać cycler obiekt, który jest używany, ponieważ możemy wywnioskować jego obecny stan. Jednak sam obiekt cycler nie jest dostępny (publicznie lub prywatnie) wszędzie. Zamiast tego dostępna jest tylko instancja itertools.cycle utworzona z obiektu cycler. Tak czy inaczej, nie ma sposobu, aby dostać się do stanu podstawowego kolektora kolorów / właściwości.

Dopasuj kolor poprzednio wykreślonego elementu

W Twoim przypadku, wygląda na to, że chcesz dopasować kolor czegoś, co właśnie zostało wykreślone. Zamiast próbować określić, jaki będzie kolor/właściwość, Ustaw kolor / etc nowego elementu na podstawie właściwości to, co jest wykreślone.

Na przykład, w przypadku, gdy opisałeś, zrobiłbym coś takiego:

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

Tutaj wpisz opis obrazka

To nie jedyny sposób, ale jest czystszy niż próba uzyskania koloru linii wykreślonej przed ręką, w tym przypadku.

 86
Author: Joe Kington,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-14 08:49:39

Uwaga: w najnowszych wersjach matplotlib (>= 1.5) _get_lines zmieniło się. Teraz musisz użyć next(ax._get_lines.prop_cycler)['color'] w Pythonie 2 lub 3 (lub ax._get_lines.prop_cycler.next()['color'] W Python 2 ), aby uzyskać następny kolor z cyklu kolorów.

W miarę możliwości użyj bardziej bezpośredniego podejścia pokazanego w dolnej części odpowiedzi @ joe-Kinga. Ponieważ _get_lines nie jest związane z API, może się zmienić w przyszłości w sposób niezgodny wstecz.

 13
Author: Andi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:32

Oto sposób, który działa w 1.5, który miejmy nadzieję będzie przyszłościowy, ponieważ nie opiera się na metodach poprzedzonych podkreślnikami:

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

To da ci listę kolorów zdefiniowanych w kolejności dla obecnego stylu.

 9
Author: Mike DePalatis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-23 06:47:35

Jasne, to wystarczy.

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

Daje:

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

Oto funkcja itertools, której matplotlib używa itertools.cykl

Edit: dzięki za komentarz, wygląda na to, że nie można skopiować iteratora. Pomysłem byłoby zrzucenie pełnego cyklu i śledzenie, jakiej wartości używasz, pozwól mi wrócić do tego.

Edit2: w porządku, to da ci następny kolor i stworzy nowy iterator, który zachowuje się tak, jakby next nie został wywołany. To nie zachowaj kolejność kolorowania, tylko następną wartość koloru, zostawiam to tobie.

Daje to następujące wyjście, zauważ, że stromość wykresu odpowiada indeksowi, np. pierwsze g jest najniższym wykresem i tak dalej.

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

Konsola

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

Obróć kolor

 6
Author: arynaq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-12 03:51:55

Chcę tylko dodać to, co @ Andi powiedział powyżej. Ponieważ color_cycle jest przestarzały w matplotlib 1.5, musisz użyć prop_cycler, jednak rozwiązanie Andi (ax._get_lines.prop_cycler.next()['color']) zwróciło mi ten błąd:

AttributeError: 'itertools.cykl 'obiekt nie posiada atrybutu 'next'

Kod, który działał dla mnie to: next(ax._get_lines.prop_cycler), co w rzeczywistości nie jest dalekie od oryginalnej odpowiedzi @ joe-kington.

Osobiście natknąłem się na ten problem podczas tworzenia osi twinx (), która resetuje kolor cycler. Potrzebowałem sposobu na poprawny cykl kolorów, ponieważ używałem style.use('ggplot'). Może być łatwiejszy / lepszy sposób, aby to zrobić, więc nie krępuj się mnie poprawić.

 3
Author: Carson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-15 17:20:45

Ponieważ matplotlib używa itertools.cycle, możemy przejrzeć cały cykl kolorów, a następnie przywrócić iterator do poprzedniego stanu:

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

To powinno zwrócić listę bez zmiany stanu iteratora.

Użyj go z matplotlib >= 1.5:

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

Lub z matplotlib

>>> list_from_cycle(ax._get_lines.color_cycle)
['r', 'g', 'b']
 1
Author: freidrichen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-22 08:38:25