Fitting graph and draw lines by selecting ranges in horizontal axis - gnuplot

I am trying to plot a graph and fitting it using a linear line.
f1(x)=a1+b1*x
fit [0:80] f1(x) 'diff-xy-bcmLyo25perS.dat' via a1,b1
f2(x)=a2+b2*x
fit [100:220] f2(x) 'diff-xy-bcmLyo25perS.dat' via a2,b2
And I tried to plot both the plots into the same graph using command:
f(x) = x < 60 ? f1(x) : f2(x)
plot 'diff-xy-bcmLyo25perS.dat' using 1:2 with lines linestyle 1 title "{/Symbol b}BCMal-C_{12}C_{8}", f(x) lw 3.0 lc rgb 'black'
I get a plot as above.
In that plot one could see that there are two lines intersecting at 80 (horizontal scale) and it makes shape like 'v'.
I wish to eliminate that 'v' shape intersection and I would like to get two separate lines, one from 0 to 80 and the other one from 100 to 220.
How I could get this?
Appreciate any help.
Thanks in advance.

You could exploit that gnuplot doesn't plot infinity and NaN values (as 1.0/0)
Using
plot_if_in_range(y,x,lower,upper) = (x>=lower && x<=upper)?(y):(1.0/0)
You could easily plot any function in given domain:
plot plot_if_in_range(exp(x) , x, -5, 2), \
plot_if_in_range(sin(x)+x, x, -2, 5)

With gnuplot 5.0 you can specify different range for different functions:
set style data lines
plot 'diff-xy-bcmLyo25perS.dat' using 1:2 ls 1, \
[0:80] f1(x) lw 3.0 lc rgb 'black',\
[100:220] f2(x) lw 3.0 lc rgb 'black'
Note, that this works only, because you first plot the data file. Plotting only
plot [0:80] f1(x), [100:220] f2(x)
wouldn't work, since the first range settings are equivalent to a global set xrange [0:80] (it has always been), so that the second function wouldn't be visible at all.
However, in your case it should work fine.

Edit:
Sorry, this is basically the same idea as Sergei Izmailov's answer which I missed.
Answer:
Use the special file "+", which provides x values for your plot that you can then sample using a function of your choice, including one that ignores input if it's outside of range. Then you can use your f1(x) and f2(x) directly:
plot "+" using ($1):(0 < $1 && $1 < 80 ? f1($1) : 1/0), \
"+" using ($1):(100 < $1 && $1 < 220 ? f2($1) : 1/0)

Related

Gnuplot with Errorbars and line of regression

I want to graph some values with errorbars but it somehow doesnt work. Can you help me please?
431.00E12 0.69 47.00E5
567.00E12 1.10 58.00E5
662.00E12 1.75 67.00E5
watched a lot of videos and tutorials and did exactly what they did but it doesnt work.. The part with Regression and so on worked fine but now I want those error bars horizontally. My textfile is in this order:
x-Value y-Value DeltaX
The DeltaX should be the Errorbar so the errorbar schould look like this: at point x, the errorbar has length from x+-DeltaX.
Could you please tell me the code that combines the regression line and the Errorbars?
plot "/Users/amar/Desktop/dgd.txt" using 1:2:3 with errorbars, f(x)
Check help xerrorbars.
A delta x which is 8 orders of magnitude smaller than the x-value will be difficult to see as errorbar. Just to demonstrate xerrorbars, I changed it to a similar order of magnitude.
With the following code:
### xerrorbars
reset session
$Data <<EOD
431.00E12 0.69 47.00E12
567.00E12 1.10 58.00E12
662.00E12 1.75 67.00E12
EOD
set key left
f(x) = a*x + b
a = 1e-15 # some initial guesses
b = -1
set fit nolog brief
fit f(x) $Data u 1:2 via a,b
plot $Data u 1:2:3 with xerrorbars pt 7 lc rgb "red", \
f(x) title sprintf("f(x) = %g * x + %g",a,b)
### end of code
You'll get:

fitting sub-range on time data in gnuplot

