在 Matplotlib 中重置颜色循环

Posted

技术标签:

【中文标题】在 Matplotlib 中重置颜色循环【英文标题】:Reset color cycle in Matplotlib 【发布时间】:2022-01-22 22:12:54 【问题描述】:

假设我有关于 3 种交易策略的数据,每种都有交易成本和没有交易成本。我想在相同的轴上绘制 6 个变体中每一个的时间序列(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()

在指示的行上放置什么合适的代码来重置颜色循环,以便在调用第二个循环时“回到开始”?

【问题讨论】:

【参考方案1】:

您可以使用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 包的一项非常重要的技能是能够遵循实际的实现/代码。它真的没有那么复杂 - 我想第一次做这件事会让人望而生畏...... 自 Matplotlib 1.5.0 起,set_color_cycle 已被弃用,不再接受 None!幸运的是,新的(更广泛的)替代 set_prop_cycle 接受 None 仍然... 将 set_prop_cycle 设置为 None 也会去掉标签。有没有办法只重置颜色? plt.gca().set_prop_cycle(color=None) 似乎不起作用。【参考方案2】:

由于@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 在评论中指出这一点。

【讨论】:

与这个答案中所说的相反plt.gca().set_prop_cycle(None) 适用于 Matplotlib 1.5(正如 @burnpanck 所指出的那样),我刚刚验证它也适用于 Matplotlib 2.0。 "deprecated" 意味着它可能会在将来被删除,即使它在当前版本中工作。见matplotlib.org/devdocs/api/_as_gen/… 我的评论不是关于弃用,而是关于不需要在set_prop_cycle 中指定循环器这一事实。 对不起。你完全正确。我误读了你的评论。我已经编辑了我的答案【参考方案3】:

既然你提到你正在使用 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)

【讨论】:

感谢您花时间写下这个答案。我知道这会起作用,因为我先验地知道我将使用上面的每个 ax.plot 命令绘制 3 个系列。但是您知道是否有一种通用方法可以在代码中的给定点“重置”颜色循环?如果不知道在代码中发出命令时颜色循环是什么(或其状态是什么)? 这是可能的,但我不会真正推荐它。请参阅编辑以回答。 我还要指出,您应该始终能够从数据中推断出您需要多少种颜色。 这很有帮助(我会接受答案)。虽然您是对的,我可以从上下文中推断出行数,但我希望使代码更具可读性。如果真的有一个reset_color_cycle 命令,我认为事情会很自然地阅读。实际上,您上面的 1 行“hack”并没有让我太困扰。为什么不建议在生产中使用它? 一般来说,您希望避免使用内部特性(按照惯例,这些特性是名称以单个下划线开头的方法或属性)。这通常表明 API 可能会在没有警告的情况下更改。这是一个特别值得关注的问题,因为我知道 matplotlib 开发人员正在讨论更改颜色循环的实现方式,因此这个 hack 可能不适用于未来版本的 matplotlib。【参考方案4】:

只需选择您的颜色并将它们分配给一个列表,然后当您绘制数据时迭代包含您的列和您希望的颜色的 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 来构建颜色列表。 有点回避这个问题。就我而言,我正在使用seaborn,一般来说,可能有一个复杂的调色板默认设置。我不想搞砸了。我只想用每次使用相同的颜色循环绘制两次......而无需提前知道该颜色循环是什么。 好吧,很公平:) 它并没有真正回避这个问题,因为它是对你所说的问题的完全有效和简单的答案,但如果你使用的是 seaborn,那么我可以看到你会如何不想通过手动选择来弄乱颜色。在这种情况下,我会按照@M4rtini 的建议进行操作,并使用get_color 从第一次绘图迭代中获取颜色并在第二次使用它们,可能他们可能想把它写下来作为你的答案。 不知何故我无法编辑你的答案,但你能在colors = ['red', 'blue', 'green'] 中插入一个逗号吗?【参考方案5】:

您可以像这样从 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”。

【讨论】:

【参考方案6】:

除了已经很出色的答案之外,您还可以考虑使用颜色图:

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)

【讨论】:

以上是关于在 Matplotlib 中重置颜色循环的主要内容,如果未能解决你的问题,请参考以下文章

使用 matplotlib 创建自己的颜色图并绘制颜色比例

在 matplotlib 用户界面中更新颜色条而不重置缩放历史记录

Matplotlib masking - 根据当前颜色值重置像素的 zorder?

如何使用 matplotlib 为所有子图设置默认颜色循环?

从matplotlib colorbar中挑选情节颜色?

hist 和 axvline 不遵循颜色循环/matplotlib 样式