2

I want to make a plot with matplotlib with really small values, shown on a log scale. This works fine until the numbers get too small and don't fit in a float.

I am representing values using SymPy arbitrary precision floats, but these are apparently converted to Python or NumPy machine floats internally in matplotlib.

For example

>>> import sympy
>>> import matplotlib.pyplot as plt
>>> plt.plot([0, 1, 2], [sympy.Float('1e-20'), sympy.Float('1e-100'), sympy.Float('1e-700')])
[<matplotlib.lines.Line2D object at 0x11ac0c208>]
>>> plt.yscale('log')

Produces plot

What it should show is the third value at 10^-700 (not at negative infinity).

Now I have very little hope of getting matplotlib to use SymPy Floats internally (if it's possible, let me know). What I would like to do is provide matplotlib with the log of the value, which I can compute myself just fine, but still display the exponential of that value on the y-axis with a log scale.

1 Answer 1

4

You can do the log calculation and then plot the data, if you want the same tick labels, you can use FuncFormatter:

import sympy
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter

x = [0, 1, 2]
y = [sympy.Float('1e-20'), sympy.Float('1e-100'), sympy.Float('1e-700')]

def log_formatter(x, pos):
    return "$10^{{{:d}}}$".format(int(x))

formatter = FuncFormatter(log_formatter)

fig, ax = plt.subplots()

y2 = list(map(lambda x:sympy.log(x, 10), y))
ax.plot(x, y2)
ax.yaxis.set_major_formatter(formatter)
ax.grid();

enter image description here

EDIT

To add log-scale minor ticks, you can create a Locator:

import sympy
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter, Locator

x = [0, 1, 2]
y = [sympy.Float('1e-20'), sympy.Float('1e-100'), sympy.Float('1e-700')]

def log_formatter(x, pos):
    return "$10^{{{:d}}}$".format(int(x))

class LogMinorLocator(Locator):
    def __call__(self):
        majorlocs = self.axis.get_majorticklocs()
        step = majorlocs[1] - majorlocs[0]
        res = majorlocs[:, None] + np.log10(np.linspace(1, 0.1, 10)) * step
        return res.ravel()

formatter = FuncFormatter(log_formatter)

fig, ax = plt.subplots(figsize=(12, 8))
y2 = list(map(lambda x:sympy.log(x, 10), y))
ax.plot(x, y2)
ax.minorticks_on()
ax.yaxis.set_major_formatter(formatter)
ax.yaxis.set_minor_locator(LogMinorLocator())
ax.grid();

Here is the output:

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

fun trick, you can decorate log_formatter with @FuncFormatter and then pass it directly to set_major_formatter
Does this use the logarithmic subticks like the normal log axes does?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.