为使用plt.fill绘制的图形创建颜色条
我刚开始学习Python(之前用的是IDL),希望我问的问题能让人理解。我一直在尝试创建一个极坐标图,里面有x个区间,每个区间的数据会被平均,并且根据这个值给它一个颜色。使用plt.fill命令时,这个过程看起来没问题,我可以定义区间并设置填充颜色。但当我想为这个图添加一个颜色条时,就出现了问题。我总是收到错误提示,内容是AttributeError: 'Figure'对象没有'autoscale_None'这个属性。
如果能给点建议就太好了,谢谢。
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.pyplot import figure, show, rc, grid
import pylab
r = np.arange(50)/5.
rstep = r[1] - r[0]
theta = np.arange(50)/50.*2.*np.pi
tstep = theta[1] - theta[0]
colorv = np.arange(50)/50.
# force square figure and square axes looks better for polar, IMO
width, height = mpl.rcParams['figure.figsize']
size = min(width, height)
# make a square figure
fig = figure(figsize=(size, size))
ax = fig.add_axes([0.1, 0.1, .8, .8])#, polar=True)
my_cmap = cm.jet
for j in range(len(r)):
rbox = np.array([r[j], r[j], r[j]+ rstep, r[j] + rstep])
for i in range(len(theta)):
thetabox = np.array([theta[i], theta[i] + tstep, theta[i] + tstep, theta[i]])
x = rbox*np.cos(thetabox)
y = rbox*np.sin(thetabox)
plt.fill(x,y, facecolor = my_cmap(colorv[j]))
# Add colorbar, make sure to specify tick locations to match desired ticklabels
cbar = fig.colorbar(fig, ticks=[np.min(colorv), np.max(colorv)])
cb = plt.colorbar()
plt.show()
* 这里有一个稍微更好的例子,展示了我的真实数据,里面到处都有缺失的部分,所以在这个例子中,我在圆的一部分做了一个大缺口。当我尝试进行网格处理时,代码似乎试图在这些区域进行插值。
r = np.arange(50)/50.*7. + 3.
rstep = r[1] - r[0]
theta = np.arange(50)/50.*1.5*np.pi - np.pi
tstep = theta[1] - theta[0]
colorv = np.sin(r/10.*np.pi)
# force square figure and square axes looks better for polar, IMO
width, height = mpl.rcParams['figure.figsize']
size = min(width, height)
# make a square figure
fig = figure(figsize=(size, size))
ax = fig.add_axes([0.1, 0.1, .8, .8])#, polar=True)
my_cmap = cm.jet
for j in range(len(r)):
rbox = np.array([r[j], r[j], r[j]+ rstep, r[j] + rstep])
for i in range(len(theta)):
thetabox = np.array([theta[i], theta[i] + tstep, theta[i] + tstep, theta[i]])
x = rbox*np.cos(thetabox)
y = rbox*np.sin(thetabox)
plt.fill(x,y, facecolor = my_cmap(colorv[j]))
# Add colorbar, make sure to specify tick locations to match desired ticklabels
#cbar = fig.colorbar(fig, ticks=[np.min(colorv), np.max(colorv)])
#cb = plt.colorbar()
plt.show()
然后涉及到网格处理时……
from matplotlib.mlab import griddata
r = np.arange(50)/5.
rstep = r[1] - r[0]
theta = np.arange(50)/50.*1.5*np.pi - np.pi
tstep = theta[1] - theta[0]
colorv = np.sin(r/10.*np.pi)
# force square figure and square axes looks better for polar, IMO
width, height = mpl.rcParams['figure.figsize']
size = min(width, height)
# make a square figure
fig = figure(figsize=(size, size))
ax = fig.add_axes([0.1, 0.1, .8, .8])#, polar=True)
my_cmap = cm.jet
x = r*np.cos(theta)
y = r*np.sin(theta)
X,Y = np.meshgrid(x,y)
data = griddata(x,y,colorv,X,Y)
cax = plt.contourf(X,Y, data)
plt.colorbar()
# Add colorbar, make sure to specify tick locations to match desired ticklabels
#cbar = fig.colorbar(fig, ticks=[np.min(colorv), np.max(colorv)])
#cb = plt.colorbar()
plt.show()
2 个回答
colorbar
需要一些东西是 ScalarMappable
的实例,才能从中生成颜色条。
因为你是手动设置每个小块,所以实际上并没有什么东西可以用来生成颜色条。
虽然有很多方法可以从你的颜色映射中“伪造”一个颜色条,但在这种情况下,有一个更简单的解决方案。
pcolormesh
完全可以满足你的需求,而且速度会更快。
举个例子:
import numpy as np
import matplotlib.pyplot as plt
# Linspace makes what you're doing _much_ easier (and includes endpoints)
r = np.linspace(0, 10, 50)
theta = np.linspace(0, 2*np.pi, 50)
fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')
# "Grid" r and theta into 2D arrays (see the docs for meshgrid)
r, theta = np.meshgrid(r, theta)
cax = ax.pcolormesh(theta, r, r, edgecolors='black', antialiased=True)
# We could just call `plt.colorbar`, but I prefer to be more explicit
# and pass in the artist that I want it to extract colors from.
fig.colorbar(cax)
plt.show()
或者,如果你更喜欢像你示例代码那样的非极坐标轴:
import numpy as np
import matplotlib.pyplot as plt
r = np.linspace(0, 10, 50)
theta = np.linspace(0, 2*np.pi, 50)
# "Grid" r and theta and convert them to cartesian coords...
r, theta = np.meshgrid(r, theta)
x, y = r * np.cos(theta), r * np.sin(theta)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.axis('equal')
cax = ax.pcolormesh(x, y, r, edgecolors='black', antialiased=True)
fig.colorbar(cax)
plt.show()
注意:如果你希望边界线稍微淡一点,只需在 pcolormesh
中指定 linewidth=0.5
或类似的参数。
最后,如果你确实想直接从你原始代码中的颜色映射生成颜色条,你需要从中创建一个 ScalarMappable
的实例,然后把这个实例传递给 colorbar
。这听起来比实际操作简单,但会稍微多一些代码。
举个例子,在你的原始代码中,如果你做类似下面的事情:
cax = cm.ScalarMappable(cmap=my_cmap)
cax.set_array(colorv)
fig.colorbar(cax)
应该就能达到你想要的效果。
我找到了一种解决办法。因为我知道有一个区域是肯定没有数据的,所以我在那个地方画了一些数据。我确保这些数据覆盖了我想要绘制的整个范围。然后我把这个区域遮住(反正这个区域是要遮住的,它显示了“地球”的位置)。现在我可以像之前那样使用plt.fill,并且用随机生成的数据的颜色条。我知道这可能不是最正确的方法,但它有效,并且不会试图对我的数据进行插值处理。
非常感谢你帮我解决这个问题。如果你知道更好的方法,我很乐意听听!
hid = plt.pcolormesh(X,Y, data, antialiased=True)
#here we cover up the region that we just plotted in
r3 = [1 for i in range(360)]
theta3 = np.arange(360)*np.pi/180.
plt.fill(theta3, r3, 'w')
#now we can go through and fill in all the regions
for j in range(len(r)):
rbox = np.array([r[j], r[j], r[j]+ rstep, r[j] + rstep])
for i in range(len(theta)):
thetabox = np.array([theta[i], theta[i] + tstep, theta[i] + tstep, theta[i]])
x = rbox*np.cos(thetabox)
y = rbox*np.sin(thetabox)
colorv = np.sin(r[j]/10.*np.pi)
plt.fill(thetabox,rbox, facecolor = my_cmap(colorv))
#And now we can plot the color bar that fits the data Tada :)
plt.colorbar()
plt.show()