Placing the legend outside the plot - python

I want to position the legend outside the drawing box. I do not find a clean way to do this. The main problem is having everything fit on the file saved. The only thing I have been able to figure out is this code:
#! /usr/bin/python
import matplotlib
# matplotlib.use('pdf')
from matplotlib.pyplot import *
subplot(111)
plot([1,2,3], label="test1")
l=legend(bbox_to_anchor=(1.05, 1), loc=2,borderaxespad=0)
tight_layout(rect=(0,0,0.8,1))
savefig('test.pdf')
There are a couple of caveats:
The tight_layout seems to be incompatible with matplotlib.use('pdf')
The 0.8 in the tight_layout has been found by trials and errors. If I replace label="test" by label="this is a very very long test", the legend will once again get out of the border of the file. I would like that to be adjusted automatically. I have not been able to retrieve the size of the legend. l.get_frame().get_width() seems to always return 1.0.
The tight_layout changes the size of the plot. What I would like to achieve is: specify the size of the plot in cm (or in inches). Placing the legend outside the plot. Having a file with the correct size in order that everything fits in, including the legend; without having to resort to trial and errors.

You can look at the answer to this question by Joe Kington.
The answer describes most of the options you can play around with regarding placement of legends.

Related

What is the meaning of colorbar.solids in Pyplot?

I am creating contour plots with matplotlib/pyplot and trying to print out a customized colorbar as well. I am also attempting to have the colorbar be printed in a completely separate image file from the plot.
sub_fig = plt.figure()
sub_ax = plt.axes()
sub_ax.axis("off")
#cs2 is a contourf object
sep_cb = plt.colorbar(cs2, cax=sub_ax)#, cax = new_figure.legend, ax=new_figure.legend, orientation="vertical")
sep_cb.shrink=0.5
sep_cb.fraction=.1
sep_cb.drawedges=True
# ~sep_cb.solids.set_edgecolor("white")
plt.savefig("colorbar_"+str(ii)+".png")
plt.clf()
So I am referring to the documentation here: https://matplotlib.org/api/_as_gen/matplotlib.pyplot.colorbar.html but I find that changing the settings does not change the appearance of my colorbar. What I want is actually to make it pretty small, and have the sections separated by some whitespace, with the values/ labels to the right. I can't shrink it, or make the values appear. No matter what, it appears like this:
colorbar
However, if I enable that sep_cb.solids line, I get some lines between the sections. But the only reason I even know about this is because of this section:
It is known that some vector graphics viewer (svg and pdf) renders white gaps between segments of the colorbar. This is due to bugs in the viewers not matplotlib. As a workaround the colorbar can be rendered with overlapping segments:
cbar = colorbar()
cbar.solids.set_edgecolor("face")
draw()
It seems that colorbar.solids can be used to set some options on the plot, but I cannot find any documentation on that directly, I don't even see it mentioned elsewhere on the page. Is this some basic pyplot thing I have managed to overlook? Any help would be appreciated, thank you.

Jupyter: Seaborn pairplot difficult to set graph dimensions for?

I was trying to create a bivariate scatterplot of each variable against every other that are in a dataframe, and I found sns.pairplot() was exactly what I needed.
However, no matter what I do (and I have tried all of the advice found in this question), the plots keep coming out too spread out, as well as in general too big. In the picture below, only the first two rows and four columns out of 12 variables display on my entire screen.
I have found out that my use of
%config InlineBackend.figure_format = 'svg'
to create non-blurry graphs on my high-ppi screen is partially at blame, as without it, I instead get this graph, which fits perfectly on my screen, but is now too small and I would prefer to scroll slightly around while having a bigger pic.
(note: the additional options below have no effect)
How can I make the grid of plots customizable in its overall size as well as spacing? As it stands, no options work, and one graphics backend (the default one) produces too small graphs, while the 'svg' backend produces too large ones.
EDIT: Editing sns.set(rc={'figure.figsize':(x,y)}) or the height/ aspect options improve nothing: the former produces no change, while the latter two change how big the individual plots are (with height=1 making them indecipherable), but the overall "grid" is still as bulky and overly large as before.
Essentially you are asking how to display the figure in its original size in a jupyter notebook.
That translates into how to add scrollbars if it exceeds the room it's given by the layout of the output cell.
I think for the horizontal direction this can be done as follows. However for the vertical direction this does not seem to work.
%matplotlib inline
# Cell2
from IPython.display import display, HTML
CSS = """div.output_area img {max-width:None !important;max-height: None !important";}"""
display(HTML('<style>{}</style>'.format(CSS)))
# Cell3
import matplotlib.pyplot as plt
fig, ax = plt.subplots(ncols=8, figsize=(20,10))

