É possível consultar o estado atual do ciclo de cores matplotlib? Em outras palavras, existe uma função get_cycle_state
que se comportará da seguinte maneira?
>>> plot(x1, y1)
>>> plot(x2, y2)
>>> state = get_cycle_state()
>>> print state
2
Onde eu espero que o estado seja o índice da próxima cor que será usada em um gráfico. Alternativamente, se ele retornasse a próxima cor ("r" para o ciclo padrão no exemplo acima), também estaria bem.
python
matplotlib
Mwaskom
fonte
fonte
Respostas:
Acessando o iterador do ciclo de cores
Não há método "voltado para o usuário" (também conhecido como "público") para acessar o iterador subjacente, mas você pode acessá-lo por meio de métodos "privados" (por convenção). No entanto, você não pode obter o estado de um
iterator
sem alterá-lo.Configurando o ciclo de cores
À parte rápida: Você pode definir o ciclo de cores / propriedades de várias maneiras (por exemplo,
ax.set_color_cycle
nas versões <1.5 ouax.set_prop_cycler
> = 1.5). Dê uma olhada no exemplo aqui para a versão 1.5 ou superior , ou o estilo anterior aqui .Acessando o iterador subjacente
No entanto, embora não haja nenhum método voltado ao público para acessar o iterável, você pode acessá-lo para um determinado objeto de eixos (
ax
) por meio da_get_lines
instância da classe auxiliar.ax._get_lines
é um toque com um nome confuso, mas é a maquinaria dos bastidores que permite aoplot
comando processar todas as formas estranhas e variadas queplot
podem ser chamadas. Entre outras coisas, é o que controla quais cores atribuir automaticamente. Da mesma forma, éax._get_patches_for_fill
necessário controlar o ciclo pelas cores de preenchimento padrão e propriedades de patch.De qualquer forma, o ciclo de cores iterável é
ax._get_lines.color_cycle
para linhas eax._get_patches_for_fill.color_cycle
patches. Em matplotlib> = 1.5, isso mudou para usar acycler
biblioteca , e o iterável é chamado emprop_cycler
vez decolor_cycle
e produz umdict
de propriedades em vez de apenas uma cor.Resumindo, você faria 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']
Você não pode ver o estado de um
iterator
No entanto, este objeto é um "vazio"
iterator
. Podemos facilmente obter o próximo item (por exemplonext_color = next(color_cycle)
, mas isso significa que a próxima cor depois disso é a que será plotada. Por design, não há como obter o estado atual de um iterador sem alterá-lo.Em
v1.5
ou superior, seria bom pegar ocycler
objeto que está sendo usado, pois poderíamos inferir seu estado atual. No entanto, ocycler
próprio objeto não está acessível (pública ou privada) em qualquer lugar. Em vez disso, apenas aitertools.cycle
instância criada a partir docycler
objeto está acessível. De qualquer maneira, não há como chegar ao estado subjacente do ciclador de cor / propriedade.Em vez disso, combine a cor do item traçado anteriormente
No seu caso, parece que você deseja combinar a cor de algo que acabou de ser plotado. Em vez de tentar determinar qual será a cor / propriedade, defina a cor / etc do seu novo item com base nas propriedades do que está plotado.
Por exemplo, no caso que você descreveu, eu faria algo assim:
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()
Não é a única maneira, mas é mais limpa do que tentar obter a cor da linha traçada de antemão, neste caso.
fonte
line, = ax.plot(x, y)
e useline.get_color()
para obter a cor da linha traçada anteriormente.ax._get_lines.color_cycle
não existe mais no 1.5?ax._get_lines.prop_cycler
você pode ter uma construção algo comoif 'color' in ax._get_lines._prop_keys:
seguido pornext(ax._get_lines.prop_cycler)['color']
para fazer o equivalente do que é sugerido comcolor_cycle
a resposta, eu acredito. Não tenho certeza se você pode obter um iterável apenas das cores, preciso explorar mais.Esta é uma maneira que funciona no 1.5 e que será preparada para o futuro, pois não depende de métodos prefixados com sublinhados:
colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
Isso lhe dará uma lista das cores definidas para o estilo atual.
fonte
Nota: nas versões mais recentes de matplotlib (> = 1.5)
_get_lines
mudou. Agora você precisa usarnext(ax._get_lines.prop_cycler)['color']
no Python 2 ou 3 (ouax._get_lines.prop_cycler.next()['color']
no Python 2 ) para obter a próxima cor do ciclo de cores.Sempre que possível, use a abordagem mais direta mostrada na parte inferior da resposta de @joe-kington. Como
_get_lines
não é voltado para API, ele pode mudar novamente de uma maneira incompatível com versões anteriores no futuro.fonte
Claro, isso vai bastar.
#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,
Dá:
<itertools.cycle object at 0x034CB288> r c m y k b g r c m
Aqui está a função itertools que matplotlib usa itertools.cycle
Edit: Obrigado pelo comentário, parece que não é possível copiar um iterador. Uma ideia seria descartar um ciclo completo e controlar qual valor você está usando, deixe-me voltar a isso.
Edit2: Tudo bem, isso lhe dará a próxima cor e fará um novo iterador que se comporta como se o próximo não tivesse sido chamado. Isso não preserva a ordem de coloração, apenas o próximo valor de cor, deixo isso para você.
Isso dá a seguinte saída, observe que a inclinação no gráfico corresponde ao índice, por exemplo, primeiro g é o gráfico mais abaixo e assim por diante.
#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
fonte
Eu só quero acrescentar o que @Andi disse acima. Como
color_cycle
está obsoleto no matplotlib 1.5, você deve usarprop_cycler
, no entanto, a solução de Andi (ax._get_lines.prop_cycler.next()['color']
) retornou este erro para mim:O código que funcionou para mim foi
next(ax._get_lines.prop_cycler)
:, que na verdade não está muito longe da resposta original de @joe-kington.Pessoalmente, tive esse problema ao fazer um eixo twinx (), que zera o ciclador de cores. Eu precisava de uma maneira de fazer o ciclo de cores corretamente porque eu estava usando
style.use('ggplot')
. Pode haver uma maneira mais fácil / melhor de fazer isso, então sinta-se à vontade para me corrigir.fonte
next(ax._get_lines.prop_cycler)
é uma alternativa possível.next(ax._get_lines.prop_cycler)
realmente mude o estado do reciclador de cores?prop_cycler
. Se você leu a resposta aceita para essa pergunta, verá que não há como acessar o estado deprop_cycler
sem alterar seu estado, porque é iterável.Como o matplotlib usa
itertools.cycle
, podemos examinar todo o ciclo de cores e, em seguida, restaurar o iterador ao seu 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
Isso deve retornar a lista sem alterar o estado do iterador.
Use-o com matplotlib> = 1,5:
>>> list_from_cycle(ax._get_lines.prop_cycler) [{'color': 'r'}, {'color': 'g'}, {'color': 'b'}]
ou com matplotlib <1,5:
>>> list_from_cycle(ax._get_lines.color_cycle) ['r', 'g', 'b']
fonte
A maneira mais simples possível que eu poderia encontrar sem fazer todo o loop pelo ciclador é
ax1.lines[-1].get_color()
.fonte
No matplotlib versão 2.2.3, há um
get_next_color()
método na_get_lines
propriedade:import from matplotlib import pyplot as plt fig, ax = plt.subplots() next_color = ax._get_lines.get_next_color()
get_next_color()
retorna uma string de cor html e avança o iterador do ciclo de cores.fonte
Como acessar o ciclo de cores (e estilo completo)?
O estado atual é armazenado em
ax._get_lines.prop_cycler
. Não há métodos embutidos para expor a "lista base" de um genéricoitertools.cycle
e, em particular,ax._get_lines.prop_cycler
(veja abaixo).Eu postei aqui algumas funções para obter informações sobre a
itertools.cycle
. Pode-se então usarstyle_cycle = ax._get_lines.prop_cycler curr_style = get_cycle_state(style_cycle) # <-- my (non-builtin) function curr_color = curr_style['color']
para obter a cor atual sem alterar o estado do ciclo .
TL; DR
Onde o ciclo de cores (e estilo completo) é armazenado?
O ciclo de estilo é armazenado em dois lugares diferentes, um para o padrão e outro para os eixos atuais (assumindo
import matplotlib.pyplot as plt
eax
é um manipulador de eixo):default_prop_cycler = plt.rcParams['axes.prop_cycle'] current_prop_cycle = ax._get_lines.prop_cycler
Observe que eles têm classes diferentes. O padrão é uma "configuração de ciclo base" e não sabe sobre nenhum estado atual para nenhum eixo, enquanto a corrente sabe sobre o ciclo a seguir e seu estado atual:
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'>
O ciclo padrão pode ter várias chaves (propriedades) para fazer o ciclo, e pode-se obter apenas as cores:
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']
Pode-se até mudar o
cycler
a ser usado para um dadoaxes
, após defini-locustom_prop_cycler
, comMas não há métodos internos para expor a "lista de base" para um genérico
itertools.cycle
e, em particular, paraax._get_lines.prop_cycler
.fonte