0

I currently work with an instrument that provides data in Wavenumber, but most of my community works in wavelength. Because of this I would like to create plots that display Wavenumber in cm^-1 along the bottom x-axis and wavelength in µm along the top. However the spacing doesn't quite match up between the two units of measurement to display a single spectrum. How do I create a different spacing for wavelength?

img

Here is an example of how a portion of one spectrum looks when plotted as a function of wavenumber against when it's plotted as a function of wavelength. Below is the code I'm currently implementing.

wn = wn_tot[425:3175] #range of 250 to 3000 cm-1
wl = 10000/wn #wavelength in microns

fig = plt.figure(1)
ax1 = plt.subplot(1,1,1)
ax2 = ax1.twiny()

ax1.plot(wn, spc[45], 'c', label='Wavenumber')
ax2.plot(wl, spc[45], 'm', label='Wavelength')

ax1.set_xlabel('Wavenumber (cm$^{-1}$)')
ax2.set_xlabel('Wavelength ($\mu$m)')
ax1.set_ylabel('Relative Intensity')
ax2.invert_xaxis()
fig.legend(loc=2, bbox_to_anchor=(0,1), bbox_transform=ax1.transAxes)
Mario
  • 1,631
  • 2
  • 21
  • 51

2 Answers2

0

As said in the comment on the OP, both scales cannot be simultaneously linear, since one cannot be obtained from the other via a linear transformation. You must hence accept that one (or both) have ticks at non-regular intervals.

The correct way to do it

Apply a transformation to the scale, which causes matplotlib to have a non-homogeneous scale.

The doc for Axes.set_yscale leads to that example which demonstrate the syntax ax1.set_xscale('function', functions=(forward, inverse)). Here in that case, the transformation functions are simply

def forward(wn):
    # cm^{-1} to μm
    return 1.0e4 / wn

def reverse(lam):
    # μm to cm^{-1} 
    return 1.0e4 / lam

However, my matplotlib is stuck on version 2.2.2 which does not have that feature, so I cannot give a working example.

The hacky way that works with older versions

Give tick positions and labels by hand, performing the calculations yourself.

# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt

def lambda_to_wave(lam):
    # μm to cm^{-1} 
    return 1.0e4 / lam


x_wave = np.linspace(2000.0, 3000.0)
y_arb = np.linspace(0.0, 1.0e6)

ticks_wavelength_values = np.linspace(3.5, 5.5, num=5)
ticks_labels = [str(lam) for lam in ticks_wavelength_values]
ticks_wavenumber_positions = lambda_to_wave(ticks_wavelength_values)

print ticks_wavelength_values
print ticks_wavenumber_positions

fig = plt.figure(1)
ax1 = plt.subplot(1,1,1)  # wavenumber
ax2 = ax1.twiny()  # wavelength
ax2.get_shared_x_axes().join(ax1, ax2)  # https://stackoverflow.com/questions/42973223/how-share-x-axis-of-two-subplots-after-they-are-created

ax1.plot(x_wave, y_arb, 'c', label='Data')
ax1.set_xlabel('Wavenumber (cm$^{-1}$)')
ax1.set_ylabel('Relative Intensity')

ax2.set_xticks(ticks_wavenumber_positions)
ax2.set_xticklabels(ticks_labels)
ax2.set_xlabel('Wavelength ($\mu$m)')

ax1.set_xlim(left=1800.0, right=3000.0)

fig.legend(loc=2, bbox_to_anchor=(0,1), bbox_transform=ax1.transAxes)
plt.show()
Leporello
  • 638
  • 4
  • 12
0

You can do without the second call to plot if you prefer: https://matplotlib.org/gallery/subplots_axes_and_figures/secondary_axis.html#sphx-glr-gallery-subplots-axes-and-figures-secondary-axis-py

wn = wn_tot[425:3175] #range of 250 to 3000 cm-1

fig = plt.figure(1)
ax1 = plt.subplot(1,1,1)

ax1.plot(wn, spc[45], 'c', label='Wavenumber')
def forward(x):
    return 10000 / x

def inverse(x):
    return 10000 / x

secax = ax.secondary_xaxis('top', functions=(forward, inverse))
ax1.set_xlabel('Wavenumber (cm$^{-1}$)')
secax.set_xlabel('Wavelength ($\mu$m)')
ax1.set_ylabel('Relative Intensity')
Jody Klymak
  • 4,979
  • 2
  • 15
  • 31