How do I update the legend label spacing after legend font size is changed in matplotlib? - matplotlib

I'm writing a script that saves a figure with multiple formatting styles among which is the font size of legend text.
The legend.labelspacing in rcparams or the matplotlibrc file specifies the label spacing in fractions of the font size, so I might expect the actual spacing to change if the font size is changed. However, since the actual spacing is probably calculated when the legend is first created, any subsequent change to the font size of existing legend text objects has no effect on the label spacing. Is there a way to update the legend label spacing after an existing legend label object's font size has been changed? In summary here's is what I would like to do:
plot something with a legend
save the figure (format according to rcparams or matplotlibrc file)
change several formatting properties (line widths, font sizes, etc.)
save the figure again with the updated formatting properties, including re-adjusted legend label spacing
Is there a way to do this without changing the rcparams and then rebuilding the figure?

Just call legend() with labelspacing parameter, here is an example:
import pylab as pl
pl.plot([0,1],[0,1], label="a")
pl.plot([0,2],[0,2], label="b")
pl.legend()
pl.savefig("p1.png")
pl.legend(labelspacing=2)
pl.savefig("p2.png")
To reuse parameters:
import pylab as pl
pl.plot([0,1],[0,1], label="a")
pl.plot([0,2],[0,2], label="b")
params = dict(loc="right", prop=dict(size=9))
pl.legend(**params)
pl.savefig("p1.png")
params["labelspacing"] = 2
pl.legend(**params)
pl.savefig("p2.png")

Related

TramineR legend position and axis

I'm working with TraMineR and I don't know how to arrange my plot. So basically what i would like to have the legend under the plot and to remove the space between the x and y axis. Any help is welcomed.
The plot:
Sample code:
seqdplot(Activities.seq, with.legend=FALSE)
legend("bottom", legend=attr(Activities.seq, "labels"),
fill=attr(Activities.seq, "cpal"),
inset=-.1, bty="o", xpd=NA, cex=.75,ncol=3)
The family of seqplot functions offers a series of arguments to control the legend as well as the axes. Look at the help page of seqplot (and of plot.stslist.statd for specific seqdplot parameters).
For instance, you can suppress the x-axis with axes=FALSE, and the y-axis with yaxis=FALSE.
To print the legend you can let seqdplot display it automatically using the default with.legend=TRUE option and control it with for examples cex.legend for the font size, ltext for the text. You can also use the ncol argument to set the number of columns in the legend.
The seqplot functions use by default layout to organize the graphic area between the plots and the legend. If you need more fine tuning (e.g. to change the default par(mar=c(5.1,4.1,4.1,2.1)) margins around the plot and the legend), you should create separately the plot(s) and the legend and then organize them yourself using e.g. layout or par(mfrow=...). In that case, the separate graphics should be created by setting with.legend=FALSE, which prevents the display of the legend and disables the automatic use of layout.
The color legend is easiest obtained with seqlegend.
I illustrate with the mvad data that ships with TraMineR. First the default plot with the legend. Note the use of border=NA to suppress the too many vertical black lines.
library(TraMineR)
data(mvad)
mvad.scode <- c("EM", "FE", "HE", "JL", "SC", "TR")
mvad.seq <- seqdef(mvad, 17:86,
states = mvad.scode,
xtstep = 6)
# Default plot with the legend,
seqdplot(mvad.seq, border=NA)
Now, we suppress the x and y axes and modify the display of the legend
seqdplot(mvad.seq, border=NA,
axes=FALSE, yaxis=FALSE, ylab="",
cex.legend=1.3, ncol=6, legend.prop=.11)
Here is how you can control the space between the plot and the x and y axes
seqdplot(mvad.seq, border=NA, yaxis=FALSE, xaxis=FALSE, with.legend=FALSE)
axis(2, line=-1)
axis(1, line=0)
Creating the legend separately and reducing the left, top, and right margins around the legend
op <- par(mar=c(5.1,0.1,0.1,0.1))
seqlegend(mvad.seq, ncol=2, cex=2)
par(op)

