Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

graph

like image 337
Philip Avatar asked Oct 04 '14 23:10

Philip


People also ask

How do you increase the Y-axis range in Python?

MatPlotLib with Python To change the range of X and Y axes, we can use xlim() and ylim() methods.

How do you change y-axis to log scale in MatPlotLib?

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.

How do I increase axis size in MatPlotLib?

To increase the space for X-axis labels in Matplotlib, we can use the spacing variable in subplots_adjust() method's argument.

What are the two ways to adjust axis limits of the plot using MatPlotLib?

To set the limit of the x-axis, use the xlim() function. To set the limit of the y-axis, use the ylim() function.


4 Answers

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()

enter image description here

like image 153
ImportanceOfBeingErnest Avatar answered Oct 24 '22 20:10

ImportanceOfBeingErnest


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: enter image description here

like image 24
magu_ Avatar answered Oct 24 '22 20:10

magu_


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)
like image 21
Philip Avatar answered Oct 24 '22 20:10

Philip


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

like image 40
mjp Avatar answered Oct 24 '22 22:10

mjp