Можно ли запросить текущее состояние цветового цикла matplotlib? Другими словами, существует ли функция get_cycle_state
, которая будет вести себя следующим образом?
>>> plot(x1, y1)
>>> plot(x2, y2)
>>> state = get_cycle_state()
>>> print state
2
Где я ожидаю, что состояние будет индексом следующего цвета, который будет использоваться в сюжете. В качестве альтернативы, если он вернул следующий цвет ( "r" для цикла по умолчанию в примере выше), это тоже будет хорошо.
Ответ 1
Доступ к итератору цикла цвета
Там нет "ориентированного на пользователя" (иначе называемого "общедоступного") метода для доступа к базовому итератору, но вы можете получить к нему доступ через "частные" (по соглашению) методы. Однако вы не сможете получить состояние iterator
без его изменения.
Установка цветового цикла
Быстрая сторона: вы можете установить цвет/цикл свойств различными способами (например, ax.set_color_cycle
в версиях <1.5 или ax.set_prop_cycler
в> = 1.5). Взгляните на пример здесь для версии 1.5 или выше или предыдущий стиль здесь.
Доступ к базовому итератору
Однако, несмотря на отсутствие открытого доступа к этому процессу, вы можете получить доступ к нему для данного объекта осей (ax
) через _get_lines
вспомогательного класса _get_lines
. ax._get_lines
- это прикосновение, смутно названное, но это закулисный механизм, который позволяет команде plot
обрабатывать все нечетные и разнообразные способы построения plot
. Среди прочего, это то, что отслеживает, какие цвета автоматически назначать. Аналогично, ax._get_patches_for_fill
управляет циклическим использованием цветов заливки по умолчанию и свойств патча.
Во всяком случае, цикл цикла iterable - ax._get_lines.color_cycle
для строк и ax._get_patches_for_fill.color_cycle
для патчей. На Matplotlib> = 1.5, это изменилось, чтобы использовать cycler
библиотеку, а итерация называют prop_cycler
вместо color_cycle
и дает dict
свойств вместо только цвета.
В общем, вы бы сделали что-то вроде:
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']
Вы не можете просмотреть состояние iterator
Однако этот объект является "голым" iterator
. Мы можем легко получить следующий элемент (например, next_color = next(color_cycle)
, но это означает, что следующий цвет после этого будет отображаться. По дизайну нет способа получить текущее состояние итератора без его изменения.
В v1.5
или выше было бы неплохо получить задействованный объект cycler
, поскольку мы могли бы вывести его текущее состояние. Однако сам объект cycler
недоступен (публично или конфиденциально) в любом месте. Вместо этого, только itertools.cycle
экземпляр, созданный из cycler
объекта доступен. В любом случае, нет никакого способа добраться до основного состояния цветового/свойства cycler.
Совместите цвет ранее нарисованного объекта вместо
В вашем случае это звучит так, будто вы хотите соответствовать цвету того, что было просто построено. Вместо того, чтобы определять, какой будет цвет/свойство, установите цвет /etc вашего нового элемента на основе свойств того, что нанесено на график.
Например, в случае, описанном вами, я бы сделал что-то вроде этого:
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]()
Это не единственный способ, но его чище, чем пытаться получить цвет начерченной строки до этого, в данном случае.
Ответ 2
Примечание. В последних версиях matplotlib ( >= 1.5) _get_lines
изменилось. Теперь вам нужно использовать next(ax._get_lines.prop_cycler)['color']
в Python 2 или 3 (или ax._get_lines.prop_cycler.next()['color']
в Python 2), чтобы получить следующий цвет из цикла цвета.
По возможности используйте более прямой подход, показанный в нижней части ответа @joe-kington. Поскольку _get_lines
не обращен к API, в будущем он может измениться несовместимым образом.
Ответ 3
Вот способ, который работает в 1.5, который, мы надеемся, будет перспективным, поскольку он не полагается на методы, добавленные с подчеркиваниями:
colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
Это даст вам список цветов, определенных для текущего стиля.
Ответ 4
Конечно, это будет сделано.
#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,
дает:
<itertools.cycle object at 0x034CB288>
r c m y k b g r c m
Вот функция itertools, в которой matplotlib использует itertools.cycle
Изменить: спасибо за комментарий, кажется, что копировать итератор невозможно. Идея состояла бы в том, чтобы сбросить полный цикл и отслеживать, какое значение вы используете, позвольте мне вернуться к этому.
Edit2: Хорошо, это даст вам следующий цвет и создаст новый итератор, который будет вести себя так, как будто следующий не был вызван. Это не сохраняет порядок окраски, просто следующее значение цвета, я оставляю это для вас.
Это дает следующий вывод: обратите внимание, что крутизна графика соответствует индексу, например, первый g - самый нижний граф и т.д.
#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()
Консоль
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]()
Ответ 5
Я просто хочу добавить, что сказал @Andi выше. Поскольку color_cycle
устарел в matplotlib 1.5, вы должны использовать prop_cycler
, однако решение Andi (ax._get_lines.prop_cycler.next()['color']
) вернуло мне эту ошибку:
AttributeError: объект 'itertools.cycle' не имеет атрибута 'next'
Код, который работал у меня, был: next(ax._get_lines.prop_cycler)
, который на самом деле не за горами от исходного ответа @joe-kington.
Лично я столкнулся с этой проблемой при создании оси doublex(), которая reset цветовой цикл. Мне нужно было правильно настроить цвет, потому что я использовал style.use('ggplot')
. Может быть, есть более простой/лучший способ сделать это, поэтому не стесняйтесь исправлять меня.
Ответ 6
Так как matplotlib использует itertools.cycle
, мы можем реально просмотреть весь цикл цвета, а затем восстановить итератор в его предыдущее состояние:
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
Это должно возвращать список без изменения состояния итератора.
Используйте его с matplotlib >= 1.5:
>>> list_from_cycle(ax._get_lines.prop_cycler)
[{'color': 'r'}, {'color': 'g'}, {'color': 'b'}]
или с matplotlib < 1.5:
>>> list_from_cycle(ax._get_lines.color_cycle)
['r', 'g', 'b']
Ответ 7
В Matplotlib версии 2.2.3 есть get_next_color()
метод на _get_lines
собственности:
import from matplotlib import pyplot as plt
fig, ax = plt.subplots()
next_color = ax._get_lines.get_next_color()
get_next_color()
возвращает HTML-строку цвета и продвигает итератор цветового цикла.