'How can I exponentially scale the Y axis with matplotlib
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?
Solution 1:[1]
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()
Solution 2:[2]
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:
Solution 3:[3]
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)
Solution 4:[4]
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
Solution 5:[5]
Add this to your code:
plt.yscale('symlog')
Source: https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.yscale.html
Solution 6:[6]
I assume you mean X axis because in your mock figure, the X axis is exponential, not the Y axis.
You can do something like this:
...
ax = plt.subplot(111)
ax.plot(Xs,Ys,color='blue',linewidth=2)
....
xlabs = [pow(10,i) for i in range(0,6)]
ax.set_xticklabels(xlabs)
ax.set_xticks(xlabs)
What I am doing here is manually creating a list of 6 Xs where each is represented by 10^i
, i.e., 10^1,10^2,...
. This will put X tick marks, and label them correctly, at [1, 10, 100, 1000, 10000, 100000]
. If you need more labels, change the 6
.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Kolen Cheung |
Solution 2 | magu_ |
Solution 3 | |
Solution 4 | Community |
Solution 5 | j0lama |
Solution 6 |