获取matplotlib颜色循环状态


89

是否可以查询matplotlib颜色周期的当前状态?换句话说,有没有一种功能get_cycle_state可以按照以下方式运行?

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

我希望状态是绘图中将使用的下一个颜色的索引。或者,如果它返回下一个颜色(在上面的示例中,默认循环为“ r”),那也可以。


即使不更改当前状态也可以做到这一点,请参见下面的答案
sancho.s ReinstateMonicaCellio

Answers:


107

访问颜色循环迭代器

没有用于访问底层迭代器的“面向用户”(也称为“公共”)方法,但是您可以通过“专用”(按惯例)方法来访问它。但是,如果iterator不更改它,您将无法获得它的状态。

设置颜色周期

快速说明:您可以通过多种方式(例如ax.set_color_cycle,版本<1.5或版本ax.set_prop_cycler> = 1.5)设置颜色/属性周期。在此处查看1.5或更高版本示例,或此处先前样式

访问底层的迭代器

但是,虽然没有公开的方法可访问Iterable,但是您可以ax通过_get_lineshelper类实例为给定的axis对象()访问它。 ax._get_lines是一个易混淆的名称,但它是一种幕后机制,它使plot命令可以处理所有plot可以调用的奇特和变化的方式。除其他事项外,它还能跟踪自动分配的颜色。同样,ax._get_patches_for_fill可以通过默认的填充颜色和色块属性来控制循环。

无论如何,颜色循环可迭代ax._get_lines.color_cycle用于线条和ax._get_patches_for_fill.color_cycle色块。在matplotlib> = 1.5上,已更改为使用该cycler,并且调用了iterable prop_cyclercolor_cycle并产生adict的属性,而不仅仅是颜色。

总而言之,您将执行以下操作:

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.cyclecycler对象创建的实例。无论哪种方式,都无法获得颜色/属性循环器的基本状态。

匹配之前绘制的项目的颜色

在您的情况下,听起来您想匹配刚绘制的颜色。不必尝试确定颜色/属性是什么,而是根据绘制的属性设置新项目的颜色/等。

例如,在您描述的情况下,我将执行以下操作:

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

在此处输入图片说明

在这种情况下,这不是唯一的方法,但是比尝试预先获得绘制线条的颜色更清洁。


我正在编写一些更高级别的函数来自动化我工作领域中的常见绘图任务。通常,这些需要绘制多个matplotlib对象,并且我想让多个对象在颜色循环中共享一种颜色,因此,如果我感到懒惰,则不必总是提供颜色参数。
mwaskom

12
如果您只想匹配特定对象的颜色,则可以随时获取其颜色。例如line, = ax.plot(x, y),然后用于line.get_color()获取先前绘制的线条的颜色。
乔·肯顿

3
似乎ax._get_lines.color_cycle在1.5中不再存在?
endolith '16

6
我认为,(接近)等效项是可迭代的,我相信ax._get_lines.prop_cycler您可以进行类似的构造,if 'color' in ax._get_lines._prop_keys:然后next(ax._get_lines.prop_cycler)['color']执行color_cycle答案中建议的等效项。我不确定您是否能获得仅这些颜色的迭代,我需要探索更多。
J理查德·斯内普

1
@endolith当然(谢谢)-但我只是觉得坐在乔已经很好并且可以接受的答案中会更好。如果他不能在周一之前解决问题-我将在此处贴上正确的答案,而不是令人恐惧的“评论中的答案”
J Richard Snape

25

这是一种在1.5中有效的方法,由于它不依赖于下划线前缀的方法,因此有望在未来得到验证:

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

这将为您提供按照当前样式定义的颜色列表。


1
在2.0.2中工作。
KevinG

16

注意:在最新版本的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,因此将来可能会以不向后兼容的方式再次更改。


如何避免查询颜色循环仪的下一个元素会改变循环仪的状态?
kazemakase

6

当然可以。

#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

这是matplotlib使用itertools.cycle的itertools函数

编辑:感谢您的评论,似乎不可能复制迭代器。一个想法是放弃一个完整的周期并跟踪您正在使用的值,让我重新开始。

Edit2:好吧,这将为您提供下一种颜色,并创建一个新的迭代器,其行为就像未调用next一样。这不会保留着色顺序,仅保留下一个颜色值,我留给您。

这给出了以下输出,请注意图中的陡度与索引相对应,例如,第一个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

旋转颜色


谢谢!为了澄清起见,它看起来并不像返回一个副本,而是对实际循环的引用。因此,调用rainbow.next()实际上也将改变下一个绘图的外观。
mwaskom

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的原始响应并不遥远。

就个人而言,我在制作twinx()轴时遇到了这个问题,该轴重置了颜色循环仪。由于使用,我需要一种使颜色正确循环的方法style.use('ggplot')。可能有一种更简单/更好的方法来执行此操作,请随时纠正我。


请格式化您的帖子为答案,而不是讨论材料(否则为评论),您的主要观点是,这next(ax._get_lines.prop_cycler)是一种可能的选择。
UmNyobe 2015年

@Carson如何避免调用next(ax._get_lines.prop_cycler)实际上改变了彩色循环仪的状态?
kazemakase

这实际上是我正在尝试做的-更改的状态prop_cycler。如果您阅读了该问题的可接受答案,您将发现没有状态可以访问prop_cycler而不更改其状态,因为它是可迭代的。
卡森

您指出的错误是Python 2 / Python 3的区别。感谢您指出这一点,我相应地编辑了答案。
安迪

1

由于使用了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']

0

在没有执行整个循环过程的情况下,我可能找到的最简单方法是ax1.lines[-1].get_color()


-1

在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颜色字符串,并推进颜色循环迭代器。


-1

如何访问颜色(和完整样式)循环?

当前状态存储在中ax._get_lines.prop_cycler。没有内置方法可以公开泛型itertools.cycle,尤其是的“基本列表” ax._get_lines.prop_cycler(请参见下文)。

我在这里发布了一些函数来获取有关的信息itertools.cycle。然后可以使用

style_cycle = ax._get_lines.prop_cycler
curr_style = get_cycle_state(style_cycle)  # <-- my (non-builtin) function
curr_color = curr_style['color']

不更改循环状态的情况下获取当前颜色。


TL; DR

颜色(和完整样式)循环存储在哪里?

样式周期被存储在两个不同的地方,一个用于缺省,和一个用于电流轴(假设import matplotlib.pyplot as pltax是轴处理程序):

default_prop_cycler = plt.rcParams['axes.prop_cycle']
current_prop_cycle = ax._get_lines.prop_cycler

注意这些有不同的类。默认值为“基本循环设置”,它不知道任何轴的任何当前状态,而当前则知道要跟随的循环及其当前状态:

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

默认循环可能有多个要循环的键(属性),而一个只能获取颜色:

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

人们甚至可以改变cycler使用对于给定的axes,定义,经过custom_prop_cycler

ax.set_prop_cycle(custom_prop_cycler)

但是,没有内置方法可以公开泛型itertools.cycle(尤其是)的“基本列表” ax._get_lines.prop_cycler

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.