How do I set the resize-behaviour of subplots? - matplotlib

I'm working with several subplots in a window. Just after plotting it looks great, problem is when I rezise the windows manually. The subplots do not adapt correctly to the new size of the window. How can I fix that?
Window after plotting, looking great:
window after resizing it manually, looks bad:
EDIT:
A simply demo-Code:
from matplotlib.pyplot import *
figure(figsize=(24,6))
subplot(131)
ylabel("test")
plot([1,2,3], label="test1")
plot([3,2,1], label="test2")
subplot(132)
ylabel("test")
plot([1,2,3], label="test1")
plot([3,2,1], label="test2")
subplot(133)
ylabel("test")
plot([1,2,3], label="test1")
plot([3,2,1], label="test2")
tight_layout()
show()
As you see, the plot looks good after plotting it. But when you start to shrink the plot horizontally, the space between plot and plot gets smaller and smaller. And at the end, the ticklabels are on others plots, because there's no space for them. I need to know how to set that the entire plot gets smaller, leving space for the labels.

Maybe not exactly the answer of my question, but it solves my problem:
After creating the figure, you connect the resize-event to an eventhandler:
cid = fig.canvas.mpl_connect('resize_event', onresize)
def onresize(event):
plt.tight_layout()
As Wicket said, I'm just calling tight_layout() again and again, but automatically.

Related

How to fix lines of axes overlapping imshow plot?

When plotting matrices using matplotlib's imshow function the lines of the axes can overlap the actual plot, see the following minimal example (matshow is just a simple wrapper around imshow):
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(3,3))
ax.matshow(np.random.random((50, 50)), interpolation="none", cmap="Blues")
plt.savefig("example.png", dpi=300)
I would expect every entry of the matrix to be represented by a square, but in the top row it is quite obvious that the axis is hiding a bit of the plot resulting in non-square entries. The same is happening for the last column. Since I want the complete matrix to be seen - every entry with the same importance - is there any way this can be fixed?
To me, this is just a visualisation issue. If I run your code and maximise the window, I do not see the overlapping you are talking about:
Otherwise, remove the spines but without hiding the ticks:
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
EDIT
Reduce the thickness of the borders:
[x.set_linewidth(0.3) for x in ax.spines.values()]
The following is the exported image:
With 0.2 the exported image looks like this:

Matplotlib widget, secondary y axis, twinx

i use jupyterlab together with matplotlib widgets. I have ipywidgets installed.
My goal is to choose which y-axis data is displayed in the bottom of the figure.
When i use the interactive tool to see the coordinates i get only the data of the right y-axis displayed. Both would be really nice^^ My minimal code example:
import matplotlib.pyplot as plt
import numpy as np
%matplotlib widgets
x=np.linspace(0,100)
y=x**2
y2=x**3
fig,ax=plt.subplots()
ax2=ax.twinx()
ax.plot(x,y)
ax2.plot(x,y2)
plt.show()
With this example you might ask why not to plot them to the same y-axis but thats why it is a minimal example. I would like to plot data of different units.
To choose which y-axis is used, you can set the zorder property of the axes containing this y-axis to a higher value than that of the other axes (0 is the default):
ax.zorder = 1
However, that will cause this Axes to obscure the other Axes. To counteract this, use
ax.set_facecolor((0, 0, 0, 0))
to make the background color of this Axes transparent.
Alternatively, use the grab_mouse function of the figure canvas:
fig.canvas.grab_mouse(ax)
See here for the (minimal) documentation for grab_mouse.
The reason this works is this:
The coordinate line shown below the figure is obtained by an event callback which ultimately calls matplotlib.Axes.format_coord() on the axes instance returned by the inaxes property of the matplotlib events that are being generated by your mouse movement. This Axes is the one returned by FigureCanvasBase.inaxes() which uses the Axes zorder, and in case of ties, chooses the last Axes created.
However, you can tell the figure canvas that one Axes should receive all mouse events, in which case this Axes is also set as the inaxes property of generated events (see the code).
I have not found a clean way to make the display show data from both Axes. The only solution I have found would be to monkey-patch NavigationToolbar2._mouse_event_to_message (also here) to do what you want.

