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)
Related
Solved: This problem occurred with matplotlib 3.4, updating to 3.5 fixed the issue.
I am plotting multiple subplots in a graph, which all have titles, labels and subplot titles. To keep everything visible and the right size, I am using constrained_layout.
I would like to add a title that is aligned to the left. However, when I specify the x position (even as 0.5 which is the default), the title overlaps with the graph.
My plots are much more complex, but this already shows my issue:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(10, 5), constrained_layout=True)
gs = fig.add_gridspec(1,1)
ax1 = fig.add_subplot(gs[0,0])
fig.suptitle('Title', ha='left')
Only changing the last line of code:
fig.suptitle('Title with x-position', x=0.5, ha='left')
I was first using tight layout, but switched to constrained_layout because tight_layout did not keep the specified size of the figure when exporting it.
I have also switched from subplots to gridspec because I read that constrained_layout does not support subplots.
I know I can add extra space with fig.set_constrained_layout_pads(h_pad=0.3), but this also adds space below the plots, which I would like to avoid.
Hopefully someone can tell me why this happens and how I can get a title aligned to the left that does not overlap with the plot!
The problem occurred in Matplotlib 3.4, updating to 3.5 fixed the issue
In this example on how to create partial dependence plots, it is explained that the ticks on the x-axis are decile marks. However, the API does not tell how to active/deactivate them. I tried finding a way of removing those, but so far no success has been achieved.
I'd appreciate any help in removing the bottom ticks. Thank you!
The lines are attributes of the PartialDependenceDisplay returned by plot_partial_dependence. The attributes are called deciles_vlines_ (for vertical lines) and deciles_hlines_ for horizontal lines. If you don't want to show them you can set them to not be visible with e.g. plt.setp
import matplotlib.pyplot as plt
disp = plot_partial_dependence(...)
plt.setp(disp.deciles_vlines_, visible=False)
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!
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
I would like to plot a set of points using pyplot in matplotlib but have none of the points be on the edge of my axes. The autoscale (or something) sets the xlim and ylim such that often the first and last points lie at x = xmin or xmax making it difficult to read in some situations.
This is more often problematic with loglog() or semilog() plots because the autoscale would like xmin and xmax to be exact powers of ten, but if my data contains only three points, e.g. at xdata = [10**2,10**3,10**4] then the first and last points will lie on the border of the plot.
Attempted Workaround
This is my solution to add a 10% buffer to either side of the graph. But is there a way to do this more elegantly or automatically?
from numpy import array, log10
from matplotlib.pyplot import *
xdata = array([10**2,10**3,10**4])
ydata = xdata**2
figure()
loglog(xdata,ydata,'.')
xmin,xmax = xlim()
xbuff = 0.1*log10(xmax/xmin)
xlim(xmin*10**(-xbuff),xmax*10**(xbuff))
I am hoping for a one- or two-line solution that I can easily use whenever I make a plot like this.
Linear Plot
To make clear what I'm doing in my workaround, I should add an example in linear space (instead of log space):
plot(xdata,ydata)
xmin,xmax = xlim()
xbuff = 0.1*(xmax-xmin)
xlim(xmin-xbuff,xmax+xbuff))
which is identical to the previous example but for a linear axis.
Limits too large
A related problem is that sometimes the limits are too large. Say my data is something like ydata = xdata**0.25 so that the variance in the range is much less than a decade but ends at exactly 10**1. Then, the autoscale ylim are 10**0 to 10**1 though the data are only in the top portion of the plot. Using my workaround above, I can increase ymax so that the third point is fully within the limits but I don't know how to increase ymin so that there is less whitespace at the lower portion of my plot. i.e., the point is that I don't always want to spread my limits apart but would just like to have some constant (or proportional) buffer around all my points.
#askewchan I just succesfully achieved how to change matplotlib settings by editing matplotlibrc configuration file and running python directly from terminal. Don't know the reason yet, but matplotlibrc is not working when I run python from spyder3 (my IDE). Just follow steps here matplotlib.org/users/customizing.html.
1) Solution one (default for all plots)
Try put this in matplotlibrc and you will see the buffer increase:
axes.xmargin : 0.1 # x margin. See `axes.Axes.margins`
axes.ymargin : 0.1 # y margin See `axes.Axes.margins`
Values must be between 0 and 1.
Obs.: Due to bugs, scale is not correctly working yet. It'll be fixed for matplotlib 1.5 (mine is 1.4.3 yet...). More info:
axes.xmargin/ymargin rcParam behaves differently than pyplot.margins() #2298
Better auto-selection of axis limits #4891
2) Solution two (individually for each plot inside the code)
There is also the margins function (for put directly in the code). Example:
import numpy as np
from matplotlib import pyplot as plt
t = np.linspace(-6,6,1000)
plt.plot(t,np.sin(t))
plt.margins(x=0.1, y=0.1)
plt.savefig('plot.png')
Obs.: Here scale is working (0.1 will increase 10% of buffer before and after x-range and y-range).
A similar question was posed to the matplotlib-users list earlier this year. The most promising solution involves implementing a Locator (based on MaxNLocator in this case) to override MaxNLocator.view_limits.