I've observed errors when rendering math in matplotlib 2.0.2, when using the default mathtext as opposed to the LaTeX math rendering engine. It seems that some glyphs (in my case the minus and the multiplication sign) is not recognized by mathtext. What makes it really weird is that the error only occurs when these particular glyphs appear in tick labels. When I deliberately type some mathy expression into e.g. the figure title, it works fine.
Consider the below example and the resultant image:
import matplotlib
import matplotlib.pyplot as plt
# Customize matplotlib
matplotlib.rcParams.update({# Use mathtext, not LaTeX
'text.usetex': False,
# Use the Computer modern font
'font.family': 'serif',
'font.serif': 'cmr10',
'mathtext.fontset': 'cm',
})
# Plot
plt.semilogy([-0.03, 0.05], [0.3, 0.05])
plt.title(r'$-6\times 10^{-2}$')
plt.savefig('test.png')
As seen on the image, multiplication and some minus signs in the tick labels have been replaced with other characters. If I use LaTeX (by setting 'text.usetex' to True), everything renders nicely. Why does this happen, and more importantly, how can I fix it without changing from mathtext to LaTeX?
Additional information
This is the warning which gets printed when running the example code:
mathtext.py:866: MathTextWarning: Font 'default' does not have a glyph for '\times' [U+d7]
MathTextWarning)
mathtext.py:867: MathTextWarning: Substituting with a dummy symbol.
warn("Substituting with a dummy symbol.", MathTextWarning)
Note that the minus signs appearing in exponents get rendered correctly. These also do not render probably if I leave out 'mathtext.fontset': 'cm', producing another, similar warning:
mathtext.py:866: MathTextWarning: Font 'default' does not have a glyph for '-' [U+2212]
MathTextWarning)
mathtext.py:867: MathTextWarning: Substituting with a dummy symbol.
warn("Substituting with a dummy symbol.", MathTextWarning)
Also, if I include 'axes.unicode_minus': False in the rcParams (and keep 'mathtext.fontset': 'cm'), all minus signs render properly, though the problem remains for the multiplication signs.
The multiplication sign error do not seem to be a problem on older versions of matplotlib (I've tested 1.5.1, 1.4.3 and 1.3.1). However, these matplotib's insist on only producing tick labels at 10⁻², 10⁻¹, 1, 10, 10² etc., and so no multiplication sign is ever needed.
Bug report
This has been submitted as a bug report to Matplotlib.
I find the STIX fonts to be acceptable substitutes for computer modern.
import matplotlib
import matplotlib.pyplot as plt
# Customize matplotlib
matplotlib.rcParams.update(
{
'text.usetex': False,
'font.family': 'stixgeneral',
'mathtext.fontset': 'stix',
}
)
# Plot
plt.semilogy([-0.03, 0.05], [0.3, 0.05])
plt.title(r'$-6\times 10^{-2}$')
plt.savefig('test.png')
This produces the following output on my laptop:
Cause of problem
I now understand what is going on. The yticklabels all have a format similar to
r'$\mathdefault{6\times10^{-2}}$'
which works fine for major tick labels, where the \times10^{-2} part is absent. I believe this fails for minor tick labels because \times does not work inside of \mathdefault{}. As stated here, \mathdefault{} is used to produce regular (non-math) text with the same font as is used for mathtext, with the limitation that far fewer symbols are available. As everything inside of \mathdefault{} is math, the use of \mathdefault{} is completely redundant, and so it can safely be removed. This solves the issue.
Solution
One could solve this using matplotlib's tick formatters. I would like however to keep the default (minor) tick label positions and (intended) formatting, and so an easier solution is simply to rip out the \mathdefault part of the tick labels:
import warnings
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.mathtext import MathTextWarning
# Customize matplotlib
matplotlib.rcParams.update({# Use mathtext, not LaTeX
'text.usetex': False,
# Use the Computer modern font
'font.family': 'serif',
'font.serif': 'cmr10',
'mathtext.fontset': 'cm',
# Use ASCII minus
'axes.unicode_minus': False,
})
# Function implementing the fix
def fix(ax=None):
if ax is None:
ax = plt.gca()
fig = ax.get_figure()
# Force the figure to be drawn
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=MathTextWarning)
fig.canvas.draw()
# Remove '\mathdefault' from all minor tick labels
labels = [label.get_text().replace('\mathdefault', '')
for label in ax.get_xminorticklabels()]
ax.set_xticklabels(labels, minor=True)
labels = [label.get_text().replace('\mathdefault', '')
for label in ax.get_yminorticklabels()]
ax.set_yticklabels(labels, minor=True)
# Plot
plt.semilogy([-0.03, 0.05], [0.3, 0.05])
plt.title(r'$-6\times 10^{-2}$')
fix()
plt.savefig('test.png')
The tricky part in writing this fix is the fact that you cannot get the tick labels before the figure has been drawn. Thus we need to first call fig.canvas.draw(). This will raise the warning, which I have suppressed. This also means that you should call fix() as late as possible, so that all axes gets drawn as they would in the end. Finally (as stated already in the question), the 'axes.unicode_minus' has been set to False to fix the similar issue with the minus signs.
The resultant image:
The keen LaTeX eye might spot that something is still slightly off regarding the minuses in the xticklabels. This is unrelated to the question, but happens because the numbers in the xticklabels are not enclosed in $...$.
Update for matplotlib 3.1.0
From matplotlib version 3.1.0, the warning is emitted through the logging module, not warnings. To silent the warning, replace
# Force the figure to be drawn
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=MathTextWarning)
fig.canvas.draw()
with
# Force the figure to be drawn
import logging
logger = logging.getLogger('matplotlib.mathtext')
original_level = logger.getEffectiveLevel()
logger.setLevel(logging.ERROR)
with warnings.catch_warnings():
warnings.simplefilter('ignore', category=MathTextWarning)
fig.canvas.draw()
logger.setLevel(original_level)
which now ignores the warning regardless of whether it is emitted through logging or warnings.
In order to mask the warning I've used filterwarnings() on message.
import warnings
warnings.filterwarnings("ignore", message="Glyph 146 missing from current font.")
So replace "Glyph 146 missing from current font." by your own error message.
Related
In matplotlib you can make the text of an axis label bold by
plt.xlabel('foo',fontweight='bold')
You can also use LaTeX with the right backend
plt.xlabel(r'$\phi$')
When you combine them however, the math text is not bold anymore
plt.xlabel(r'$\phi$',fontweight='bold')
Nor do the following LaTeX commands seem to have any effect
plt.xlabel(r'$\bf \phi$')
plt.xlabel(r'$\mathbf{\phi}$')
How can I make a bold $\phi$ in my axis label?
Unfortunately you can't bold symbols using the bold font, see this question on tex.stackexchange.
As the answer suggests, you could use \boldsymbol to bold phi:
r'$\boldsymbol{\phi}$'
You'll need to load amsmath into the TeX preamble:
matplotlib.rc('text', usetex=True)
matplotlib.rcParams['text.latex.preamble']=[r"\usepackage{amsmath}"]
If you intend to have consistently bolded fonts throughout the plot, the best way may be to enable latex and add \boldmath to your preamble:
# Optionally set font to Computer Modern to avoid common missing font errors
matplotlib.rc('font', family='serif', serif='cm10')
matplotlib.rc('text', usetex=True)
matplotlib.rcParams['text.latex.preamble'] = [r'\boldmath']
Then your axis or figure labels can have any mathematical latex expression and still be bold:
plt.xlabel(r'$\frac{\phi + x}{2}$')
However, for portions of labels that are not mathematical, you'll need to explicitly set them as bold:
plt.ylabel(r'\textbf{Counts of} $\lambda$'}
In case anyone stumbles across this from Google like I did, another way that doesn't require adjusting the rc preamble (and conflicting with non-latex text) is:
ax.set_ylabel(r"$\mathbf{\partial y / \partial x}$")
When using LaTeX to typeset all text in a figure, you can make "normal" (non-equation) text bold by using \textbf:
ax.set_title(r"\textbf{some text}")
None of these solutions worked for me and I was astonished to find something so simple was so infuriating to achieve. In the end, this is what worked for my use case. I would advise adapting this for your own use:
plt.suptitle(r"$ARMA({0}, {1})$ Multi-Parameter, $\bf{{a}}$, Electrode Response".format(n_i, m), fontsize=16)
The {0} and {1} refer to positional arguments supplied to format method, meaning 0 refers to variable n_i and 1 refers to variable m.
Note: In my setup, for some reason, \textbf did not work. I have read somewhere that \bf is deprecated in LaTeX, but for me this is what worked.
I wanted to do something similar only then plot 'K' with subscript '1/2' as label and in bold. This worked without changing any of the rc parameters.
plt.figure()
plt.xlabel(r'$\bf{K_{1/2}}$')
As this answer Latex on python: \alpha and \beta don't work? points out. You may have a problem with \b so \boldsymbol may not work as anticipated. In that case you may use something like: '$ \\\boldsymbol{\\\beta} $' in your python code. Provided you use the preamble plt.rcParams['text.latex.preamble']=[r"\usepackage{amsmath}"]
Update for recent Matplotlib versions
In more recent versions of Matplotlib, the preamble must be specified as a string.
import matplotlib.pyplot as plt
plt.rcParams.update(
{
"text.usetex": True,
"text.latex.preamble": r"\usepackage{bm}",
# Enforce default LaTeX font.
"font.family": "serif",
"font.serif": ["Computer Modern"],
}
)
# ...
plt.xlabel(r"$\bm{\phi}$")
This uses the default LaTeX font "Computer Modern" for a more natural look.
Instead of \bm, may can alternatively use the older \boldsymbol from \usepackage{amsmath}.
I am trying to produce a scatterplot WITHOUT lines connecting each marker using matplotlib.pyplot.plot(). I am using Python 2.
I have tried setting specifying in the plt.plot() function that linestyle='None'. I have also tried linestyle='' (this has no impact on the line) and linestyle=None (this raises an error). I have also tried setting the line width to 0, and I have tried altering the linestyle to a different type of dash. Neither worked.. the code ran without an error message and the connecting lines remained without alteration.
#!/usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
x=[0,1,2,3]
y=[2,4,6,8]
yError=[0.1,0.2,0.2,0.3]
fig = plt.figure(facecolor="w",figsize=(6.5,4.0))
ax = fig.add_subplot(111)
ax.plot(x, y, color='blue',marker='o',label='generic', linestyle='None')
ax.errorbar(x,y, yerr=yError, color='blue',label='_nolegend_')
plt.show()
The output I am hoping for is a scatterplot without connecteing lines.
The lines are plot by your errorplot.
Add there also the linestyle='None' kwarg, then they'll disappear.
You can even leave away the complete plot command when also adding the marker definition to errorplot.
Then you only should adapt also the label of course.
The shortest and best readable form imo would rather be using the format kwarg to specify color, marker and no line in one short term:
ax.errorbar(x,y, fmt='bo', yerr=yError, label='generic')
Simple question: how do I get Python to use scientific notation in its plots by default? From various posts on SO I can write something like
from numpy import linspace
import matplotlib.pyplot as plt
plt.figure()
plt.plot(linspace(1e6,2e6),linspace(1e6,1e7))
plt.figure()
plt.plot(linspace(8e6,9e6),linspace(2e6,2.5e7))
plt.ticklabel_format(style='sci', axis='both', scilimits=(-2,2))
plt.show()
but ticklabel_format only acts on the last plot generated by matplotlib. (If plt.ticklabel_format() is put at the beginning of the code, I also get a blank figure showing the x,y axes.)
You can modify the default behaviour of matplotlib by edditing your "rc" file. See Customizing matplotlib.
In you case, it looks like you could adjust the item:
axes.formatter.limits : -2, 2 # use scientific notation if log10
# of the axis range is smaller than the
# first or larger than the second
I have a quite cluttered plot with y-ticklabels that need to be very long. I've resorted into wrapping them into multiline text with textwrap. However that makes the labels overlap (or at least come too close), between categories.
I can't solve it by spacing the ticks, making the graph larger, changing font or making the text smaller. (I've already pushed these limits)
As I see it, I could resolve and make it work if I could adjust the line spacing/height to be less than what the font requests.
So imagine for simplicity's sake the following tick-label desperately needs shorter line distance between lines/line height:
from matplotlib import pyplot as plt
plt.barh(0.75, 10, height=0.5)
plt.ylim(0, 2)
plt.yticks([1], ["A very long label\nbroken into 2 line"])
plt.subplots_adjust(left=0.3)
plt.show()
I've checked plt.tick_params() the rcParams without finding any obvious solution. I'm using latex to format the text, but trying to use \hspace(0.5em} in the tick label string seemed not to work/only make things worse.
Any suggestion as to how the line spacing can be decreased would be much appreciated.
You can use the linespacing keyword in your plt.yticks line. For example:
plt.yticks([1], ["A very long label\nbroken into 2 line"],linespacing=0.5)
You can play with the exact value of linespacing to fit your needs. Hope that helps.
Here's the original output of your code:
And here it is with a linespacing of 0.5:
Attempt using this:
pylab.rcParams['xtick.major.pad']='???'
Mess around with the ??? value to get something you like. You could also try (sing the OO interface):
fig = plt.figure()
ax = fig.add_subplot(111)
ax.tick_params(axis='both', which='major', labelsize=8)
ax.set_yticks([1], ["A very long label\nbroken into 2 line"], linespacing=0.5)
plt.show()
The labelsize command will change the size of your font.
Use a combination of the above with the rcparams setup.
Updated MRE with subplots
I'm not sure of the usefulness of the original question and MRE. The margin padding seems to be properly adjusted for large x and y labels.
The issue is reproducible with subplots.
Using matplotlib 3.4.2
fig, axes = plt.subplots(ncols=2, nrows=2, figsize=(8, 6))
axes = axes.flatten()
for ax in axes:
ax.set_ylabel(r'$\ln\left(\frac{x_a-x_b}{x_a-x_c}\right)$')
ax.set_xlabel(r'$\ln\left(\frac{x_a-x_d}{x_a-x_e}\right)$')
plt.show()
Original
I am plotting a dataset using matplotlib where I have an xlabel that is quite "tall" (it's a formula rendered in TeX that contains a fraction and is therefore has the height equivalent of a couple of lines of text).
In any case, the bottom of the formula is always cut off when I draw the figures. Changing figure size doesn't seem to help this, and I haven't been able to figure out how to shift the x-axis "up" to make room for the xlabel. Something like that would be a reasonable temporary solution, but what would be nice would be to have a way to make matplotlib recognize automatically that the label is cut off and resize accordingly.
Here's an example of what I mean:
import matplotlib.pyplot as plt
plt.figure()
plt.ylabel(r'$\ln\left(\frac{x_a-x_b}{x_a-x_c}\right)$')
plt.xlabel(r'$\ln\left(\frac{x_a-x_d}{x_a-x_e}\right)$', fontsize=50)
plt.title('Example with matplotlib 3.4.2\nMRE no longer an issue')
plt.show()
The entire ylabel is visible, however, the xlabel is cut off at the bottom.
In the case this is a machine-specific problem, I am running this on OSX 10.6.8 with matplotlib 1.0.0
Use:
import matplotlib.pyplot as plt
plt.gcf().subplots_adjust(bottom=0.15)
# alternate option without .gcf
plt.subplots_adjust(bottom=0.15)
to make room for the label, where plt.gcf() means get the current figure. plt.gca(), which gets the current Axes, can also be used.
Edit:
Since I gave the answer, matplotlib has added the plt.tight_layout() function.
See matplotlib Tutorials: Tight Layout Guide
So I suggest using it:
fig, axes = plt.subplots(ncols=2, nrows=2, figsize=(8, 6))
axes = axes.flatten()
for ax in axes:
ax.set_ylabel(r'$\ln\left(\frac{x_a-x_b}{x_a-x_c}\right)$')
ax.set_xlabel(r'$\ln\left(\frac{x_a-x_d}{x_a-x_e}\right)$')
plt.tight_layout()
plt.show()
In case you want to store it to a file, you solve it using bbox_inches="tight" argument:
plt.savefig('myfile.png', bbox_inches="tight")
An easy option is to configure matplotlib to automatically adjust the plot size. It works perfectly for me and I'm not sure why it's not activated by default.
Method 1
Set this in your matplotlibrc file
figure.autolayout : True
See here for more information on customizing the matplotlibrc file: http://matplotlib.org/users/customizing.html
Method 2
Update the rcParams during runtime like this
from matplotlib import rcParams
rcParams.update({'figure.autolayout': True})
The advantage of using this approach is that your code will produce the same graphs on differently-configured machines.
plt.autoscale() worked for me.
You can also set custom padding as defaults in your $HOME/.matplotlib/matplotlib_rc as follows. In the example below I have modified both the bottom and left out-of-the-box padding:
# The figure subplot parameters. All dimensions are a fraction of the
# figure width or height
figure.subplot.left : 0.1 #left side of the subplots of the figure
#figure.subplot.right : 0.9
figure.subplot.bottom : 0.15
...
There is also a way to do this using the OOP interface, applying tight_layout directly to a figure:
fig, ax = plt.subplots()
fig.set_tight_layout(True)
https://matplotlib.org/stable/api/figure_api.html
for some reason sharex was set to True so I turned it back to False and it worked fine.
df.plot(........,sharex=False)
You need to use sizzors to modify the axis-range:
import sizzors as sizzors_module
sizzors_module.reshape_the_axis(plt).save("literlymylief.tiff")