Skip to content

Commit 3a25b84

Browse files
committed
ENH: Add function two-tuple, and remove string options
1 parent a776200 commit 3a25b84

File tree

4 files changed

+127
-265
lines changed

4 files changed

+127
-265
lines changed

examples/subplots_axes_and_figures/secondary_axis.py

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030

3131
###########################################################################
3232
# The conversion can be a linear slope and an offset as a 2-tuple. It can
33-
# also be more complicated. The strings "inverted", "power", and "linear"
34-
# are accepted as valid arguments for the ``conversion`` kwarg, and scaling
35-
# is set by the ``otherargs`` kwarg.
33+
# also be more complicated, so we allow the user to pass a pair of
34+
# functions as the conversion interface, the first is the forward conversion,
35+
# and the second is the inverse.
3636
#
3737
# .. note ::
3838
#
@@ -48,55 +48,43 @@
4848
ax.set_ylabel('PSD')
4949
ax.set_title('Random spectrum')
5050

51-
secax = ax.secondary_xaxis('top', conversion='inverted', otherargs=1)
51+
52+
def forward(x):
53+
return 1 / x
54+
55+
56+
def inverse(x):
57+
return 1 / x
58+
59+
secax = ax.secondary_xaxis('top', conversion=(forward, inverse))
5260
secax.set_xlabel('period [s]')
5361
secax.set_xscale('log')
5462
plt.show()
5563

5664
###########################################################################
57-
# Considerably more complicated, the user can define their own transform
58-
# to pass to ``conversion``. Here the conversion is arbitrary linearly
59-
# interpolated between two (sorted) arrays.
65+
# Sometime we want to relate the axes in a transform that is ad-hoc from
66+
# the data, and is derived empirically. In that case we can set the
67+
# forward and inverse transforms to just be linear interpolations from the
68+
# one data set to the other. Note below that ``xnew`` is an analytical
69+
# function, so we wouldn't need to resort to interpolation, but imagine
70+
# it is instead derived from data.
6071

6172
fig, ax = plt.subplots(constrained_layout=True)
6273
ax.plot(np.arange(1, 11), np.arange(1, 11))
6374

64-
65-
class LocalArbitraryInterp(Transform):
66-
"""
67-
Return interpolated from data. Note that both arrays
68-
have to be ascending for this to work in this example. (Could
69-
have more error checking to do more generally)
70-
"""
71-
72-
input_dims = 1
73-
output_dims = 1
74-
is_separable = True
75-
has_inverse = True
76-
77-
def __init__(self, xold, xnew):
78-
Transform.__init__(self)
79-
self._xold = xold
80-
self._xnew = xnew
81-
82-
def transform_non_affine(self, values):
83-
q = np.interp(values, self._xold, self._xnew, )
84-
return q
85-
86-
def inverted(self):
87-
""" we are just our own inverse """
88-
return LocalArbitraryInterp(self._xnew, self._xold)
89-
90-
# this is anarbitrary mapping defined by data. Only issue is that it
91-
# should be one-to-one and the vectors need to be ascending for the inverse
92-
# mapping to work.
9375
xold = np.arange(0, 11, 0.2)
9476
xnew = np.sort(10 * np.exp(-xold / 4))
9577

9678
ax.plot(xold[3:], xnew[3:])
9779
ax.set_xlabel('X [m]')
9880

99-
secax = ax.secondary_xaxis('top', conversion=LocalArbitraryInterp(xold, xnew))
81+
def forward(x):
82+
return np.interp(x, xold, xnew)
83+
84+
def inverse(x):
85+
return np.interp(x, xnew, xold)
86+
87+
secax = ax.secondary_xaxis('top', conversion=(forward, inverse))
10088
secax.xaxis.set_minor_locator(AutoMinorLocator())
10189
secax.set_xlabel('Exponential axes')
10290

lib/matplotlib/axes/_axes.py

Lines changed: 13 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141

4242
rcParams = matplotlib.rcParams
4343

