从图中移除色条

43

这应该很容易,但我却遇到了困难。基本上,我在matplotlib中有一个subplot,每次调用函数时都会在其中绘制一个hexbin图,但每次调用函数时我都会得到一个新的colorbar,所以我真正想做的是更新colorbar。不幸的是,这似乎行不通,因为colorbar附加的对象正在被subplot.hexbin重新创建。

def foo(self):
   self.subplot.clear()
   hb = self.subplot.hexbin(...)
   if self.cb:
      self.cb.update_bruteforce() # Doesn't work (hb is new)
   else:
      self.cb = self.figure.colorbar(hb)

我现在遇到了一个麻烦的问题,想要完全删除颜色条轴并重新创建它。不幸的是,当我删除颜色条轴时,子图轴不会重新占据空间,并且调用self.subplot.reset_position()没有产生我想要的效果。

def foo(self):
   self.subplot.clear()
   hb = self.subplot.hexbin(...)
   if self.cb:
      self.figure.delaxes(self.figure.axes[1])
      del self.cb
      # TODO: resize self.subplot so it fills the 
      #    whole figure before adding the new colorbar
   self.cb = self.figure.colorbar(hb)

我认为每次调用函数时都创建一个新的六边形散点图并不是明智之举。 我认为您想要的是以某种方式更新现有图表的数据(我对六边形散点图不太熟悉,无法确定如何实现)。我有一些问题。这个图表是否带动画效果? 是否会出现多个并排的色条? 您能否发布一个带有虚假数据的运行示例? - Paul
我考虑过这个,但是我觉得这是一条更难的路,因为这是一个交互式绘图工具。用户可以更改条形数、网格大小、轴刻度、数据源等。我现在会继续努力解决这个颜色条问题,如果遇到困难,我会考虑将其简化为一个可共享的工作示例。谢谢Paul。 - Adam Fraser
您可以为colorbar创建一个特定的轴并清除此轴。fig.colorbar(cax=cax)cax.cla() - MaxNoe
10个回答

37
我认为问题在于使用del取消了变量,但未取消引用的对象colorbar。如果您想从图中删除并消失colorbar,则必须使用colorbar实例的方法remove,为此您需要将colorbar存储在变量中,有两个选项:
  1. 在创建时将colorbar保存在一个值中,如其他答案中所示,例如cb=plt.colorbar()
  2. 检索现有的colorbar,可以按照(并点赞:))我在这里写的内容执行:如何从matplotlib中的图中检索colorbar实例 然后:

cb.remove() plt.draw() #更新绘图


完整的代码和结果:

from matplotlib import pyplot as plt 
import numpy as np

plt.ion() 
plt.imshow(np.random.random(15).reshape((5,3))) 
cb = plt.colorbar() 
plt.savefig('test01.png') 
cb.remove() 
plt.savefig('test02.png')

test01.png test02.png


1
我不明白这个答案如何帮助恢复由色条保留的空间,因为问题中特别要求。调用 cb.remove() 只是删除了色条,留下了空白的空间。如果您添加另一个色条,它将出现在这个空白的空间上方,依此类推。 - Leogout
1
我不明白,这不是我的情况。当我调用cb.remove()时,色条被移除,图像被调整大小以占据整个图形。如果需要,我可以提供一个示例。 - Vincenzooo
没问题,感谢您的建议,这可能是我的问题。我已经找到了一种方法,通过使用手动创建的斧头作为色条容器,实现了我需要的功能。很抱歉挖出这么旧的帖子。 - Leogout
没问题,我认为添加一个例子还是很有用的。 - Vincenzooo

14

好的,这是我的解决方案。不是很优雅,但也不是一个糟糕的hack。

def foo(self):
   self.subplot.clear()
   hb = self.subplot.hexbin(...)
   if self.cb:
      self.figure.delaxes(self.figure.axes[1])
      self.figure.subplots_adjust(right=0.90)  #default right padding
   self.cb = self.figure.colorbar(hb)

对于我的需求,这个方法很有效,因为我只有一个子图。对于使用多个子图或在不同位置绘制色条时遇到相同问题的人需要进行调整。


