python / matplotlib – parásito de doble eje de escala

Intentar trazar un espectro, es decir, velocidad versus intensidad, con un eje x inferior = velocidad, en el eje gemelo superior = frecuencia

La relación entre ellos (fórmula doppler) es

f = (1-v/c)*f_0 

donde f es la frecuencia resultante, v la velocidad, c la velocidad de la luz y f_0 la frecuencia en v = 0, es decir. la v_lsr.

He intentado resolverlo mirando http://matplotlib.sourceforge.net/examples/axes_grid/parasite_simple2.html , donde se resuelve mediante

 pm_to_kms = 1./206265.*2300*3.085e18/3.15e7/1.e5 aux_trans = matplotlib.transforms.Affine2D().scale(pm_to_kms, 1.) ax_pm = ax_kms.twin(aux_trans) ax_pm.set_viewlim_mode("transform") 

mi problema es, ¿cómo reemplazo la función pm_to_kms con mi función para la frecuencia?

¿Alguien sabe como resolver esto?

La solución que terminé usando fue:

 ax_hz = ax_kms.twiny() x_1, x_2 = ax_kms.get_xlim() # i want the frequency in GHz so, divide by 1e9 ax_hz.set_xlim(calc_frequency(x_1,data.restfreq/1e9),calc_frequency(x_2,data.restfreq/1e9)) 

Esto funciona perfectamente, y la solución mucho menos complicada.

EDITAR: Encontré una respuesta muy elegante. EDIT2: se modificó la llamada de transformación de acuerdo con el comentario de @ u55

Básicamente, esto implica definir nuestra propia conversión / transformación. Debido a las excelentes equivalencias de las unidades AstroPy, se vuelve aún más fácil de entender y más ilustrativo.

 from matplotlib import transforms as mtransforms import astropy.constants as co import astropy.units as un import numpy as np import matplotlib.pyplot as plt plt.style.use('ggplot') from mpl_toolkits.axes_grid.parasite_axes import SubplotHost class Freq2WavelengthTransform(mtransforms.Transform): input_dims = 1 output_dims = 1 is_separable = False has_inverse = True def __init__(self): mtransforms.Transform.__init__(self) def transform_non_affine(self, fr): return (fr*un.GHz).to(un.mm, equivalencies=un.spectral()).value def inverted(self): return Wavelength2FreqTransform() class Wavelength2FreqTransform(Freq2WavelengthTransform): input_dims = 1 output_dims = 1 is_separable = False has_inverse = True def __init__(self): mtransforms.Transform.__init__(self) def transform_non_affine(self, wl): return (wl*un.mm).to(un.GHz, equivalencies=un.spectral()).value def inverted(self): return Freq2WavelengthTransform() aux_trans = mtransforms.BlendedGenericTransform(Wavelength2FreqTransform(), mtransforms.IdentityTransform()) fig = plt.figure(2) ax_GHz = SubplotHost(fig, 1,1,1) fig.add_subplot(ax_GHz) ax_GHz.set_xlabel("Frequency (GHz)") xvals = np.arange(199.9, 999.9, 0.1) # data, noise + Gaussian (spectral) lines data = np.random.randn(len(xvals))*0.01 + np.exp(-(xvals-300.)**2/100.)*0.5 + np.exp(-(xvals-600.)**2/400.)*0.5 ax_mm = ax_GHz.twin(aux_trans) ax_mm.set_xlabel('Wavelength (mm)') ax_mm.set_viewlim_mode("transform") ax_mm.axis["right"].toggle(ticklabels=False) ax_GHz.plot(xvals, data) ax_GHz.set_xlim(200, 1000) plt.draw() plt.show() 

Esto ahora produce los resultados deseados: introduzca la descripción de la imagen aquí

Su “función lineal” es una “ley de escala simple” (con un desplazamiento). Simplemente reemplace la definición pm_to_kms con su función.