在 Matplotlib 中重置颜色循环
Posted
技术标签:
【中文标题】在 Matplotlib 中重置颜色循环【英文标题】:Reset color cycle in Matplotlib 【发布时间】:2022-01-22 22:12:54 【问题描述】:假设我有关于 3 种交易策略的数据,每种都有交易成本和没有交易成本。我想在相同的轴上绘制 6 个变体中每一个的时间序列(3 个策略 * 2 个交易成本)。我希望用alpha=1
和linewidth=1
绘制“有交易成本”线,而我希望用alpha=0.25
和linewidth=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.subplots
、fig.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 masking - 根据当前颜色值重置像素的 zorder?