How to set a default font weight for x & y labels with matplotlib

Using matplotlib 3.0.1, I had to make a figure where all text had to be in Open Sans Light. As the figure is quite complex, with several panels etc, I wanted to set that up using matplotlib.pyplot.rc(..) to define the default font and font weight for all elements of the figure.
The code below provides a minimal example. Because not everybody might have Open Sans installed, here I use the default font and the 'bold' weight, instead of 'light', to make it more general. So I set the default font weight, and size, then I plot a basic axis with x and y labels and some extra text.
import matplotlib.pyplot as plt
plt.style.use('default')
plt.rc('font', weight='bold', size=15)
fig, ax = plt.subplots(num=1, clear=True)
ax.set(xlabel='x-label', ylabel='y-label')
ax.text(0.2,0.4,'Text')
I expect all bits of text to be in 'bold' font weight. The tick labels and the extra bit of text are, but the x-label and y-label are not. They are in the normal weight. Screen capture here.
The x and y labels seem to get the default font name and size, but not the default weight.
As a workaround, I tried to set a default font weight in the 'axes' options, but that does not seem to be possible. "axes.labelsize" affects the font size of the x,y labels, but the font weight cannot be separately specified.
So it seems the font size is inherited by the x and y labels, if not otherwise specified through axes.labelsize, but the font weight is not inherited by the labels, nor can it be specified as a default for the axes. Note that the font name behaves yet differently. It is inherited by the x, y labels but cannot have a different default in the 'axes' option.
Is there a logic, or is that a bug?
Is there a solution to this? Or do I have to directly set the font weight onto the x-label and y-label objects?

Matplotlib: How to use one multiple mathtext fonts in one figure?

In Matplotlib 2, using rcParams, I can set mathtext.fontset="cm" or mathtext.fontset="dejavusans" to control the font used in math-mode text in my figure. Is there a way so that within a single figure, I draw one text with one fontset, then a different text with a different fontset?

pandas.plot and pyplot.save_fig create different sized PNGs for same figsize

When I call the same function that uses pandas.plot with the same figsize, I get different sized PNG files. The width is same but the height in pixels changes. I suspect that the length of the x-axis labels changes the height.I have not yet tried directly calling the matplotlib functions.
I have also tried plt.rcParams['figure.figsize'] = (7,4). The problem does not appear to be in how figsize is set. My print_fig_info always produces the desire values.
# Primitive way that confirmed that the figure size does not change
def print_fig_info(label=""):
print(label,str(plt.gcf().get_size_inches()))
def my_plot(df):
global c
print_fig_info("Before plot")
df.plot(kind='bar', figsize=(7,4))
print_fig_info("After plot")
# want to make output files unique
c += 1
plt.savefig("output"+str(c), bbox_inches='tight', dpi='figure')
In your call to savefig you explicitely ask matplotlib to change the figsize to the minimal size that still fits all the elements in via bbox_inches='tight'.
Or in other words, bbox_inches='tight' is especially designed for changing the figure size to the minimum bounding box, and matplotlib is therefore doing what it's being asked for.
Solution: Don't use bbox_inches='tight'.

changing the font of the xthicklabels from a stylesheet in matplotlib

I want to use a non-standard font for labels on the axes of my matplotlib graph. I cannot assume users have installed the font, so I am providing the ttf file.
I have it working by doing the following:
fname = '/bla/bla/myfont.ttf'
ticks_font = matplotlib.font_manager.FontProperties(fname = fontPath, style='normal', size=12, weight='normal', stretch='normal')
for label in ax.get_xticklabels() :
label.set_fontproperties(ticks_font)
However, as I have many graphs with different styles, I want to move all layout to stylesheets. I've tried to do:
font.serif : '/bla/bla/myfont.ttf'
font.family :'serif'
But that doesn't seem to work.