在Matplotlib中重置颜色循环

83

假设我有三种交易策略的数据,每种策略都有有无交易成本两个变体。我想要在同一坐标轴上绘制每个变体的时间序列(3种策略*2个交易成本)。我希望“有交易成本”线条的 alpha=1linewidth=1 ,而“没有交易成本”则用alpha=0.25linewidth=5 来绘制。但是,我希望每种策略的两个版本颜色相同。

我想要的结果大致如下:

fig, ax = plt.subplots(1, 1, figsize=(10, 10))

for c in with_transaction_frame.columns:
    ax.plot(with_transaction_frame[c], label=c, alpha=1, linewidth=1)

****SOME MAGIC GOES HERE TO RESET THE COLOR CYCLE

for c in no_transaction_frame.columns:
    ax.plot(no_transaction_frame[c], label=c, alpha=0.25, linewidth=5)

ax.legend()

当第二个循环被调用时,适当的代码放在指定行上以重置颜色循环,使其“回到起点”是什么?

6个回答

115
您可以使用Axes.set_color_cycle将颜色循环重置为原始设置。查看这段代码,其中有一个函数来执行实际操作:
def set_color_cycle(self, clist=None):
    if clist is None:
        clist = rcParams['axes.color_cycle']
    self.color_cycle = itertools.cycle(clist

并在Axes上使用它的方法:

def set_color_cycle(self, clist):
    """
    Set the color cycle for any future plot commands on this Axes.

    *clist* is a list of mpl color specifiers.
    """
    self._get_lines.set_color_cycle(clist)
    self._get_patches_for_fill.set_color_cycle(clist)

这基本上意味着你可以使用None作为唯一参数调用set_color_cycle函数,并且它将被替换为在rcParams['axes.color_cycle']中找到的默认循环。

我使用以下代码尝试了这个方法并得到了预期的结果:

import matplotlib.pyplot as plt
import numpy as np

for i in range(3):
    plt.plot(np.arange(10) + i)

# for Matplotlib version < 1.5
plt.gca().set_color_cycle(None)
# for Matplotlib version >= 1.5
plt.gca().set_prop_cycle(None)

for i in range(3):
    plt.plot(np.arange(10, 1, -1) + i)

plt.show()

显示颜色循环重置功能的代码输出


感谢@8one6。当你知道如何使用时,Matplotlib是非常强大的 - 我认为真正的问题在于它的功能并没有很好地记录下来,因此我认为掌握开源Python包的一个非常重要的技能就是能够跟随实际的实现/代码。它真的不是那么复杂 - 我想第一次做可能只是有点令人生畏... - pelson
20
自 Matplotlib 1.5.0 起,set_color_cycle 已被弃用,并且不再接受 None!幸运的是,新的(更广泛的)替代品 set_prop_cycle 仍然接受 None... - burnpanck
3
将set_prop_cycle设置为None也会删除标签。有没有一种方法只重置颜色?plt.gca().set_prop_cycle(color=None)似乎不起作用。 - Chogg

39

由于 @pelson 给出的答案使用了 set_color_cycle,而这在 Matplotlib 1.5 中已经被弃用,所以我认为使用 set_prop_cycle 的更新版本会很有用:

import matplotlib.pyplot as plt
import numpy as np

for i in range(3):
    plt.plot(np.arange(10) + i)

plt.gca().set_prop_cycle(None)

for i in range(3):
    plt.plot(np.arange(10, 0, -1) + i)

plt.show()

需要注意的是,我不得不将np.arange(10,1,-1)更改为np.arange(10,0,-1)。前者只生成了9个元素的数组。这可能是由于使用不同版本的Numpy导致的。我的版本是1.10.2。

编辑:去除了使用rcParams的需求。感谢@divenex在评论中指出这一点。


2
与此答案所述相反,plt.gca().set_prop_cycle(None) 在 Matplotlib 1.5 中就可以使用(正如 @burnpanck 所指出的),我刚刚验证了它在 Matplotlib 2.0 中也可以使用。 - divenex
1
"Deprecated" 意味着它可能会在未来被移除,即使它在当前版本中仍然有效。请参阅 http://matplotlib.org/devdocs/api/_as_gen/matplotlib.axes.Axes.set_color_cycle.html。 - Ramon Crehuet
1
我的评论不是关于弃用的问题,而是关于在set_prop_cycle中没有必要指定循环器的事实。 - divenex
抱歉,你是完全正确的。我误读了你的评论。我已经编辑了我的答案。 - Ramon Crehuet

5

既然您提到正在使用seaborn,我建议您这样做:

with sns.color_palette(n_colors=3):

    ax.plot(...)
    ax.plot(...)

这将设置颜色调色板以使用当前活动的颜色循环,但仅使用前三种颜色。它还是一个通用解决方案,适用于任何需要设置临时颜色循环的情况。
请注意,实际上唯一需要在with块下面的是您正在执行的任何操作以创建Axes对象(即plt.subplotsfig.add_subplot()等)。这只是因为matplotlib颜色循环本身的工作方式。
要实现您想要的“重置”颜色循环是可能的,但这是一种hack方法,我不会在任何生产代码中使用它。以下是如何实现它的方法:
f, ax = plt.subplots()
ax.plot(np.random.randn(10, 3))
ax._get_lines.color_cycle = itertools.cycle(sns.color_palette())
ax.plot(np.random.randn(10, 3), lw=5, alpha=.25)

enter image description here


感谢您抽出时间编写这个答案。我明白这将起作用,因为我预先知道我将使用每个上面的ax.plot命令绘制3个系列。但是您是否知道是否有一种通用方法在代码中的特定位置“重置”颜色循环?而不需要在发出该命令的代码点上具体了解颜色循环是什么(或其状态如何)? - 8one6
1
可以做到,但这是一个我不太建议的hack。请参见答案的编辑。 - mwaskom
1
我还要指出的是,您应该始终能够从数据中推断出需要多少种颜色。 - mwaskom
这非常有帮助(我会接受这个答案)。虽然你说的没错,我可以从上下文中推断出行数,但我希望保持代码更易读。如果真的有一个“reset_color_cycle”命令,我认为事情会读起来很自然。实际上,你上面的一行“hack”对我来说并不太烦人。为什么你不建议在生产中使用它? - 8one6
通常情况下,您应避免使用内部功能(按照惯例,这些方法或属性的名称以单个下划线开头)。这通常表示API可能会在没有警告的情况下更改。这在这里特别是一个问题,因为我知道matplotlib开发人员正在讨论如何实现颜色循环的更改,因此这种hack可能在未来的matplotlib版本中无法正常工作。 - mwaskom
感谢您的精彩评论。您认为这是Matplotlib一个值得实现的功能请求吗? - 8one6

3

只需选择您的颜色并将它们分配到列表中,然后在绘制数据时迭代访问一个包含您的列和所需颜色的zip对象。

colors = ['red', 'blue', 'green']

for col, color in zip(colors, with_transaction_frame.columns):
    ax.plot(with_transaction_frame[col], label=col, alpha=1.0, linewidth=1.0, color=color)

for col, color in zip(no_transaction_frame.columns):
    ax.plot(no_transaction_frame[col], label=col, alpha=0.25, linewidth=5, color=color)

zip 函数可以将多个列表中的元素依次以元组的形式聚合成一个新的列表,方便你同时对多个列表进行迭代操作。


你可以通过在第一个循环中调用 ax.plot 的返回值上的 get_color 来构建颜色列表。 - M4rtini
有点绕弯子。在我的情况下,我正在使用 seaborn ,通常可能会有一个复杂的默认调色板。我不想改变它。我只想用相同的颜色循环两次绘图......而无需预先知道该颜色循环是什么。 - 8one6
好的,没问题 :) 这并不是回避问题,因为这是一个完全有效且简单的回答,正如你所说的那样,但如果你在使用seaborn,则可以理解为什么不想通过手动选择颜色来搞乱它们。在这种情况下,我建议按照@M4rtini的建议使用 get_color 来获取第一次绘图迭代中的颜色,并在第二次使用它们,可能他们会为你写出这个答案。 - Ffisegydd
不知何故,我无法编辑您的答案,但是您能在 colors = ['red', 'blue', 'green'] 中插入一个逗号吗? - PhilipB

2
您可以通过以下方式从seaborn获取颜色:colors = sns.color_palette()。然后,Ffisegydd的回答将非常有效。您还可以使用模数/余数运算符(%)来获取要绘制的颜色:mycolor = colors[icolumn % len(colors)]。我经常自己使用这种方法。所以您可以这样做:for icol, column in enumerate(with_transaction_frame.columns): mycolor = colors[icol % len(colors)] ax.plot(with_transaction_frame[col], label=col, alpha=1.0, color=mycolor)。尽管如此,Ffisegydd的回答可能更加“pythonic”。

0

除了已经很好的答案,您还可以考虑使用颜色映射:

import matplotlib.pyplot as plt
import numpy as np

cmap = plt.cm.viridis

datarange = np.arange(4)

for d in datarange:
    # generate colour by feeding float between 0 and 1 to colormap
    color = cmap(d/np.max(datarange)) 
    plt.plot(np.arange(5)+d, c=color)

for d in datarange:
    # generate colour by feeding float between 0 and 1 to colormap
    color = cmap(d/np.max(datarange))
    plt.plot(-np.arange(5)+d, c=color)

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接