有人遇到了相同的问题:与散点图相关联的色条,每次绘制新的散点图时都会再现。张贴的解决方案可行。我想知道是否有人想到了更“简洁”的解决方案。 - joaquin

8
如果您有一个matplotlib图形对象,您只需要执行fig.delaxes(fig.axes[1]) 例如:
带有色条的绘图
import matplotlib.pyplot as plt

# setup some generic data
N = 37
x, y = np.mgrid[:N, :N]
Z = (np.cos(x*0.2) + np.sin(y*0.3))

# mask out the negative and positive values, respectively
Zpos = np.ma.masked_less(Z, 0)
Zneg = np.ma.masked_greater(Z, 0)

fig, ax1 = plt.subplots(figsize=(13, 3), ncols=1)

# plot just the positive data and save the
# color "mappable" object returned by ax1.imshow
pos = ax1.imshow(Zpos, cmap='Blues', interpolation='none')

# add the colorbar using the figure's method,
# telling which mappable we're talking about and
# which axes object it should be near
fig.colorbar(pos, ax=ax1)

在此输入图像描述

移除色标

import matplotlib.pyplot as plt

# setup some generic data
N = 37
x, y = np.mgrid[:N, :N]
Z = (np.cos(x*0.2) + np.sin(y*0.3))

# mask out the negative and positive values, respectively
Zpos = np.ma.masked_less(Z, 0)
Zneg = np.ma.masked_greater(Z, 0)

fig, ax1 = plt.subplots(figsize=(13, 3), ncols=1)

# plot just the positive data and save the
# color "mappable" object returned by ax1.imshow
pos = ax1.imshow(Zpos, cmap='Blues', interpolation='none')

# add the colorbar using the figure's method,
# telling which mappable we're talking about and
# which axes object it should be near
fig.colorbar(pos, ax=ax1)

fig.delaxes(fig.axes[1])

enter image description here


5
我使用fig.clear()和display.clear_output()成功解决了相同的问题。
import matplotlib.pyplot as plt
import IPython.display as display
import matplotlib.tri as tri
from pylab import *
%matplotlib inline

def plot_res(fig):
    ax=fig.add_axes([0,0,1,1])
    ax.set_xlabel("x")
    ax.set_ylabel('y')
    plotted=ax.imshow(rand(250, 250))
    ax.set_title("title")
    cbar=fig.colorbar(mappable=plotted)
    display.clear_output(wait=True)
    display.display(plt.gcf())
    fig.clear()

fig=plt.figure()
N=20
for j in range(N):
    plot_res(fig)

3

我需要移除色条,因为我正在使用pcolormesh绘制图形,并在循环中向图形添加色条。每次循环都会创建一个新的色条,十次循环后我将拥有十个色条。这是不好的。

要移除色条,我给pcolormesh和colorbar命名一个变量,然后在循环结束时将它们分别移除。在移除pcolormesh之前移除色条是很重要的。

伪代码:

 for i in range(0,10):
   p = plt.pcolormesh(datastuff[i])
   cb = plt.colorbar(p)
   plt.savefig('name_'+i)

   cb.remove()
   p.remove()

需要在使用pcolormesh之前先移除colorbar。


1
我在使用子图时遇到了一些问题。这个解决方案非常有帮助。我将colorbars添加到一个列表中,然后使用该列表来删除每一个。 - mauve
是的,就是这样! - j_fu
这太直观了...你看到就能懂。@blaylockbk 我欠你一个人情! - Whir

3

我曾经遇到过类似的问题并进行了一些尝试。我想出了两种可能更加优雅的解决方案:

  1. 清空整个图并重新添加子图(如果需要,还可以添加色条)。

  2. 如果总是有色条存在,您可以简单地使用自动缩放更新轴,这也会更新色条。

我已经尝试过imshow方法,但我认为其他绘图方法的操作类似。

from pylab import *
close('all') #close all figures in memory

#1. Figures for fig.clf method
fig1 = figure()
fig2 = figure()
cbar1=None
cbar2=None
data = rand(250, 250)

