How can I perform string interpolation in a .gpi file? - string

I am trying to understand how the .gpi file syntax works. I did a couple searches on Google and it looks like GPI files were invented by Garmin for plotting GPS points. My goal is to print the value of a variable.
For example,
tag=sprintf("%s", ARG1)
plot sprintf("%s/tags/%s.dat", history_dir, tag) \
u 1:2 w filledcurve x1 ls 20 t 'Tag ${tag} emitted ', \
'' u 1:4 w filledcurve x1 ls 21 t 'Packages which emitted ${tag}', \
'' u 1:3 w filledcurve x1 ls 22 t 'Tag ${tag} overridden'
I've tried using ${tag} but that is only printing the literal value and not the variable value.

I figured it out.
Hint: sprintf is a C function.
tag=sprintf("%s", ARG1)
plot sprintf("%s/tags/%s.dat", history_dir, tag) \
u 1:2 w filledcurve x1 ls 20 t sprintf("Tag %s emitted", tag), \
'' u 1:4 w filledcurve x1 ls 21 t sprintf("Packages which emitted %s", tag), \
'' u 1:3 w filledcurve x1 ls 22 t sprintf("Tag %s overridden", tag)

Related

Show a label of x and y values, and possibly other values when hovering over a function with the mouse in GNUPLOT

Is there a way that when i hover over a function, say f(x) = x**2, that it pops up a label when i move my mouse onto the function at, say, x=2, it shows a label of "x = 2 \n (new line) y = 4" or something like that? Also if that is possible can I make it so that it includes external values? What i mean by this is if i have a gradient formula, for x**2, 2*x, I can tell GNUPLOT to say below the x and y label values, "Gradient: 4". It doesn't have to be the derivative but that is just an example of what i mean.
I guess you are looking for hypertext. In the gnuplot console check help hypertext.
If you move the mouse pointer close to a datapoint, some text will pop-up. It will be easier to "catch" the point and get the hypertext shown if you plot the labels with a pointsize a bit larger, e.g. ps 3 and "invisible", i.e. lt -2 which is the background color.
This works with an interactive wxt terminal. You need to check other terminals. Tested with gnuplot 5.2.6.
Code:
### add hypertext (wxt terminal)
reset session
# create some test data
set print $Data
do for [x=-5:5] {
print sprintf("%g %g", x, x**2)
}
set print
plot $Data u 1:2:(sprintf("x=%g\ny=%g",$1,$2)) w labels hypertext point pt 7 ps 3 lt -2 notitle, \
'' u 1:2 w lp pt 7 title "f(x)"
### end of code
Result: (screenshot)
Addition:
Example with more points and plotted with lines. There is no snapping to the curve, you have to move along the curve.
Code:
### add hypertext (wxt terminal)
reset session
# create some test data
set samples 201
set xrange[-5:5]
set table $Data
plot '+' u 1:($1**2):(2*$1) w table
unset table
set xlabel 'x'
set ylabel 'y'
plot $Data u 1:2:(sprintf("x=%.2f\ny=%.2f\ndy/dx=%.2f",$1,$2,$3)) w labels hypertext point pt 7 ps 1 lt -2 notitle, \
'' u 1:2 w l lw 2 title "f(x)"
### end of code
Result: (screen capture)
Addition2: (using functions for interactive zoom-in)
Code:
### add hypertext (wxt terminal) with functions for interactive zoom-in
reset session
set xlabel 'x'
set xrange[-5:5]
set ylabel 'y'
set samples 201
f(x) = x**2
g(x) = 2*x
plot '+' u (x):(f(x)):(sprintf("x=%.2g\ny=%.2g\ndy/dx=%.2g",x,f(x),g(x))) w labels hypertext point pt 7 ps 1 lt -2 notitle, \
f(x) w l lw 2 title "f(x)"
### end of code

How to merge all plots in one window in gnuplot