44-
4544
def _has_item(data, name):
4645
"""Return whether *data* can be item-accessed with *name*.
4746
@@ -637,56 +636,16 @@ def indicate_inset_zoom(self, inset_ax, **kwargs):
637636

638637
return rectpatch, connects
639638

640-
def secondary_xaxis(self, location, *, conversion=None,
641-
otherargs=None, **kwargs):
639+
640+
@docstring.dedent_interpd
641+
def secondary_xaxis(self, location, *, conversion=None, **kwargs):
642642
"""
643643
Add a second x-axis to this axes.
644644
645645
For example if we want to have a second scale for the data plotted on
646646
the xaxis.
647-
648-
Warnings
649-
--------
650-
651-
This method is experimental as of 3.1, and the API may change.
652-
653-
Parameters
654-
----------
655-
location : string or scalar
656-
The position to put the secondary axis. Strings can be 'top' or
657-
'bottom', scalar can be a float indicating the relative position
658-
on the axes to put the new axes (0 being the bottom, and 1.0 being
659-
the top.)
660-
661-
conversion : scalar, two-tuple of scalars, string, or Transform
662-
If a scalar or a two-tuple of scalar, the secondary axis is
663-
converted via a linear conversion with slope given by the first
664-
and offset given by the second. i.e. ``conversion = [2, 1]``
665-
element for a parent axis between 0 and 1 gives a secondary axis
666-
between 1 and 3.
667-
668-
If a string, if can be one of "linear", "power", and "inverted".
669-
If "linear", the value of ``otherargs`` should be a float or
670-
two-tuple as above. If "inverted" the values in the secondary axis
671-
are inverted and multiplied by the value supplied by ``oterargs``.
672-
If "power", then the original values are transformed by
673-
``newx = otherargs[1] * oldx ** otherargs[0]``.
674-
675-
Finally, the user can supply a subclass of `.transforms.Transform`
676-
to arbitrarily transform between the parent axes and the
677-
secondary axes.
678-
See :doc:`/gallery/subplots_axes_and_figures/secondary_axis`
679-
for an example of making such a transform.
680-
681-
682-
Other Parameters
683-
----------------
684-
**kwargs : `~matplotlib.axes.Axes` properties.
685-
Other miscellaneous axes parameters.
686-
687-
Returns
688-
-------
689-
ax : axes._secondary_axes.Secondary_Axis
647+
648+
%(_secax_docstring)s
690649
691650
Examples
692651
--------
@@ -699,72 +658,30 @@ def secondary_xaxis(self, location, *, conversion=None,
699658
fig, ax = plt.subplots()
700659
ax.loglog(range(1, 360, 5), range(1, 360, 5))
701660
ax.set_xlabel('wavenumber [cpkm]')
702-
secax = ax.secondary_xaxis('top', conversion='inverted',
703-
otherargs=1.)
661+
secax = ax.secondary_xaxis('top', conversion=(lambda x: 1 / x,
662+
lambda x: 1 / x))
704663
secax.set_xlabel('wavelength [km]')
664+
plt.show()
705665
706666
707667
"""
708668
if (location in ['top', 'bottom'] or isinstance(location, Number)):
709669
secondary_ax = Secondary_Axis(self, 'x', location,
710-
conversion, otherargs=otherargs,
711-
**kwargs)
670+
conversion, **kwargs)
712671
self.add_child_axes(secondary_ax)
713672
return secondary_ax
714673
else:
715674
raise ValueError('secondary_xaxis location must be either '
716675
'"top" or "bottom"')
717676

718-
def secondary_yaxis(self, location, *, conversion=None,
719-
otherargs=None, **kwargs):
677+
def secondary_yaxis(self, location, *, conversion=None, **kwargs):
720678
"""
721679
Add a second y-axis to this axes.
722680
723681
For example if we want to have a second scale for the data plotted on
724-
the xaxis.
725-
726-
Warnings
727-
--------
728-
729-
This method is experimental as of 3.1, and the API may change.
682+
the yaxis.
730683
731-
Parameters
732-
----------
733-
location : string or scalar
734-
The position to put the secondary axis. Strings can be 'left' or
735-
'right', scalar can be a float indicating the relative position
736-
on the axes to put the new axes (0 being the left, and 1.0 being
737-
the right.)
738-
739-
conversion : scalar, two-tuple of scalars, string, or Transform
740-
If a scalar or a two-tuple of scalar, the secondary axis is
741-
converted via a linear conversion with slope given by the first
742-
and offset given by the second. i.e. ``conversion = [2, 1]``
743-
element for a parent axis between 0 and 1 gives a secondary axis
744-
between 1 and 3.
745-
746-
If a string, if can be one of "linear", "power", and "inverted".
747-
If "linear", the value of ``otherargs`` should be a float or
748-
two-tuple as above. If "inverted" the values in the secondary axis
749-
are inverted and multiplied by the value supplied by ``oterargs``.
750-
If "power", then the original values are transformed by
751-
``newy = otherargs[1] * oldy ** otherargs[0]``.
752-
753-
Finally, the user can supply a subclass of `.transforms.Transform`
754-
to arbitrarily transform between the parent axes and the
755-
secondary axes.
756-
See :doc:`/gallery/subplots_axes_and_figures/secondary_axis`
757-
for an example of making such a transform.
758-
759-
760-
Other Parameters
761-
----------------
762-
**kwargs : `~matplotlib.axes.Axes` properties.
763-
Other miscellaneous axes parameters.
764-
765-
Returns
766-
-------
767-
ax : axes._secondary_axes.Secondary_Axis
684+
%(_secax_docstring)s
768685
769686
Examples
770687
--------
@@ -782,8 +699,7 @@ def secondary_yaxis(self, location, *, conversion=None,
782699
"""
783700
if location in ['left', 'right'] or isinstance(location, Number):
784701
secondary_ax = Secondary_Axis(self, 'y', location,
785-
conversion, otherargs=otherargs,
786-
**kwargs)
702+
conversion, **kwargs)
787703
self.add_child_axes(secondary_ax)
788704
return secondary_ax
789705
else:

0 commit comments

Comments
 (0)