def makefig(fig,cbar):
  fig.clf()
  ax = fig.add_subplot(111)
  im = ax.imshow(data)
  if cbar:
    cbar=None
  else:
    cbar = fig.colorbar(im)
  return cbar


#2. Update method
fig_update = figure()
cbar3=None
data_update = rand(250, 250)
img=None

def makefig_update(fig,im,cbar,data):
  if im:
    data*=2 #change data, so there is change in output (look at colorbar)
    #im.set_data(data) #use this if you use new array
    im.autoscale()
    #cbar.update_normal(im) #cbar is updated automatically
  else:
    ax = fig.add_subplot(111)
    im = ax.imshow(data)
    cbar=fig.colorbar(im)
  return im,cbar,data

#Execute functions a few times
for i in range(3):
  print i
  cbar1=makefig(fig1,cbar1)
  cbar2=makefig(fig2,cbar2)
  img,cbar3,data_update=makefig_update(fig_update,img,cbar3,data_update)
cbar2=makefig(fig2,cbar2)

fig1.show()
fig2.show()
fig_update.show()

然而,解决方案1的问题在于,如果您在图中有多个绘图,那么它肯定会清除整个图形,对吗? - JPH
是的,它会清除整个图形。我不知道如何仅清除单个子图。 - cass

3
我的解决方案是使用一个仅用于保存颜色条的Axes,需要时完全清除它。
例如,定义如下内容:
figure, ax = plt.subplots() # All the plotting is done on `ax`.
cax = ax.inset_axes([1.03, 0, 0.1, 1], transform=ax.transAxes) # Colorbar is held by `cax`. 

然后根据需要重复这个步骤:
cax.clear()
colorbar = figure.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap),
                           ax=ax,
                           cax=cax,
                           **kwargs)

enter image description here


1
你的方法帮了我,谢谢。 - bactone

1
不想贬低此 博客文章(Joseph Long)的作者,但这显然是我迄今为止找到的最佳解决方案。它包括代码片段、精彩的解释和许多示例。
简而言之,从任何命令的轴 ax 的输出中,如: plotimagescattercollection 等,例如:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5,5), dpi=300)
ax = fig.add_subplot(1, 1, 1)

data = ax.plot(x,y)
# or
data = ax.scatter(x, y, z)
# or
data = ax.imshow(z)
# or 
data = matplotlib.collection(patches)
ax.add_collection(data)

使用 make_axes_locatable 和绘图的原始轴,您可以创建一个颜色条轴。
from mpl_toolkits.axes_grid1 import make_axes_locatable

# the magical part
divider = make_axes_locatable(ax)
caxis = divider.append_axes("right", size="5%", pad=0.05)
fig.colorbar(data, cax=caxis)

plt.show()

创建的色条将与图形或子图具有相同的大小,并且您可以在使用divider.append_axes命令时修改其宽度、位置和填充。

1
我正在使用matplotlib 1.4.0。这是我解决这个问题的方法:
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt

# A contour plot example:
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10.0 * (Z2 - Z1)
#

# first drawing
fig = plt.figure()
ax = fig.add_subplot(111)  # drawing axes
c = ax.contourf(Z)   # contour fill c
cb = fig.colorbar(c)  # colorbar for contour c

# clear first drawimg
ax.clear()  # clear drawing axes
cb.ax.clear()  # clear colorbar axes

# replace with new drawing
# 1. drawing new contour at drawing axes
c_new = ax.contour(Z)  
# 2. create new colorbar for new contour at colorbar axes
cb_new = ax.get_figure().colorbar(c_new, cax=cb.ax) 

plt.show()

上面的代码绘制了一个带有颜色条的轮廓填充图,清除它并在同一图中绘制一个新的轮廓图和新的颜色条。
通过使用cb.ax,我能够识别颜色条轴并清除旧的颜色条。 并且指定cax=cb.ax只是在旧的颜色条轴上绘制新的颜色条。

-1
"

在我的案例中,"on_mappable_changed" 起了作用。然而,根据文档,该方法“通常…不应手动调用。”

"
if self.cb:
    self.cb.on_mappable_changed(hb)
else:
    self.cb = self.fig.colorbar(hb)

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