How to get legend next to plot in Seaborn?

I am plotting a relplot with Seaborn, but getting the legend (and an empty axis plot) printed under the main plot.
Here is how it looks like (in 2 photos, as my screen isn't that big):
Here is the code I used:
fig, axes = plt.subplots(1, 1, figsize=(12, 5))
clean_df['tax_class_at_sale'] = clean_df['tax_class_at_sale'].apply(str)
sns.relplot(x="sale_price_millions", y='gross_sqft_thousands', hue="neighborhood", data=clean_df, ax=axes)
fig.suptitle('Sale Price by Neighborhood', position=(.5,1.05), fontsize=20)
fig.tight_layout()
fig.show()
Does someone has an idea how to fix that, so that the legend (maybe much smaller, but it's not a problem) is printed next to the plot, and the empty axis disappears?
Here is my dataset form (in 2 screenshot, to capture all columns. "sale_price_millions" is the target column)
Since you failed to provide a Minimal, Complete, and Verifiable example, no one can give you a final working answer because we can't reproduce your figure. Nevertheless, you can try specifying the location for placing the legend as following and see if it works as you want
sns.relplot(x="sale_price_millions", y='gross_sqft_thousands', hue="neighborhood", data=clean_df, ax=axes)
plt.legend(loc=(1.05, 0.5))

How to avoid the matplotlib bounding box

I would like to have charts without axis lines, and in general without the overall box of which the two axes are only a symmetrical half. This should work to emphasize values that overlap with the border, and also make things more aesthetic as in some seaborn and ggplot examples out there.
Can this be accomplished?
You could color the axes spines in white, so they are not visible on white background.
For example:
ax.spines['bottom'].set_color('white')
ax.spines['top'].set_color('white')
ax.spines['right'].set_color('white')
ax.spines['left'].set_color('white')
Not sure exactly what you want to achieve, but if you need to get rid of the bounding box in all you figures you can modify default matplotlib parameters (like the seaborn does):
import matplotlib.pyplot as plt
plt.rc('axes.spines', **{'bottom':True, 'left':True, 'right':False, 'top':False})
this will leave only the bottom and left part of the bounding box (you can remove everything by putting False everywhere). In this case you get something like this
Data area is controlled by the Spine class and you can do more with it if you'd like:
spines_api
spines_demo

Matplotlib annotate doesn't work on log scale?

I am making log-log plots for different data sets and need to include the best fit line equation. I know where in the plot I should place the equation, but since the data sets have very different values, I'd like to use relative coordinates in the annotation. (Otherwise, the annotation would move for every data set.)
I am aware of the annotate() function of matplotlib, and I know that I can use textcoords='axes fraction' to enable relative coordinates. When I plot my data on the regular scale, it works. But then I change at least one of the scales to log and the annotation disappears. I get no error message.
Here's my code:
plt.clf()
samplevalues = [100,1000,5000,10^4]
ax = plt.subplot(111)
ax.plot(samplevalues,samplevalues,'o',color='black')
ax.annotate('hi',(0.5,0.5), textcoords='axes fraction')
ax.set_xscale('log')
ax.set_yscale('log')
plt.show()
If I comment out ax.set_xcale('log') and ax.set_ycale('log'), the annotation appears right in the middle of the plot (where it should be). Otherwise, it doesn't appear.
Thanks in advance for your help!
It may really be a bug as pointed out by #tcaswell in the comment but a workaround is to use text() in axis coords:
plt.clf()
samplevalues = [100,1000,5000,10^4]
ax = plt.subplot(111)
ax.loglog(samplevalues,samplevalues,'o',color='black')
ax.text(0.5, 0.5,'hi',transform=ax.transAxes)
plt.show()
Another approach is to use figtext() but that is more cumbersome to use if there are already several plots (panels).
By the way, in the code above, I plotted the data using log-log scale directly. That is, instead of:
ax.plot(samplevalues,samplevalues,'o',color='black')
ax.set_xscale('log')
ax.set_yscale('log')
I did:
ax.loglog(samplevalues,samplevalues,'o',color='black')