Matplotlib: how to make tight_layout independent of ticklabels - python

Here is part of my code, which exports figures with tight layout.
...
fig.set_size_inches(8,6.8)
fig.tight_layout(rect=(0, 0, 1, 0.9))
# fig.savefig(path,bbox_inches='tight',dpi=100)
fig.savefig(path,dpi=100)
plt.gcf().clear()
plt.close(fig)
...
Unfortunately when I looping over different frames and y axis labels are changing, there are frames when ticklabels getting very close to the edge and then entire plot is shrinked as shown in figures between red and green lines.
https://dl.dropboxusercontent.com/u/248943005/Montage.png
I tried several cases, but this effect is always coming up in one or another way. It seems like it would be good to make tight layout somehow independent of ticklabels. Is that possible? If no, are there alternatives?

Related

How to adjust subplots borders in matplotlib automatically?

When plotting multiple plots using plt.subplots, most of the time the spacing between subplots is not ideal so the the xtick labels of the top plot would overlap with the title of the bottom plots. There is a way to fix this manually by calling say plt.subplots_adjust(hspace=0.5) and changing the parameters interactively to obtain a decent looking plot. Is there a way to calculate the subplot_adjust parameter automatically? Meaning finding the minimum hspace and wspace so that there is not overlap between texts of the plots.
You can use tight_layout https://matplotlib.org/stable/tutorials/intermediate/tight_layout_guide.html or constrained_layout https://matplotlib.org/stable/tutorials/intermediate/constrainedlayout_guide.html
I'm pretty certain that the closest your going to find to an inbuilt calculation method is:
plt.tight_layout()
or
figure.Figure.tight_layout() #if you are using the object version of the code

enlarging the figure in the pop-out window in matplotlib

How to enlarge a figure inside the pop-out window? I use the following code to generate my figure:
self._fig = plt.figure()
#self._fig = plt.figure(figsize=(2,1)) # tried this, didn't work, only change the size of the pop-out window, but now the figure itself
ax1 = self._fig.add_subplot(211,projection='3d')
# some code for plotting the lines and drawing the spherical surfaces, which is not shown here
ax1.set_xlim(-6,6)
ax1.set_ylim(-6,6)
ax1.set_zlim(-15,15)
ax1.set_aspect(2.5, 'box') # the axis limit and aspect limit is chosen so that the whole figure has the same scale in all directions
#ax1.view_init(elev=90, azim=0)
ax1.set_xlabel('X-axis')
ax1.set_ylabel('Y-axis')
ax1.set_zlabel('Z-axis')
ax1.grid(True)
You can see that there is lot of unused space in the pop-out window, and the figure looks really small. I want to maximize the size of the figure so that it fills the whole pop-out window. Now even if I manually enlarge the pop-out window, the figure still looks the same.
I tried varying the axis limits, but it doesn't seem to work. I tried setting the
figsize in the first line, but it only changes the size of the pop-out window, but the figure itself.
Another problem is that I want to change the 'camera-view' of the figure so that the z-axis (the lone axis) is horizontal. Again, I tried a range of different values in ax.view_init, but I can't get the view I want. I only allows me rotate around the z-axis, while what I need to do is to rotate around x or y-axis by 90deg.
Try calling plt.tight_layout()
before you call plt.show()

Matplotlib: Intelligent figure scale / legend location

Some code gives me the following matplotlib figure:
Unfortunately, the figure size is fixed and hence on the top right, the legend and the lines overlap. Is there any way to have the legend not stack on top of the lines?
I am aware that legend allows ax2.legend(loc=0), where 0 will put it into the "best" location. However, with two y axis as here, this will stack both legends on top of each other - not really the best allocation.
My next best try would be to "scale up" the figure, as manually done with an interactive graph, where I have only scaled up both axis:
Doing this with the "real" figure scale requires iterated "trying numbers and checking how far it goes" procedure - which may need to be redone if the graph changes. Is there any way of having matplotlib compute the scale "intelligently"?
If the best location plt.legend(loc='best') fails, try putting the legend outside of the plot:
plt.legend(loc='upper left', bbox_to_anchor=(1.02, 1), borderaxespad=0)
You can scale only legend, not the whole plot. Link here
More on legends here and also here.

On adjusting margins in matplotlib

I am trying to minimize margins around a 1X2 figure, a figure which are two stacked subplots. I searched a lot and came up with commands like:
self.figure.subplots_adjust(left=0.01, bottom=0.01, top=0.99, right=0.99)
Which leaves a large gap on top and between the subplots. Playing with these parameters, much less understanding them was tough (things like ValueError: bottom cannot be >= top)
My questions :
What is the command to completely minimize the margins?
What do these numbers mean, and what coordinate system does this follow (the non-standard percent thing and origin point of this coordinate system)? What are the special rules on top of this coordinate system?
Where is the exact point this command needs to be called? From experiment, I figured out it works after you create subplots. What if you need to call it repeatedly after you resize a window and need to resize the figure to fit inside?
What are the other methods of adjusting layouts, especially for a single subplot?
They're in figure coordinates: http://matplotlib.sourceforge.net/users/transforms_tutorial.html
To remove gaps between subplots, use the wspace and hspace keywords to subplots_adjust.
If you want to have things adjusted automatically, have a look at tight_layout
Gridspec: http://matplotlib.sourceforge.net/users/gridspec.html

How do I animate the ticks on the x-axis?

I have a matplotlib axes instance inside which I'm animating an AxesImage via blitting.
What I'd like to do is animate the ticks on the x-axis as well.
I am updating the data on the AxesImage (and subsequently) drawing its artist quite frequently, and on each update I'd like to move one extra tick placed to highlight the position of something.
This is what I'm doing right now:
axis = axes.get_xaxis
im.set_data(new_data)
axis.set_ticks([10,20,30,x,t])
axis.set_ticklabels(["p", "u", "z", "z", "i"])
axes.draw_artist(im)
axes.draw_artist(axis)
While I see the ticks correctly updating, the labels are not. I think that the axes bbox does not include the axes, is this possible? If so, how can I animate it? Should I copy and restore from somewhere else?
The axes bbox doesn't include anything outside of the "inside" of the axes (e.g. it doesn't include the tick labels, title, etc.)
One quick way around this is to just grab the entire region of the figure when you're blitting. (E.g. background = canvas.copy_from_bbox(fig.bbox))
This can cause problems if you have multiple subplots and only want to animate one of them. In that case, you can do something along the lines of background = canvas.copy_from_bbox(ax.bbox.expanded(1.1, 1.2)). You'll have to guesstimate the ratios you need, though.
If you need the exact extent of the tick labels, it's a bit trickier. The easiest way is to iterate through the ticklabel objects and get the union with ax.bbox. You can make this a one-liner: ax.bbox.union([label.get_window_extent() for label in ax.get_xticklabels()]).
At any rate, one of those three options should do what you need, I think.

Categories