How can I extend the plot area to take up the whole canvas and essentially remove the gray area in the image? I've looked around and I haven't found any solutions, although maybe it's because I don't know what to call that area so searching is challenging.
Using fig.tight_layout() reduces the gray but does not remove it.
from matplotlib import pyplot as plt
y = [1,3,5,6,7,8,12,13,15,15,16,25,26,28,29,36]
x = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
fig, ax = plt.subplots(figsize=(8,4), dpi=85)
ax.stackplot(x, y, color='w', colors=('#ededed',))
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
ax.set_ylim([0,max(y)*1.2])
ax.set_xlim([0,30])
plt.show()
Specify that the Axes take up the whole Figure when you create the Axes --
fig = plt.figure()
ax = fig.add_axes((0,0,1,1), frameon=False)
ax.plot([2,5,4])
now, there's a lot there isn't room for any more, e.g. external tick labels; and Tkinter may be doing some padding itself; but this is as large as you can make an Axes. ( (0,0,1,1) is extent in Figure coordinates.)
Related
I'm trying to fit the sizes of 2x2 subplots so they line up correctly.
I want to create the following subplot/axes structure:
ax1 has a set aspect ratio in data coordinates (e.g. data is not scaled when aspect=1)
ax2 and ax3 have a set (box) aspect ratio in display/figure coordinates (e.g. they appear as squares when box_aspect=1)
ax1 and ax3 share the x axis limits and have the same width
ax1 and ax2 share the y axis limits and have the same height
ax4 fits in the last box so that it has the width of ax2 and the height of ax3
I need this for the following project (it's an animation):
Imagine having ax1 as the world space in which a point is moving. (the coordinates should not be warped)
The adjacent plots show the x(ax3) and y(ax2) coordinates of the point over time. (I want to be able to set the aspect ratio of these subplots)
The last axes/plot is not related to any of the coordinates but should fit in nicely.
I created a minimal example and added descriptive text to the picture:
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = plt.subplot(221, anchor='SE', aspect=1, xlim=(0,1), ylim=(0,2))
ax2 = plt.subplot(222, anchor='SW', box_aspect=1, sharey=ax1, xlim=(0,3))
ax3 = plt.subplot(223, anchor='NE', box_aspect=1, sharex=ax1, ylim=(0,4))
ax4 = plt.subplot(224, anchor='NW', xlim=(0,5), ylim=(0,6))
plt.show()
Notes:
the anchors are set so there is not so much whitespace between the subplots
the x and y limits are arbitrarily chosen and should not matter
I encountered a similar issue as I was plotting GeoJson data in matplotlib.
I solved it by creating a one subplot figure setting aspect=1
# import the required libraries
import geopandas as gpd
import matplotlib.pyplot as plt
# Define the file path
fp = r"\Your-file-full-path\file.geojson"
# Read the GeoJSON file similarly as Shapefile
mygeojson = gpd.read_file(fp)
# Create a figure with one subplot
fig = plt.figure()
# Plot the grid with column-to-plot (as you set cmap, scheme, and aspect hyper-params)
mygeojson.plot(aspect=1, column = 'geojson-column-to-plot', cmap = 'gist_rainbow', scheme = 'equalinterval', k=9, linewidth=0, legend=True);
# Add title
plt.title("Your GeoDataFrame object title");
# Remove white space around the figure
plt.tight_layout()
I am trying to create axesless contour plot of my raster data. I managed to create the contour plot however I can't remove the axes completely. I can turn them off with plt.axis('off') but axes whitespaces are still there.
What I do:
cnt = plt.contour(my_data)
plt.clabel(cnt, inline=1, fontsize=10)
plt.axis('off')
Edit
My output method
plt.savefig(image_path, transparent=False, bbox_inches='tight', pad_inches=0)
Results:
Before plt.axis('off')
After plt.axis('off')
I had the same issue with the imshow but I've managed to solve it here, however the same technique can't be used with contours.
So how can I plot contours without axes and any whitespaces they leave behind?
Edit
So I managed to determine that the problem is not in 'plt.axis('off')' part of the code. The line does in fact completely remove the axes and it is visible when I call plt.show() however when I try to save the plot with 'plt.savefig()' I get that undesirable whitespaces. Why is that?
My code with output:
cnt = plt.contour(my_data)
plt.clabel(cnt, inline=1, fontsize=10)
plt.axis('off')
# no whitespaces
plt.show()
# whitespaces are present
plt.savefig(image_path, transparent=False, bbox_inches='tight', pad_inches=0)
Possible solution!?
I did find the way to make my images almost what I wanted with:
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
plt.contour(data)
extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.axis('off')
plt.savefig(image_path, transparent=False, bbox_inches=extent, pad_inches=0)
However I cant change the aspect ratio of the plot. I think that I do not understand this solution to the fullest.
This is actually due to savefig's defaults. The figure can have a transparent background (e.g. try fig.patch.set(facecolor='none'); fig.canvas.print_png), but it's being overridden when you call plt.savefig.
If you want a transparent background, you'll need to specify transparent=True in your call to savefig. Otherwise, it will override the figure's current background color and set it to opaque white.
Have a look at the documentation for savefig for more details.
As an example:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.random((5, 5))
fig, ax = plt.subplots()
cnt = ax.contour(data)
ax.clabel(cnt)
ax.axis('off')
fig.savefig('test.png', bbox_inches='tight', transparent=True)
Of course, this looks identical on this page, but if you open it up in an image viewer you'll notice that it has a proper transparent background:
Edit:
I may have misunderstood what you're asking. If you want the contour plot to take up the entire figure with no room left for tick labels, etc on the side, it's easiest to define the plot that way to begin with.
For example (note that this applies to any type of plot, not just contouring):
import numpy as np
import matplotlib.pyplot as plt
data = np.random.random((5, 5))
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
cnt = ax.contour(data)
ax.clabel(cnt)
ax.axis('off')
plt.show()
If you're still having issues, it's probably because you're using fig.savefig(..., bbox_inches='tight'). That specifically requires the tick labels to be included in the saved image, even if they're invisible and outside of the bounds of the figure.
Try something similar to:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.random((5, 5))
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])
cnt = ax.contour(data)
ax.clabel(cnt)
ax.axis('off')
fig.savefig('test.png')
I cannot get the colorbar on imshow graphs like this one to be the same height as the graph, short of using Photoshop after the fact. How do I get the heights to match?
This combination (and values near to these) seems to "magically" work for me to keep the colorbar scaled to the plot, no matter what size the display.
plt.colorbar(im,fraction=0.046, pad=0.04)
It also does not require sharing the axis which can get the plot out of square.
You can do this easily with a matplotlib AxisDivider.
The example from the linked page also works without using subplots:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
plt.figure()
ax = plt.gca()
im = ax.imshow(np.arange(100).reshape((10,10)))
# create an axes on the right side of ax. The width of cax will be 5%
# of ax and the padding between cax and ax will be fixed at 0.05 inch.
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
plt.colorbar(im, cax=cax)
I appreciate all the answers above. However, like some answers and comments pointed out, the axes_grid1 module cannot address GeoAxes, whereas adjusting fraction, pad, shrink, and other similar parameters cannot necessarily give the very precise order, which really bothers me. I believe that giving the colorbar its own axes might be a better solution to address all the issues that have been mentioned.
Code
import matplotlib.pyplot as plt
import numpy as np
fig=plt.figure()
ax = plt.axes()
im = ax.imshow(np.arange(100).reshape((10,10)))
# Create an axes for colorbar. The position of the axes is calculated based on the position of ax.
# You can change 0.01 to adjust the distance between the main image and the colorbar.
# You can change 0.02 to adjust the width of the colorbar.
# This practice is universal for both subplots and GeoAxes.
cax = fig.add_axes([ax.get_position().x1+0.01,ax.get_position().y0,0.02,ax.get_position().height])
plt.colorbar(im, cax=cax) # Similar to fig.colorbar(im, cax = cax)
Result
Later on, I find matplotlib.pyplot.colorbar official documentation also gives ax option, which are existing axes that will provide room for the colorbar. Therefore, it is useful for multiple subplots, see following.
Code
fig, ax = plt.subplots(2,1,figsize=(12,8)) # Caution, figsize will also influence positions.
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
fig.colorbar(im1, ax=ax)
Result
Again, you can also achieve similar effects by specifying cax, a more accurate way from my perspective.
Code
fig, ax = plt.subplots(2,1,figsize=(12,8))
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
cax = fig.add_axes([ax[1].get_position().x1-0.25,ax[1].get_position().y0,0.02,ax[0].get_position().y1-ax[1].get_position().y0])
fig.colorbar(im1, cax=cax)
Result
#bogatron already gave the answer suggested by the matplotlib docs, which produces the right height, but it introduces a different problem.
Now the width of the colorbar (as well as the space between colorbar and plot) changes with the width of the plot.
In other words, the aspect ratio of the colorbar is not fixed anymore.
To get both the right height and a given aspect ratio, you have to dig a bit deeper into the mysterious axes_grid1 module.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
import numpy as np
aspect = 20
pad_fraction = 0.5
ax = plt.gca()
im = ax.imshow(np.arange(200).reshape((20, 10)))
divider = make_axes_locatable(ax)
width = axes_size.AxesY(ax, aspect=1./aspect)
pad = axes_size.Fraction(pad_fraction, width)
cax = divider.append_axes("right", size=width, pad=pad)
plt.colorbar(im, cax=cax)
Note that this specifies the width of the colorbar w.r.t. the height of the plot (in contrast to the width of the figure, as it was before).
The spacing between colorbar and plot can now be specified as a fraction of the width of the colorbar, which is IMHO a much more meaningful number than a fraction of the figure width.
UPDATE:
I created an IPython notebook on the topic, where I packed the above code into an easily re-usable function:
import matplotlib.pyplot as plt
from mpl_toolkits import axes_grid1
def add_colorbar(im, aspect=20, pad_fraction=0.5, **kwargs):
"""Add a vertical color bar to an image plot."""
divider = axes_grid1.make_axes_locatable(im.axes)
width = axes_grid1.axes_size.AxesY(im.axes, aspect=1./aspect)
pad = axes_grid1.axes_size.Fraction(pad_fraction, width)
current_ax = plt.gca()
cax = divider.append_axes("right", size=width, pad=pad)
plt.sca(current_ax)
return im.axes.figure.colorbar(im, cax=cax, **kwargs)
It can be used like this:
im = plt.imshow(np.arange(200).reshape((20, 10)))
add_colorbar(im)
When you create the colorbar try using the fraction and/or shrink parameters.
From the documents:
fraction 0.15; fraction of original axes to use for colorbar
shrink 1.0; fraction by which to shrink the colorbar
All the above solutions are good, but I like #Steve's and #bejota's the best as they do not involve fancy calls and are universal.
By universal I mean that works with any type of axes including GeoAxes. For example, it you have projected axes for mapping:
projection = cartopy.crs.UTM(zone='17N')
ax = plt.axes(projection=projection)
im = ax.imshow(np.arange(200).reshape((20, 10)))
a call to
cax = divider.append_axes("right", size=width, pad=pad)
will fail with: KeyException: map_projection
Therefore, the only universal way of dealing colorbar size with all types of axes is:
ax.colorbar(im, fraction=0.046, pad=0.04)
Work with fraction from 0.035 to 0.046 to get your best size. However, the values for the fraction and paddig will need to be adjusted to get the best fit for your plot and will differ depending if the orientation of the colorbar is in vertical position or horizontal.
An alternative is
shrink=0.7, aspect=20*0.7
shrink scales the height and width, but the aspect argument restores the original width. Default aspect ratio is 20. The 0.7 is empirically determined.
I encountered this problem recently, I used ax.twinx() to solve it. For example:
from matplotlib import pyplot as plt
# Some other code you've written
...
# Your data generation goes here
xdata = ...
ydata = ...
colordata = function(xdata, ydata)
# Your plotting stuff begins here
fig, ax = plt.subplots(1)
im = ax.scatterplot(xdata, ydata, c=colordata)
# Create a new axis which will be the parent for the colour bar
# Note that this solution is independent of the 'fig' object
ax2 = ax.twinx()
ax2.tick_params(which="both", right=False, labelright=False)
# Add the colour bar itself
plt.colorbar(im, ax=ax2)
# More of your code
...
plt.show()
I found this particularly useful when creating functions that take in matplotlib Axes objects as arguments, draw on them, and return the object because I then don't need to pass in a separate axis I had to generate from the figure object, or pass the figure object itself.
If you don't want to declare another set of axes, the simplest solution I have found is changing the figure size with the figsize call.
In the above example, I would start with
fig = plt.figure(figsize = (12,6))
and then just re-render with different proportions until the colorbar no longer dwarfs the main plot.
Define data
x = np.linspace(0,2*np.pi,100)
y = 2*np.sin(x)
Plot
fig = plt.figure()
ax = plt.axes()
fig.add_subplot(ax)
ax.plot(x,y)
Add second axis
newax = plt.axes(axisbg='none')
Gives me ValueError: Unknown element o, even though it does the same thing as what I am about to describe. I can also see that this works (no error) to do the same thing:
newax = plt.axes()
fig.add_subplot(newax)
newax.set_axis_bgcolor('none')
However, it turns the background color of the original figure "gray" (or whatever the figure background is)? I don't understand, as I thought this would make newax transparent except for the axes and box around the figure. Even if I switch the order, same thing:
plt.close('all')
fig = plt.figure()
newax = plt.axes()
fig.add_subplot(newax)
newax.set_axis_bgcolor('none')
ax = plt.axes()
fig.add_subplot(ax)
ax.plot(x,y)
This is surprising because I thought the background of one would be overlaid on the other, but in either case it is the newax background that appears to be visible (or at least this is the color I see).
What is going on here?
You're not actually adding a new axes.
Matplotlib is detecting that there's already a plot in that position and returning it instead of a new axes object.
(Check it for yourself. ax and newax will be the same object.)
There's probably not a reason why you'd want to, but here's how you'd do it.
(Also, don't call newax = plt.axes() and then call fig.add_subplot(newax) You're doing the same thing twice.)
Edit: With newer (>=1.2, I think?) versions of matplotlib, you can accomplish the same thing as the example below by using the label kwarg to fig.add_subplot. E.g. newax = fig.add_subplot(111, label='some unique string')
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
# If you just call `plt.axes()` or equivalently `fig.add_subplot()` matplotlib
# will just return `ax` again. It _won't_ create a new axis unless we
# call fig.add_axes() or reset fig._seen
newax = fig.add_axes(ax.get_position(), frameon=False)
ax.plot(range(10), 'r-')
newax.plot(range(50), 'g-')
newax.axis('equal')
plt.show()
Of course, this looks awful, but it's what you're asking for...
I'm guessing from your earlier questions that you just want to add a second x-axis? If so, this is a completely different thing.
If you want the y-axes linked, then do something like this (somewhat verbose...):
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
newax = ax.twiny()
# Make some room at the bottom
fig.subplots_adjust(bottom=0.20)
# I'm guessing you want them both on the bottom...
newax.set_frame_on(True)
newax.patch.set_visible(False)
newax.xaxis.set_ticks_position('bottom')
newax.xaxis.set_label_position('bottom')
newax.spines['bottom'].set_position(('outward', 40))
ax.plot(range(10), 'r-')
newax.plot(range(21), 'g-')
ax.set_xlabel('Red Thing')
newax.set_xlabel('Green Thing')
plt.show()
If you want to have a hidden, unlinked y-axis, and an entirely new x-axis, then you'd do something like this:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2)
newax = fig.add_axes(ax.get_position())
newax.patch.set_visible(False)
newax.yaxis.set_visible(False)
for spinename, spine in newax.spines.iteritems():
if spinename != 'bottom':
spine.set_visible(False)
newax.spines['bottom'].set_position(('outward', 25))
ax.plot(range(10), 'r-')
x = np.linspace(0, 6*np.pi)
newax.plot(x, 0.001 * np.cos(x), 'g-')
plt.show()
Note that the y-axis values for anything plotted on newax are never shown.
If you wanted, you could even take this one step further, and have independent x and y axes (I'm not quite sure what the point of it would be, but it looks neat...):
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2, right=0.85)
newax = fig.add_axes(ax.get_position())
newax.patch.set_visible(False)
newax.yaxis.set_label_position('right')
newax.yaxis.set_ticks_position('right')
newax.spines['bottom'].set_position(('outward', 35))
ax.plot(range(10), 'r-')
ax.set_xlabel('Red X-axis', color='red')
ax.set_ylabel('Red Y-axis', color='red')
x = np.linspace(0, 6*np.pi)
newax.plot(x, 0.001 * np.cos(x), 'g-')
newax.set_xlabel('Green X-axis', color='green')
newax.set_ylabel('Green Y-axis', color='green')
plt.show()
You can also just add an extra spine at the bottom of the plot. Sometimes this is easier, especially if you don't want ticks or numerical things along it. Not to plug one of my own answers too much, but there's an example of that here: How do I plot multiple X or Y axes in matplotlib?
As one last thing, be sure to look at the parasite axes examples if you want to have the different x and y axes linked through a specific transformation.
This should be easy but I'm having a hard time with it. Basically, I have a subplot in matplotlib that I'm drawing a hexbin plot in every time a function is called, but every time I call the function I get a new colorbar, so what I'd really like to do is update the colorbar. Unfortunately, this doesn't seem to work since the object the colorbar is attached to is being recreated by 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)
I'm now in this annoying place where I'm trying to delete the colorbar axes altogether and simply recreate it. Unfortunately, when I delete the colorbar axes, the subplot axes don't reclaim the space, and calling self.subplot.reset_position() isn't doing what I thought it would.
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)
I think the problem is that with del you cancel the variable, but not the referenced object colorbar.
If you want the colorbar to be removed from plot and disappear, you have to use the method remove of the colorbar instance and to do this you need to have the colorbar in a variable, for which you have two options:
holding the colorbar in a value at the moment of creation, as shown in other answers e.g. cb=plt.colorbar()
retrieve an existing colorbar, that you can do following (and upvoting :)) what I wrote here: How to retrieve colorbar instance from figure in matplotlib
then:
cb.remove() plt.draw() #update plot
Full code and result:
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')
Alright, here's my solution. Not terribly elegant, but not a terrible hack either.
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)
This works for my needs since I only ever have a single subplot. People who run into the same problem when using multiple subplots or when drawing the colorbar in a different position will need to tweak.
I managed to solve the same issue using fig.clear() and 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)
If you have a matplotlib figure object you just need to do fig.delaxes(fig.axes[1])
For example:
Plot with colorbar
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)
Remove colorbar
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])
I had a similar problem and played around a little bit. I came up with two solutions which might be slightly more elegant:
Clear the whole figure and add the subplot (+colorbar if wanted) again.
If there's always a colorbar, you can simply update the axes with autoscale which also updates the colorbar.
I've tried this with imshow, but I guess it works similar for other plotting methods.
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()
I needed to remove colorbars because I was plotting a pcolormesh and adding colorbar to a figure in a loop. Each loop would create a new colorbar and after ten loops I would have ten colorbars. That was bad.
To remove colorbars, I name the pcolormesh and colorbar a variable, then at the end of my loop I remove each. It is important to remove the colorbar before removing the pcolormesh.
Psudo Code:
for i in range(0,10):
p = plt.pcolormesh(datastuff[i])
cb = plt.colorbar(p)
plt.savefig('name_'+i)
cb.remove()
p.remove()
Again, it was necessary to remove the colorbar before the pcolormesh
I am using matplotlib 1.4.0. This is how I solve this problem:
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()
Above code draws a contour fill plot with colorbar, clear it and draw a new contour plot with new colorbar at the same figure.
By using
cb.ax
i am able to identify the colorbar axes and clear the old colorbar.
And specifying cax=cb.ax simply draws the new colorbar in the old colorbar axes.
Don't want to take anything away from the author of this blog post (Joseph Long) but this is clearly the best solution I've found so far. It includes pieces of code, great explanations and many examples.
To summarize, from any output of an axis ax of the command: plot, image, scatter, collection, etc. such as:
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)
You create a color bar axis using the make_axes_locatable and the original axis of the plot.
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()
The created colorbar will have the same size as the figure or subplot and you can modify it's width, location, padding when using the divider.append_axes command.
My solution consists in having an Axes whose only purpose is to hold the colorbar, and clear it entirely when needed.
For example, define those once:
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`.
Then do this as many times as needed:
cax.clear()
colorbar = figure.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap),
ax=ax,
cax=cax,
**kwargs)
"on_mappable_changed" worked in my case. However, according to docs, the method "Typically ... should not be called manually."
if self.cb:
self.cb.on_mappable_changed(hb)
else:
self.cb = self.fig.colorbar(hb)