Show a label of x and y values, and possibly other values when hovering over a function with the mouse in GNUPLOT - 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

Related

How to display mouse coordinates in multiplots?

In interactive terminals, e.g. like wxt or qt, gnuplot can show the coordinates of the mouse cursor on the status line of the terminal window. This is helpful in many cases and works fine for a single plot but does not work for a multiplot.
gnuplot documentation explicitely mentions (check help mouse)
Mousing is not available inside multiplot mode. When multiplot is completed using unset multiplot, then the mouse will be turned on again but acts only on the most recent plot within the multiplot (like replot does).
This is a pity, because if it would be very convenient to quickly read out some data values from different graphs in a multiplot environment.
Question: is there maybe nevertheless a way to show the coordinates in a multiplot environment?
Since gnuplot 5.4.0 there is the "experimental" option set mouse mouseformat function string_valued_function(x, y). Check help mouseformat.
With this, you can create some special functions. What you need to do is:
memorize the GPVAL_... graph parameters for each plot by adding them to a string variable
calculate the coordinates for the corresponding plot starting from x,y of the last plot
If you have overlapping plots, the script will show the coordinates of the later added plot (which is "on top").
So, hopefully this will be also useful to others. No guarantee that the following script is free of bugs. Any simplifications and better ideas would be very welcome.
Ideally, this multiplot mouse coordinates would be implemented in gnuplot itself.
I noted that the conversion of the coordinates doesn't seem to be "perfect", although I guess the formulae are correct, e.g. converting x,y mouse coordinates into terminal pixel coordinates I got a range from (-1 to 639) and (0 to 379) in a wxt 640,480 terminal. Any explanations are welcome.
Script: (requires gnuplot>=5.4.0)
### display mouse cursor coordinates in multiplot
reset session
set term wxt size 640,480
set samples 11
f(x) = x
g(x) = exp(x)
GraphParams = ''
addGraphParams(logAxisX,logAxisY) = GraphParams.\
sprintf(' "%g %g %g %g %g %g %g %g %g %g %g %g"', \
GPVAL_X_MIN, GPVAL_X_MAX, GPVAL_TERM_XMIN, GPVAL_TERM_XMAX, \
GPVAL_X_LOG, logAxisX, \
GPVAL_Y_MIN, GPVAL_Y_MAX, GPVAL_TERM_YMIN, GPVAL_TERM_YMAX, \
GPVAL_Y_LOG, logAxisY)
getGraphParam(graphNo,paramNo) = real(word(word(GraphParams,graphNo),paramNo))
getXmin(n) = getGraphParam(n,1)
getXmax(n) = getGraphParam(n,2)
getTXmin(n) = getGraphParam(n,3)
getTXmax(n) = getGraphParam(n,4)
getLogX(n) = getGraphParam(n,5)
getLogAxisX(n) = int(getGraphParam(n,6))
getYmin(n) = getGraphParam(n,7)
getYmax(n) = getGraphParam(n,8)
getTYmin(n) = getGraphParam(n,9)
getTYmax(n) = getGraphParam(n,10)
getLogY(n) = getGraphParam(n,11)
getLogAxisY(n) = int(getGraphParam(n,12))
getPlotIdx(x,y) = (plotIdx=NaN, sum [_i=1:words(GraphParams)] ( \
TermX(x)>=getTXmin(_i) && TermX(x)<=getTXmax(_i) && \
TermY(y)>=getTYmin(_i) && TermY(y)<=getTYmax(_i) ? plotIdx=_i : 0), plotIdx)
GraphX(n,x,y) = (n==n ? real(TermX(x)-getTXmin(n))/(getTXmax(n)-getTXmin(n)) : NaN)
GraphY(n,x,y) = (n==n ? real(TermY(y)-getTYmin(n))/(getTYmax(n)-getTYmin(n)) : NaN)
FirstX(x,y) = (_n=getPlotIdx(x,y), _n==_n ? getLogAxisX(_n) ? \
getLogX(_n)**(GraphX(_n,x,y)*(log(getXmax(_n))/log(getLogX(_n)) - \
log(getXmin(_n))/log(getLogX(_n))) + log(getXmin(_n))/log(getLogX(_n))) : \
getXmin(_n) + GraphX(_n,x,y)*(getXmax(_n)-getXmin(_n)) : NaN)
FirstY(x,y) = (_n=getPlotIdx(x,y), _n==_n ? getLogAxisY(_n) ? \
getLogY(_n)**(GraphY(_n,x,y)*(log(getYmax(_n))/log(getLogY(_n)) - \
log(getYmin(_n))/log(getLogY(_n))) + log(getYmin(_n))/log(getLogY(_n))) : \
getYmin(_n) + GraphY(_n,x,y)*(getYmax(_n)-getYmin(_n)) : NaN)
# take GPVAL_... from last plot
TermX(x) = GPVAL_TERM_XMIN + (x-GPVAL_X_MIN) * \
(GPVAL_TERM_XMAX-GPVAL_TERM_XMIN)/(GPVAL_X_MAX-GPVAL_X_MIN)
TermY(y) = GPVAL_TERM_YMIN + (y-GPVAL_Y_MIN) * \
(GPVAL_TERM_YMAX-GPVAL_TERM_YMIN)/(GPVAL_Y_MAX-GPVAL_Y_MIN)
mouse(x,y) = sprintf("%g, %g",FirstX(x,y),FirstY(x,y))
set mouse mouseformat function mouse(x,y)
set multiplot
set size 0.5,1.0
set origin 0.0,0.0
set grid x,y
set key bottom right samplen 2
plot [0:10] '+' u 1:(g($1)) w lp pt 7 lw 2 lc rgb "red" ti " x^2"
GraphParams = addGraphParams(0,0)
set size 0.37,0.45
set origin 0.07, 0.5
set logscale y
set obj 1 rect from graph 0,0 to graph 1,1 fc "white" behind
plot [0:10] '+' u 1:(g($1)) w lp pt 7 lw 2 lc rgb "red" ti " x^2"
unset obj 1
GraphParams = addGraphParams(0,1)
set size 0.5,0.5
set origin 0.5,0.5
set logscale x 2
set logscale y
plot [0.5:100] '+' u (g($1)):1 w lp pt 7 lw 2 lc rgb "web-green" ti "x"
GraphParams = addGraphParams(1,1)
set size 0.5,0.5
set origin 0.5,0.0
unset logscale x
unset logscale y
plot [1:10] '+' u 1:(f($1)) w lp pt 7 lw 2 lc rgb "web-green" ti "x"
GraphParams = addGraphParams(0,0)
unset multiplot
### end of code
Result: (Screen capture from wxt terminal)
Note the moving mouse cursor and the coordinates in the status line and that the coordinates are (NaN,NaN) between the individual graphs.

