Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib log(log(x)) scale

I need a plot which doesn't fit the usual 'log-log' definition, but has a linear x scale and a double logarithmic y scale. I tried to create this in matplotlib with

import numpy as np
from matplotlib import pyplot as plt

# constants that result from other part of program
b = 9.144
c = -3.579

# values for plot
XL = np.linspace(273.15,373.15,101)
YL = 10 ** 10 ** (b + c * np.log10(XL)) - 0.7

# functions for scale transformation
def fw(x):
    return np.log10(np.log10(x))

def bw(x):
    return 10 ** 10 ** x

plt.plot(XL - 273.15, YL)
plt.yscale('function', functions=(fw, bw))
plt.show()

Please find this image for reference. However this program has two problems:

  1. The y axis ticks start at 100 for a reason that I do not understand, leaving the largest part of the graph without any ticks.
  2. I get the following warnings from NumPy regarding the log10 calls from fw(x):
RuntimeWarning: divide by zero encountered in log10
  return np.log10(np.log10(x))
RuntimeWarning: invalid value encountered in log10
  return np.log10(np.log10(x))

I get that probably the first warning leads to the second, but I do not see why there would be a division by zero. Any assistance to enlightenment on both problems would be greatly appreciated.

like image 232
hansjoachim Avatar asked Dec 13 '25 02:12

hansjoachim


1 Answers

The numerical errors occur because the first np.log10 can return zero or a negative number for inputs of 1.0 or smaller. These zero or negative values cause errors in the second np.log10, since the logarithm is undefined for these values. Note that inputs of 1.0 or smaller can be caused by the axis ticks, even if they don't appear in your data. This problem can be fixed by adding a value of 1.0 before the first np.log10, and then subtracting the same value in the inverse function, ensuring that the second np.log10 is never passed an invalid value.

The tick placement is because FuncScale uses an AutoLocator, which evenly spaces the ticks on a linear scale. We can get a better result by using FuncScaleLog, which places the ticks logarithmically using a LogLocator. We can then define our scale functions with only the first logarithm, and let FuncScaleLog handle the second one. If your data spans many orders of magnitude, you might have the same problem where the ticks are mostly located towards the end of the axis. This is because the scale is actually doubly logarithmic, not singly logarithmic as LogLocator assumes. You can always override the ticks manually with plt.yticks(ticks), but to do better in general you would have to create a custom scale class as mentioned in the comments above.

Putting this all together, we have something like this:

from matplotlib import pyplot as plt
import numpy as np

Xs = np.linspace(-2.0, 2.0)
Ys = 10.0**Xs

plt.plot(Xs, Ys)
plt.yscale("functionlog", functions=(
    lambda x: np.log10(x + 1.0),
    lambda x: 10.0**x - 1.0))
plt.show()

Example image

like image 86
amtinits Avatar answered Dec 14 '25 15:12

amtinits



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!