imshow in subplot with interactive mode

I cannot get matshow() or imshow() to actually display the plot when both of the following conditions are true: (1) interactive mode is on: import matplotlib.pyplot as plot; plot.ion(), and (2) I am trying to use matshow on a specific subplot: fig = plot.figure(); ax = fig.add_subplot(111); ax.matshow([[1,2],[3,0]]).
Using plot.matshow([[1,2],[3,0]]) (note: no explicit axes) works find in interactive mode, but will always create a new figure window with a single axes object. The above code with the subplot also works fine without interactive mode using plot.show(), and will put the image on the correct axes.
More oddly, the above code with the subplot will show the image if I interact with the figure, such as by using the zoom tool and clicking randomly in the figure window (there is no visible axes object, but I just click somewhere in the middle of the figure window).
Any ideas what might be causing this, how I could fix it, or how I could get around it to use matshow or imshow on a specified subplot (the end use case is to have more than 1 subplot in the figure)? This occurs in python (2.7.6) and ipython (1.1.1)
This may have something to do with this documentation:
Display an array as a matrix in a new figure window.
However, you may as well use imshow with suitable arguments:
import matplotlib.pyplot as plt
plt.imshow(mat, interpolation='nearest', origin='upper', aspect='equal')
This should do the same thing while being a bit less odd. This is actually exactly what matshow does internally. It just adds a few tick markers to the image.
Also, by having a look at the source (or closely reading the help string), you may try to do:
plt.matshow(mat, fignum=0)
This should force it use current axis, which it picks by using gca.
In addition to this, there is ax.matshow which you used, as well. Actually plt.matshow is a very thin wrapper around ax.matshow, mostly to create the new image.
If you still have problems with matshow or imshow in subplots, please make a minimal complete example for us to try! Here is something I tried in the interactive shell (IPython):
figure()
ax = subplot(121)
ax2 = subplot(122)
ax.matshow(random.random((20,30)))
ax2.plot(linspace(-1,1,100), linspace(-1,1,100)**2)
draw()
(Could the problem be a missing draw?)
What I got:

How to prevent Matplotlib from clipping away my axis labels?

I'm preparing some plots for a scientific paper, which need to be wide and short in order to fit into the page limit. However, when I save them as pdf, the x axis labels are missing, because (I think) they're outside the bounding box.
Putting the following into an iPython notebook reproduces the problem.
%pylab inline
pylab.rcParams['figure.figsize'] = (8.0, 2.0)
plot([1,5,2,4,6,2,1])
xlabel("$x$")
ylabel("$y$")
savefig("test.pdf")
The resulting pdf file looks like this:
How can I change the bounding box of the pdf file? Ideally I'd like a solution that "does it properly", i.e. automatically adjusts the size so that everything fits neatly, including getting rid of that unnecessary space to the left and right - but I'm in a hurry, so I'll settle for any way to change the bounding box, and I'll guess numbers until it looks right if I have to.
After a spot of Googling, I found an answer: you can give bbox_inches='tight' to the savefig command and it will automatically adjust the bounding box to the size of the contents:
%pylab inline
pylab.rcParams['figure.figsize'] = (8.0, 2.0)
plot([1,5,2,4,6,2,1])
xlabel("$x$")
ylabel("$y$")
savefig("test.pdf",bbox_inches='tight')
Those are some tight inches, I guess.
Note that this is slightly different from Ffisegydd's answer, since it adjusts the bounding box to the plot, rather than changing the plot to fit the bounding box. (But both are fine for my purposes.)
You can use plt.tight_layout() to have matplotlib adjust the layout of your plot. tight_layout() will automatically adjust the dimensions, and can also be used when you have (for example) overlapping labels/ticks/etc.
%pylab inline
pylab.rcParams['figure.figsize'] = (8.0, 2.0)
plot([1,5,2,4,6,2,1])
xlabel("$x$")
ylabel("$y$")
tight_layout()
savefig("test.pdf")
Here is a .png of the output (can't upload pdfs to SO but I've checked it and it works the same way for a pdf).
If you are preparing the plot for a scientific paper, I suggest to do the 'clipping' by yourself,
using
plt.subplots_adjust(left,right,bottom,top,..)
after the creation of the figure and before saving it. If you are running from an ipython console you can also call subplots_adjust after the generation of the figure, and tune the margins by trial and error. Some backends (I think at least the Qt backend) also expose a GUI for this feature.
Doing this by hand takes time, but most times provides a more precise result, especially with Latex rendering in my experience.
This is the only option whenever you have to stack vertically or horizontally two figures (with a package like subfigure for example), as tight_layout will not guarantee the same margins in the two figures, and the axis will result misaligned in the paper.
This is a nice link on using matplotlib for publications, covering for example how to set the figure width to match the journal column width.

