I'm trying to create a matplotlib plot with an exponential(?) Y axis like the fake one I've mocked up below. For my data I want to spread the values out as they approach the max Y value. And I'd like to compress the values as Y gets close to zero.
All the normal 'log' examples do the opposite: they compress values as they get away from zero. Which is what 'log' does of course. How can I create an exponential(?) scaling instead?
MatPlotLib with Python To change the range of X and Y axes, we can use xlim() and ylim() methods.
pyplot library can be used to change the y-axis scale to logarithmic. The method yscale() takes a single value as a parameter which is the type of conversion of the scale, to convert y-axes to logarithmic scale we pass the “log” keyword or the matplotlib. scale. LogScale class to the yscale method.
To increase the space for X-axis labels in Matplotlib, we can use the spacing variable in subplots_adjust() method's argument.
To set the limit of the x-axis, use the xlim() function. To set the limit of the y-axis, use the ylim() function.
From matplotlib 3.1 onwards you can define any custom scale easily via
ax.set_yscale('function', functions=(forward, inverse))
Also see https://matplotlib.org/3.1.1/gallery/scales/scales.html
In this case, e.g.:
from functools import partial
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(1, 40, 100)
y = np.linspace(1, 4, 100)
fig, ax = plt.subplots()
ax.plot(x, y)
# Set y scale to exponential
ax.set_yscale('function', functions=(partial(np.power, 10.0), np.log10))
ax.set(xlim=(1,40), ylim=(1,4))
ax.set_yticks([1, 3, 3.5, 3.75, 4.0])
plt.show()
I don't think its directly possible. But of course you can always try cheating. In my example I just write something else in the label:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(1, 40, 100);
y = np.linspace(1, 4, 100);
# Actually plot the exponential values
plt.plot(x, np.e**y)
ax = plt.gca()
# Set x logaritmic
ax.set_xscale('log')
# Rewrite the y labels
y_labels = np.linspace(min(y), max(y), 4)
ax.set_yticks(np.e**y_labels)
ax.set_yticklabels(y_labels)
plt.show()
Which results into:
This is not fully general because the locators are hard coded for my case. But this worked for me. I had to create a new scale called ExponentialScale used ma.power with a base of 1.1. Way too complicated for a seemingly simple thing:
class ExponentialScale(mscale.ScaleBase):
name = 'expo'
base = 1.1
logbase = math.log(base)
def __init__(self, axis, **kwargs):
mscale.ScaleBase.__init__(self)
self.thresh = None #thresh
def get_transform(self):
return self.ExponentialTransform(self.thresh)
def set_default_locators_and_formatters(self, axis):
# I could not get LogLocator to do what I wanted. I don't understand
# the docs about "subs" and the source was not clear to me.
# So I just spell out the lines I want:
major = [1, 5, 10, 12, 14, 16, 18, 20, 25, 28, 30] + range(31,60)
axis.set_major_locator(ticker.FixedLocator(major))
class ExponentialTransform(mtransforms.Transform):
input_dims = 1
output_dims = 1
is_separable = True
def __init__(self, thresh):
mtransforms.Transform.__init__(self)
self.thresh = thresh
def transform_non_affine(self, a):
res = ma.power(ExponentialScale.base, a)
return res
def inverted(self):
return ExponentialScale.InvertedExponentialTransform(self.thresh)
class InvertedExponentialTransform(mtransforms.Transform):
input_dims = 1
output_dims = 1
is_separable = True
def __init__(self, thresh):
mtransforms.Transform.__init__(self)
self.thresh = thresh
def transform_non_affine(self, a):
denom = np.repeat(ExponentialScale.logbase, len(a))
return np.log(a) / denom
def inverted(self):
return ExponentialScale.ExponentialTransform(self.thresh)
simply add this into your code for log scale:
plt.figure()
ax = plt.subplot(111)
ax.set_yscale('log')
But if you do want a exponential scale, this question answers it: link to question
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With