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 oczekuję, że stan będzie indeksem następnego koloru, który zostanie użyty na wykresie. Alternatywnie, jeśli zwróci następny kolor („r” jako domyślny cykl w powyższym przykładzie), to też będzie w porządku.
python
matplotlib
mwaskom
źródło
źródło
Odpowiedzi:
Dostęp do iteratora cyklu kolorów
Nie ma metody „widocznej dla użytkownika” (aka „publicznej”), aby uzyskać dostęp do bazowego iteratora, ale można uzyskać do niego dostęp za pomocą metod „prywatnych” (zgodnie z konwencją). Jednak nie można uzyskać stanu
iterator
bez zmiany.Ustawianie cyklu kolorów
Krótko na bok: cykl koloru / właściwości można ustawić na różne sposoby (np.
ax.set_color_cycle
W wersjach <1.5 lubax.set_prop_cycler
> = 1.5). Spójrz na przykład tutaj dla wersji 1.5 lub nowszej lub do poprzedniego stylu tutaj .Dostęp do bazowego iteratora
Jednakże, chociaż nie ma publicznej metody dostępu do iterowalnej metody, możesz uzyskać do niej dostęp dla danego obiektu axes (
ax
) za pośrednictwem_get_lines
instancji klasy pomocniczej.ax._get_lines
to dotyk myląco nazwany, ale to zakulisowa machina, która pozwalaplot
komendzie przetwarzać wszystkie dziwne i różnorodne sposoby, któreplot
można wywołać. Między innymi śledzi, jakie kolory mają być przypisane automatycznie. Podobnie możnaax._get_patches_for_fill
kontrolować przełączanie między domyślnymi kolorami wypełnienia i właściwościami łat.W każdym razie iterowalny cykl kolorów dotyczy
ax._get_lines.color_cycle
linii iax._get_patches_for_fill.color_cycle
łat. Na matplotlib> = 1.5 zmieniło się to, aby używaćcycler
biblioteki , a iterowalna jest wywoływanaprop_cycler
zamiastcolor_cycle
i dajedict
właściwości zamiast tylko koloru.Podsumowując, 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']
Nie możesz wyświetlić stanu pliku
iterator
Jednak przedmiot ten jest „goły”
iterator
. Możemy łatwo uzyskać następny element (np.next_color = next(color_cycle)
Ale to oznacza, że następny kolor po nim jest tym, co zostanie naniesione. Zgodnie z projektem nie ma możliwości uzyskania aktualnego stanu iteratora bez zmiany go.W
v1.5
lub większym, byłoby miło uzyskaćcycler
używany obiekt, ponieważ moglibyśmy wywnioskować jego aktualny stan. Jednakcycler
sam obiekt nie jest nigdzie dostępny (publicznie ani prywatnie). Zamiast tego dostępna jest tylkoitertools.cycle
instancja utworzona zcycler
obiektu. Tak czy inaczej, nie ma sposobu, aby dostać się do podstawowego stanu cyklera kolorów / właściwości.Zamiast tego dopasuj kolor wydrukowanego wcześniej przedmiotu
W twoim przypadku brzmi to tak, jakbyś chciał dopasować kolor do czegoś, co właśnie zostało wykreślone. Zamiast próbować określić, jaki będzie kolor / właściwość, ustaw kolor / itp. Nowego elementu na podstawie właściwości wykreślonego elementu.
Na przykład w przypadku, który 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()
To nie jedyny sposób, ale jest czystszy niż próba wcześniejszego uzyskania koloru kreślonej linii, w tym przypadku.
źródło
line, = ax.plot(x, y)
a następnie użyj,line.get_color()
aby uzyskać kolor narysowanej wcześniej linii.ax._get_lines.color_cycle
nie istnieje już w wersji 1.5?ax._get_lines.prop_cycler
którym można mieć konstrukcję podobną do tej,if 'color' in ax._get_lines._prop_keys:
po której następujenext(ax._get_lines.prop_cycler)['color']
odpowiednik tego, co jest sugerowanecolor_cycle
w odpowiedzi. Nie jestem pewien, czy można uzyskać iterowalne tylko kolory, musiałbym zbadać więcej.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śleniami:
colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
Spowoduje to wyświetlenie listy kolorów zdefiniowanych w kolejności dla obecnego stylu.
źródło
Uwaga: w najnowszych wersjach matplotlib (> = 1.5)
_get_lines
uległa zmianie. Teraz musisz użyćnext(ax._get_lines.prop_cycler)['color']
w Pythonie 2 lub 3 (lubax._get_lines.prop_cycler.next()['color']
w Pythonie 2 ), aby uzyskać następny kolor z cyklu kolorów.Tam, gdzie to możliwe, stosuj bardziej bezpośrednie podejście pokazane w dolnej części odpowiedzi @ joe-kington. Ponieważ
_get_lines
nie jest dostępny dla interfejsu API, może się zmienić w przyszłości w sposób niezgodny wstecz.źródło
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 używa matplotlib itertools.cycle
Edycja: Dzięki za komentarz, wygląda na to, że nie można skopiować iteratora. Pomysł polegałby na zrzuceniu pełnego cyklu i śledzeniu używanej wartości, wrócę 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 zachowuje kolejności kolorowania, tylko kolejną wartość koloru, zostawiam to Tobie.
Daje to następujący wynik, zauważ, że stromość na wykresie 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
źródło
Chcę tylko dodać to, co @Andi powiedział powyżej. Ponieważ
color_cycle
jest przestarzałe w matplotlib 1.5, musisz jednak użyćprop_cycler
rozwiązania Andi (ax._get_lines.prop_cycler.next()['color']
) zwróciło mi ten błąd:Kod, który działał dla mnie, to:
next(ax._get_lines.prop_cycler)
, to:, który właściwie nie jest daleko od oryginalnej odpowiedzi @ joe-kington.Osobiście napotkałem ten problem podczas tworzenia osi twinx (), która resetuje cykler kolorów. Potrzebowałem sposobu, aby poprawnie zmienić cykl kolorów, ponieważ używałem
style.use('ggplot')
. Może istnieć łatwiejszy / lepszy sposób, aby to zrobić, więc nie krępuj się mnie poprawić.źródło
next(ax._get_lines.prop_cycler)
to, że jest to możliwa alternatywa.next(ax._get_lines.prop_cycler)
faktycznie zmienia stan cyklera kolorów?prop_cycler
. Jeśli przeczytasz zaakceptowaną odpowiedź na to pytanie, zobaczysz, że nie ma możliwości uzyskania dostępu do stanuprop_cycler
bez zmiany jego stanu, ponieważ jest iterowalny.Ponieważ matplotlib używa
itertools.cycle
, możemy faktycznie 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
Powinno to 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 <1.5:
>>> list_from_cycle(ax._get_lines.color_cycle) ['r', 'g', 'b']
źródło
Najprostszym możliwym sposobem, jaki mogłem znaleźć bez wykonywania całej pętli przez cykler, jest
ax1.lines[-1].get_color()
.źródło
W matplotlib w wersji 2.2.3
get_next_color()
na_get_lines
właściwości istnieje metoda :import from matplotlib import pyplot as plt fig, ax = plt.subplots() next_color = ax._get_lines.get_next_color()
get_next_color()
zwraca ciąg koloru html i przesuwa iterator cyklu kolorów.źródło
Jak uzyskać dostęp do cyklu kolorów (i całego stylu)?
Bieżący stan jest przechowywany w
ax._get_lines.prop_cycler
. Nie ma wbudowanych metod ujawniania „listy bazowej” dla generycznegoitertools.cycle
, aw szczególności dlaax._get_lines.prop_cycler
(patrz poniżej).Opublikowałem tutaj kilka funkcji, aby uzyskać informacje na temat pliku
itertools.cycle
. Można by wtedy użyćstyle_cycle = ax._get_lines.prop_cycler curr_style = get_cycle_state(style_cycle) # <-- my (non-builtin) function curr_color = curr_style['color']
aby uzyskać aktualny kolor bez zmiany stanu cyklu .
TL; DR
Gdzie przechowywany jest cykl kolorów (i całego stylu)?
Cykl stylu jest przechowywany w dwóch różnych miejscach, jednym dla domyślnych , a drugim dla bieżących osi (przy założeniu
import matplotlib.pyplot as plt
iax
jest operatorem osi):default_prop_cycler = plt.rcParams['axes.prop_cycle'] current_prop_cycle = ax._get_lines.prop_cycler
Zauważ, że mają one różne klasy. Domyślnie jest to „ustawienie cyklu podstawowego” i nie ma informacji o żadnym bieżącym stanie dla żadnej osi, podczas gdy prąd wie o cyklu, który ma nastąpić i jego aktualnym stanie:
print('type(default_prop_cycler) =', type(default_prop_cycler)) print('type(current_prop_cycle) =', type(current_prop_cycle)) []: type(default_prop_cycler) = <class 'cycler.Cycler'> []: type(current_prop_cycle) = <class 'itertools.cycle'>
Domyślny cykl może mieć kilka klawiszy (właściwości) do przełączania i można uzyskać tylko kolory:
print('default_prop_cycler.keys =', default_prop_cycler.keys) default_prop_cycler2 = plt.rcParams['axes.prop_cycle'].by_key() print(default_prop_cycler2) print('colors =', default_prop_cycler2['color']) []: default_prop_cycler.keys = {'color', 'linestyle'} []: {'color': ['r', 'g', 'b', 'y'], 'linestyle': ['-', '--', ':', '-.']} []: colors = ['r', 'g', 'b', 'y']
Można nawet zmienić
cycler
użycie dla danegoaxes
, po zdefiniowaniu tego zacustom_prop_cycler
pomocąAle nie ma wbudowanych metod ujawniania „listy bazowej” dla generycznego
itertools.cycle
, aw szczególności dlaax._get_lines.prop_cycler
.źródło