在matplotlib中绘制时循环颜色:基于每个实例的跟踪状态

时间:2014-06-27 19:58:12

标签: python matplotlib closures

我试图在Matplotlib中为Axes实例构建一个简单的状态跟踪函数。每次我创建一个新的轴对象(直接或通过其他函数,如subplots()),我希望实例有一个绑定方法,a.next_color(),我可以用来循环颜色,因为我创建要添加到轴的新行。我写了这样的话:

def set_color_sequence(colors = ['r', 'g', 'b', 'c', 'm', 'y']):
    i = [0]
    def cf(self):
        i[0] += 1
        return colors[(i[0]-1) % len(colors)]
    return cf

认为我把它添加到父类是聪明的:

plt.Axes.next_color = set_color_sequence()

问题是状态变量i似乎由所有 Axes实例共享,而不是每个新实例都有自己的实例州。确保所有新实例都有自己的状态跟踪功能的优雅方法是什么? (顺便说一句,我想在不修改原始matplotlib代码的情况下这样做。)

3 个答案:

答案 0 :(得分:0)

如果您为next_color属性分配了{{1>}属性,那么您现有的功能 <{1}}的实例而不是类本身。

首先,有了 Axes你基本上是在实施一个 generator以迂回的方式。至 简化事情,我们可以在一行中实现同样的事情 set_color_sequence

itertools.cycle

实际上,这是matplotlib跟踪颜色所在位置的方式 周期。例如,如果你看一个实例 from itertools import cycle ... axes_instance.next_color = cycle(['r', 'g', 'b', 'c', 'm', 'y']).next 您会看到它具有该属性 matplotlib.axes._subplots.AxesSubplot_get_lines.color_cycle(试着打电话 itertools.cycle)。

现在看看这两个例子:

color_cycle.next()

在第一种情况下,发生的是分配

class MyClass1(object):

    # next_color is an attribute of the *class itself*
    next_color = cycle(['r', 'g', 'b', 'c', 'm', 'y']).next


class MyClass2(object):

    def __init__(self):

        # next_color is an attribute of *this instance* of the class
        self.next_color = cycle(['r', 'g', 'b', 'c', 'm', 'y']).next
首次导入时,

只进行一次评估。这意味着 每当您创建next_color = cycle(['r', 'g', 'b', 'c', 'm', 'y]).next 的新实例时,其MyClass1 属性将指向完全相同的next_color实例,并且 因此itertools.cycle的所有实例都将共享一个共同状态:

MyClass1

但是,每当有新的时候,a = MyClass1() b = MyClass1() print a.next_color is b.next_color # True print a.next_color(), a.next_color(), b.next_color() # r g b 方法会一次又一次地被调用 正在创建类的实例。因此,__init__的每个实例都会获得 它自己的MyClass2,因此它自己的状态:

itertools.cycle

如果你的目标是继承a = MyClass2() b = MyClass2() print a.next_color is b.next_color # False print a.next_color(), a.next_color(), b.next_color() # r g r ,你需要完成作业 它将被子类的每个新实例调用的地方 (可能在plt.Axes)。但是,如果您只想添加此内容 现有实例的方法,然后您需要做的就是:

__init__

答案 1 :(得分:0)

AMacK的评论让我得到了一个简单的解决方案:

def next_color(self):
    return next(self._get_lines.color_cycle)
plt.Axes.next_color = next_color

这并没有我在上面概述的自定义颜色序列行为,但它确实获得了我在代码中寻找的实例行为,这些行为简洁明了。 (如果我想生成自定义序列,我可以直接覆盖color_cycle迭代器。)

答案 2 :(得分:0)

ax1已经有类似的东西。给定AxesSubplot Axes的实例(或任何派生_get_lines的实例),您可以使用prop_cycler访问行属性。这包括ax2._get_lines.prop_cycler = ax1._get_lines.prop_cycler ,可以使用以下方法覆盖:

# Generate some data
x = linspace(-10, 10)
y_lin = 2*x
y_quad = x**2

# Create empty axes
_, ax1 = subplots()

# Plot linear
l1 = ax1.plot(y_lin)
ax1.set_ylabel('linear', color=l1[0].get_color())
ax1.tick_params('y', colors=l1[0].get_color())

# Generate secondary y-axis
ax2 = ax1.twinx()
# Make sure that plots on ax2 continue color cycle
ax2._get_lines.prop_cycler = ax1._get_lines.prop_cycler

# Plot quadratic
l2 = ax2.plot(y_quad)
ax2.set_ylabel('quadratic', color=l2[0].get_color())
ax2.tick_params('y', colors=l2[0].get_color())

例如:

ax2._get_lines.prop_cycler = ax1._get_lines.prop_cycler

哪个输出:

enter image description here

离开%pylab inline让我们:

Matplotlib output after copying <code>prop_cycler</code> state

注意:如果您使用matplotlib.pyplot魔法在jupyter(或nteract)中工作(因此您将无法直接访问plt.Axes模块)。所以像bluebird这样的东西有点麻烦。