2
votes

Version 5.2 contains set nonlinear. One can use it to construct broken axes as suggested in the demo here. I am trying to follow the steps for x axis also logarithmic.The current code is below as well as the data. The problem that I cannot figure out is the scaling of the first order after the break, this is illustrated in this plot: enter image description here

As you can see the problem is between 0.1 and 1. What am I doing wrong?

data="test2.txt"
unset nonlinear x
x1min=1e-6
x1max=1e-5
x2min=1e-1
x2max=1e+3
ymin =1e-1
ymax =1e+3
dx=(x2min - x1max)
set yrange  [ymin:ymax]
unset key
set format x "10^{%T}
set xtics font ",12"
set ytics font ",12"
set y2tics font ",12"
axis_gap=1
f(x) = (x <= x1max)  ? log10(x) : (x < x2min)            ? NaN : log10(x - dx + axis_gap)
g(x) = (x <= x1max) ? 10**x     : (x < x1max + axis_gap) ? NaN : 10**(x + dx - axis_gap)
set xrange  [x1min:x2max] nereverse nowriteback
set nonlinear x  via f(x) inverse g(x)
set logscale y
set xtics  (1e-6 0,2e-6 1,3e-6 1,4e-6 1,5e-6 1,6e-6 1,7e-6 1,8e-6 1,9e-6 1,1e-5 0,2e-5 1,3e-5 1,4e-5 1,5e-5 1,6e-5 1,7e-5 1,8e-5 1,9e-5 1,1e-2 0,2e-2 1,3e-2 1,4e-2 1,5e-2 1,6e-2 1,7e-2 1,8e-2 1,9e-2 1,1e-1 0,2e-1 1,3e-1 1,4e-1 1,5e-1 1,6e-1 1,7e-1 1,8e-1 1,9e-1 1,1 0,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9 1,10 0,20 1,30 1,40 1,50 1,60 1,70 1,80 1,90 1,100 0,200 1,300 1,400 1,500 1,600 1,700 1,800 1,900 1,1000 0)
plot data u 1:2

where the data is:

5 0.471238898038469
4.18879020478639e-6 0.7
4.18879020478639e-6 2
4.18879020478639e-6 8.8
4.18879020478639e-6 2.8
1 4.18879020478639e-6
1 4.18879020478639e-6
98.174770424681 1.68
98.174770424681 3.4
0.125663706143592 161.809725
0.125663706143592 425.60861
0.125663706143592 425.60861
0.125663706143592 425.60861
0.125663706143592 483.467845
144 43.14926
1 50.99458
1 51.975245
1 52.95591
1 54.91724
1 57.859235
1 66.68522
1 69.627215
1 78.4532
1.728 32.361945
1.728 40.207265
1.728 41.18793
1.728 43.14926
1.728 45.11059
1.728 48.052585
1.728 54.91724
1.728 65.704555
3.375 46.091255
3.375 50.99458
3.375 54.91724
3.375 56.87857
3.375 57.859235
3.375 64.72389
8 24.516625
8 43.14926
8 46.091255
8 51.975245
8 54.91724
8 59.820565
15.625 47.07192
15.625 48.052585
15.625 50.99458
15.625 52.95591
15.625 55.897905
64 42.168595
64 42.168595
64 48.052585
125 35.30394
125 42.168595
125 50.013915

UPDATE

I start a bounty on this question. And I am adding another example to work with, a simpler one:

unset key
unset nonlinear x
unset nonlinear x2
axis_gap=1
x1min=1
x1max=3
x2min=5
x2max=20
ymin =1e-1
ymax =1e+3
dx=(x2min - x1max)
x1minp=log10(x1min)
x1maxp=log10(x1max)
x2minp=log10(x2min)
x2maxp=log10(x2max)
set yrange  [ymin:ymax]
set xrange  [x1min:x2max] noreverse nowriteback
f(x) = (x <= x1max) ? log10(x) : (x < x2min) ? NaN : log10(x/(x2min/x1max)*axis_gap)
g(x) = (x <= x1maxp) ? 10**(x) : (x < log10(x1max*axis_gap)) ? NaN :  10**(x) + dx + log10(axis_gap)
set nonlinear x  via f(x) inverse g(x)
set logscale y
set xtics (1e0 0,2e0 0,3e0 0,4e0 0,5e0 0,6e0 0,7e0 0,8e0 0,9e0 0,1e1 0,2e1 0)
plot 0 w l

