Plotly: Plotting one trace with two x-axes that are not linearly related

The problem

I am trying to plot data that can be presented as either Absorbance vs Wavelength (nm) or Absorbance vs Wavenumber (cm⁻¹). Note the conversion is cm⁻¹ = 10⁷/nm.

I wish to plot a single trace with one y-axis (‘Absorbance’) and two x-axes ( ‘Wavelength (nm)’ and ‘Wavenumber (cm⁻¹)’ )

Presently, my plan is to graph the both traces but keep one invisible. However, I do not understand how to implement a secondary x-axis. Additionally, is there a way to scale the Wavenumber axis properly such that both the visible and invisible plots overlay each other?

Sample Data and Starting Code

import pandas as pd
import plotly.graph_objects as go

data = pd.DataFrame[{'Wavelength' = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}
                     'Absorbance' = {1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.04, 1.03, 1.02, 1.01}
                     }]

def nm_convert(nm):
    wavenumber = 10000000/nm
    return wavenumber

data['Wavenumber'] = nm_convert(data['Wavelength'])

trace1 = go.Scatter(x=dataset['Wavelength'], y=dataset['Absorbance'], mode='lines', line=dict(width=1.5), name='nm_trace')
trace2 = go.Scatter(x=dataset['Wavenumber'], y=dataset['Absorbance'], name='cm-1 trace', visible=False)


Example Image

I’ve made an example of the result I might be looking for. However, I am open to alternative suggestions.

enter image description here

Answer

There are several questions here, and I believe @vestland has answered the primary question. But to expand on his answer and to address your second question you can use a combination of tickvals and ticktext to achieve the “rescaling” of the second axis, another example here.

Below is an example adapted from vestland’s work, with the following comments

  • there’s two series here, the second is not shown with opacity=0
  • for xaxis2 the tickvals are Wavelength but the ticktext is set to the Wavenumber (also rounded with round)
  • using customdata and hovertemplate on the first series to provide a unified hover
import pandas as pd
import plotly.graph_objects as go

data = pd.DataFrame({'Wavelength' : [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000],
                     'Absorbance' : [1.00,1.01,1.02,1.03,1.04,1.05,1.04,1.03,1.02,1.01]})
data['Wavenumber'] = 10000000/data['Wavelength']

fig = go.Figure()
fig.add_traces(go.Scatter(x=data['Wavelength'], 
                          y=data['Absorbance'],
                          customdata=data['Wavenumber'],
                          showlegend=False,
                          hovertemplate=
                            "Wavelength: %{x}<br>" +
                            "Wavenumber: %{customdata:.0f}<br>" +
                            "Absorbance: %{y}" +
                            "<extra></extra>",))
fig.add_traces(go.Scatter(x=data['Wavelength'], 
                          y=data['Absorbance'], 
                          showlegend=False, 
                          opacity=0, 
                          hoverinfo='skip',
                          xaxis="x2"))

fig.update_layout(xaxis= {'title':'Wavelength','showgrid':False,})
fig.update_layout(yaxis= {'title':'Absorbance','showgrid':False,})
fig.update_layout(xaxis2= {'title':'Wavenumber', 'showgrid':False,
                           'anchor': 'y', 
                           'overlaying': 'x', 
                           'side': 'top',
                           'tickvals':data['Wavelength'],
                           'ticktext':data['Wavenumber'].round(decimals=-2)})
fig.show()

enter image description here