17
17
import numpy as np
18
18
19
19
import matplotlib .dates as mdates
20
- from matplotlib .ticker import AutoMinorLocator
21
20
22
21
fig , ax = plt .subplots (layout = 'constrained' )
23
22
x = np .arange (0 , 360 , 1 )
@@ -96,48 +95,47 @@ def one_over(x):
96
95
plt .show ()
97
96
98
97
# %%
99
- # Sometime we want to relate the axes in a transform that is ad-hoc from
100
- # the data, and is derived empirically. In that case we can set the
101
- # forward and inverse transforms functions to be linear interpolations from the
102
- # one data set to the other.
98
+ # Sometime we want to relate the axes in a transform that is ad-hoc from the data, and
99
+ # is derived empirically. Or, one axis could be a complicated nonlinear function of the
100
+ # other. In these cases we can set the forward and inverse transform functions to be
101
+ # linear interpolations from the one set of independent variables to the other.
103
102
#
104
103
# .. note::
105
104
#
106
105
# In order to properly handle the data margins, the mapping functions
107
106
# (``forward`` and ``inverse`` in this example) need to be defined beyond the
108
- # nominal plot limits.
109
- #
110
- # In the specific case of the numpy linear interpolation, `numpy.interp`,
111
- # this condition can be arbitrarily enforced by providing optional keyword
112
- # arguments *left*, *right* such that values outside the data range are
113
- # mapped well outside the plot limits.
107
+ # nominal plot limits. This condition can be enforced by extending the
108
+ # interpolation beyond the plotted values, both to the left and the right,
109
+ # see ``x1n`` and ``x2n`` below.
114
110
115
111
fig , ax = plt .subplots (layout = 'constrained' )
116
- xdata = np .arange (1 , 11 , 0.4 )
117
- ydata = np .random .randn (len (xdata ))
118
- ax .plot (xdata , ydata , label = 'Plotted data' )
119
-
120
- xold = np .arange (0 , 11 , 0.2 )
121
- # fake data set relating x coordinate to another data-derived coordinate.
122
- # xnew must be monotonic, so we sort...
123
- xnew = np .sort (10 * np .exp (- xold / 4 ) + np .random .randn (len (xold )) / 3 )
124
-
125
- ax .plot (xold [3 :], xnew [3 :], label = 'Transform data' )
126
- ax .set_xlabel ('X [m]' )
112
+ x1_vals = np .arange (2 , 11 , 0.4 )
113
+ # second independent variable is a nonlinear function of the other.
114
+ x2_vals = x1_vals ** 2
115
+ ydata = 50.0 + 20 * np .random .randn (len (x1_vals ))
116
+ ax .plot (x1_vals , ydata , label = 'Plotted data' )
117
+ ax .plot (x1_vals , x2_vals , label = r'$x_2 = x_1^2$' )
118
+ ax .set_xlabel (r'$x_1$' )
127
119
ax .legend ()
128
120
121
+ # the forward and inverse functions must be defined on the complete visible axis range
122
+ x1n = np .linspace (0 , 20 , 201 )
123
+ x2n = x1n ** 2
124
+
129
125
130
126
def forward (x ):
131
- return np .interp (x , xold , xnew )
127
+ return np .interp (x , x1n , x2n )
132
128
133
129
134
130
def inverse (x ):
135
- return np .interp (x , xnew , xold )
136
-
131
+ return np .interp (x , x2n , x1n )
137
132
133
+ # use axvline to prove that the derived secondary axis is correctly plotted
134
+ ax .axvline (np .sqrt (40 ), color = "grey" , ls = "--" )
135
+ ax .axvline (10 , color = "grey" , ls = "--" )
138
136
secax = ax .secondary_xaxis ('top' , functions = (forward , inverse ))
139
- secax .xaxis . set_minor_locator ( AutoMinorLocator () )
140
- secax .set_xlabel ('$X_{other} $' )
137
+ secax .set_xticks ([ 10 , 20 , 40 , 60 , 80 , 100 ] )
138
+ secax .set_xlabel (r'$x_2 $' )
141
139
142
140
plt .show ()
143
141
0 commit comments