1

I want to plot points on the interval x in [0, 4]. My function performs something interesting for very small values, so I would like to create a non-linear scale that would use more space for smaller values of x. Logarithmic scale would be a great solution, but the problem is that my x-axis must include 0, which is not part of logarithmic axis.

So I considered using a power scale. After some googling I came across the following solution.

def stratify(ax, power=2):
    f     = lambda x: (x + 1)**(1 / power)
    f_inv = lambda y: y**power - 1
    ax.set_xscale('function', functions=(f, f_inv))

x = np.linspace(0, 4, 100)
y = np.sqrt(x)

fig, ax = plt.subplots()
ax.plot(x, y)
stratify(ax, 2)

plt.show()

The function stratify changes the x-scale of the plot to the square root function. This looks kind of correct. Below is a minimal example plot corresponding to the above code (not actual data).

Plot produced by the attached code

I would like to have control over the nonlinearity in the x-scale, that is why I have introduced the power parameter. However, when I change the power parameter to value different from 2, the plot does not change at all. This is very surprising for me. I would appreciate if somebody could advise me how I can control the extent of non-linearity in x-axis. If possible, I would like it even more non-linear, making the interval [0, 0.5] take half of the plot.

EDIT While the current solution by @Thomas works as intended, the plotting routine throws a lot of errors when one attempts to do anything with it. For example, I would like to insert extra ticks, as the original only has integer ticks for whatever reason. Inserting extra ticks via ax.set_xticks(ax.get_xticks() + [0.5]) results in an error posx and posy should be finite values. What is this error, and how can it be bypassed?

1
  • I adressed that EDIT in the comment to my answer below. Commented Nov 10, 2021 at 7:59

1 Answer 1

1

For me, there's a change when switching from power=2 to power=10. Just be careful to edit it at the right position, i.e. when calling stratify=X.

Here's power=2: enter image description here Here's power=10: enter image description here

However, here's another suggestion that is slightly more aggressive:

import numpy as np
import matplotlib.pyplot as plt


def stratify(ax, scale=1):
    f = lambda x: np.log(x / scale + 1)
    f_inv = lambda y: scale * (np.exp(y) - 1)
    ax.set_xscale('function', functions=(f, f_inv))


x = np.linspace(0, 4, 100)
y = np.sqrt(x)

fig, axs = plt.subplots(1, 3)
for i, scale in enumerate([10,1,0.1]):
    ax = axs[i]
    ax.set_title(f'Scale={scale}')
    ax.plot(x, y)
    stratify(ax, scale=scale)

plt.show()

Resulting in enter image description here

Another option are zoom regions.

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

6 Comments

Add that comment / link as an edit to your answer. Comments get deleted.
There is another problem. I would like to add extra ticks. But when I insert ax.set_xticks(ax.get_xticks() + [0.5]), I get an error posx and posy should be finite values
For me this works if I set the ticks after calling stratify.
After some fighting I found the culprit. Matplotlib does not stick to the xrange of [0, 4] when plotting, it attempts to make a slightly wider range, e.g. [-0.05, 4.05] to have nice margins. However, the mapping you provided does not work for negative numbers as the logarithm blows up. I designed a more fancy function that performs y=x for negative numbers and the original function you suggested for positive numbers. Now there are no errors and everything seems to work.
|

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.