【问题标题】:Matplotlib - add colorbar to a sequence of line plotsMatplotlib - 将颜色条添加到一系列线图
【发布时间】:2011-12-01 14:02:14
【问题描述】:

对于变量 z 的许多不同值,我有两个变量 (x,y) 的一系列线图。我通常会添加带有这样的图例的线图:

import matplotlib.pyplot as plt

fig = plt.figure()
ax  = fig.add_subplot(111)
# suppose mydata is a list of tuples containing (xs, ys, z) 
# where xs and ys are lists of x's and y's and z is a number. 
legns = []
for(xs,ys,z) in mydata:
   pl = ax.plot(xs,ys,color = (z,0,0))
   legns.append("z = %f"%(z))
ax.legends(legns) 
plt.show()

但我有太多图表,图例会覆盖图表。我宁愿有一个颜色条来指示与颜色对应的 z 值。我在画廊中找不到类似的东西,我所有的尝试都处理了彩条失败。显然,在尝试添加颜色条之前,我必须创建一组图。

有没有简单的方法来做到这一点?谢谢。

编辑(澄清):

我想做这样的事情:

import matplotlib.pyplot as plt
import matplotlib.cm     as cm

fig = plt.figure()
ax  = fig.add_subplot(111)
mycmap = cm.hot
# suppose mydata is a list of tuples containing (xs, ys, z) 
# where xs and ys are lists of x's and y's and z is a number between 0 and 1
plots = []
for(xs,ys,z) in mydata:
   pl = ax.plot(xs,ys,color = mycmap(z))
   plots.append(pl)
fig.colorbar(plots)
plt.show()

但是根据 Matplotlib 参考,这不起作用,因为绘图列表不是“可映射的”,无论这意味着什么。

我使用LineCollection 创建了一个替代绘图函数:

def myplot(ax,xs,ys,zs, cmap):
    plot = lc([zip(x,y) for (x,y) in zip(xs,ys)], cmap = cmap)
    plot.set_array(array(zs))
    x0,x1 = amin(xs),amax(xs)
    y0,y1 = amin(ys),amax(ys)
    ax.add_collection(plot)
    ax.set_xlim(x0,x1)
    ax.set_ylim(y0,y1)
    return plot

xsys 是 x 和 y 坐标列表的列表,zs 是为每行着色的不同条件的列表。不过感觉有点像一团糟……我想会有一种更简洁的方法来做到这一点。我喜欢plt.plot() 函数的灵活性。

【问题讨论】:

  • 您的问题有点令人困惑——您是要创建自己的 colormap 以对应您的 z 值颜色,还是只是创建一个可以附加到一系列线图以节省空间?颜色条的一种替代方法可能是在定义图例时使用 bbox_to_anchor 在图形边界之外添加包含图例。
  • 只是为了附加一个颜色条。我不介意使用其中一种内置颜色图。
  • 即使在图表之外,图例也不是一个可行的选项。我在这张图中有 50 多条线。此外,图例将比读者需要的信息多得多。它会使图表混乱。它们是不同温度值的模拟曲线。颜色条将在视觉上充分告知读者每条曲线的温标(热、冷和非常冷)。每条线的确切温度值并不那么重要。
  • 这样做的方法是通过一个行集合,就像你现在一样(尽管你可以稍微清理几个调用)。 plot 返回 Line2D 对象,它们固有地具有离散颜色,而不是可以显示在颜色条上的“可映射”颜色。话虽如此,您可以从颜色图中创建一个“假”标量可映射对象并为其显示颜色条,但这比仅使用 LineCollection 更有效。
  • 我知道这是超级旧的,但这似乎也很相关:stackoverflow.com/questions/26545897/…

标签: python matplotlib colorbar


【解决方案1】:

(我知道这是一个老问题,但是...)彩条需要matplotlib.cm.ScalarMappableplt.plot 产生不可标量映射的线条,因此,为了制作彩条,我们需要制作可映射的标量。

好的。所以ScalarMappable 的构造函数采用cmapnorm 实例。 (规范将数据缩放到 0-1 的范围,您已经使用过的 cmaps 并采用 0-1 之间的数字并返回颜色)。所以在你的情况下:

import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.normalize(min=0, max=1))
plt.colorbar(sm)

因为您的数据已经在 0-1 范围内,您可以将 sm 创建简化为:

sm = plt.cm.ScalarMappable(cmap=my_cmap)

希望对某人有所帮助。

编辑:对于 matplotlib v1.2 或更高版本,代码变为:

import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.normalize(vmin=0, vmax=1))
# fake up the array of the scalar mappable. Urgh...
sm._A = []
plt.colorbar(sm)

编辑:对于 matplotlib v1.3 或更高版本,代码变为:

import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1))
# fake up the array of the scalar mappable. Urgh...
sm._A = []
plt.colorbar(sm)

编辑:对于 matplotlib v3.1 或更高版本简化为:

import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1))
plt.colorbar(sm)