Saving images in Python at a very high quality

How can I save Python plots at very high quality?
That is, when I keep zooming in on the object saved in a PDF file, why isn't there any blurring?
Also, what would be the best mode to save it in?
png, eps? Or some other? I can't do pdf, because there is a hidden number that happens that mess with Latexmk compilation.
If you are using Matplotlib and are trying to get good figures in a LaTeX document, save as an EPS. Specifically, try something like this after running the commands to plot the image:
plt.savefig('destination_path.eps', format='eps')
I have found that EPS files work best and the dpi parameter is what really makes them look good in a document.
To specify the orientation of the figure before saving, simply call the following before the plt.savefig call, but after creating the plot (assuming you have plotted using an axes with the name ax):
ax.view_init(elev=elevation_angle, azim=azimuthal_angle)
Where elevation_angle is a number (in degrees) specifying the polar angle (down from vertical z axis) and the azimuthal_angle specifies the azimuthal angle (around the z axis).
I find that it is easiest to determine these values by first plotting the image and then rotating it and watching the current values of the angles appear towards the bottom of the window just below the actual plot. Keep in mind that the x, y, z, positions appear by default, but they are replaced with the two angles when you start to click+drag+rotate the image.
Just to add my results, also using Matplotlib.
.eps made all my text bold and removed transparency. .svg gave me high-resolution pictures that actually looked like my graph.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# Do the plot code
fig.savefig('myimage.svg', format='svg', dpi=1200)
I used 1200 dpi because a lot of scientific journals require images in 1200 / 600 / 300 dpi, depending on what the image is of. Convert to desired dpi and format in GIMP or Inkscape.
Obviously the dpi doesn't matter since .svg are vector graphics and have "infinite resolution".
You can save to a figure that is 1920x1080 (or 1080p) using:
fig = plt.figure(figsize=(19.20,10.80))
You can also go much higher or lower. The above solutions work well for printing, but these days you want the created image to go into a PNG/JPG or appear in a wide screen format.
Okay, I found spencerlyon2's answer working. However, in case anybody would find himself/herself not knowing what to do with that one line, I had to do it this way:
beingsaved = plt.figure()
# Some scatter plots
plt.scatter(X_1_x, X_1_y)
plt.scatter(X_2_x, X_2_y)
beingsaved.savefig('destination_path.eps', format='eps', dpi=1000)
In case you are working with seaborn plots, instead of Matplotlib, you can save a .png image like this:
Let's suppose you have a matrix object (either Pandas or NumPy), and you want to take a heatmap:
import seaborn as sb
image = sb.heatmap(matrix) # This gets you the heatmap
image.figure.savefig("C:/Your/Path/ ... /your_image.png") # This saves it
This code is compatible with the latest version of Seaborn. Other code around Stack Overflow worked only for previous versions.
Another way I like is this. I set the size of the next image as follows:
plt.subplots(figsize=(15,15))
And then later I plot the output in the console, from which I can copy-paste it where I want. (Since Seaborn is built on top of Matplotlib, there will not be any problem.)

Categories

Resources