In this version, I finally got the concept of the nonlinear and I rather think in terms of distance of a tick from origin (the gnuplot's invisible axis) which is determined by f(x), where x are numbers from the plotted range and f(x) is the one in set nonlinear x via f(x) inverse g(x), and g(x) gets the number associated with a tick at position x from origin (the number that is put into y(x) y plotted on y-axis and is displayed on x axis above/below the tick). This may sound complicated by I find this much better than some nonlinear visible and linear invisible axes concept, provided I actually understand it correctly this time.

The solution has to not only work without warning: could not confirm linked axis inverse mapping function on both samples but also there needs to be an explanation what is wrong with the functions in this update, ideally in terms of distances and numbers as suggested above rather than visible/invisible gnuplot jargon.

Update 2

I also noticed this, see the pictures.

f(x) = (x <= x1max) ? log10(x) : (x < x2min) ? NaN : log10(x/(x2min/x1max)*axis_gap)
g(x) = (x <= x1maxp) ? 10**(x) : (x < log10(x1max*axis_gap)) ? NaN :  10**(x*(x2min/x1max)) + dx + log10(axis_gap)

You can see that the coordinates displayed in the bottom left of the window behave as expected (axis_gap=1.1) but the tics (and if you plot data points (1 1, 2 1, 3 1, 5 1, 6 1,...)) the have the wrong placement): enter image description here enter image description here

2

2 Answers

1
votes

Revised answer:

Have a look at the gnuplot documentation:

[new command in version 5.2] This command is similar to the set link command except that only one of the two linked axes is visible. The hidden axis remains linear. Coordinates along the visible axis are mapped by applying g(x) to hidden axis coordinates. f(x) maps the visible axis coordinates back onto the hidden linear axis.

Example:

set xrange [1:1000]

set nonlinear x via log10(x) inverse 10**x

This example establishes a log-scaled x axis. It is an alternative way of achieving the effect of set log x. The hidden axis in this case has range [0:3], obtained by calculating [log10(xmin):log10(xmax)]. You must provide both the forward and inverse expressions.

In other words: f(x) is a function which takes your x-data input value and maps it on a linear range. If you set logscale x you already have such a mapping and the function f(x) is simply log10(x). And g(x) is the inverse function 10**x

If you have a broken axis, as in your case, you have to define a suitable mapping function with different regions. Below, I've chosen a continuous function, where BrokenAxisPos is the value (with respect to the axis before the break) where the second axis should start. See the following graph:

enter image description here

The range x1max to x2min which you want to have omitted is squeezed by the function f(x) into the range x1max to BrokenAxisPos (I set it to 1e-4). It requires some little calculation to get the factors of function f(x) right. For values above x2min, the function f(x) shifts the original x-value by some appropriate value. And after some litte calculations, g(x) is the inverse of the function f(x).

With the code:

### broken nonlinear (logarithmic) axis
reset session
set size square 

data="test2.txt"

x1min=1e-6; x1max=1e-5
x2min=1e-1; x2max=1e+3
ymin =1e-1; ymax =1e+3
unset key

# settings x-axis
unset logscale x
set xrange[x1min:x2max]
set format x "10^{%T}"
set xtics font ",12"
# manually set major xtics
set xtics add ("0.1" 1e-1, "1" 1, "10" 10, "100" 100, "1000" 1000)
set mxtics 10
# settings y-axis
set logscale y
set format y "%g"
set yrange  [ymin:ymax]
set ytics font ",12"

set grid xtics, ytics

BrokenAxisPos = 1e-4
m = log10(BrokenAxisPos/x1max)/log10(x2min/x1max)
f(x) = (x <= x1max) ? log10(x) : x<x2min ? log10(x1max**(1-m)*x**m) : log10(x*BrokenAxisPos/x2min)
g(x) = (x <= log10(x1max)) ? 10**x : x<log10(BrokenAxisPos) ? (10**x/x1max**(1-m))**(1/m) : (x2min/BrokenAxisPos)*10**x

set nonlinear x via f(x) inverse g(x)

# manually hide x1,x2 axis and y-gridlines by placing a rectangle
set obj 1 rect from x1max*1.2, graph 0 to x2min*0.8, graph 1 fs solid 1.0 border bgnd front

plot data u 1:2 w p
### end of code

By the way, you can also use discontinuous functions by setting the "gap"-part to NaN, as you and @Ethan did. Then you can simply skip the calculation and use of m.

f(x) = (x <= x1max) ? log10(x) : x<x2min ? NaN : log10(x*BrokenAxisPos/x2min)
g(x) = (x <= log10(x1max)) ? 10**x : x<log10(BrokenAxisPos) ? NaN : (x2min/BrokenAxisPos)*10**x

But then you have to adapt the hiding of the axes from

set obj 1 rect from x1max*1.2, graph 0 to x2min*0.8, graph 1 fs solid 1.0 border bgnd front

to

set obj 1 rect from x1max, graph 0 to x2min, graph 1 fs solid 1.0 border bgnd front

because x1max*1.2 and x2min*0.8 (which are used to still show the x-grid line) will be mapped to NaN.

With continuous f(x) and g(x), you'll get the result:

enter image description here

1
votes

The logical error in your approach is that to shift along a log-scale axis you need to multiply or divide by the offset, not add or subtract. So your mapping would be something like:

axis_gap = 1.e4
f(x) = (x <= x1max)  ? log10(x) : (x < x2min)            ? NaN : log10( (x/dx) / axis_gap)
g(x) = (x <= x1max) ? 10**x     : (x < x1max + axis_gap) ? NaN : 10**( (x*dx) * axis_gap)

yielding the figure below enter image description here