Подтвердить что ты не робот

Получить состояние цветового цикла matplotlib

Можно ли запросить текущее состояние цветового цикла matplotlib? Другими словами, существует ли функция get_cycle_state, которая будет вести себя следующим образом?

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

Где я ожидаю, что состояние будет индексом следующего цвета, который будет использоваться в сюжете. В качестве альтернативы, если он вернул следующий цвет ( "r" для цикла по умолчанию в примере выше), это тоже будет хорошо.

4b9b3361

Ответ 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-строку цвета и продвигает итератор цветового цикла.