How to make edge marker color

Matplotlib has the option to specify a edge color, e.g.
How to set the border color of the dots in matplotlib's scatterplots?
In gnuplot, I work it around with
array data[500]
do for [i=1:500] {data[i]=invnorm(rand(0))}
pl data ps 2 pt 7, "" ps 2 pt 6 lc "black"
However, this requires a second loop over the data (and more duplicated typing, in particular when more complex column operations are involved). Moreover, the legend is not as intended.
How can the legend be fixed? Or are there better ways?
(E.g. for postscript terminals a solution is to parse the output, set out "| sed '/ hpt 0 360/s/ Pnt//; s,hpt.*arc fill, 2 copy & stroke LTb Circle, ' > out.ps.)
You could use the plotting style with circles. Check help circles.
Code:
### "points" with outline --> with circles
reset session
set print $Data
do for [i=1:200] {
print sprintf("%g %g", invnorm(rand(0)),invnorm(rand(0)))
}
set print
set style circle radius graph 0.01 wedge
set style fill solid 1.0 border lc "black"
plot $Data u 1:2 w circles fc "red"
### end of code
Result:
Addition:
Just for fun... here is a way to plot triangles (or squares, diamonds or pentagons) with border.
The simple way to plot the data twice, once with closed symbol and again with open symbol does not give the desired result (see left graph). Actually, you have to duplicate each line and plot alternating closed and open symbols (right graph). However, I haven't yet found a solution to show the symbol with border in the legend.
Code:
### triangles with border
reset session
set print $Data
do for [i=1:100] {
print sprintf("%g %g", invnorm(rand(0)),invnorm(rand(0)))
}
set print
# duplicate each line
set print $Data2
do for [i=1:|$Data|] {
print $Data[i]
print $Data[i]
}
set print
set key out center top noautotitle
myPt = 9 # 5=square, 7=circle, 9=triangle up, 11=triangle down, 13=diamond, 15=pentagon
myLw = 1.5
myPs = 4
myColor(col) = int(column(col))%2 ? 0x000000 : 0xffff00
myPoint(col) = int(column(col))%2 ? myPt-1: myPt
set multiplot layout 1,2
plot $Data u 1:2 w p pt myPt ps myPs lc "yellow" ti "serial", \
'' u 1:2 w p pt myPt-1 ps myPs lw myLw lc "black"
plot $Data2 u 1:2:(myPoint(0)):(myColor(0)) w p pt var ps myPs lw myLw lc rgb var, \
keyentry w p pt myPt ps myPs lc rgb 0xffff00 ti "alternating"
unset multiplot
### end of code
Result:

How do I draw a circle at every point in a gnuplot splot?

I'm trying to plot a set of 3d points stored in a file, as in the standard
set style data lines
splot 'data.dat'
Except I want to draw a circle in the x-y plane along with every point drawn, so that what will be rendered in the end will be a curving tube with the central line on the inside.
I've been able to draw individual circles using parameters, but I'm not sure how you'd do what I've described here.
Is this possible?
If you really want circles, the following might be a solution which comes to my mind. But maybe you actually want a surface plotted? For this there might be other solutions.
Code:
### circles along datapoints
reset session
# create some 3D test data
set samples 50
set table $Data
plot [0:1.5] '+' u (cos(2*pi*$1)):(sin(2*pi*$1)):($1*10) w table
unset table
# define the circle
Radius = 0.1
set samples 24
set table $Circle
plot [0:1] '+' u (cos(2*pi*$1)):(sin(2*pi*$1)) w table
unset table
Offset(i,axis) = real(word($Data[i],axis))
set view 65,124
splot $Data u 1:2:3 w lp pt 7 lw 2 lc rgb "red", \
for [i=1:|$Data|] $Circle u ($1+Offset(i,1)):($2+Offset(i,2)):(Offset(i,3)) w l notitle
### end of code
Result:
Addition:
Here is a slightly modified version (maybe there is a simpler way to achieve it) where you create a datablock $Tube which can be plotted with surfaces. The circles are still parallel to the xy-plane. Although, my suspicion is that you actually might wanted to have the circles orthogonal to the direction of the input data path.
Code:
### circle surface along datapoints
reset session
# create some test data
set samples 50
set table $Data
plot [0:1.5] '+' u (cos(2*pi*$1)):(sin(2*pi*$1)):($1*10) w table
unset table
# define the circle
Radius = 0.1
set samples 24
set table $Circle
plot [0:1] '+' u (cos(2*pi*$1)):(sin(2*pi*$1)) w table
unset table
D(i,axis) = real(word($Data[i],axis))
C(i,axis) = real(word($Circle[i],axis))
# generate "tube" datapoints
set print $Tube
do for [i=1:|$Circle|] {
do for [j=1:|$Data|] {
print sprintf("%.3f %.3f %.3f", C(i,1)+D(j,1), C(i,2)+D(j,2), D(j,3))
}
print "" # empty line
}
set print
set pm3d depthorder noborder
set pm3d lighting specular 0.5
set view 65,124
splot $Data u 1:2:3 w lp pt 7 lw 2 lc rgb "red", \
$Tube u 1:2:3 w pm3d notitle
### end of code
Result:

Merge key entries in gnuplot

I would like to plot data with lines and points with different colors. It seems to exist different solutions:
https://stackoverflow.com/a/31887351/4373898 , https://stackoverflow.com/a/31562632/4373898
https://gnuplot-tricks.blogspot.fr/2009/12/defining-some-new-plot-styles.html
However, none of them handle the key properly, showing only one entry with both the line and a point with different colors...
Is there another way to achieve it?
This is minimal (non-)working example.
set key bottom right
plot '+' using 1:1 title "same data with different lines and points color" with lines lc 'blue', '' using 1:1 every 3 notitle with points ps 1.2 pt 7 lc 'red';
Kind regards,
Alexis.
If your plot is not too complex, you could perhaps achieve this by playing with the spacing parameter of the legend. Setting spacing to -1 achieves that the labels/symbols overlap:
set terminal pngcairo enhanced
set output 'fig.png'
set xr [-pi/2:pi/2]
set yr [0:1]
set key at graph 0.95,graph 0.9 spacing -1
plot \
cos(x) w l lc rgb 'dark-red' lw 2, \
'-' w p lc rgb 'royalblue' pt 7 ps 2 t ' '
-1 0.540302
0 1
1 0.540302
e
which gives
However, the disadvantage is that the setting is in a sense global - should the plot contain more functions/data files, everything would overlap. In order to use this "method" in this particular case, it would be necessary to invoke multiplot and create the keys separately.
Having 3 different colors for a linespoints plot is certainly "non-standard" and as you noticed a challenge especially for the proper key.
Here is a suggestion without using multiplot. Personally, I would use multiplot only if absolutely necessary. Otherwise you will lose the benefits of automargin and autoscale and you have to deal with margins and ranges, etc. yourself as you did in your solution.
Since the automatic creation of the key is the problem, then "do-it-yourself".
Update: simplified plot command and legend at graph coordinates.
This suggestion draws the legend via labels and arrows. You give the position and distances of the legend in graph coordinates.
The mix of points and linespoints within a for loop is taken from here.
Script: (works with gnuplot>=5.0.0, Jan. 2015)
### legend for plot with linespoints and different colors for line, fill and border
reset session
# no. lw pt colorLine colorFill colorBorder label
mySettings = '\
1 2 5 0x00ff00 0xffff00 0xff0000 "x + 1" \
2 2 7 0x0000ff 0xff00ff 0x000000 "x^2 - 8" \
' # end of settings
myLw(n) = real(word(mySettings,n*7-5)) # linewidth
myPt(n) = int(word(mySettings,n*7-4)) # pointtype
cL(n) = int(word(mySettings,n*7-3)) # color line
cF(n) = int(word(mySettings,n*7-2)) # color fill
cB(n) = int(word(mySettings,n*7-1)) # color border
myLabel(n) = word(mySettings,n*7) # label
myLt(n) = n==1 ? 1 : -2
myPtL(v,i) = i==1 ? 0 : i==2 ? myPt(v) : myPt(v)-1
myColor(v,i) = i==1 ? cL(v) : i==2 ? cF(v) : cB(v)
myPs = 3 # fixed pointsize
# Legend position graph units
xPos = 0.05 # x-position
yPos = 0.95 # y-position
dy = 0.07 # y distance
dx = 0.025 # x length
set for [i=1:words(mySettings)/7] arrow from graph xPos-dx,yPos-(i-1)*dy to graph xPos+dx,yPos-(i-1)*dy \
lw myLw(i) lc rgb myColor(i,1) nohead back
set for [i=1:2] for [j=2:3] label myLabel(i) at graph xPos,yPos-(i-1)*dy left offset 3,0 \
point pt myPtL(i,j) ps myPs lc rgb myColor(i,j) lw myLw(i) front
set samples 11 # samples for functions
set key noautotitle
set grid x
set grid y
f1(x) = x + 1
f2(x) = x**2 - 8
plot for [i=1:3] f1(x) w lp lt myLt(i) pt myPtL(1,i) ps (i>>1)*myPs lc rgb myColor(1,i) lw myLw(1), \
for [i=1:3] [-5:5] f2(x) w lp lt myLt(i) pt myPtL(2,i) ps (i>>1)*myPs lc rgb myColor(2,i) lw myLw(2)
### end of script
Result:
Thanks ewcz for your answer, it is a first step toward the expected result. However, as you stated it, this is a little bit trickier to adapt it if you have multiple functions/data to display on the same plot.
Below is a minimal working example with two functions (and thus, two key entries) with a line, points, and points outline of different colors.
# These parameters are used to compute the spacing between entries of the key
pointSize = 1;
yticsScale = 1;
# We use the default spacing (1.25)
keySpacing = pointSize*yticsScale*1.25;
# Initial coordinate of the key
keyY = 4; # In character system
keyX = 0.87; # In graph system
# Just to generate data
set samples 20;
set xrange [-pi:pi];
set term pngcairo;
set output 'graph.png';
set xlabel "x"
set ylabel "y"
# Set the alignment (and thus the coordinate point) of the key
# Set the spacing to -1 to stack different (thanks to ewcz for the idea)
set key bottom right spacing -1
# Start a multiplot
set multiplot
# Make plots as big as possible
set origin 0,0
set size 1,1
# Set the key position
set key at graph keyX, character keyY
# Plot multiple times the same function with different styles.
# Make sure that all functions have a title (empty if necessary).
plot cos(x+pi) w l lc "light-red", \
cos(x+pi) w p pt 5 ps 1.8 lc "dark-red" t ' ', \
cos(x+pi) w p pt 5 ps 1.2 lc "red" t ' '
# Update key coordinates for the next plot
keyY = keyY + keySpacing
# Draw the key of the next plot at the new coordinates
set key at graph keyX, character keyY
plot cos(x) w l lc "light-blue", \
cos(x) w p pt 7 ps 1.8 lc "dark-blue" t ' ', \
cos(x) w p pt 7 ps 1.2 lc "blue" t ' ';
# That's all
unset multiplot
set output;
The resulting plot:
Hope that will help others.
Kind regards.
Alexis
Edit:
The previous code works if both functions/data have the same ranges (on x and y) allowing autoscale to work properly.
In the case of data where you do not know the ranges, you must compute it before plotting.
# Just to generate data
set samples 20;
# First data will be defined on [-pi:pi] with values between -1 and 1.
set table '1.dat'
plot [-pi:pi] cos(x)
unset table
# Second data will be defined on [-pi/2,pi/2] with values between 0 and -2
set table '2.dat'
plot [-pi/2:pi/2] 2*cos(x+pi)
unset table
# These parameters are used to compute the spacing between entries of the key
pointSize = 1;
yticsScale = 1;
keySpacingScale = 1.25; # Gnuplot default spacing
keySpacing = pointSize * yticsScale * keySpacingScale; # Spacing computation
set pointsize pointSize;
set ytics scale yticsScale;
set key spacing -1; # Make key entries overlapping (thanks ewcz for the idea)
# Initial coordinate of the key
keyY = 4.5; # In character system
keyX = 0.98; # In graph system
set term pngcairo;
set output 'graph.png';
# Remove redundant objects
# Borders, labels, tics will be drawn for each plot, this is not necessary as all plots will be stacked. So remove then.
set border 0
set tics textcolor "white" # Dirty tricks to keep plots aligned but to not show the tics
set xlabel " " # The same
set ylabel " " # The same
# Compute the ranges
min(v1, v2) = (v1 < v2) ? v1 : v2;
max(v1, v2) = (v1 > v2) ? v1 : v2;
# Get min and max for the data
stats [*:*] [*:*] '1.dat' name 'f1' nooutput;
stats [*:*] [*:*] '2.dat' name 'f2' nooutput;
# Get the range limits
xmin = min(f1_min_x, f2_min_x)
xmax = max(f1_max_x, f2_max_x)
ymin = min(f1_min_y, f2_min_y)
ymax = max(f1_max_y, f2_max_y)
# Autoscale the range to match all the data
set xrange [* < xmin:xmax < *] writeback
set yrange [* < ymin:ymax < *] writeback
# Start a multiplot
set multiplot
# Make plots as big as possible
set origin 0,0
set size 1,1
# Set the key
set key bottom right at graph keyX, character keyY
# Plot multiple times the same function with different styles.
# Make sure that all functions have a title (empty if necessary).
plot '1.dat' w l lc "light-red" t "cos(x)", \
'' w p pt 5 ps 1.8 lc "dark-red" t ' ', \
'' w p pt 5 ps 1.2 lc "red" t ' '
# Update key coordinates for the next plot
keyY = keyY + keySpacing
# Draw the key of the next plot at the new coordinates
set key at graph keyX, character keyY
# Display at least once the labels
set border
set tics textcolor "black"
set xlabel "x"
set ylabel "y"
# Disable ranges autoscaling
set xrange restore
set yrange restore
plot '2.dat' w l lc "light-blue" t "2cos(x+pi)", \
'' w p pt 5 ps 1.8 lc "dark-blue" t ' ', \
'' w p pt 5 ps 1.2 lc "blue" t ' '
# That's all
unset multiplot
set output;
One more time the resulting plots:
Kind regards,
Alexis

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.

Resources