1

I want to something similar to How to add a second x-axis in matplotlib, i.e. have a top x-axis that displays a wavelength and a bottom axis that displays the corresponding frequency.

Reproducing linked example gives me a plot that looks like this: enter image description here

This plot was produced with:

#setting up the plot
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.gridspec as gridspec

fig = plt.figure()
fig.tight_layout()
ax = plt.subplot()
  

#Here it gets interesting!
def tick_function(X):
   c = 299792458
   V = c/X
   V = V*1e6
   V = np.round(V,0)
   V[2] = 3000
   V = V.astype(int)
   return(V)

ax = plt.subplot()
ax_top = ax.twiny()
ax.set_xscale("log", nonposx='clip')
ax.set_yscale("log", nonposy='clip')
ax_top.set_xscale("log", nonposx='clip')
ax.set_xlim([8e10,5e14])
ax.set_ylim([5e33,2e36])

axTicks = ax.get_xticks()   
ax_top_Ticks = axTicks
ax_top.set_xticks(ax_top_Ticks)
ax_top.set_xlim(ax.get_xlim())
ax_top.set_xbound(ax.get_xbound())
ax_top.set_xticklabels(tick_function(ax_top_Ticks))

Now, rather than plotting the top major x-ticks at the position of the bottom major x-axis, I'd like to have them shifted. I.e., I would like to have the top major x-ticks at positions 1000, 100, 10, 1 and the minor ticks shifted accordingly.


This is what I'd like it too look like:

enter image description here

I found this plot, that's what I want! http://inspirehep.net/record/877424/files/fig2.png Note, since lambda=c/f and ax & ax_top are logarithmic the spacing of the minor ticks has to be inverted to!

Community
  • 1
  • 1
Sebastiano1991
  • 867
  • 1
  • 10
  • 26

1 Answers1

1

The trick is to choose the wavelengths you want and convert them to frequencies. Then use those frequencies as positions for the upper ticks.

#setting up the plot
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

fig = plt.figure()
ax = plt.subplot()


def conversion_freq_lam(inp):
   c = 299792458
   outp = c/inp
   outp = outp.astype(int)
   return outp

#ax = plt.subplot(gs1[0])
ax = plt.subplot(111)
ax_top = ax.twiny()
ax.set_xscale("log", nonposx='clip')
ax.set_yscale("log", nonposy='clip')
ax_top.set_xscale("log", nonposx='clip')
ax.set_xlim([8e10,5e14])
ax.set_ylim([5e33,2e36])

goal_lambdas = np.array([100000, 10000, 1000, 100, 10, 1, 0.1, 0.01])
goal_freqs = conversion_freq_lam(goal_lambdas)
ax_top_Ticks = goal_freqs * 1e6  # magic factor 1e6 from your attempt. Units?
ax_top.set_xticks(ax_top_Ticks)
ax_top.set_xlim(ax.get_xlim())
ax_top.set_xbound(ax.get_xbound())
ax_top.set_xticklabels(goal_lambdas)

plt.savefig('test_2axes.png')

This produces the following plot: enter image description here

The magic number 1e6 used as a scaling factor I took from your question. I assume it is caused by the units of the axis.

Edit:

To have correctly spaced minor ticks at the top axis (for example at 2, 3, 4, ..., 20, 30, 40, 50, ...) add the following code block:

def find_minor_vals(goals):
    minors = []
    factors = np.arange(2, 10, 1)
    for val in goals:
        minors.extend(list(val * factors))
    print minors
    return np.array(minors)

goal_lambdas_minor = find_minor_vals(goal_lambdas)
goal_freqs_minor = conversion_freq_lam(goal_lambdas_minor) * 1e6
minor_locator = FixedLocator(goal_freqs_minor)
ax_top.xaxis.set_minor_locator(minor_locator)

Which results in the following picture:

enter image description here

Community
  • 1
  • 1
lkriener
  • 187
  • 2
  • 7
  • Great, thanks! Now this is almost perfect however, there is still a minor issue: for one order of magnitude ax_top hast 7 minor ticks, where as ax has 8 minor ticks. So, in consequence the minor ticks of top_ax are not located at 20,30,40 ect.... – Sebastiano1991 Jan 10 '18 at 17:27
  • 1
    @Sebastiano1991 Indeed...I guess I can use the same trick with `goal_lambdas` to `goal_freqs` for the minor ticks as well. Give me a moment... – lkriener Jan 10 '18 at 17:33
  • 1
    @Sebastiano1991 Edited the post – lkriener Jan 10 '18 at 17:50