Let me start by saying that I am working on:
$ gnuplot --version
gnuplot 5.2 patchlevel 2
I would like to plot and fit date/time data in gnuplot and have the fit only performed and subsequently displayed on a sub-range of the plot.
Example data that I played with can e.g. be found here.
EDIT: I realized that the data in the file don't match the timefmt signature, I added a /06 to each line so that the point would be drawn in the middle of the year which allowed to nicely plot it together with also monthly data from the same source.
I can get the desired result with the code below where I plot three functions, one over the full range of the plot and two others both of which only cover part of the date range.
set key left
set yrange[-0.75:1.0]
set xdata time
set timefmt '%Y/%m'
r=10e-10
e(x) = r*x+s
fit e(x) 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 via r,s
a=10e-10
f(x) = a * x + b
set xrange ["1970/06":"2018/06"]
fit f(x) 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 via a,b
g(x) = ( x > "1970/06" ) ? f(x) : 1/0
set xrange ["1850/06":"1970/06"]
c=9.24859e-11
h(x) = c * x + d
fit h(x) 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 via c,d
i(x) = ( x < "1970/06" ) ? h(x) : 1/0
set xrange ["1849/06":"2018/06"]
set term png size 1500,1000
set output 'annual_average_with_fit.png'
plot 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 with lp lw 2 t'annual avg (decadally smoothed)', e(x) t'full range fit' lw 2, i(x) t'1850-1970 fit' lw 2, g(x) t'1970-2018 fit' lw 2
which yields this plot
This is all good and well, but (and this is where the question comes in) in principle I should be able to achieve the same result also by other means.
First: I restrict the range of the file data to a certain range to fit it only on that range. In principle I should be able to do the same using this (type of) syntax:
fit ["1970/06":"2018/06"] f(x) 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 via a,b
which however gives
Read 168 points
Skipped 168 points outside range [x=1970:2018]
[...] No data to fit
which seems weird given that the set xrange clearly has the desired effect.
Secondly trying to restrict the plotting of the curve to the fit range with
plot 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 with lp lw 2 t'annual avg (decadally smoothed)', ["1970/06":"2018/06"] f(x) t''
does not plot the function at all.
I might be overlooking something very basic, but having tried various things I don't see what it is
The following (a bit cleaned up) code should do what you want (tested with gnuplot 5.2.5).
I guess the problem is that you tried to fit a range ["1970/06":"2018/06"], but your data is only until 2017. So better leave it open, e.g. ["1970/06":] or ["1970/06":*].
edit: added a limited range fit i(x)
reset session
set term png size 1500,1000
set output 'annual_average_with_fit.png'
set key left
set yrange[-0.75:1.0]
set xdata time
set timefmt '%Y/%m'
set format x '%Y'
FILE = 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt'
r=10e-10
f(x) = r*x+s
fit [*:*] f(x) FILE using 1:2 via r,s
c=9.24859e-11
g(x) = c * x + d
fit [*:"1970/06"] g(x) FILE using 1:2 via c,d
a=10e-10
h(x) = a * x + b
fit ["1970/06":*] h(x) FILE using 1:2 via a,b
p=1e-9
i(x) = p * x + q
fit [strptime("%Y/%m", "1910/06"):strptime("%Y/%m", "1945/06")] i(x) FILE using 1:2 via p,q
set xrange [*:*]
plot FILE using 1:2 with lp lw 2 t'annual avg (decadally smoothed)', \
f(x) t 'full range fit' lw 2, \
[:"1970/06"] g(x) t '1850-1970 fit' lw 2, \
["1970/06":] h(x) t '1970-2018 fit' lw 2,\
[strptime("%Y/%m", "1910/06"):strptime("%Y/%m", "1945/06")] i(x) t '1910-1945 fit' lw 2
set output
Output:

How to draw a filledcurve graph with GNUPlot by using 2 csv files

