Related
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
I would like to draw an arrow with not a single colour, but a colour gradient along its length. Does anyone know how to achieve that? Some pseudo-code for creating an arrow that starts red and ends blue:
set palette defined (0 "red", 1 "blue")
set cbr [0:1]
set arrow from 0,0 to 1,1 linecolor palette cb [0:1] # this doesn't work
Besides #Friedrich's solution, I would like to suggest a more general solution (although more complicated).
I assume you want to plot something else besides the arrow.
In case your graph needs to use a palette I guess you're in "trouble", because I'm not sure whether gnuplot supports more than one palette in a plot command
(see Gnuplot 5.2 splot: Multiple pm3d palette in one plot call). So, you have to implement the palette for your arrow by yourself (see e.g. Gnuplot: transparency of data points when using palette). If you want to do bent arrows using Cubic Bézier check (https://stackoverflow.com/a/60389081/7295599).
Code:
### arrow with color gradient (besides other palette in plot)
reset session
array A[4] = [-4,-2,4,2] # arrow coordinates x0,y0,x1,y1
Ax(t) = A[1] + t*(A[3]-A[1])
Ay(t) = A[2] + t*(A[4]-A[2])
AColorStart = 0xff0000 # red
AColorEnd = 0x0000ff # blue
r(c) = (c & 0xff0000)>>16
g(c) = (c & 0x00ff00)>>8
b(c) = (c & 0x0000ff)
AColor(t) = ((int(r(AColorStart)*(1-t)+r(AColorEnd)*t))<<16) + \
((int(g(AColorStart)*(1-t)+g(AColorEnd)*t))<<8) + \
int(b(AColorStart)*(1-t)+b(AColorEnd)*t)
array AHead[1] # dummy array for plotting a single point, here: arrow head
set angle degrees
set style arrow 1 lw 3 lc rgb var size 0.5,15 fixed
set palette grey
plot '++' u 1:2:($1*$2) w image notitle, \
[0:0.99] '+' u (Ax($1)):(Ay($1)):(AColor($1)) w l lw 3 lc rgb var notitle,\
AHead u (Ax(0.99)):(Ay(0.99)):(Ax(1)-Ax(0.99)):(Ay(1)-Ay(0.99)):(AColor($1)) w vec as 1 notitle
### end of code
Result:
Addition:
For what it is worth, here is a variation which allows plotting of multiple arrows each with a different palette. I guess it requires gnuplot 5.2, because of indexing the datablock $PALETTE[i].
Code:
### multiple arrows each with different color gradients (besides other palette in plot)
reset session
# define palettes
set print $myPalettes
test palette # get default palette into datablock $PALETTE
print $PALETTE # add palette to $myPalettes
set palette rgb 33,13,10 # define next palette
test palette # get palette into datablock $PALETTE
print $PALETTE # add palette to $myPalettes
set palette defined (0 "blue", 1 "black", 2 "red") # define next palette
test palette # get palette into datablock $PALETTE
print $PALETTE # add palette to $myPalettes
set print
ColorComp(p,t,c) = int(word($myPalettes[p*257+int(255*t)+1],c+1)*0xff)
AColor(p,t) = (ColorComp(p,t,1)<<16) + (ColorComp(p,t,2)<<8) + ColorComp(p,t,3)
set size ratio -1
set angle degrees
unset key
set style arrow 1 lw 3 lc rgb var size 0.5,15 fixed
array AHead[1] # dummy array for plotting a single point, here: arrow head
set palette grey # yet another palette for the background
# x0 y0 x1 y1 paletteNo
$Arrows <<EOD
-4 -4 4 0 0
-4 -2 4 2 1
-4 0 4 4 2
EOD
Ax(i,t) = word($Arrows[i],1) + t*(word($Arrows[i],3)-word($Arrows[i],1))
Ay(i,t) = word($Arrows[i],2) + t*(word($Arrows[i],4)-word($Arrows[i],2))
Palette(i) = int(word($Arrows[i],5))
plot '++' u 1:2:($1*$2) w image, \
for [i=1:|$Arrows|] [0:0.99:0.01] '+' u (Ax(i,$1)):(Ay(i,$1)):(AColor(Palette(i),$1)) w l lw 4 lc rgb var, \
for [i=1:|$Arrows|] AHead u (Ax(i,0.99)):(Ay(i,0.99)): \
(Ax(i,1)-Ax(i,0.99)):(Ay(i,1)-Ay(i,0.99)):(AColor(Palette(i),$1)) w vec as 1
### end of code
Result:
With line palette one can color-code a line. With a second command one could set the head, via set arrow or with a plot vector command
set palette defined (0 "red", 1 "blue")
set cbr [0:1]
set arrow 1 from 0.9,0.9 to 1,1 lc "blue"
plot sample [t=0:1] "+" us (t):(t):(t) w l palette
Thus two commands are necessary. The head of the arrow has single colour, which you have to specify.
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
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:
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)