I know similar question are asked and answered multiple times in SO.
Here I have something unique that includes the fitting for each plot.
I am using
f(x) = (a0 + a1/x)
fit f(x) 'test.data' using 1:2 via a0,a1
plot 'test.data' using 1:2 w points pt 1 t , f(x) t sprintf("K_{fit} = a_0 + a_1/T", a0)
f(x) = (a0 + a1/x)
fit f(x) 'test.data' using 1:3 via a0,a1
plot 'test.data' using 1:3 w points pt 1 t , f(x) t sprintf("K_{fit} = a_0 + a_1/T", a0)
here I am skipping other plot commands to keep the query short.
f(x) = (a0 + a1/x)
fit f(x) 'test.data' using 1:8 via a0,a1
plot 'test.data' using 1:8 w points pt 1 , f(x) t sprintf("K_{fit} = a_0 + a_1/T", a0)
f(x) = (a0 + a1/x)
fit f(x) 'test.data' using 1:9 via a0,a1
plot 'test.data' using 1:9 w points pt 1 t , f(x) t sprintf("K_{fit} = a_0 + a_1/T", a0)
Using the above plots, I am getting one box for each plot.
How I can merge all plots in a single window?
The data files is having 9 columns (1st colum will be x-axis while others are y-axos) and inserting plot commands for each plot makes the gnuplot script too long. Is there any workaround so that I do not need to type p"plot each time and the job can be done by some loop?
I tried to manage all plots in a single box using
plot for [i=1:9] 'test.data' using (i):i notitle with boxplot lt -1, \
f(x) = (a0 + a1/x)
fit f(x) 'test.data'for [i=1:9] using (i):i via a0,a1
plot 'test.data' for [i=1:9] using (i):i w points pt 1 t , f(x) t sprintf("K_{fit} = a_0 + a_1/T", a0)
but I am getting below error
fit f(x) 'test.data'for [i=1:9] using (i):i via a0,a1
^
"test.gnu", line 23: Need via and either parameter list or file
Below is my test.data file
100.0 0.45564E+02 0.20558E+02 0.53903E+02 0.24899E+02 0.56334E+02 0.26169E+02 0.58482E+02 0.27273E+02
200.0 0.17118E+02 0.81681E+01 0.18147E+02 0.86680E+01 0.18397E+02 0.87831E+01 0.18598E+02 0.88736E+01
300.0 0.10908E+02 0.53456E+01 0.11307E+02 0.55301E+01 0.11398E+02 0.55703E+01 0.11470E+02 0.56013E+01
400.0 0.81160E+01 0.40313E+01 0.83328E+01 0.41288E+01 0.83808E+01 0.41496E+01 0.84181E+01 0.41655E+01
500.0 0.64937E+01 0.32506E+01 0.66311E+01 0.33115E+01 0.66611E+01 0.33243E+01 0.66841E+01 0.33340E+01
600.0 0.54231E+01 0.27282E+01 0.55185E+01 0.27700E+01 0.55390E+01 0.27787E+01 0.55547E+01 0.27853E+01
700.0 0.46602E+01 0.23525E+01 0.47305E+01 0.23830E+01 0.47455E+01 0.23894E+01 0.47569E+01 0.23942E+01
800.0 0.40878E+01 0.20687E+01 0.41419E+01 0.20920E+01 0.41533E+01 0.20968E+01 0.41620E+01 0.21005E+01
900.0 0.36419E+01 0.18465E+01 0.36847E+01 0.18649E+01 0.36937E+01 0.18687E+01 0.37006E+01 0.18716E+01
1000.0 0.32843E+01 0.16677E+01 0.33192E+01 0.16826E+01 0.33264E+01 0.16857E+01 0.33320E+01 0.16880E+01
If you check help fit you won't find that gnuplot can fit in a loop as in a plot loop.
But you can fit several data columns in a do for loop, check help do.
And you can store the fit parameters in arrays for plotting them later in a plot for loop. I hope you can figure out how the example code below works.
Code:
### fit multiple columns in a loop
reset session
f(x) = a0 + a1/x
# arrays for fit parameters
array arr0[8]
array arr1[8]
# create some random test data
do for [i=1:8] {
arr0[i] = int(rand(0)*50)+5
arr1[i] = int(rand(0)*10)+5
}
set print $Data
do for [x=10:50] {
line = sprintf("%g",x/100.)
do for [i=1:8] {
a0 = arr0[i]
a1 = arr1[i]
line = line.sprintf(" %.3f",f(x/100.)+10*i)
}
print line
}
set print
# fit columns in a loop and put fit values into array
do for [i=1:8] {
fit f(x) $Data u 1:i+1 via a0,a1
arr0[i] = a0
arr1[i] = a1
}
set key Left
plot for [i=1:8] $Data u 1:i+1 ti sprintf("%d: a0=%.1f, a1=%.1f",i,arr0[i],arr1[i]), \
for [i=1:8] tmp=(a0=arr0[i],a1=arr1[i]) f(x) w l lc rgb "red" not
### end of code
Result:
Addition (with OP's data)
Code:
### fit multiple columns in a loop
reset session
f(x) = a0 + a1/x
# arrays for fit parameters
array arr0[8]
array arr1[8]
$Data <<EOD
100.0 0.45564E+02 0.20558E+02 0.53903E+02 0.24899E+02 0.56334E+02 0.26169E+02 0.58482E+02 0.27273E+02
200.0 0.17118E+02 0.81681E+01 0.18147E+02 0.86680E+01 0.18397E+02 0.87831E+01 0.18598E+02 0.88736E+01
300.0 0.10908E+02 0.53456E+01 0.11307E+02 0.55301E+01 0.11398E+02 0.55703E+01 0.11470E+02 0.56013E+01
400.0 0.81160E+01 0.40313E+01 0.83328E+01 0.41288E+01 0.83808E+01 0.41496E+01 0.84181E+01 0.41655E+01
500.0 0.64937E+01 0.32506E+01 0.66311E+01 0.33115E+01 0.66611E+01 0.33243E+01 0.66841E+01 0.33340E+01
600.0 0.54231E+01 0.27282E+01 0.55185E+01 0.27700E+01 0.55390E+01 0.27787E+01 0.55547E+01 0.27853E+01
700.0 0.46602E+01 0.23525E+01 0.47305E+01 0.23830E+01 0.47455E+01 0.23894E+01 0.47569E+01 0.23942E+01
800.0 0.40878E+01 0.20687E+01 0.41419E+01 0.20920E+01 0.41533E+01 0.20968E+01 0.41620E+01 0.21005E+01
900.0 0.36419E+01 0.18465E+01 0.36847E+01 0.18649E+01 0.36937E+01 0.18687E+01 0.37006E+01 0.18716E+01
1000.0 0.32843E+01 0.16677E+01 0.33192E+01 0.16826E+01 0.33264E+01 0.16857E+01 0.33320E+01 0.16880E+01
EOD
# fit columns in a loop and put fit values into array
set fit nolog
do for [i=1:8] {
fit f(x) $Data u 1:i+1 via a0,a1
arr0[i] = a0
arr1[i] = a1
}
set key Left
plot for [i=1:8] $Data u 1:i+1 ti sprintf("%d: a0=%.1f, a1=%.1f",i,arr0[i],arr1[i]), \
for [i=1:8] tmp=(a0=arr0[i],a1=arr1[i]) f(x) w l lc rgb "red" not
### end of code
Result:
Addition: (after your comments)
You can also plot the data in a loop.
You can simply define functions for the linetype and the dashtype. Dashtype dt 1 is a solid line and dt 2 is a dashed line basically identical to dt "-". Type test in the gnuplot console and you will see the different linestyles.
Maybe also an explanation for the term tmp=(a0=arr0[i],a1=arr1[i]). You can add a definition in the plot command (see help plot), but since we need two definitions a0=arr0[i] and a1=arr1[i] we use serial evaluation (see help operators binary) and assign it to a dummy variable tmp.
Your functions and the plot command would then be:
myLineType(i) = (i-1)/2+1 # Attention: /2 in gnuplot is integer division if `i` is integer!
myDashType(i) = (i-1)%2+1 # % is modulo
plot for [i=1:8] $Data u 1:i+1 w l lw 2 lt myLineType(i) dt myDashType(i) not, \
for [i=1:8] tmp=(a0=arr0[i],a1=arr1[i]) f(x) w p lt myLineType(i) not

Is there a way to plot an arc with given start, centre, end point in gnuplot?

The final goal is to plot a contour path in Cartesian space existing of simple element types (only lines + arcs/circles). The parameter of the circular element is presented by a start, centre and end point. I have no influence, whether the arc is fully closed (=ellipsis or circle) or only a quarter, half a circle or something in between. It's given by a file.
I found already the circle command, but there is only a begin/end angle provided.
I know, that's possible to calculate the angles out of the 3 points, but
i'm looking for a smart command like CAD-applications do with start, centre, end.
Thanks for you thoughts.
Two ways come to my mind for plotting arcs between 3 given points where one of them is the center point.
You explicitely asked for ellipses, so the second approach might be the one for you. Nevertheless, because of its simplicity I will show the simple rotating vector approach as well.
Both approaches work easiest if you have your data in a datablock (check this: gnuplot: load datafile 1:1 into datablock) in order to extract the point coordinates from a certain data line.
1. Rotating & scaling vectors:
a simple way which will always work (but will not necessarily give ellipses): rotate the first vector towards the second vector in the given rotation direction and linearly scale its length. This will result in circle arcs when the two vectors have identical length. Depending on the angles and lengths of the two vectors, it might result in ellipses or spirals.
2. Calculate suitable ellipses:
a bit more complicated is plotting ellipses. I'm not sure, but I guess with the above 3 given points you can draw an infinite number of ellipses through these points. The restriction in the example below is that the longer vector will be taken as the larger elliptic axis. The ellipse will be scaled and rotated such that the endpoint of the smaller vector will be on the ellipse. If the angle difference of the vectors is 0 degrees it will be interpreted as 360 degrees. However, it will be impossible to find an ellipse if the two vectors have an angle difference of 0, 180 or 360 degrees and at the same time different lengths. The code below will nevertheless draw some arcs.
I hope there is no bug in the code and maybe it can be simplified. Suggestions are welcome.
Data: SO57408781_Arcs.dat
File without headerline. Columns: x0 y0 x1 y1 x2 y2 rotation_direction
-7 7 -5 7 -5 7 +1
0 7 2 7 0 9 +1
7 7 9 7 5 7 +1
-4 0 0 4 -2 -2 +1
5 0 8 0 5 5 +1
9 -2 0 -5 7 -7 +1
-7 -7 -5 -7 -4 -9 +1
Code:
### draw ars/ellipses with center point and startpoint to endpoint
reset session
set size square
set angle degrees
FileToDatablock(f,d) = GPVAL_SYSNAME[1:7] eq "Windows" ? \
sprintf('< echo %s ^<^<EOD & type "%s"',d,f) : \
sprintf('< echo "\%s <<EOD" & cat "%s"',d,f) # Linux/MacOS
FILE = 'SO57408781_Arcs.dat'
# file structure
# x0 y0 x1 y1 x2 y2 rotation_direction
load FileToDatablock(FILE,'$Arcs')
# value extraction from datablock
x(i,n) = word($Arcs[i],2*n+1)
y(i,n) = word($Arcs[i],2*n+2)
Direction(i) = word($Arcs[i],7) # rotation direction +1=CCW, -1=CW
# length and angles
L(dx,dy) = sqrt(dx**2 + dy**2)
Angle(x0,y0,x1,y1) = (_dx=x1-x0, _dy=y1-y0, _L=sqrt(_dx**2 + _dy**2), _L==0 ? NaN : \
(_dy>=0 ? acos(_dx/_L) : 360-acos(_dx/_L) ))
# get x,y vector lengths, angles and rotation direction
getXYVAD(n) = (x0=x(n,0), y0=y(n,0), x1=x(n,1), y1=y(n,1), x2=x(n,2), y2=y(n,2), \
v1=L(x1-x0,y1-y0), v2=L(x2-x0,y2-y0), \
a1=Angle(x0,y0,x1,y1), a2=Angle(x0,y0,x2,y2), rd=Direction(n) )
# calculate parameters for the arc
getParamsA(i) = (ca=a2>a1, cd=rd>0, a12=(360*(cd && ca ? 0 : 1) + (a2-a1)*(cd?1:-1))*(cd?1:-1) )
xPosA(i,t) = x0 + (v1*(1-t) + v2*t)*cos(a1+a12*t)
yPosA(i,t) = y0 + (v1*(1-t) + v2*t)*sin(a1+a12*t)
# calculate parameters for the ellipse
getParamsE(n) = (v1>=v2 ? (va=v1, vb=v2, aa=a1, ab=a2) : (va=v2, vb=v1, aa=a2, ab=a1), \
ca = a2>=a1, cd = rd>0, cv = v1>=v2, \
a12 = 360*(a1-a2!=0 && ca==cd ? 0 : cd==cv?1:-1) + (cv?1:-1)*(a2-a1), \
a3 = asin(sqrt(va**2 - (vb*cos(a12))**2) / va), \
a = (abs(a12)<90 ? a3 : abs(a12)<180 ? 180-a3 : abs(a12)<270 ? 180+a3 : 360-a3)*sgn(a12), \
Ra=va, Rb = (abs(a12)==180 || abs(a12)==360) ? Ra : va*vb*abs(sin(a12))/sqrt(va**2 - (vb*cos(a12))**2))
xPosE(i,t) = x0+Ra*cos(a*t)*cos(aa)-Rb*sin(a*t)*sin(aa)
yPosE(i,t) = y0+Ra*cos(a*t)*sin(aa)+Rb*sin(a*t)*cos(aa)
set xrange[-11:11]
set yrange[-11:11]
set key out center top noautotitle
set multiplot layout 1,2
set key title 'Rotating \& scaling vectors'
plot for [i=1:|$Arcs|] [t=0:1] '+' u (getXYVAD(i), getParamsA(i), xPosA(i,t)):(yPosA(i,t)) w l lc "web-green",\
$Arcs u 1:2:($3-$1):($4-$2) w vec lc "blue", \
'' u 1:2:($5-$1):($6-$2) w vec lc "red", \
'' u 1:2 w p pt 7 lc "black" ti "Center point", \
'' u 3:4 w p pt 7 lc "blue" ti "Start point", \
'' u 5:6 w p pt 7 lc "red" ti "End point", \
'' u 1:2:($0+1) w labels offset -0.7,-0.7
set key title 'Drawing ellipses'
plot for [i=1:|$Arcs|] [t=0:1] '+' u (getXYVAD(i), getParamsE(i), xPosE(i,t)):(yPosE(i,t)) w l lc "web-green",\
$Arcs u 1:2:($3-$1):($4-$2) w vec lc "blue", \
'' u 1:2:($5-$1):($6-$2) w vec lc "red", \
'' u 1:2 w p pt 7 lc "black" ti "Center point", \
'' u 3:4 w p pt 7 lc "blue" ti "Start point", \
'' u 5:6 w p pt 7 lc "red" ti "End point", \
'' u 1:2:($0+1) w labels offset -0.7,-0.7
unset multiplot
### end of code
Result:

how to plot a tree graph in gnuplot

I am trying to make a tree like graph in gnuplot, so far I have points with labels arranged into the tree like structure. I would like to connect nodes (points), so each node will be connected to the node on higher level.
gnuplot code:
set term png
set output "tree.png"
set xrange [0:50]
set yrange [0:50]
plot 'data.txt' using 1:2:3 with points pointtype 7 lt 1 title 'title', \
'' using 1:2:3 with labels offset 0,char 1
data:
20 35 1
15 30 1.1
10 25 1.1.1
5 20 1.1.1.1
15 20 1.1.1.2
25 30 1.2
30 25 1.2.1
25 20 1.2.1.1
35 20 1.2.1.2
Thank you
This will most likely require to process the input data file externally in order to generate the list of edges on-the-fly.
If I understood your question correctly, each node (point) in your input is associated with a "tree-path" which is just a string of dot-separated integers expressing the position of the point on each level of the graph (tree). Now, two nodes A and B should be connected iff the path of B is longer than path A by 1 (1 extra level) and the path of A is a prefix of path of B (A is parent of B).
To do this, one could assume that the points on the input are already hierarchically ordered as in your example (i.e., that the coordinates of a parent are defined before any of its children) and proceed as follows:
set term png
set output "tree.png"
set xrange [0:50]
set yrange [0:50]
fName = 'data.txt'
parseEdges = sprintf('< gawk '' \
NF==3{ \
coords = $1 " " $2; \
data[$3] = coords; \
n = split($3, path, "."); \
if(n == 1) next; \
prefix=path[1]; \
for(i=2;i<n;i++){ prefix = prefix "." path[i] } \
if(prefix in data){ \
print data[prefix] "\n" coords "\n"; \
} \
} \
'' %s', fName);
plot \
fName using 1:2:3 with points pointtype 7 lt 1 title 'title', \
'' using 1:2:3 with labels offset 0,char 1, \
parseEdges w l
Here, the gawk script splits the path specification in the third column, extracts its prefix (by stripping the least significant part of the path) and finally checks if the coordinates for this prefix (path of the parent) are already available and if yes, it prints a block of coordinates which is then shown as the connecting edge in the final output. The result is then:

How to restrict yrange for fit in gnuplot

I used the following scripts for plotting and fitting.
Data set:
2.474 2.659
0.701 2.637
0.582 2.643
0.513 2.666
0.403 2.639
0.308 2.615
0.218 2.561
0.137 2.537
Script:
reset
set key bottom right
f(x) = a*atan(x/b); a = 2.65; b = 2.5
fit f(x) 'test.txt' u 1:2 via a,b
plot 'test.txt' u 1:2 w p not, f(x) t 'f(x)'
The plot looks like this:
I am trying to restrict it between min_y and max_y. The following intuitive code failed horribly,
fit [y=2.537:2.659] f(x) 'test.txt' u 1:2 via a,b
Any suggestion on restriction would be highly appreciated! Thanks!
The range option only specifies which input points should be used, not restricting the output. So far as I can see from the manual, restrictions on the output value of f(x) aren't really possible (and so far as I can see from the problem, not really desirable).
You should be able also to do it simply by defining a fit range [][].
The following code works also with gnuplot4.6 which was the version in 2014.
"Data.dat":
1 2
2 3
3 4
1 9
2 8
3 7
Code:
### fit with limited y-range
reset
set xrange[0:10]
set yrange[0:10]
f(x) = a*x + b
set multiplot layout 3,1
fit [*:*][0:5] f(x) "Data.dat" u 1:2 via a,b
plot "Data.dat" u 1:2 w p pt 7 lc rgb "red" not,\
f(x) t sprintf("Fitrange: [*:*][0:5]\nf(x) = %g*x + %g",a,b)
fit [*:*][5:10] f(x) "Data.dat" u 1:2 via a,b
plot "Data.dat" u 1:2 w p pt 7 lc rgb "red" not,\
f(x) t sprintf("Fitrange: [*:*][5:10]\nf(x) = %g*x + %g",a,b)
fit [*:*][0:10] f(x) "Data.dat" u 1:2 via a,b
plot "Data.dat" u 1:2 w p pt 7 lc rgb "red" not,\
f(x) t sprintf("Fitrange: [*:*][0:10]\nf(x) = %g*x + %g",a,b)
unset multiplot
### end of code
Result:
This is an old question, but I arrived here looking for a solution to a similar problem. The answer is to use the stats command:
stats 'test.txt'
This will analyze, by default, the y data and set a bunch of STATS_* variables, which you can use in your fit statement along with the ternary operator:
fit f(x) 'test.txt' u 1:($2 >= STATS_min && $2 <= STATS_max ? $2 : NaN) via a,b
You can also add a using clause to the stats statement to further filter the data to match your fit statement, if needed.

Resources