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:
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):
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:
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:
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
Related
I'm plotting a function with gnuplot:
round(x) = x - floor(x) < 0.5 ? floor(x) : ceil(x)
f(x) = round((sqrt(((x * 0.4)+1.0)) - 1.0) * 10)
set terminal svg size 1920,1080
set output 'testplot.svg'
plot f(x) title 'testplot'
After this reaches an x-value of 300, the y-value will be 100. Any x-value greater than 300 should be capped at y=100. I'm not sure how to accomplish this in gnuplot; I tried using if statements but because x isn't defined until the plot command is run, I was getting undefined variable x errors.
I know I could simply create a table with these values, but this is a problem I'd rather learn a gnuplot solution for, if such a thing exists.
Any tips are greatly appreciated!
Use the ternary operator:
set xrange [0:1000]
set yrange [0:150]
capF(x) = (x > 300) ? 100. : f(x)
plot capF(x) title "f(x) capped at y=100 for x>300"
How to set this xrange please to see the function g(x) as the gauss function? Thank you
c = 299792458
kB = 1.380649*10**(-23)
T = 10000
m_he = 6.64424*10**(-27)
nun_he = 4.55746e+14
nuth_he = (2*kB*T/m_he)**(0.5)
konst2 = 11e+12
g(x) = 1-konst2/((pi)**(0.5)*(nuth_he))*exp(-((x-nun_he)**2)/(nuth_he**2))
set xtics rotate by -90
set term pngcairo size 800,1200 enhanced font "Segoe UI,18"
set output "out.png"
set format y "%4.2sx10^{%T}"
x0=4.55746e+14
set xrange [x0-0.00001e+14:x0+0.00001e+14]
plot g(x) with lines lw 2.5 linecolor rgb "medium-blue", "<echo '4.55746e+14 1'" with points ls 7 ps 2
Please check the basics of the Gauss function.
Basically, the factor σ determines the width of the peak (in your case nuth_he).
So if you choose the range for example x0-3σ to x0+3σ you should nicely see your curve.
x0=4.55746e+14
set xrange [x0-nuth_he*3:x0+nuth_he*3]
However, what should this be?
"<echo '4.55746e+14 1'" with points ls 7 ps 2
Drawing a single point or line? But again, this will be orders of magnitudes different in y from your Gaussian curve. With this point or line in the same plot you won't see a peak or dip of your Gauss curve.
I saw some examples like this one:
f(x) = log10(x)
g(x) = 10**x
set nonlinear x via f(x) inverse g(x)
So this one is equivalent to just log-scaling the x.
But I don’t get why we need to write the inverse function?
And also I have this situation:
For data in x>=0 range I need to scale x in a way that it shows in an almost-half plot;
For data in -100<=x<0 I need to scale x in a way that it shows in a small part of plot;
For data in x<-100 I need to scale x in a way as for data in x>=0.
So let’s imagine we have an a4paper and gnuplot creates his plots on it. I want to have a plot result that will be drawn in an x scales like:
If x>=0 1cm = 5
If -100<=x<0 1cm = 100
If x<-100 1cm = 5
(I don’t mean it’s important to me to have only this centimeters, it just says that I need a correlation between delta of two x values and real length between them.)
I’m so sorry I can’t understand this mechanics of scaling.
The forward function tells gnuplot where to draw user coordinate [x,y] on the page. Call that location [x',y']. Only the forward function is needed for this. But the interactive terminals echo back mouse position and allow you to click on the plot for various purposes. In order to know what a mouse click on [x',y'] means, the program has to convert it back to the original [x,y]. For that it needs the inverse function.
For an example of using different scaling functions over different portions of the full plot, see the online demo nonlinear1.dem reproduced below
# This example shows how a nonlinear axis definition can be used to
# create a "broken axis". X coordinates 0-100 are at the left,
# X coordinates 500-1000 are at the right, there is a small gap between them.
# So long as no data points with (100 < x < 500) are plotted, this works as expected.
#
# f(x) maps x axis (discontinuous) to shadow axis coords (continuous linear range [0:1000])
# g(x) maps shadow axis coords to x axis readout
#
set title "A 'broken' x axis can be defined using 'set nonlinear x'"
# Define the broken-axis mapping
axis_gap = 25.
f(x) = (x <= 100) ? x : (x < 500) ? NaN : (x - 400 + axis_gap)
g(x) = (x <= 100) ? x : (x < 100 + axis_gap) ? NaN : (x + 400 - axis_gap)
set xrange [15:600] noextend
set nonlinear x via f(x) inverse g(x)
set xtics 50.
set xtics rotate by -90 nomirror
set ytics nomirror
set border 3
unset key
# Creation of the broken axis marks (this should be automated)
set arrow 500 from 100, graph 0 to 500, graph 0 nohead lt 500 lw 2 lc bgnd front
set arrow 501 from 100, graph 0 length graph .01 angle 75 nohead lw 2 front
set arrow 502 from 100, graph 0 length graph -.01 angle 75 nohead lw 2 front
set arrow 503 from 500, graph 0 length graph .01 angle 75 nohead lw 2 front
set arrow 504 from 500, graph 0 length graph -.01 angle 75 nohead lw 2 front
plot 'silver.dat' with yerrorbars lw 2, '' with lines
I am writing a report and need to make some nice plots to explain my problem. To do so, I would like to plot two curves f(x) and g(x) and fill the space between xmin and xmax. Is there a way to do it? By the way I am using gnuplot 5.2
I have tried this but without success:
f(x) = 1+x
g(x) = 1-x
plot '+' using (x >= 0.75 && x < 1 ? 1 : 1/0 ):(f($1)):(g($1)) with filledcurves closed
The command you show is close to working. Modify it to be
plot '+' using (x >= 0.75 && x < 1 ? $1 : 1/0 ):(f($1)):(g($1)) with filledcurves
note that
the filter is (condition ? $1 : 1/0) not (condition ? 1 : 1/0)
do not use the keyword closed
you must have a reasonable number of samples over the range being selected. One way to get this would be set samples 1000 but see alternative below
A slightly better method might to use a sampling range rather than a filter on x.
f(x) = 1+x
g(x) = 1-x
set xrange [0:2]; set yrange [0:3]
plot sample [x=0.75:1.0] '+' using ($1):(f($1)):(g($1)) with filledcurves
i'm having some problems with gnuplot
I have to draw a cdf function and i'm interested in the values of variable x when F(x) is equal to 0.1 and 0.9
How can I tell Gnuplot to show me on the x axis the value corresponding to a given value on the y value (in my example those values are 0.1 and 0.9)
thanks
You're basically asking gnuplot to solve an equation. In your particular case, actually two equations: F(x)=0.1 and F(x)=0.9. As far as I know this cannot be done, but I might be wrong. What you can do if you simply want a graphical solution, is make a conditional plot, and ask that when F(x) is very close to 0.1 0.9, gnuplot plots something other than the function.
For example, assume f(x)=x^2 and you want to know "graphically" for which x f(x)=0.1. Then you can request the value abs(f(x) - 0.1) be small, for example < 0.01. Then tell gnuplot to go to zero (just an example!) if this is the case, otherwise plot f(x)=x^2:
f(x)=x**2
set xrange [-2:2]
set samples 1000
plot abs(f(x)-1) < 0.01 ? 0 : f(x)
Which yields:
The two peaks that go to zero mark graphically on the x axis the solution to the equation f(x)=0.1. Of course, you need gnuplot to sample this point in order to see a peak. Thus you need to play with set samples and set xrange.
From your question it is not clear whether you have a function F(x) as expression or just a x,y-data file. I assume that your function is monotonic increasing in x and y.
Two solutions come to my mind:
via simple linear interpolation
via curve fitting
Let's create some test data. For this, let's assume your function is known (as expression) and something like this (check help norm): F(x) = a*norm(b*x + c)
Let's take a = 1; b = 0.8; c = -4. In the example below, sampling will be only 8, just for illustration purpose.
You can easily set samples 200 and you will get the same results as for the curve fitting method below. From gnuplot 5.0 on, you could write the data into a datablock instead of a file on disk.
Data: SO22276755.dat
0 3.16712e-05
1.42857 0.002137
2.85714 0.043238
4.28571 0.283855
5.71429 0.716145
7.14286 0.956762
8.57143 0.997863
10 0.999968
Script 1: (basically works for gnuplot 4.6.0, March 2012)
### interpolate x-values
reset
FILE = "SO22276755.dat"
yis = '0.10 0.90'
yi(n) = real(word(yis,n))
xis = ''
xi(n) = real(word(xis,n))
Interpolate(yi) = (x1-x0)/(y1-y0)*(yi-y0) + x0
getXis(xis) = xis.(n=words(xis), n<words(yis) ? yi=real(word(yis,n+1)) : 0, \
y0<=yi && y1>=yi ? sprintf(" %g",Interpolate(yi)) : '')
set key left top noautotitle
set grid x,y
plot x1=y1=NaN FILE u (x0=x1,x1=$1):(y0=y1,y1=$2,xis=getXis(xis),y1) \
w l lc rgb "blue" ti "data", \
'+' u (xi=xi(int($0+1))):(yi=yi(int($0+1))):\
(sprintf("(%.4g|%.4g)",xi,yi)) every ::0::1 \
w labels point pt 7 lc rgb "red" right offset -1,0 ti "interpolated"
### end of script
Result:
Script 2: (basically works for gnuplot>=4.6.0, March 2012)
With this approach you are fitting your known function F(x) to constant lines, i.e. your desired values 0.1 and 0.9. For this, a file will be created (could be a datablock for gnuplot>=5.0) and it will basically look like this SO22276755.fit:
0 0.1
1 0.1
0 0.9
1 0.9
### interpolate x-values
reset
F(x) = a*norm(b*x+c) # function
a = 1
b = 0.8
c = -4
yis = '0.10 0.90'
yi(n) = real(word(yis,n))
xis = ''
xi(n) = real(word(xis,n))
set key left top noautotitle
set grid x,y
# create fit levels file
LEVELS = "SO22276755.fit"
set table LEVELS
set samples 2
plot for [i=1:words(yis)] '+' u (yi(i))
unset table
xmin = 0
xmax = 10
set xrange[xmin:xmax]
set samples 100
xis = ''
do for [i=1:words(yis)] {
xi = (xmin+xmax)*0.5 # set start value
fit F(xi) LEVELS u 1:2 index i-1 via xi
xis = xis.sprintf(" %g",xi)
}
plot F(x) w l lc rgb "web-green" ti "F(x)", \
'+' u (xi=xi(int($0+1))):(yi=yi(int($0+1))):(sprintf("(%.4g|%.4g)",xi,yi)) \
every ::0::1 w labels point pt 7 lc rgb "red" righ offset -1,0 ti "fitted"
### end of script
Result: