3

I want to plot a data series on two x and y axes in order to have 4 different axes. First the x (energy in eV) vs. the y (normalized counts) axis and then x (wavelength which is inversely related to energy) vs. y (counts) axis. My code for this is:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
from scipy.constants import h, c, e


def E(wavelength):
   return (h*c)/(wavelength*e)

wavelen = np.linspace(800e-9,1600e-9,200)
E_eV = E(wavelen)
loc, scale = 950e-9, 3.0
counts = mlab.normpdf(wavelen,950e-9,100e-9)/100
counts_norm = counts/10000


fig, ax  = plt.subplots()

ax1 = ax
ax2 = ax.twinx()
ax3 = ax.twiny()

plt.ticklabel_format(style='sci', scilimits=(0,0))

ax1.plot(E_eV, counts_norm)
ax1.set_xlim(E(1600e-9),E(800e-9))
ax1.set_ylabel('normalized counts')
ax1.set_xlabel('energy (eV)')
ax2.plot(E_eV, counts)
ax2.set_xlim(E(1600e-9),E(800e-9))
ax2.set_ylabel('counts')
ax3.plot(wavelen*1e9, counts_norm)
ax3.set_xlim(1600,800)
ax3.set_xlabel('wavelength (nm)')
ax3.ticklabel_format(style='plain')


plt.tight_layout()
plt.show()

As you can see the curves are not scaled in the right way so that they overlap and have the same dimensions in x-direction. Can you help me how to set the right parameters for the x (wavelength) axis at the top?

1 Answer 1

2

I recommend plotting only on your primary axes and then synchronizing the labels of the twin axes. I edited your example to show how this could be accomplished for a static plot.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
from scipy.constants import h, c, e

def E(wavelength):
    return (h*c)/(wavelength*e)
def getWaveLength(energy):
    return (h*c)/(energy*e)
def getCounts(normcounts):
    return normcounts*1000

wavelen = np.linspace(800e-9,1600e-9,200)
E_eV = E(wavelen)
loc, scale = 950e-9, 3.0
counts = mlab.normpdf(wavelen,950e-9,100e-9)/100
counts_norm = counts/10000

fig, ax1  = plt.subplots()

ax2 = ax1.twinx()
ax3 = ax1.twiny()

plt.ticklabel_format(style='sci', scilimits=(0,0))

ax1.plot(E_eV, counts_norm)
ax1.set_xlim(E(1600e-9),E(800e-9))
ax1.set_ylabel('normalized counts')
ax1.set_xlabel('energy (eV)')
ax2.set_ylabel('counts')
ax3.set_xlabel('wavelength (nm)')
ax3.ticklabel_format(style='plain')

# get the primary axis x tick locations in plot units
xtickloc = ax1.get_xticks() 
# set the second axis ticks to the same locations
ax3.set_xticks(xtickloc)
# calculate new values for the second axis tick labels, format them, and set them
x2labels = ['{:.3g}'.format(x) for x in getWaveLength(xtickloc)]
ax3.set_xticklabels(x2labels)
# force the bounds to be the same
ax3.set_xlim(ax1.get_xlim()) 

#same for y
ytickloc = ax1.get_yticks()
ax2.set_yticks(ytickloc)
ax2.set_yticklabels([str(int(y)) for y in getCounts(ytickloc)])
ax2.set_ylim(ax1.get_ylim())

plt.tight_layout()
plt.show()
Sign up to request clarification or add additional context in comments.

2 Comments

static axis can be a solution, but actually I would like the formatter to calculate the ticks automatically
I've done it interactively in my application by bundling the method in my example up into a function and calling it periodically with a timer.

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.