# Plots with a secondary, non-linear, axis scale

**URL:** <https://discourse.julialang.org/t/plots-with-a-secondary-non-linear-axis-scale/56661>\
**Category:** Visualization\
**Tags:** plotting, pyplot\
**Created:** [March 7, 2021, 8:26am UTC](https://discourse.julialang.org/t/plots-with-a-secondary-non-linear-axis-scale/56661 "2021-03-07T08:26:24Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [March 7, 2021, 8:26am UTC](https://discourse.julialang.org/t/plots-with-a-secondary-non-linear-axis-scale/56661/1 "2021-03-07T08:26:24Z")

</div>

Based on this stack overflow [post](https://stackoverflow.com/questions/59349185/non-linear-second-axis-in-matplotlib), a non-linear invertible function can be used to define a secondary PyPlot axis scale. Two questions, if you will:

(1) Is it possible to do this with other plot backends?

(2) If instead of functions, we have arrays of monotonous data points providing the relation between the two axes, how to define interpolation functions that can be consumed by PyPlot as in example below?

_ **NOTE:** the simple example is just for illustration purposes_  
 ![PyPlot_with_both_time_depth_nonlinear_scales](https://global.discourse-cdn.com/julialang/original/3X/9/9/9923d9d1dc19c5fe07be7a7f9943707fc048c51f.png)

```julia
using PyPlot
# For medium with a velocity gradient: V(z) = k*z + V0
# Invertible function defines secondary axis in PyPlot plot
Depth2Time(depth) = @. log(k*depth + V0)/k - log(V0)/k
Time2Depth(time) = @. exp(k*(time + log(V0)/k))/k - V0/k

const k = 1.5; # 1/s 
const V0 = 1500; # m/s 

depth = 0:2:2000 # meter
yz = @. sin(2π*10*k*depth/V0) * exp(-100*k^2*(depth - 500)^2/V0^2)

fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.25)

ax.set_xlabel("Depth [m]", color=:blue)
ax.tick_params(axis="x", colors= "blue")
ax.set_ylabel("Amplitude")
ax.plot(depth, yz)

# THIS IS THE KEY PYPLOT LINE:
ax2 = ax.secondary_xaxis(-0.2, functions=(Depth2Time, Time2Depth), color=:red)
ax2.set_xlabel("Time [s]")
plt.show()

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [March 11, 2021, 11:26pm UTC](https://discourse.julialang.org/t/plots-with-a-secondary-non-linear-axis-scale/56661/2 "2021-03-11T23:26:37Z")

</div>

As explained in this matplotlib [documentation](https://mpltest2.readthedocs.io/en/v6.6.7-doc/gallery/subplots_axes_and_figures/secondary_axis.html), sometimes we want to relate the axes in a transform that is derived empirically. We can then set the forward and inverse transform functions to be linear interpolations between the two datasets.

However, PyPlot does not seem to consume Julia’s interpolation functions, issuing error:

```julia
RuntimeError: <PyCall.jlwrap (in a Julia function called from Python)
JULIA: MethodError: no method matching (::Interpolations.GriddedInterpolation{Float64, 1, Float64, Gridded{Linear}, Tuple{Vector{Float64}}})(::Matrix{Float64})

```

See MWE below:

```julia
using PyPlot, Interpolations

N = 200;
velocity0 = rand(1500:3000,N-1) # interval velocity [m/s]
depth0 = collect(LinRange(0,2,N)) # depth [km]
time0 = [0; cumsum(1000*diff(depth0)./velocity0)] # time [s]

itp_fwd = interpolate((time0,), depth0, Gridded(Linear()))
itp_inv = interpolate((depth0,), time0, Gridded(Linear()))

Time2Depth(time) = itp_fwd(time) # s to km
Depth2Time(depth) = itp_inv(depth) # km to s

time = collect(0:0.002: 0.8)
Depth2Time(Time2Depth(time)) ≈ time # checks that inverse transforms work fine

yt = @. sin(2π*30*time) * exp(-900*(time - 0.3)^2)

fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.25)

ax.set_xlabel("Time [s]", color=:blue)
ax.tick_params(axis="x", colors= "blue")
ax.set_ylabel("Amplitude")
ax.plot(time, yt)
plt.xlim(extrema(time))
# Follow key PyPlot command fails with functions from interpolations.jl:
ax2 = ax.secondary_xaxis(-0.2, functions=(Time2Depth, Depth2Time), color=:red)

ax2.set_xlabel("Depth [km]")
plt.show()

```

Any feedback would be welcome. Thanks.

---

<div class="post-metadata">

**Author:** ![Ralph\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ralph_smith/32/10344_2.png) [@Ralph\_Smith](https://discourse.julialang.org/u/Ralph_Smith)\
**Post date:** [March 13, 2021, 5:02pm UTC](https://discourse.julialang.org/t/plots-with-a-secondary-non-linear-axis-scale/56661/3 "2021-03-13T17:02:00Z")

</div>

The error message suggests that you just need to broadcast:

```julia
Time2Depth(time) = itp_fwd.(time) # s to km
Depth2Time(depth) = itp_inv.(depth) # km to s

```

Interpolators are implemented for scalars and (column) vectors, so one might get away with using `vec()`, but it seems safest to preserve the array layout.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [March 13, 2021, 5:21pm UTC](https://discourse.julialang.org/t/plots-with-a-secondary-non-linear-axis-scale/56661/4 "2021-03-13T17:21:01Z")

</div>

@Ralph_Smith, thank you very much indeed! 🙂  
I would not have figured this one out, as everything else was working fine without broadcasting those functions.  
Brilliant.
