This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is there a way of drawing a caption box in matplotlib
Is it possible to add graph description under graph in pylab?
Let's say that I plot the following graph:
import pylab
x = [1,2,3,2,4,5,6,4,7,8]
pylab.plot(x)
pylab.title('My Plot')
pylab.xlabel('My x values')
pylab.ylabel('My y values')
pylab.show()
I also want to insert a few lines that describe the graph, perhaps something like this (not real code):
pylab.description('Figure 1.1 is designed for me to learn basics of Pylab')
Is that possible?
Also, I am vague on differences between pylab and matplotlib, so if there is a solution that works when using matplotlib, it will probably work.
Thank You in Advance
figtext is useful for this since it adds text in the figure coordinates, that is, 0 to 1 for x and y, regardless of the axes. Here's an example:
from pylab import *
figure()
gca().set_position((.1, .3, .8, .6)) # to make a bit of room for extra text
plot([1,2], [3,4])
figtext(.95, .9, "This is text on the side of the figure", rotation='vertical')
figtext(.02, .02, "This is text on the bottom of the figure.\nHere I've made extra room for adding more text.\n" + ("blah "*16+"\n")*3)
xlabel("an interesting axis label")
show()
Here I've used axes.set_position() to make some extra room on the bottom of the figure by making the axes a bit smaller. Here I added room for lots of text and also so the text doesn't bump into the axes label, though it's probably a bit excessive.
Although you asked for text on the bottom, I usually put such labels on the side, so they are more clearly notes and not part of the figure. (I've found it useful, for example, to have a little function that automatically puts the name of the file that generated each figure onto the figure.)
Related
This question already has an answer here:
Matplotlib: Horizontal Linelength in Legend
(1 answer)
Closed 1 year ago.
I have the following code to generate the plots with a shared legend
from matplotlib.legend_handler import HandlerLine2D, HandlerTuple
import matplotlib.pyplot as pt
fig = pt.figure(figsize = (12,4))
gd = fig.add_gridspec(1,2)
p1 = fig.add_subplot(gd[0])
p2 = fig.add_subplot(gd[1])
redLine, = p1.plot([1,2,3], [4,2,5], 'r-')
greenLine, = p1.plot([1,2,3], [8,9,1], 'g--')
redDot, = p2.plot([1,2,3], [4,2,5], 'ro')
greenDot, = p2.plot([1,2,3], [8,9,1], 'gs')
leg = p2.legend([(redLine, redDot), (greenLine, greenDot)], ['Red', 'Green'], handler_map = {tuple: HandlerTuple(ndivide=None)})
Doing this however makes the legend lines a bit too short to clearly differentiate between solid line and dashed, so I'm trying to figure out how to make them longer without making the entire legend bigger.
From the documentation here https://matplotlib.org/stable/api/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D, it seems I should be able to do this by setting the sketch_params property. I have the following
legLines = leg.get_lines()
pt.setp(legLines, sketch_params = (1,2,3))
but this tells me it must be real number, not tuple -- contrary to what the documentation suggests. Also note the numbers in this example are arbitrary since I was just trying to understand how to use this.
I've tried a bunch of different stuff to get this shared legend to happen, and this is by far the closest I've gotten. So I was just hoping someone could help explain how I'm misusing the sketch_params attribute, since it sounds like I should be able to specify the length with that.
EDIT:
It was mentioned in the comments that to get sketch_params to work, I can simply do
for line in legLines:
line.set_sketch_params(1,2,3)
But it turns out that doesn't actually let me change the length of the lines like I wanted to. So I changed the question for more general help on how to achieve that.
Sorry for the noise, turns out the answer is quite simple, and I found it on this post How to adjust the size of matplotlib legend box? and Matplotlib: Horizontal Linelength in Legend. Just need to use the handlelength keyword. Apologies, I wasn't phrasing the question correctly when looking for it. Marking this as duplicate.
Hello there!
I am trying to create a figure consisting of a chloropleth map and a bar plot in Matplotlib. To achieve this, i am using the Geopandas library alongside Pandas and Matplotlib. I've run into an interesting problem that i couldn't find any answer for on the internet. Here's the problem:
This link leads to an image that replicates the problem.
As it can be seen on the image above, the map on the top (generated by Geopandas) does not span the same width as the bar chart on the bottom. There is too much whitespace to the left and the right of the figure. I want to get rid of this whitespace and make the map fit horizontally on the space that is allocated to it. I am also leaving a code sample below for those who wish to recreate it:
fig = plt.figure(figsize = (25.60,14.40)) #Here, i am setting the overall figure size
ax_1 = fig.add_subplot(2,1,1) #This will be the map
istanbul_districts.plot(ax = ax_1,
edgecolor = "black",
alpha = 1,
color = "Red") #Istanbul_districts is a GeoDataFrame object.
ax_2 = fig.add_subplot(2,1,2)
labels = list(health.loc[:,"district_eng"].value_counts().sort_values(ascending = False).index)
from numpy import arange
bar_positions = arange(len(labels)) + 1
bar_heights = h_inst_per_district_eng.loc[:,"health_count"].values.astype(int)
ax_2.bar(bar_positions,bar_heights,
width = 0.7,
align = "center",
color = "blue") #This is a generic barplot from Matplotlib
I am leaving a second image that shows the end result of the code snippet above:
This link also leads to an image that replicates the problem.
It can be clearly seen above that the axes of the two subplots do not start and end on the same location. Perhaps that could be the problem? What can be done to make them the same size?
Thanks to all those answer for their time in advance!
Adding an explanation, since you have found one solution.
If you specify matplotlib figure with two axes in a way you did, you get the figure split in half. Both axes are the same. Let's say that the original ratio of the figure is 1:1, your axes will be both 1:2.
This arbitrary ratio is fine for a bar chart, which can be scaled to essentially any ratio. It does not matter much if it is horizontal or vertical (from a plotting perspective, not data-viz).
However, if you want your map to show correct non-distorted shapes, you can't just specify the aspect ratio. That just follows the data. So if you have a map, which bounding box has 1:1 ratio, you can't expect that it will fill the whole 1:2 axis. GeoPandas changes the aspect ratio to follow the map's ratio.
The reason why the first example leaves gaps on side and the "solution" does not is this. Because the leftover space is on top and on the bottom the axis, it is not shown in the solution. Because it is on sides in the issue, it just stays there. If you had your plots next to each other instead of above, it would be vice versa.
Hope it is clearer.
Hello again!
swatchai's comment set me up on the right track and i found the culprit. Simply adjusting the figsize to a value like (19,19) fixed the problem. I'd still be happy if anyone can explain exactly why this happens.
Here's what it looks like when the figsize is a square (19,19):
Thanks for your efforts!
sns.boxplot(data=df, width=0.5)
plt.title(f'Distribution of scores for initial and resubmission\
\nonly among students who resubmitted at all.\
\n(n = {df.shape[0]})')
I want to use a bigger font, and leave more space in the top white margin so that the title doesn't get crammed in. Surprisingly, I am totally unable to find the option despite some serious googling!
The basic problem you have is that the multi-line title is too tall, and is rendered "off the page".
A few options for you:
the least effort solution is probably to use tight_layout(). plt.tight_layout() manipulates the subplot locations and spacing so that labels, ticks and titles fit more nicely.
if this isn't enough, also look at plt.subplots_adjust() which gives you control over how much whitespace is used around one or more subfigures; you can modify just one aspect at at time, and all the other settings are left alone. In your case, you could use plt.subplots_adjust(top=0.8).
If you are generating a final figure for publication or similar, you might be aiming to tweak a lot to perfect it. In this case, you can precisely control the (sub)plot locations, using add_axes (see this example https://stackoverflow.com/a/17479417).
Here is an example, with a 6-line title for emphasis. The left panel shows the default - with half the title clipped off. The right panel has all measurements the same except the top; the middle has automatically removed whitespace on all sides.
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
data = 55 + 5* np.random.randn(1000,) # some data
vlongtitle = "\n".join(["long title"]*6) # a 6-line title
# using tight_layout, all the margins are reduced
plt.figure()
sns.boxplot(data, width=0.5)
plt.title(vlongtitle)
plt.tight_layout()
# 2nd option, just edit one aspect.
plt.figure()
sns.boxplot(data, width=0.5)
plt.title(vlongtitle)
plt.subplots_adjust(top=0.72)
I would like the title in the legend of my matplotlib figure to be more distant from the content of the legend. Currently, I have the following:
I see the set_title function of the Legend class accepts a prop dictionary, which should be the one described in the text properties page. This one contains the field bbox, where a pad property could be added. But when I try something like the following
legend.set_title('Legend', prop={'bbox':{'pad':somepad}})
python complains that bbox is not an accepted parameter.
I'm using matplotlib 2.1.0 under Python 3.6.3 on Arch Linux.
An obvious workaround would be add a linebreak, like this:
legend.set_title('Legend\n ')
Although one might like the result, matplotlib has the great advantage that everything can be configured to the slightest detail, so I'm looking for a solution which gives me more fine-grained control over this spacing.
Of course introducing a linebreak in the title text as legend.set_title('Legend\n ') is a valid option.
If you don't like that you can set the separation between title and legend handle box manually as
legend._legend_box.sep = 20
Complete example:
import matplotlib.pyplot as plt
plt.plot([1,2], label="some")
plt.plot([1,3], label="label")
legend = plt.legend(title="Legend title", ncol=2)
legend._legend_box.sep = 20
plt.show()
The default separation is labelspacing * fontsize, hence
plt.rcParams["legend.labelspacing"] * plt.rcParams["font.size"] == 0.5 * 10 == 5
Disclaimer: I am very inexperienced using matplotlib and python in general.
Here is the figure I'm trying to make:
Using GridSpec works well for laying out the plots, but when I try to include a colorbar on the right of each row, it changes the size of the corresponding subplot. This seems to be a well known and unavoidable problem with GridSpec. So at the advice of this question: Matplotlib 2 Subplots, 1 Colorbar
I've decided to remake the whole plot using ImageGrid. Unfortunately the documentation only lists the options cbar_mode=[None|single|each] whereas I want 1 colobar per row. Is there a way to do this inside a single ImageGrid? or will I have to make 2 grids and deal with the nightmare of alignment.
What about the 5th plot at the bottom? Is there a way to include that in the image grid somehow?
The only way I can see this working is to somehow nest two ImageGrids into a GridSpec in a 1x3 column. this seems overly complicated and difficult so I don't want to build that script until I know its the right way to go.
Thanks for any help/advice!
Ok I figured it out. It seems ImageGrid uses subplot somehow inside it. So I was able to generate the following plot using something like
TopGrid = ImageGrid( fig, 311,
nrows_ncols=(1,2),
axes_pad=0,
share_all=True,
cbar_location="right",
cbar_mode="single",
cbar_size="3%",
cbar_pad=0.0,
cbar_set_cax=True
)
<Plotting commands for the top row of plots and colorbar>
BotGrid = ImageGrid( fig, 312,
nrows_ncols=(1,2),
axes_pad=0,
share_all=True,
cbar_location="right",
cbar_mode="single",
cbar_size="3%",
cbar_pad=0.0,
)
<Plotting commands for bottom row . . .>
StemPlot = plt.subplot(313)
<plotting commands for bottom stem plot>
EDIT: the whitespace in the color plots is intentional, not some artifact from adding the colorbars