I do have a question with matplotlib in python. I create different figures, where every figure should have the same height to print them in a publication/poster next to each other.
If the y-axis has a label on the very top, this shrinks the height of the box with the plot. So I use MaxNLocator to remove the upper and lower y-tick. In some plots, I want to have the 1.0 as a number on the y-axis, because I have normalized data. So I need a solution, which expands in these cases the y-axis and ensures 1.0 is a y-Tick, but does not corrupt the size of the figure using tight_layout().
Here is a minimal example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
x = np.linspace(0,1,num=11)
y = np.linspace(1,.42,num=11)
fig,axs = plt.subplots(1,1)
axs.plot(x,y)
locator=MaxNLocator(prune='both',nbins=5)
axs.yaxis.set_major_locator(locator)
plt.tight_layout()
fig.show()
Here is a link to a example-pdf, which shows the problems with height of upper boxline.
I tried to work with adjust_subplots() but this is of no use for me, because I vary the size of the figures and want to have same the font size all the time, which changes the margins.
Question is:
How can I use MaxNLocator and specify a number which has to be in the y-axis?
Hopefully someone of you has some advice.
Greetings,
Laenan
Assuming that you know in advance how many plots there will be in 1 row on a page one way to solve this would be to put all those plots into one figure - matplotlib will make sure they are alinged on axes:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
x = np.linspace(0, 1, num=11)
y = np.linspace(1, .42, num=11)
fig, (ax1, ax2) = plt.subplots(1,2, figsize=(8,3), gridspec_kw={'wspace':.2})
ax1.plot(x,y)
ax2.plot(x,y)
locator=MaxNLocator(prune='both', nbins=5)
ax1.yaxis.set_major_locator(locator)
# You don't need to use tight_layout and using it might give an error
# plt.tight_layout()
fig.show()
This question already has answers here:
Matplotlib log scale tick label number formatting
(6 answers)
Closed 2 years ago.
I'd like the y axis to show only the number 100, 200, and 300, and not in scientific notation. Any thoughts?
Current plot
Simplified code:
from matplotlib import pyplot as plt
import numpy as np
x = np.logspace(2, 6, 20)
y = np.logspace(np.log10(60), np.log10(300), 20)
plt.scatter(x, y[::-1])
plt.xscale('log')
plt.yscale('log')
plt.show()
The major and minor locators determine the positions of the ticks. The standard positions are set via the AutoLocator. The NullLocator removes them. A MultipleLocator(x) shows ticks every multiple x.
For the y axis, setting standard tick positions shows the ticks at the top closer to each other, as determines by the log scale. Doing the same for the x axis, however, due to the large range, would put them too close together. So, for the x axis the positions determined by the LogLocator can stay in place.
The formatters control how the ticks should be displayed. The ScalarFormatter sets the default way. There is an option scilimits that determines for which ranges of values a scientific notation should be used. As 1.000.000 usually gets displayed as 1e6, setting scilimits=(-6,9) avoids this.
from matplotlib import pyplot as plt
from matplotlib import ticker
import numpy as np
x = np.logspace(2, 6, 20)
y = np.logspace(np.log10(60), np.log10(300), 20)
plt.scatter(x, y[::-1])
plt.xscale('log')
plt.yscale('log')
ax = plt.gca()
# ax.xaxis.set_major_locator(ticker.AutoLocator())
ax.xaxis.set_minor_locator(ticker.NullLocator()) # no minor ticks
ax.xaxis.set_major_formatter(ticker.ScalarFormatter()) # set regular formatting
# ax.yaxis.set_major_locator(ticker.AutoLocator()) # major y tick positions in a regular way
ax.yaxis.set_major_locator(ticker.MultipleLocator(100)) # major y tick positions every 100
ax.yaxis.set_minor_locator(ticker.NullLocator()) # no minor ticks
ax.yaxis.set_major_formatter(ticker.ScalarFormatter()) # set regular formatting
ax.ticklabel_format(style='sci', scilimits=(-6, 9)) # disable scientific notation
plt.show()
This question already has answers here:
Changing the tick frequency on the x or y axis
(13 answers)
Closed 4 years ago.
When generating a Matplotlib line or scatter plot, what axis attribute specifies the spacing between ticks? I do not want to explicitly specify where each tick should be as prompted by this related question
ax.ticks(np.arange(-100, 100, 5))
What is the matplotlib axis attribute that controls the tick spacing? It should behave something like the following.
ax.set_x_tick_spacing(5)
This would use the same default xlim and origin point (usually 0) as the default settings.
A more recent answer to the related question illustrates using the matplotlib.ticker.MultipleLocator object. The axis ticks are this type of matplotlib object. Here is an example of it's use.
ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(5))
will place ticks 5 units apart on the x-axis, and
ax.xaxis.set_minor_locator(matplotlib.ticker.MultipleLocator(1))
will place minor ticks 1 unit apart on the x-axis.
Here is an example from the matplotlib Plotting Cookbook
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
X = np.linspace(-15, 15, 1024)
Y = np.sinc(X)
ax = plt.axes()
ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(5))
ax.xaxis.set_minor_locator(matplotlib.ticker.MultipleLocator(1))
plt.plot(X, Y, c = 'k')
plt.show()
When making a semi-log plot (y is log), the minor tick marks (8 in a decade) on the y axis appear automatically, but it seems that when the axis range exceeds 10**10, they disappear. I tried many ways to force them back in, but to no avail. It might be that they go away for large ranges to avoid overcrowding, but one should have a choice?
solution for matplotlib >= 2.0.2
Let's consider the following example
which is produced by this code:
import matplotlib.pyplot as plt
import matplotlib.ticker
import numpy as np
y = np.arange(12)
x = 10.0**y
fig, ax=plt.subplots()
ax.plot(x,y)
ax.set_xscale("log")
plt.show()
The minor ticklabels are indeed gone and usual ways to show them (like plt.tick_params(axis='x', which='minor')) fail.
The first step would then be to show all powers of 10 on the axis,
locmaj = matplotlib.ticker.LogLocator(base=10,numticks=12)
ax.xaxis.set_major_locator(locmaj)
where the trick is to set numticks to a number equal or larger the number of ticks (i.e. 12 or higher in this case).
Then, we can add minor ticklabels as
locmin = matplotlib.ticker.LogLocator(base=10.0,subs=(0.2,0.4,0.6,0.8),numticks=12)
ax.xaxis.set_minor_locator(locmin)
ax.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
Note that I restricted this to include 4 minor ticks per decade (using 8 is equally possible but in this example would overcrowd the axes). Also note that numticks is again (quite unintuitively) 12 or larger.
Finally we need to use a NullFormatter() for the minor ticks, in order not to have any ticklabels appear for them.
solution for matplotlib 2.0.0
The following works in matplotlib 2.0.0 or below, but it does not work in matplotlib 2.0.2.
Let's consider the following example
which is produced by this code:
import matplotlib.pyplot as plt
import matplotlib.ticker
import numpy as np
y = np.arange(12)
x = 10.0**y
fig, ax=plt.subplots()
ax.plot(x,y)
ax.set_xscale("log")
plt.show()
The minor ticklabels are indeed gone and usual ways to show them (like plt.tick_params(axis='x', which='minor')) fail.
The first step would then be to show all powers of 10 on the axis,
locmaj = matplotlib.ticker.LogLocator(base=10.0, subs=(0.1,1.0, ))
ax.xaxis.set_major_locator(locmaj)
Then, we can add minor ticklabels as
locmin = matplotlib.ticker.LogLocator(base=10.0, subs=(0.1,0.2,0.4,0.6,0.8,1,2,4,6,8,10 ))
ax.xaxis.set_minor_locator(locmin)
ax.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
Note that I restricted this to include 4 minor ticks per decade (using 8 is equally possible but in this example would overcrowd the axes). Also note - and that may be the key here - that the subs argument, which gives the multiples of integer powers of the base at which to place ticks (see documentation), is given a list ranging over two decades instead of one.
Finally we need to use a NullFormatter() for the minor ticks, in order not to have any ticklabels appear for them.
From what I can tell, as of Matplotlib 3.5.2:
With 8 or fewer major tick marks, the minor ticks show
with 9 to 11 major tick marks, subs="auto" will show the minor tick marks
with 12 or more, you need to set subs manually.
Using subs="auto"
from matplotlib import pyplot as plt, ticker as mticker
fig, ax = plt.subplots()
y = np.arange(11)
x = 10.0**y
ax.semilogx(x, y)
ax.xaxis.set_major_locator(mticker.LogLocator(numticks=999))
ax.xaxis.set_minor_locator(mticker.LogLocator(numticks=999, subs="auto"))
Setting subs manually
from matplotlib import pyplot as plt, ticker as mticker
fig, ax = plt.subplots()
y = np.arange(12)
x = 10.0**y
ax.semilogx(x, y)
ax.xaxis.set_major_locator(mticker.LogLocator(numticks=999))
ax.xaxis.set_minor_locator(mticker.LogLocator(numticks=999, subs=(.2, .4, .6, .8)))
Major ticks with empty labels will generate ticks but no labels.
ax.set_yticks([1.E-6,1.E-5,1.E-4,1.E-3,1.E-2,1.E-1,1.E0,1.E1,1.E2,1.E3,1.E4,1.E5,])
ax.set_yticklabels(['$10^{-6}$','','','$10^{-3}$','','','$1$','','','$10^{3}$','',''])
Wrapping the excellent answer from importanceofbeingernest for matplotlib >= 2.0.2 into a function:
import matplotlib.pyplot as plt
from typing import Optional
def restore_minor_ticks_log_plot(
ax: Optional[plt.Axes] = None, n_subticks=9
) -> None:
"""For axes with a logrithmic scale where the span (max-min) exceeds
10 orders of magnitude, matplotlib will not set logarithmic minor ticks.
If you don't like this, call this function to restore minor ticks.
Args:
ax:
n_subticks: Number of Should be either 4 or 9.
Returns:
None
"""
if ax is None:
ax = plt.gca()
# Method from SO user importanceofbeingernest at
# https://stackoverflow.com/a/44079725/5972175
locmaj = mpl.ticker.LogLocator(base=10, numticks=1000)
ax.xaxis.set_major_locator(locmaj)
locmin = mpl.ticker.LogLocator(
base=10.0, subs=np.linspace(0, 1.0, n_subticks + 2)[1:-1], numticks=1000
)
ax.xaxis.set_minor_locator(locmin)
ax.xaxis.set_minor_formatter(mpl.ticker.NullFormatter())
This function can then be called as
plt.plot(x,y)
plt.xscale("log")
restore_minor_ticks_log_plot()
or more explicitly
_, ax = plt.subplots()
ax.plot(x, y)
ax.set_xscale("log")
restore_minor_ticks_log_plot(ax)
The answers here ignore the convenient fact that the log-scaled axis already has the requisite locators. At least as of Matplotlib 3.6, it is enough to use set_params() with values that force minor ticks:
import matplotlib.pyplot as plt
import numpy as np
y = np.arange(12)
x = 10.0**y
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_xscale('log')
ax.xaxis.get_major_locator().set_params(numticks=99)
ax.xaxis.get_minor_locator().set_params(numticks=99, subs=[.2, .4, .6, .8])
plt.show()
In a Python plot I would like to use a secondary x-axis to display some alternative values. I'm also quite fond of the latex fonts, and would like those fonts to present throughout the plot. However, I find that when I set up my secondary axis, the latex font disappears. Here's a minimum working example:
import numpy as np
import matplotlib.pyplot as plt
Xvalues = np.linspace(0,10,100)
Yvalues = np.sqrt(Xvalues)
Xticks = np.linspace(0,10,6)
AltXvalues = np.log10(Xvalues+1)
AltLabels = ["%.2f" % x for x in AltXvalues] # Round these values
fig = plt.figure()
plt.rcParams['text.usetex'] = True
ax1 = fig.add_subplot(1,1,1)
ax1.plot(Xvalues, Yvalues)
ax1.set_xticks(Xticks)
ax1.set_xlabel('$x_1$')
ax1.set_ylabel('$y$')
ax2 = ax1.twiny()
ax2.set_xlabel('$\\log_{10}\\,(x_1+1)$')
ax2.set_xticks(Xticks)
ax2.set_xticklabels(AltLabels)
plt.show()
How can I ensure that the latex font is continued on the secondary axis?
Its because you are making those labels into strings when you set AltLabels. The different font you see on the primary axis tick labels is because those labels are printed in LaTeX's math-mode. So, the simple fix is to add the math-mode operators to the AltLabel strings:
AltLabels = ["$%.2f$" % x for x in AltXvalues] # Round these values
(Note the $ signs)