.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "gallery/subplots_axes_and_figures/secondary_axis.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note Click :ref:here  to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr_gallery_subplots_axes_and_figures_secondary_axis.py: ============== Secondary Axis ============== Sometimes we want a secondary axis on a plot, for instance to convert radians to degrees on the same plot. We can do this by making a child axes with only one axis visible via .axes.Axes.secondary_xaxis and .axes.Axes.secondary_yaxis. This secondary axis can have a different scale than the main axis by providing both a forward and an inverse conversion function in a tuple to the *functions* keyword argument: .. GENERATED FROM PYTHON SOURCE LINES 13-41 .. code-block:: default import matplotlib.pyplot as plt import numpy as np import datetime import matplotlib.dates as mdates from matplotlib.ticker import AutoMinorLocator fig, ax = plt.subplots(constrained_layout=True) x = np.arange(0, 360, 1) y = np.sin(2 * x * np.pi / 180) ax.plot(x, y) ax.set_xlabel('angle [degrees]') ax.set_ylabel('signal') ax.set_title('Sine wave') def deg2rad(x): return x * np.pi / 180 def rad2deg(x): return x * 180 / np.pi secax = ax.secondary_xaxis('top', functions=(deg2rad, rad2deg)) secax.set_xlabel('angle [rad]') plt.show() .. image-sg:: /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_001.png :alt: Sine wave :srcset: /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_001.png, /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_001_2_0x.png 2.0x :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 42-49 Here is the case of converting from wavenumber to wavelength in a log-log scale. .. note:: In this case, the xscale of the parent is logarithmic, so the child is made logarithmic as well. .. GENERATED FROM PYTHON SOURCE LINES 49-77 .. code-block:: default fig, ax = plt.subplots(constrained_layout=True) x = np.arange(0.02, 1, 0.02) np.random.seed(19680801) y = np.random.randn(len(x)) ** 2 ax.loglog(x, y) ax.set_xlabel('f [Hz]') ax.set_ylabel('PSD') ax.set_title('Random spectrum') def one_over(x): """Vectorized 1/x, treating x==0 manually""" x = np.array(x).astype(float) near_zero = np.isclose(x, 0) x[near_zero] = np.inf x[~near_zero] = 1 / x[~near_zero] return x # the function "1/x" is its own inverse inverse = one_over secax = ax.secondary_xaxis('top', functions=(one_over, inverse)) secax.set_xlabel('period [s]') plt.show() .. image-sg:: /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_002.png :alt: Random spectrum :srcset: /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_002.png, /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_002_2_0x.png 2.0x :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 78-93 Sometime we want to relate the axes in a transform that is ad-hoc from the data, and is derived empirically. In that case we can set the forward and inverse transforms functions to be linear interpolations from the one data set to the other. .. note:: In order to properly handle the data margins, the mapping functions (forward and inverse in this example) need to be defined beyond the nominal plot limits. In the specific case of the numpy linear interpolation, numpy.interp, this condition can be arbitrarily enforced by providing optional keyword arguments *left*, *right* such that values outside the data range are mapped well outside the plot limits. .. GENERATED FROM PYTHON SOURCE LINES 93-123 .. code-block:: default fig, ax = plt.subplots(constrained_layout=True) xdata = np.arange(1, 11, 0.4) ydata = np.random.randn(len(xdata)) ax.plot(xdata, ydata, label='Plotted data') xold = np.arange(0, 11, 0.2) # fake data set relating x coordinate to another data-derived coordinate. # xnew must be monotonic, so we sort... xnew = np.sort(10 * np.exp(-xold / 4) + np.random.randn(len(xold)) / 3) ax.plot(xold[3:], xnew[3:], label='Transform data') ax.set_xlabel('X [m]') ax.legend() def forward(x): return np.interp(x, xold, xnew) def inverse(x): return np.interp(x, xnew, xold) secax = ax.secondary_xaxis('top', functions=(forward, inverse)) secax.xaxis.set_minor_locator(AutoMinorLocator()) secax.set_xlabel('$X_{other}$') plt.show() .. image-sg:: /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_003.png :alt: secondary axis :srcset: /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_003.png, /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_003_2_0x.png 2.0x :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 124-128 A final example translates np.datetime64 to yearday on the x axis and from Celsius to Fahrenheit on the y axis. Note the addition of a third y axis, and that it can be placed using a float for the location argument .. GENERATED FROM PYTHON SOURCE LINES 128-184 .. code-block:: default dates = [datetime.datetime(2018, 1, 1) + datetime.timedelta(hours=k * 6) for k in range(240)] temperature = np.random.randn(len(dates)) * 4 + 6.7 fig, ax = plt.subplots(constrained_layout=True) ax.plot(dates, temperature) ax.set_ylabel(r'$T\ [^oC]$') plt.xticks(rotation=70) def date2yday(x): """Convert matplotlib datenum to days since 2018-01-01.""" y = x - mdates.date2num(datetime.datetime(2018, 1, 1)) return y def yday2date(x): """Return a matplotlib datenum for *x* days after 2018-01-01.""" y = x + mdates.date2num(datetime.datetime(2018, 1, 1)) return y secax_x = ax.secondary_xaxis('top', functions=(date2yday, yday2date)) secax_x.set_xlabel('yday [2018]') def celsius_to_fahrenheit(x): return x * 1.8 + 32 def fahrenheit_to_celsius(x): return (x - 32) / 1.8 secax_y = ax.secondary_yaxis( 'right', functions=(celsius_to_fahrenheit, fahrenheit_to_celsius)) secax_y.set_ylabel(r'$T\ [^oF]$') def celsius_to_anomaly(x): return (x - np.mean(temperature)) def anomaly_to_celsius(x): return (x + np.mean(temperature)) # use of a float for the position: secax_y2 = ax.secondary_yaxis( 1.2, functions=(celsius_to_anomaly, anomaly_to_celsius)) secax_y2.set_ylabel(r'$T - \overline{T}\ [^oC]$') plt.show() .. image-sg:: /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_004.png :alt: secondary axis :srcset: /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_004.png, /gallery/subplots_axes_and_figures/images/sphx_glr_secondary_axis_004_2_0x.png 2.0x :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 185-192 .. admonition:: References The use of the following functions, methods, classes and modules is shown in this example: - matplotlib.axes.Axes.secondary_xaxis - matplotlib.axes.Axes.secondary_yaxis .. rst-class:: sphx-glr-timing **Total running time of the script:** ( 0 minutes 5.215 seconds) .. _sphx_glr_download_gallery_subplots_axes_and_figures_secondary_axis.py: .. only :: html .. container:: sphx-glr-footer :class: sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-python :download:Download Python source code: secondary_axis.py  .. container:: sphx-glr-download sphx-glr-download-jupyter :download:Download Jupyter notebook: secondary_axis.ipynb  .. only:: html .. rst-class:: sphx-glr-signature Keywords: matplotlib code example, codex, python plot, pyplot Gallery generated by Sphinx-Gallery _