gnuplot: cap f(x) at integer after certain value passed - gnuplot

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"

Related

How to fill a section between two curves inside a defined range?

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

GNUplot samples and isosamples for ackermann function

So I've been learning GNUplot and one thing that's been bugging me is the sample and isosample rate (esp when computing Ackermann functions). I know that isosample rate is supposed to control the number of isolines while the other does the same thing for sampling along each isoline.
This code plots the ackermann function (source: gnuplot sourceforge). Why do only some specific values of samples and isosamples generate the plot? The only other values which worked were (4,2) and (2,4). For the remaining values I got a stack overflow or recursive depth limit exceeded.
ack(m,n) = (m == 0) ? n + 1 : (n == 0) ? ack(m-1,1) : ack(m-1,ack(m,n-1))
set xrange [0:3]
set yrange [0:3]
set isosamples 4
set samples 4
set title "Plot of the ackermann function"
splot ack(x, y)
Can somebody please explain why only (4,4) , (4,2), (2,4) work?
There are two issues here:
As mentioned in the comments above, you cannot evaluate the Ackermann function for non-integer values. If your samples/isosamples value do not exactly match the length of your xrange/yrange then gnuplot will try to evaluate the function at non-integer values. You can avoid that by explicit conversion to integers:
ack(m,n) = (m == 0) ? n + 1 : (n == 0) ? ack(m-1,1) : ack(m-1,ack(m,n-1))
ack_protected(x,y) = ack(int(x),int(y))
set xrange [0:3]
set yrange [0:3]
set isosamples 50
set samples 50
set title "Plot of the ackermann function"
splot ack_protected(x, y)
gives
You also cannot evaluate the Ackermann function for integer values of (m,n) that would exceed the recursion limit of gnuplot, which is 250. The combination m=4, n=3 already exceeds that limit.

The function disappear close to zero

I have problem with plotting fitted function.
The part of the ploted function close to zero disappears and connected with the hyperbola or something which should not be there at all. This happen only if I change set xrange to something smaller than 0. I have to do this because I have lot of data points to close zero so it would look very ugly if I would not changed it.
I tried to use conditionals x>0?f(x):1/0 but it does not help. The hyperbola disappear but the function does not continue down as it should.
I use this code:
set terminal postscript eps size 3.5,2.62 enhanced color
set output "a.eps"
set xrange [-1:]
f(x)=a*b*x/(1+a*x)
fit f(x) "./a" via a, b
plot "./a" w p title "", f(x) w l title "Langmuir isotherm"
That is simply a matter of sampling. The default sampling rate is 100 (show samples), which isn't enough to show fast-varying functions. Increase the sampling rate with e.g.
set samples 1000
to have your function plotted correctly.
A second point is, that discontinuities aren't shown properly if no sample is located exactly at that position. Consider the following plot to demonstrate this:
set xrange [-1:1]
set multiplot layout 2,1
set samples 100
plot 1/x
set samples 101
plot 1/x
unset multiplot
So, if you want to plot the function correctly on both sides of the discontinuity, you must either define a small region around the discontinuity as undefined, or you plot the parts on the left and right separately:
set xrange [-1:]
f(x)=a*b*x/(1+a*x)
fit f(x) "./a" via a, b
left(x) = (x < -1/a ? f(x) : 1/0)
right(x) = (x > -1/a ? f(x) : 1/0)
plot "./a" w p title "", left(x) w l lt 2 title "Langmuir isotherm", right(x) w l lt 2 notitle

How to make the same scale of x and y in gnuplot without full autoscaling?

I know that "set view equal xy" automatically sets the same scales for x and y, but it is not exactly what I want. I expect that I define 'xrange [a:b]' and 'yrange [c:?]' and the last number '?' would be defined automatically from the knowledge of 'set view equal xy' and from the knowledge of the lengths of the axes.
I can try to explain in other words. I write the following:
reset
set xrange [-5:5]
set yrange [-1:?]
set view equal xy
plot sin(x) with lines
The resulting scaling depends on the plotted function and gnuplot does not follow the entered values. But it should be possible to calculate '?' just from the knowledge of the visible lengths of axes (Lx,Ly) and condition of 'equal scales' (Sx=Sy):
Sx = Sy
Lx/(-5-5) = Ly/(-1-?)
? = - Ly/Lx * (-5-5) -1
This is what I expect from gnuplot when asking for equal scales. Could anyone help me to achieve this 'not fully auto'-scaling?
Thanks in advance.
I'm not fully sure what you want, but using set yrange [-1:*] should work fine. That autoscales only the upper y-value:
set xrange [-5:5]
set yrange [-1:*]
set size ratio -1
plot 2*x, 0.2*x
The output with 4.6.5 is:

GNUPLOT: Show a x value given a y value

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:

Resources