If I have 2 csv files ("CSV1.csv" dataname_1 and "CSV2.csv" dataname_2), how can I draw filled curve graph from the data of 2 csv files. The formats of these CSV files are identical, where 2 is timestamps and 5 is the value thus the using 2:5
I am trying this:
plot dataname_2 using 2:5 title "Above" with filledcurves above lc rgb 'blue',\
dataname_1 using 2:5 title "Below" with filledcurves below lc rgb 'red',\
dataname_2 using 2:5 title "Engine Starts" with lines lc rgb "#1E90FF",\
dataname_1 using 2:5 title "Engine Hours" with lines lc rgb "#FF1493"
I need to modify the code above so that the output is:
A solution which will probably always work is to prepare the data with whatever external tool in such a way that gnuplot can handle and plot it. I'm aware that the philosophy of gnuplot is to concentrate on plotting and not necessarily on data preparation for plotting. However, it is always good to have a minimum set of features to do some basic data preparation.
In your case you have several problems, well, let's call it challenges ;-)
with filledcurves requires data within the same file or datablock
however, gnuplot cannot easily merge datafiles line by line (it can with some workaround, see: https://stackoverflow.com/a/61559658/7295599). Simply appending would be no problem with gnuplot.
the latter doesn't help you since with filledcurves needs identical x for upper and lower curve, i.e. x,y1,y2 and your data has x1,y1 and x2,y2
however, gnuplot cannot easily resample data (it can with some workaround, see: Resampling data with gnuplot)
with filledcurves cannot directly fill curves with non-monotonic increasing x (not the case with your data. Here just for illustration purposes) (it can with some workaround see: https://stackoverflow.com/a/53769446/7295599 or https://stackoverflow.com/a/56176717/7295599)
So a workaround for all this could be the following (works with gnuplot 5.2, maybe can be tweaked to work with earlier versions):
Assumptions:
Data x1,y1 and x2,y2 in two files or datablocks
Data has not necessarily identical x, i.e. x1,y1 and x2,y2
Data may contain non-monotonic x
the two curves have only one intersection (well, the workaround below will just take the first one)
Procedure:
if not already, get the data into a datablock.
Find the intersection of the curves.
Create new datablocks: Filled1 using Data1 from the beginning to the intersection point and using Data2 backwards from the intersection point to the beginning. Filled2 using Data1 from the end backwards to the intersection point and using Data2 from the intersection point to the end.
Plot $Data1 and $Data2 with lines and $Filled1 and $Filled2 with filledcurves
Steps 2 and 3, probably will not be much shorter in another programming language unless there are dedicated functions.
Get files to datablock: (see also here gnuplot: load datafile 1:1 into datablock)
# get files to datablocks
set table $Data1
plot 'myFile1.dat' u 1:2 w table
set table $Data2
plot 'myFile2.dat' u 1:2 w table
unset table`
Code: (copy&paste for gnuplot >=5.2)
### fill intersecting curves from two files not having identical x
reset session
$Data1 <<EOD
1 1
2 0
4 1
3 3
5 5
6 6
8 8
9 9
EOD
$Data2 <<EOD
1 3
3.5 5
7.5 1
9 7
EOD
# orientation of 3 points a,b,c: -1=clockwise, +1=counterclockwise
Orientation(a,b,c) = sgn((word(b,1)-word(a,1))*(word(c,2)-word(a,2)) - \
(word(c,1)-word(a,1))*(word(b,2)-word(a,2)))
# check for intersection of segment a-b with segment c-d,
# 0=no intersection, 1=intersection
IntersectionCheck(a,b,c,d) = \
Orientation(a,c,b) == Orientation(a,d,b) || \
Orientation(c,a,d) == Orientation(c,b,d) ? 0 : 1
# coordinate of intersection
M(a,b) = real(word(a,1)*word(b,2) - word(a,2)*word(b,1))
N(a,b,c,d) = (word(a,1)-word(b,1))*(word(c,2)-word(d,2)) - \
(word(a,2)-word(b,2))*(word(c,1)-word(d,1))
Px(a,b,c,d) = (M(a,b)*(word(c,1)-word(d,1)) - (word(a,1)-word(b,1))*M(c,d))/N(a,b,c,d)
Py(a,b,c,d) = (M(a,b)*(word(c,2)-word(d,2)) - (word(a,2)-word(b,2))*M(c,d))/N(a,b,c,d)
Intersection(a,b,c,d) = sprintf("%g %g", Px(a,b,c,d), Py(a,b,c,d))
stop = 0
do for [i=1:|$Data1|-1] {
a = $Data1[i]
b = $Data1[i+1]
do for [j=1:|$Data2|-1] {
c = $Data2[j]
d = $Data2[j+1]
if (IntersectionCheck(a,b,c,d)) {
i0 = i; j0 = j
stop=1; break }
}
if (stop) { break }
}
# create the datablocks for the outline to be filled
set print $Filled1
do for [k=1:i0] { print $Data1[k] }
print Intersection(a,b,c,d)
do for [k=j0:1:-1] { print $Data2[k] }
set print $Filled2
do for [k=|$Data1|:i0+1:-1] { print $Data1[k] }
print Intersection(a,b,c,d)
do for [k=j0+1:|$Data2|] { print $Data2[k] }
set print
set key top left
plot $Filled1 u 1:2 w filledcurves lc rgb 0x3f48cc, \
$Filled2 u 1:2 w filledcurves lc rgb 0xed1c24, \
$Data1 u 1:2 w lp pt 7 lw 5 lc rgb 0x99d9ea, \
$Data2 u 1:2 w lp pt 7 lw 5 lc rgb 0xff80c0
### end of code
Result:

Dollar-sign from gnuplot code generates conflict with epslatex

In order to make filled curves only in selected areas of the function parameter (x-axis), I found a good idea with a filter on the thread "Fill several sections below a curve of data in Gnuplot".
The problem is that with epslatex, the used $ sign to point to the variable is creating an error when trying to compile with latex.
Is there any other possibility to address the variable?
set samples 100
set xrange [-2:2]
f(x) = -x**2 + 4
set linetype 1 lc rgb '#A3001E'
set style fill transparent solid 0.35 noborder
filter(x,min,max) = (x > min && x < max) ? x : 1/0
plot '+' using (filter($1, -1, -0.5)):(f($1)) with filledcurves x1 lt 1 notitle,\
'' using (filter($1, 0.2, 0.8)):(f($1)) with filledcurves x1 lt 1 notitle,\
'' using 1:(f($1)) with lines lw 3 lt 1 title 'curve'
I guess the problem comes from the $ in the automatic title, which is created by gnuplot. Use title to overwrite this by a custom one:
plot ... title 'my custom escaped title'
Or use column(1) instead of the short cut $1.

Conditional execution of the plot command

I'm using gnuplot to create graphs of a rather simply structured file. In particular, the file contains 8 columns, the first 4 columns describe "measured" values, the second 4 "predicted" values. I want to create two plots, one comprising only measured values, one comprising both.
Since my gnuplot script is rather long, I would like to introduce a variable to set the flag whether predicted values should be contained or not. I want to use different line types, axis and labels, and hence, cannot use the "using" approach.
Here is what I use so far:
plot 'file.txt' using 2:xticlabels(1) title "Val1 (Measured)" with
lines axes x1y1, '' using 3:xticlabels(1) title "Val2 (Measured)"
with linespoints axes x1y2;
if (settingCompareToAggretagion == 1)
plot 'file.txt' using 4:xticlabels(1) title "Val1 (Aggregated)" with lines
axes x1y1, '' using 5:xticlabels(1) title "Val2" with linespoints axes x1y2;
There is obviously the problem that I get two plots but one. I also tried to "attach" the second set of plots to the command with an inline if condition (bool.exrp. ? x : y) but this is not working.
Thank you!
That can be done with the replot command:
plot x, x**2
replot x+5, x**2 + 5
That plots a single graph with four plots.
In your case the following should work:
plot 'file.txt' u 2:xtic(1) t "Val1 (Measured)" w l,\
'' u 3 t "Val2 (Measured)" w lp axes x1y2
if (settingCompareToAggregation == 1) {
replot 'file.txt' u 4 t "Val1 (Aggregated)" w l,\
'' u 5 t "Val2 (Aggregated)" w lp axes x1y2
}

Resources