将色条添加到一系列线图中

78

我有一系列的线图,用于表示两个变量(x,y)在不同变量z的数值下的情况。通常我会像这样添加带有图例的线图:

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()函数的灵活性。

4个回答

133

(我知道这是一个旧问题,但是...)颜色条需要一个matplotlib.cm.ScalarMappableplt.plot生成的是不可缩放映射的线条,因此,为了制作颜色条,我们需要制作一个可缩放映射。

好的。因此,一个ScalarMappable的构造函数接受一个和一个norm实例。(norm将数据缩放到0-1的范围内,cmap已经与您一起使用,并采用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)

37

以下是一种使用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()而无需重新生成其信息,因此并不会非常浪费。

在此输入图片描述


20

这是一个稍微简化的示例,受到BorisHooked(感谢您的精彩想法!)答案的启发:

1. 离散色条

离散色条更加复杂,因为由mpl.cm.get_cmap()生成的颜色映射不是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)

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();

这将生成一个带有离散色条的图表: enter image description here


2. 连续色条

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

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();

这将生成一张带有连续色条的图: enter image description here

[顺带提一下] 在这个例子中,我个人不知道为什么 cmap.set_array([]) 是必要的(否则我们会得到错误信息)。如果有人了解其原理,请评论 :)


2
我花了一段时间才意识到在一般情况下你的 c=cmap.to_rgba(i + 1) 应该是 c=cmap.to_rgba(value),其中 value 是应映射为该颜色的线所关联的值。 - Bobson Dugnutt

6
作为这里其他答案试图使用虚拟图形的办法,这并不是很好的风格。下面是一个通用代码,用于生成离散的色条。
离散的色条的创建方式与连续的色条相同,只是需要使用不同的归一化方法。在这种情况下,应该使用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()

enter image description here


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