【讨论】:

  • 这也不起作用。 TypeError: __init__() got an unexpected keyword argument 'max'
  • 这对我有用:stackoverflow.com/a/6601210/125507 我只是使用 linspace 而不是 z1 因为我正在绘制颜色图本身,所以数字是任意的。 :)
  • 看起来 matplotlib v1.3 示例应该进一步调整,目前它会产生警告:“MatplotlibDeprecationWarning: The normalize class alias was deprecated in version 1.3. Use Normalize instead.”
  • 谢谢,这拯救了我的一天。但它并没有按原样工作:我不得不用 sm.set_array(Zs) 替换 sm._A = [],其中 Zs 是一个包含转换为颜色的值的数组。
  • 将其与子图一起使用时,即使我指定颜色条属于特定轴,它也会从我的其他轴中删除图。有谁知道如何解决这个问题?
【解决方案2】:

这是一种在仍然使用 plt.plot() 的情况下执行此操作的方法。基本上,您制作一个一次性情节并从那里获取颜色条。

import matplotlib as mpl
import matplotlib.pyplot as plt

min, max = (-40, 30)
step = 10

# Setting up a colormap that's a simple transtion
mymap = mpl.colors.LinearSegmentedColormap.from_list('mycolors',['blue','red'])

# Using contourf to provide my colorbar info, then clearing the figure
Z = [[0,0],[0,0]]
levels = range(min,max+step,step)
CS3 = plt.contourf(Z, levels, cmap=mymap)
plt.clf()

# Plotting what I actually want
X=[[1,2],[1,2],[1,2],[1,2]]
Y=[[1,2],[1,3],[1,4],[1,5]]
Z=[-40,-20,0,30]
for x,y,z in zip(X,Y,Z):
    # setting rgb color based on z normalized to my range
    r = (float(z)-min)/(max-min)
    g = 0
    b = 1-r
    plt.plot(x,y,color=(r,g,b))
plt.colorbar(CS3) # using the colorbar info I got from contourf
plt.show()

有点浪费,但很方便。如果您制作多个绘图也不是很浪费,因为您可以调用 plt.colorbar() 而无需为其重新生成信息。

【讨论】:

  • 很好的解决方案! (对于这个问题似乎没有简单的解决方案;除了使用 linecollection)
  • 解决问题的好方法,但我认为更好的解决方案是制作一个可映射的标量实例(请参阅我的答案)。
  • 有没有什么办法可以用颜色条来做到这一点,该颜色条会在配色方案中产生连续变化,而不是上图所示的带状离散变化?
  • 需要一些改进:根据颜色图,我猜蓝线的 Z=-35,即使施加了 -40。
  • 我更喜欢这种方法:stackoverflow.com/a/11558629/7042795
【解决方案3】:

这是一个稍微简化的示例,灵感来自 BorisHooked 给出的最佳答案(感谢您的好主意!):

1。 独立彩条

离散颜色条涉及更多,因为mpl.cm.get_cmap() 生成的颜色图不是colorbar() 参数所需的可映射图像。需要生成一个dummy mappable,如下所示:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)

cmap = mpl.cm.get_cmap('jet', n_lines)

fig, ax = plt.subplots(dpi=100)
# Make dummie mappable
dummie_cax = ax.scatter(c, c, c=c, cmap=cmap)
# Clear axis
ax.cla()
for i, yi in enumerate(y.T):
    ax.plot(x, yi, c=cmap(i))
fig.colorbar(dummie_cax, ticks=c)
plt.show();

这将产生一个带有离散颜色条的图:


2。 连续彩条

连续的颜色条较少涉及,因为mpl.cm.ScalarMappable() 允许我们获得colorbar() 的“图像”。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl


n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)

norm = mpl.colors.Normalize(vmin=c.min(), vmax=c.max())
cmap = mpl.cm.ScalarMappable(norm=norm, cmap=mpl.cm.jet)
cmap.set_array([])

fig, ax = plt.subplots(dpi=100)
for i, yi in enumerate(y.T):
    ax.plot(x, yi, c=cmap.to_rgba(i + 1))
fig.colorbar(cmap, ticks=c)
plt.show();

这将产生一个带有连续颜色条的图:

[旁注] 在这个例子中,我个人不知道为什么cmap.set_array([]) 是必要的(否则我们会收到错误消息)。如果有人了解底层原理,请发表评论:)

【讨论】:

  • 使用虚拟情节几乎没有意义;我为离散案例写了一个没有这样弯路的答案。
  • 我花了一段时间才意识到你的c=cmap.to_rgba(i + 1) 在一般情况下应该是c=cmap.to_rgba(value),其中value 是与应该映射到该颜色的线相关联的值。跨度>
【解决方案4】:

由于这里的其他答案确实尝试使用虚拟图,这不是很好的风格,这是一个通用代码

离散颜色条

离散颜色条的生成方式与创建连续颜色条的方式相同,只是使用了不同的标准化。在这种情况下,应该使用BoundaryNorm

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1., n_lines + 1)

cmap = plt.get_cmap("jet", len(c))
norm = matplotlib.colors.BoundaryNorm(np.arange(len(c)+1)+0.5,len(c))
sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)
sm.set_array([])  # this line may be ommitted for matplotlib >= 3.1

fig, ax = plt.subplots(dpi=100)
for i, yi in enumerate(y.T):
    ax.plot(x, yi, c=cmap(i))
fig.colorbar(sm, ticks=c)
plt.show()

【讨论】:

  • 还不确定,但这不是全部答案...虽然这给出了一个离散的颜色条,但在更改规范变量时它不允许继续一个
猜你喜欢
  • 2014-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-07
  • 1970-01-01
  • 2016-06-22
  • 2020-11-13
  • 2017-07-15
相关资源
最近更新 更多