2

I have some 3d data that I am plotting with pcolormesh.

On the x-axis is time, on the y-axis is height.

The height has a potential (E) associated with it, but the mapping from height (y) to potential (E) is non-linear.

I want to add an axis on the right hand side of my figure showing the potential that is correct based on the values on the left hand side. I do not particularly care about the left and right ticks lining up (as is the case in this solution). If anything 'nice number' ticks on the right axis would be useful.

I have tried setting the ylim of the top and bottom points as per the celsius-farenheit example in the matplotlib docs, but this assumes a linear scale between the start and end point which is not the case here.

Finally I tried using a funcformatter, but the scaling for the potential requires two external constants to be given, and I can't find a way that constants can be passed to a funcformatter.

So far my code looks like:

import numpy as np
import matplotlib.pyplot as plt


time = np.arange(0.0, 11.0, 1.0)
y = np.arange(0.0, 11.0, 1.0)
data = np.random.randint(1,100, size=(11,11))

fig,ax=plt.subplots(1,1)

im=ax.pcolormesh(time,y,data,shading='nearest')
ax.set_xlabel('time')
ax.set_ylabel('height')
ax.set_ylim(y.min(),y.max())

ax_E = ax.twinx()
ax_E.set_ylabel('Potential E')

plt.savefig('test.png')

Currently the right hand y axis has a linear scale from 0 to 1.0. I would like this replacing with a scale showing the potential correct according to the values of y on the left hand y-axis.

The function I want to use for the potential is something like:

def get_E(mu, ymax, y):
    p2 = 2.0*mu/ymax**3
    Jmin = 2.0*np.sqrt(p2)*ymax
    pmin = Jmin/(2.0*y)
    E = np.sqrt(mu**2*pmin**2 + mu**2) - mu
    return E

i.e. highly nonlinear, with 2 constants (mu and ymax) passed to it.

Any help you can give would be greatly appreciated. I have done my best to search for a solution to this specific problem already, but my apologies if I have missed anything. Please do ask any questions to clarify.

FluidFox
  • 107
  • 10
  • Seems to be a duplicate of [this](https://stackoverflow.com/questions/49874482/align-twinx-with-second-axis-with-non-linear-scale), [this](https://stackoverflow.com/questions/59349185/non-linear-second-axis-in-matplotlib?rq=1), or [this](https://stackoverflow.com/questions/7630778/matplotlib-align-origin-of-right-axis-with-specific-left-axis-value?rq=1). – Mr. T Jan 17 '21 at 12:53
  • I have seen the first 'this'. I dont care about aligning L and R ticks so wonder if it can be done more simply. Third 'this' addresses different scales when you want to align the origin. This is not what I am asking, but perhaps I wasn't clear. Second 'this' is on track with what I want to do, but as I say the function I want to use is quite long and requires 2 external arguments to be passed to it. Is there a way to pass external arguments through secondary_xaxis(functions=) or funcformatter as I can't see a way in any examples or documentation. Thanks for your help! – FluidFox Jan 17 '21 at 15:33
  • Thanks. I experimented using a funcformatter as I say in my question, but the issue is that I don't think I can pass arguments (mu and ymax) to it. Is there a way to do this? Or can I pass arguments other than the axis values (y) to a lambda function somehow? – FluidFox Jan 17 '21 at 16:10
  • You cannot pass arguments to the functions in secondary axis, but you can rewrite your forward and inverse functions to use global variables pretty trivially, or just embed those variables in the functions. – Jody Klymak Jan 17 '21 at 16:22

0 Answers0