I'm currently trying to use Seaborn to create plots for my academic papers. The plots look great and easy to generate, but one problem that I'm having some trouble with is having the fine control on the font size in the plots.
My font size in my paper is 9pt and I would like to make sure the font size in my plots are either 9pt or 10pt. But in seaborn, the font size is mainly controlled through font scale sns.set_context("paper", font_scale=0.9). So it's hard for me to find the right font size except through trial and error. Is there a more efficient way to do this?
I also want to make sure the font size is consistent between different seaborn plots. But not all my seaborn plots have the same dimension, so it seems like using the same font_scale on all the plots does not necessarily create the same font size across these different plots?
I've attached my code below. I appreciate any comments on how to format the plot for a two column academic paper. My goal is to be able to control the size of the figure without distorting the font size or the plot. I use Latex to write my paper.
# Seaborn setting
sns.set(style='whitegrid', rc={"grid.linewidth": 0.1})
sns.set_context("paper", font_scale=0.9)
plt.figure(figsize=(3.1, 3)) # Two column paper. Each column is about 3.15 inch wide.
color = sns.color_palette("Set2", 6)
# Create a box plot for my data
splot = sns.boxplot(data=df, palette=color, whis=np.inf,
width=0.5, linewidth = 0.7)
# Labels and clean up on the plot
splot.set_ylabel('Normalized WS')
plt.xticks(rotation=90)
plt.tight_layout()
splot.yaxis.grid(True, clip_on=False)
sns.despine(left=True, bottom=True)
plt.savefig('test.pdf', bbox_inches='tight')
You are right. This is a badly documented issue. But you can change the font size parameter (by opposition to font scale) directly after building the plot. Check the following example:
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset("tips")
b = sns.boxplot(x=tips["total_bill"])
b.axes.set_title("Title",fontsize=50)
b.set_xlabel("X Label",fontsize=30)
b.set_ylabel("Y Label",fontsize=20)
b.tick_params(labelsize=5)
plt.show()
, which results in this:
To make it consistent in between plots I think you just need to make sure the DPI is the same. By the way it' also a possibility to customize a bit the rc dictionaries since "font.size" parameter exists but I'm not too sure how to do that.
NOTE: And also I don't really understand why they changed the name of the font size variables for axis labels and ticks. Seems a bit un-intuitive.
It is all but satisfying, isn't it? The easiest way I have found to specify when setting the context, e.g.:
sns.set_context("paper", rc={"font.size":8,"axes.titlesize":8,"axes.labelsize":5})
This should take care of 90% of standard plotting usage. If you want ticklabels smaller than axes labels, set the 'axes.labelsize' to the smaller (ticklabel) value and specify axis labels (or other custom elements) manually, e.g.:
axs.set_ylabel('mylabel',size=6)
you could define it as a function and load it in your scripts so you don't have to remember your standard numbers, or call it every time.
def set_pubfig:
sns.set_context("paper", rc={"font.size":8,"axes.titlesize":8,"axes.labelsize":5})
Of course you can use configuration files, but I guess the whole idea is to have a simple, straightforward method, which is why the above works well.
Note: If you specify these numbers, specifying font_scale in sns.set_context is ignored for all specified font elements, even if you set it.
I've just spent way too long trying to find out the actual values of the "paper" sns context. I could only find it for "talk" and honestly I am raging! Just going to use
sns.set_context("paper", rc{"font.size":8,"axes.titlesize":8,"axes.labelsize":5})
even though these might be the values anyway!!!!
Related
I have a reasonably complicated grid of subplots that involves two sets (one on the left and another on the right) of columns plotting a set of quantities for each row, separated by a common legend to label the entries in each row.
Here is a sample of what I want to accomplish
Using matplotlib with constrained_layout = True works 95% perfectly for applying the out optimal sizes & spacing for the columns, down to the tricky case of having the legend run down the middle. The remaining 5% is highlighted in red, where the wordy x-axis tick labels seem to push away the columns: it would be perfect if there was a way to make the layout engine "ignore" the tick labels in determining the spacing.
Methods using other libraries are also appreciated. Thank you in advance.
What I tried:
subplots_adjust
GridSpec
The main difficulty with those attempts:
constrained_layout is incompatible with those settings, so one must sacrifice the optimized legend spacing at the cost of getting the column spacing right, or vice versa.
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
I want to know how to get my x axis labels to display bigger so that the team labels aren't overlapping. I'm sure it's just a matter of configuring the chart size
My code:
plt.plot(prem_data.Team, prem_data.attack_scored,'o')
plt.plot(prem_data.Team, prem_data.defence_saves)
plt.xlabel("Team")
plt.ylabel("Attack goals scored & Defence tackles")
plt.legend(["attack scored", "defence saved"])
plt.show()
I can imagine there being two mutually non-exclusive solutions.
Directly alter the size of the font. This can be achieved via calling plt.rcParams.update({'font.size': <font_size>}), assuming that you have imported matplotlib.pyplot under the alias plt, as you have done in the source code provided. You would probably want to set the <font_size> to be small to prevent overlapping labels, but this would require some experimentation.
Increase the size of the figure. This can be done in a number of ways, but perhaps the simplest method you can implement with minimal edits to your current code would be to use the command plt.rcParams["figure.figsize"] = <fig_size> where <fig_size> is a tuple specifying the size of the figure in inches, such as (10, 5).
With some trial and error, you should be able to manipulate the size of the font and the figure to produce a plot with improved readability.
Note: The method for altering figure size I introduced above is not the most conventional way to go about this problem. Instead, it is much more common to use matplotlib.pyplot.figure or similar variants. For more information, I recommend that you check out this thread and the documentation.
When generating a new figure or axis with matplotlib (or pyplot), there is (I assume) some sort of automated way to determine how many ticks are appropriate for each axis.
Unfortunately, this often results in labels which are too close to be read comfortably, or even overlap. I'm aware of the ways to specify tick locations and labels explicitly (e.g. ax.set_xticks, ax.set_xtick_labels, but I wonder if whatever does the automatic tick distribution if nothing is specified can be influenced by some global matplotlib parameter(s).
Do such global parameters exist, and what are they?
I'm generating lots of figures automatically and save them, and it can get a little annoying having to treat them all individually ...
In case there is no simple way to tell matplotlib to thin out the labels, is there some other workaround to achieve more generous spacing between them?
by reading the documentation of xticks matplotlib.pyplot.xticks there seems to be no such global arguments.
However it is very simple to get around it by using the explicit xticks and xticks_labels taking into account that you can:
change the font size (decrease it)
rotate the labels (by 45°) or make them vertical (less overlapping).
increase the fig size.
program a function that generates adaptif xticks based on your input.
and many other possible workarounds.
I while ago, I was comparing the output of two functions using python and matplotlib. The result was as good as simple, since plotting with matplotlib is quite easy: I just plotted two arrays with different markers. Piece of cake.
Now I find myself with the same problem, but now I have a lot of pair of curves to compare. I initially tried plotting everything with different colors and markers. This did not satisfy me since the ranges of each curve are not quite the same. In addition to this, I quickly ran out of colors and markers that were sufficiently different to identify (RGBCMYK, after that, custom colors resemble any of the previous ones).
I also tried subplotting each pair of curves, obtaining a window with many plots. Too crowded.
I tried one window per plot, too many windows.
So I was just wondering if there is any existing widget or if you have any suggestion (or a different idea) to accomplish this:
I want to see a pair of curves and then select easily the next one, with a slidebar, button, mouse scroll, or any other widget or event. By changing curves, the previous one should disappear, the legend should change and its axis as well.
Well I managed to do it with an event handler for mouse clicks. I will change it for something more useful, but I post my solution anyway.
import matplotlib.pyplot as plt
figure = plt.figure()
# plotting
plt.plot([1,2,3],[10,20,30],'bo-')
plt.grid()
plt.legend()
def on_press(event):
print 'you pressed', event.button, event.xdata, event.ydata
event.canvas.figure.clear()
# select new curves to plot, in this example [1,2,3] [0,0,0]
event.canvas.figure.gca().plot([1,2,3],[0,0,0], 'ro-')
event.canvas.figure.gca().grid()
event.canvas.figure.gca().legend()
event.canvas.draw()
figure.canvas.mpl_connect('button_press_event', on_press)
Sounds like you want to embed matplotlib in an application. There are some resources available for that:
user interface examples
Embedding in WX
I really like using traits. If you follow the tutorial Writing a graphical application for scientific programming , you should be able to do what you want. The tutorial shows how to interact with a matplotlib graph using graphical user interface.