This question is related to gnuplot histogram: How to put values on top of bars.
I have a datafile file.dat:
x y1 y2
1 2 3
2 3 4
3 4 5
and the gnuplot:
set style data histogram;
set style histogram rowstacked;
plot newhistogram 'foo', 'file.dat' u 2:xtic(1) t col, '' u 3 t col;
Now I want to place the sums of columns 2 and 3 above the bars. The obvious solution
plot newhistogram 'foo', 'file.dat' u 2:xtic(1) t col, '' u 3 t col, \
'' u ($0-1):($2+$3+0.2):($2+$3) notitle w labels font "Arial,8";
puts the labels in the correct place, but the calculated sum is wrong. That is, in ($0-1):($2+$3+0.2):($2+$3), the second $2 appears to evaluate to zero.
What's going wrong here and how do I fix it?
You must give an explicit string as label:
plot newhistogram 'foo', 'file.dat' u 2:xtic(1) t col, '' u 3 t col, \
'' u ($0-1):($2+$3):(sprintf('%.1f', $2+$3)) notitle w labels offset 0,1 font "Arial,8"
As other improvement, I would use the offset option which allows you to give an displacement in character units, which doesn't depend on the yrange.
(Side note: if a value from a column is used, then one can skip the explicit formatting of the label, like using 1:2:2 with labels, but in general one should use sprintf to format the label)
Related
You can rotate text labels as well as ticlabels.
However, if I have multiline labels, how can I keep alignment and line spacing with rotated labels which have a rotation angle other than 0°, 90°, 180° or 270°?
As you can see in the example below, aligment and line spacing are not kept with other than the above mentioned angles. What's going wrong there? I haven't checked yet whether this maybe depends on the aspect ratio of x- and y-axes? Any ideas how to fix this?
Script:
### how to keep alignment and line spacing with rotated labels?
reset session
$Data <<EOD
1 0 "multiline\nlabel"
2 30 "multiline\nlabel"
3 45 "multiline\nlabel"
4 60 "multiline\nlabel"
5 90 "multiline\nlabel"
EOD
set termoption font ",14"
set offset 1,1,2,2
set key noautotitle
plot $Data u 1:(1):3:2 w labels rotate var right , \
'' u 1:(2):3:2 w labels rotate var center, \
'' u 1:(3):3:2 w labels rotate var left
### end of script
Result:
Here another illustration that something does not work as expected:
I filed a bug report. Actually, 180° is not correct as well, line1 and line2 are swapped.
Here are two attempts to workaround the problem that apparently gnuplot rotates multiline labels around the anchor point for each line independently instead of a common anchor point for the whole label. Although, there are exceptions for 90° and 270° as you can see in the jumping text in the animation.
1. Attempt: split the multiline labels and plot them separately and add x- and y-offset depending on angle and line number. This seems to work for wxt terminal, but I haven't tested whether this works for all terminals and graph sizes.
Line(s,n) = ((c=strstrt(s,"\n"))==0 && n==2 ? '' : n==1 ? s[1:c-1] : s[c+1:strlen(s)] )
dx(n,a) = n==1 ? -sin(a) : sin(a)
dy(n,a) = n==1 ? 0.5*cos(a) : -0.5*cos(a)
2. Attempt: the first attempt will not work for ticlabels because you would create two shifted tics. Hence, in the second example, in addition to xtics with an offset for the first line, x2tics with an offset for the second line are used. However, depending on the graph height, you have to set the y-offset manually which is rather tedious. I haven't found yet a way to do this automatically (maybe with help of the GPVAL_ variables and a replot).
As a 3rd option, you could also play with multiplot and overlay differently shifted xtics, which I would consider a painful workaround as well.
I hope that there are (or will be) better solutions.
Script 1:
### rotated multiline labels (two lines)
reset session
set key noautotitle
set angle degrees
s = "multiline\nlabel"
dx(n,a) = n==1 ? -sin(a) : sin(a)
dy(n,a) = n==1 ? 0.5*cos(a) : -0.5*cos(a)
Line(s,n) = ((c=strstrt(s,"\n"))==0 && n==2 ? '' : n==1 ? s[1:c-1] : s[c+1:strlen(s)] )
set yzeroaxis ls 1
set term gif animate size 640,480 delay 10 font ",11"
set output "SO74000192_2.gif"
do for [a=0:355:5] {
s = sprintf("multi line:\nAngle: % 4d°",a)
set multiplot layout 1,2 ti "rotated multiline labels"
set title "as is"
plot 0, 1, 3, 5, 6, \
'+' u (0):(1):(s):(a) every ::0::0 w labels rotate var right, \
'+' u (0):(3):(s):(a) every ::0::0 w labels rotate var center, \
'+' u (0):(5):(s):(a) every ::0::0 w labels rotate var left
set title "workaround"
plot 0, 1, 3, 5, 6, \
for [j=1:2] '+' u (0):(1):(Line(s,j)):(a) every ::0::0 \
w labels rotate var right offset dx(j,a), dy(j,a), \
for [j=1:2] '+' u (0):(3):(Line(s,j)):(a) every ::0::0 \
w labels rotate var center offset dx(j,a), dy(j,a), \
for [j=1:2] '+' u (0):(5):(Line(s,j)):(a) every ::0::0 \
w labels rotate var left offset dx(j,a), dy(j,a)
unset multiplot
}
set output
### end of script
Result 1:
Script 2:
### rotated multiline xticlabels (two lines)
reset session
set angle degrees
set key noautotitle
$Data <<EOD
1 2 label
2 5 "multiple\nlines"
3 4 "another label"
4 7 "another two\nline label"
5 4 "single line again"
6 3 "multiline\nagain"
EOD
Line(s,n) = ((c=strstrt(s,"\n"))==0 && n==2 ? '' : n==1 ? s[1:c-1] : s[c+1:strlen(s)] )
dx(n,a) = n==1 ? -sin(a) : sin(a)
dy(n,a) = n==1 ? 0.5*cos(a) : -0.5*cos(a)
set boxwidth 0.8
set style fill solid 0.3
set yrange[0:]
a = 65
set xtic out nomirror rotate by a right offset -sin(a),0.5*cos(a) font ",11"
set multiplot layout 1,2 title "rotated multine labels"
set title "as is"
plot $Data u 1:2:xtic(3) w boxes
set title "workaround"
set link x2 via x inverse x
set x2tics rotate by a right font ",11" offset sin(a), graph -1.14 # manually adjust y-offset depending on graph
plot $Data u 1:2 w boxes, \
'' u 1:(-1):xtic(Line(strcol(3),1)), \
'' u 1:(-1):x2tic(Line(strcol(3),2))
unset multiplot
### end of script
Result 2:
A datafile contains three columns with the first and second representing the x & y position of a circle at a time (in seconds) given by the third column. For example, the first two lines in "data.txt" give the position of two circles at time=0, followed by two blank lines then the position of the two circles at time=0.1 sec and so forth. The first few lines of data.txt are:
0 0 0
-1 -1 0
1 1.0 0.1
-1 -0.5 0.1
1.2 1.25 0.2
-0.5 -0.25 0.2
...
The Gnuplot code producing a series of frames (a movie) with the position of the two circles in time is:
set terminal gif size 1200,1200 animate delay 500
set output "movie.gif"
stats "data.txt" u 1:2 name "A"
set style circle radius graph 0.025; set style fill solid
set xrange [A_min_x*1.1:A_max_x*1.1]
set yrange [A_min_y*1.1:A_max_y*1.1]
do for [i=0:A_blocks-2] {
plot "data.txt" index i u 1:2 w circle
}
I'm trying to add a label or textbox of the form "Time=?" to each frame where the question mark is replaced by the number from the third column. Any suggestions on how to do it?
This answer requires the current version of gnuplot (5.4)
Assuming that all the column 3 entries in a given data block are the same, it should be sufficient to say
plot "data.txt" index i u 1:(FOO=strcol(3),column(2)) w circle title sprintf("Time = %s",FOO)
This updates the value of FOO for each line used. The title uses whatever the final update was for that plot. If you must specifically choose the column 3 value from a particular line within the block, or calculate something like an average, that's a harder problem. In that case please clarify.
Edit:
The reason this requires 5.4 is that earlier versions evaluated the title before reading the data for that plot. Version 5.4 evaluates the title after reading the data.
The first (not too obvious) solution which comes to my mind: plot your data in a loop and assign the value of the 3rd column to a variable, e.g. t. Use keyenty to print the legend with your time re-using the variable t. In order to avoid a symbol in the legend use plotting style with points and a point with pointsize 0.
Code:
### animation with time label
reset session
$Data <<EOD
0 0 0
-1 -1 0
1 1.0 0.1
-1 -0.5 0.1
1.2 1.25 0.2
-0.5 -0.25 0.2
EOD
set terminal gif size 400,400 animate delay 100
set output "SO70474478.gif"
stats $Data u 1:2 name "A" nooutput
set style circle radius graph 0.025; set style fill solid
set xrange [A_min_x*1.1:A_max_x*1.1]
set yrange [A_min_y*1.1:A_max_y*1.1]
set key top left
do for [i=0:A_blocks-1] {
plot $Data index i u 1:(t=$3,$2) w circle notitle, \
keyentry w points ps 0 ti sprintf("Time: %.1f",t)
}
set output
### end of code
Result:
I have a file with the coordinates (x1, y1), (x2, y2) of a double pendulum at an instant of time t. I would like to know how to animate this file.
n t x y
1. 0. 0.3435957874318018 -0.36323812418181006
2. 0. 0.3490554835456995 -0.8632083150115673
1. 0.03 0.3299928793095484 -0.3756390549516834
2. 0.03 0.34657472136383705 -0.8753640218225479
1. 0.06 0.3099940098067639 -0.3923056383534705
2. 0.06 0.3437105896402516 -0.891167535319286
This are the first three positions of the double pendulum, 'n' is the particle number
The following might be a starting point.
Since I don't have enough data for illustration, I generated some data (note: it's not "real" double pendulum data). The assumption is that the anchor point is a at 0,0. Furthermore, it might be desired to not just draw the points with points but also the bars of the pendulum with vectors. It's a bit tricky and not obvious how to draw the second bar since you have to remember the previous position of mass1. I hope you can figure out how to code works.
Code:
### animate double pendulum
reset session
# create some test data (no "real" double pendulum data, just for illustration)
set print $Data
do for [t=0:15] {
print sprintf("%d. %g %g %g", 1, t, x1=cos(t*pi/20+pi/8), y1=-sin(t*pi/20+pi/8))
print sprintf("%d. %g %g %g", 2, t, x2=x1+cos(t/10.), y2=y1-sin(t/10.))
}
set print
set size ratio -1
set xrange[-1.5:2.0]
set yrange[-2:0.1]
set term gif size 400,300 animate delay 20
set output "DoublePendulum.gif"
do for [i=0:15] {
plot $Data u (0):(0):3:4 every ::2*i::2*i w vectors lc "grey" nohead notitle, \
x2=y2=NaN '' u (x1=x2,x2=$3):(y1=y2,y2=$4):(x1-x2):(int($0)%2==0 ? NaN: y1-y2) every ::2*i::2*i+1 w vectors lc "grey" nohead notitle, \
'' u 3:4 every ::2*i::2*i w p pt 7 ps 3 lc "red" title "1", \
'' u 3:4 every ::2*i+1::2*i+1 w p pt 7 ps 2 lc "blue" title "2", \
}
set output
# just for illustration: plot all positions at the same time
set term wxt # or any other terminal
plot $Data u (0):(0):3:4 every 2 w vectors lc "grey" nohead notitle, \
x2=y2=NaN '' u (x1=x2,x2=$3):(y1=y2,y2=$4):(x1-x2):(int($0)%2==0 ? NaN: y1-y2) w vectors lc "grey" nohead notitle, \
'' u 3:4 every 2 w p pt 7 ps 3 lc "red" title "1", \
'' u 3:4 every 2::1 w p pt 7 ps 2 lc "blue" title "2", \
### end of code
Result: (no "real" double pendulum data, just for illustration)
All positions (in wxt terminal):
I am following this post to make a pie chart using Gnuplot. The only problem with the approach is that I can't align my percentage labels. What am I missing here?
DATA FILE:
"Others" 1.085117e-01 3.904323e-02
"D_o" 2.894902e-01 6.145359e-01
"{/Symbol b}_o" 5.760601e-01 3.760299e-01
"O_h" 5.393108e-01 1.000000e+00
"D_p" 6.743313e-01 2.284404e-01
"{/Symbol a}_p" 1.000000e+00 1.271822e-01
"{/Symbol b}_f" 4.020115e-01 2.233656e-01
"D_m" 2.389996e-01 8.577689e-02
"{/Symbol a}_m" 3.601146e-01 1.033153e-01
"{/Symbol b}_m" 5.596836e-01 1.947165e-01
CODE:
#!/usr/bin/gnuplot
# Terminal & Encoding
set terminal epscairo enhanced color dashed rounded size 8.5, 4.5
set output 'mu_piechart.eps'
set termoption enhanced
set encoding utf8
# Get Status
filename = './datafile.dat'
stats filename u 2 noout
# Get Angles & Percentages
ANG(x)=x*360.0/STATS_sum
PER(x)=x*100.0/STATS_sum
# Square Canvas
set size square
set xrange [-1:1.5]
set yrange [-1.25:1.25]
set style fill solid 1
# Remove Base Properties (Titles, Tics, Axis, Palette)
unset key
unset tics
unset border
unset colorbox
# Initial Angle, Mid Angle, Initial Color
A = 0.0; M = 0.0; i = 0;
# Palette
set palette defined (1 1 0.788 0.055, 2 0.090 0.161 0.659)
# Plot
plot for [i=0:STATS_records-1] filename u (0):(0):(1):(A):(A=A+ANG($2)):(i) every ::i::i with circle linecolor palette,\
filename u (M=A+ANG($2), A=2*M-A, M=M*pi/360.0, -0.5*cos(M)):(-0.5*sin(M)):(PER($2) > 8.00 ? sprintf('%.1f\%', PER($2)) : " ") every ::1 w labels center font ',10',\
for [i=0:STATS_records-1] filename u (1.45):(i*0.25)-1.11:($1) every ::i::i with labels left,\
for [i=0:STATS_records-1] '+' u (1.3):(i*0.25)-1.11:(i) pt 5 ps 4 lc palette
exit
OUTPUT:
The percentages positions are not correct in the figure generated by the script.
Your labels are at wrong positions, because your label plot starts at 1, i.e. you skip the first entry.
Also, what I don't understand is, why you plot the pie parts counterclockwise, and the labels clockwise.
Here is a working version of you script, without some parts which are superfluous for demonstration. Both labels and pie parts are plotted starting at an angle of A = 0 (note the second initialization between the two plots):
reset
# Get Status
filename = './datafile.dat'
stats filename u 2 noout
# Get Angles & Percentages
ANG(x)=x*360.0/STATS_sum
PER(x)=x*100.0/STATS_sum
# Y position of key point and label
YLBL(row) = 2.0 * (row - 0.5*(STATS_records - 1))/(STATS_records - 1)
# Square Canvas
set size square
set xrange [-1:1.5]
set yrange [-1.25:1.25]
set style fill solid 1
# Remove Base Properties (Titles, Tics, Axis, Palette)
unset key
unset tics
unset border
unset colorbox
# Palette
set palette defined (1 1 0.788 0.055, 2 0.090 0.161 0.659)
# Plot
A = 0.0
plot filename u (0):(0):(1):(A):(A=A+ANG($2)):0 with circle linecolor palette,\
A = 0,\
filename u (M=A+ANG($2), A=2*M-A, M=M*pi/360.0, 0.5*cos(M)):(0.5*sin(M)):(PER($2) > 8.0 ? sprintf('%.1f\%', PER($2)) : "" ) w labels center,\
filename u (1.3):(YLBL($0)):1 with labels offset char 3 left ,\
filename u (1.3):(YLBL($0)):0 pt 5 ps 4 lc palette
The script contains some other improvements:
You don't need to iterate over STATS_records
The text and point for the key are plotted at the same position, the label is shifted with the offset parameter by three character units (offset char 3). That makes fine-tuning easier.
My question about how to automate placing labels at the edge of a plot for data points that exceed the range of plot. This would be done for points that are outliers that, if plotted, skew the scale of the plot such that the plot is no longer useful, but still need to be noted.
For example, let's say I've got a data file called 'mydata.dat' that looks like this:
1 2
3 3
7 4
8 6
50 8
If I plot the data using
set yrange [0:10]
set xrange [0:10]
plot 'mydata.dat' w lp pt 1
then the last point (50,8) will not be plotted. To acknowledge, however, that there is a point outside the plotted range, a label and point would be placed at the edge (i.e., at 10,8) of the plot using
set label "" at 10,8 point pt 1
set label "50" at 9.75,8 right
Is there a way to automate this process? A way to have GNUplot read the entire data file and set points and labels at the edge of the plot?
To plot all point which are outside of the specified range, you'll need a second plot command using the labels plotting style.
In the using statement you can then check for the point being outside the range. If it is, you place a point with a corresponding label, otherwise you skip the point (using 1/0 as value):
set xrange [0:10]
set yrange [0:10]
set linetype 1 pt 7
plot 'mydata.dat' w lp lt 1, \
'' using ($1 > 10 ? 10 : 1/0):2:(sprintf('%d', $1)) with labels right offset -2,-0.25 point lt 1 notitle
Here, I check only for the x-value being larger than the specified maximum x-value. Maybe you need to adapt it in order to catch also outliers in y-direction or those smaller than 0.