How to use axes programmatically in Gnuplot? - bash

If you need to draw some graphs against x1y1 or x1y2 axes, depending on the maximum y value of each graph, what would be the proper syntax for that?
My data file has several columns. The first one contains x values, the others y1, y2 and so on.
After using the stats command, I could easily define each axis value for each graph:
stats '$data_file' u 1:2 nooutput;
y1max = STATS_max_y;
if (y1max > ymax) { y1axis = 'x1y2' } else { y1axis = 'x1y1' };
stats '$data_file' u 1:3 nooutput;
y2max = STATS_max_y;
if (y2max > ymax) { y2axis = 'x1y2' } else { y2axis = 'x1y1' };
...
After that, I can draw the graphs with this plot cmd
plot '$data_file' using 1:2 axes x1y1 notitle with lines lc rgb 'black' lw 1,\
'' using 1:3 axes x1y2 notitle with lines lc rgb 'green' lw 1;
which works, but it is not programmatically at all.
But this one doesn't works
plot '$data_file' using 1:2 axes #y1axis notitle with lines lc rgb 'black' lw 1,\
'' using 1:3 axes #y2axis notitle with lines lc rgb 'green' lw 1;
where I used the Substitution of string variables as macros (The character # is used to trigger substitution of the current value of a string variable into
the command line...) which is written in the docs.
Nor the eval command likes to work for me.
Could you please provide an example for that purpose, which is working or any a good advise. THX!

I got two errors/warnings when using the above code in a bash script as an oneliner the following way
echo "gnuplot cmds;plot ..." | gnuplot
warning: y3axis is not a string variable
axes must be x1y1, x1y2, x2y1 or x2y2
As written in the docs, the macro could not be expanded the same time as it is defined, what is the case if written on the same line.
To avoid the above mistake we have to use only
echo -e "gnuplot cmds;\nplot ..." | gnuplot
and it works like it should.
Credits to #Christoph and #Ethan who put me on the right track.

Related

How to keep alignment and line spacing with rotated labels in gnuplot?

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:

Animation with gnuplot : only one cruve plotted in an animation whereas 2 expected

I have the following script which is expected to produce the animations of 2 curves :
#!/bin/bash
for i in {1..397}; do
gnuplot -p <<-EOFMarker
set terminal png;
set output "pic$i.png";
set title "power spectrum";
set xlabel "scale (k)";
set ylabel "P(k)";
set key top left;
set grid;
set ytics out nomirror;
set xtics out nomirror;
set logscale x;
set logscale y;
set format x "10^{%L}";
set yrange [0:30000];
plot "CAMB-1.3.5/matter_camb$i" u 1:2 w l;
replot "EFTCAMB_v3_beta/matter_eftcamb$i" u 1:2 w l;
EOFMarker
done
# Build movie with ffmpeg
ffmpeg -start_number 1 -i pic%d.png movie.mpeg
Every works fine excepted the fact that only one curve is plotted in animation (CAMB-1.3.5) :
Here is an example of frame :
Why the "replot" command is not taken into account in the generated image ?
Just for the sake of the SO-rule "no answer in comments":
Check help plot:
Syntax:
plot {<ranges>} <plot-element> {, <plot-element>, <plot-element>}
Examples:
plot sin(x)
plot sin(x), cos(x)
plot f(x) = sin(x*a), a = .2, f(x), a = .4, f(x)
plot "datafile.1" with lines, "datafile.2" with points
plot [t=1:10] [-pi:pi*2] tan(t), \
"data.1" using (tan($2)):($3/$4) smooth csplines \
axes x1y2 notitle with lines 5
plot for [datafile in "spinach.dat broccoli.dat"] datafile

gnuplot animation 2D vector fields

I'm trying to animate 2D vector with gnuplot. I want to show one line i.e, one vector at a time.
My Data Structure is as follows: They x,y,u,v
2.24448 0.270645 1.00 1.00
3.24448 0.270645 0.500 1.20
I'm able to create a static plot sing following command:
plot "datam.dat" using 1:2:3:4 with vectors filled head lw 3
Here is the output:
Here is my question: I would like to animate and show one row (i.e,) one vector at a time, how to accomplish this in GNU plot using GIF?
Thanks
Animated GIFs are created with set terminal gif animate. Check help gif for details.
Below is a simple example (tested with gnuplot 5.2). You have to make a new plot for each frame. So, put your plot command into a do for-loop. With every ::i::i you are plotting only the ith line (check help every). If you don't know the total number of lines of your datafile, do stats "YourFile.dat" and the variable STATS_records will tell you this number.
Code:
### animated graph with vectors
reset session
set term gif size 300,300 animate delay 12 loop 0 optimize
set output "AnimateVectors.gif"
# create some dummy data
set angle degrees
N = 60
set samples N
set table $Data
plot [0:360] '+' u (cos($1)):(sin($1)):(sin($1)):(cos($1)) w table
unset table
set xrange[-2.5:2.5]
set yrange[-2.5:2.5]
do for [i=0:N-1] {
plot $Data u 1:2:3:4 every ::i::i w vectors lw 2 lc rgb "red" notitle
}
set output
### end of code
Result:
Addition:
This would be the non-animated version, e.g. in a wxt-terminal.
Code:
### non-animated graph with vectors
reset session
set term wxt size 400,400
# create some dummy data
set angle degrees
N = 60
set samples N
set table $Data
plot [0:360] '+' u (cos($1)):(sin($1)):(sin($1)):(cos($1)) w table
unset table
set xrange[-2.5:2.5]
set yrange[-2.5:2.5]
plot $Data u 1:2:3:4 w vectors lw 1.5 lc rgb "red" notitle
### end of code
Result:
Addition2:
Do you maybe mean something like this? A "semi"-animated arrow? By the way, as you can see the arrow look quite different in gif and wxt terminal.
Code:
### "semi"-animated graph with vectors
reset session
set term gif size 300,300 animate delay 12 loop 0 optimize
set output "AnimateVectorsSemi.gif"
# create some dummy data
set angle degrees
N = 60
set samples N
set table $Data
plot [0:360] '+' u (cos($1)):(sin($1)):(sin($1)):(cos($1)) w table
unset table
set xrange[-2.5:2.5]
set yrange[-2.5:2.5]
do for [i=0:N-1] {
plot $Data u 1:2:3:4 every ::0::i w vectors lw 1.5 lc rgb "red" notitle
}
set output
### end of code
Result:

Centering labels on a pie chart

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.

how can I create an animated, labeled point following a trajectory in gnuplot?

After quite a bit of tweaking to an existing GNU plot, I managed to get the following:
reset
# png
set terminal png size 350,262 enhanced font 'Verdana,10'
# color definitions
set style line 1 lc rgb '#0060ad' lt 1 lw 2 pt 7 ps 2 # --- blue
unset key
set border 0
unset tics
# set view 342,0
set xrange [-300:300]
set yrange [-300:300]
n=0
do for [ii=1:99] {
n=n+1
set output sprintf('png/spiral%03.0f.png',n)
plot '1_1910.txt' every ::1::ii w l ls 1, \
'2_1910.txt' every ::1::ii w l ls 1
}
do for [ii=1:99] {
n=n+1
set output sprintf('png/spiral1%03.0f.png',n)
plot '1_1920.txt' every ::1::ii w l ls 1, \
'2_1920.txt' every ::1::ii w l ls 1
}
The idea is to create a set of PNG files, and then concatenate them all with animation.
I concatenate them all together using
convert -delay 2 -loop 0 png/*.png animation.gif
I want to make more tweaks:
I don't want to keep the line that follows the whole plot (even though animated). Instead, I want a "bullet point" (maybe the size of 5-10 pixels) with text moving next to it for each frame for each data point in the plot.
I want a text at the bottom right for the first set of frames (in the first 1:99 loop) and a different text replacing it, again in the bottom right corner (in the next 1:99 loop).
My code is based on this:
http://www.gnuplotting.org/animation-iv-trajectory/
but I can't figure out why his code has a "trail" that disappears, and why he has also a bullet point and I don't. As I said, I also want text that moves with the bullet point (same text for each data file).
I think you can do it with loops like this:
n = 0
do for [ii=0:60] {
n=n+1
set output sprintf('png/spiral%03.0f.png',n)
plot 'data.dat' every ::ii::ii w p ls 1, \
'data.dat' using 1:2:("foo") every ::ii::ii w labels offset 2
}
set label 1 "hyde" at 275,-275
do for [ii=60:0:-1] {
n=n+1
set output sprintf('png/spiral%03.0f.png',n)
plot 'data.dat' every ::ii::ii w p ls 1, \
'data.dat' using 1:2:("foo") every ::ii::ii w labels offset 2
}
Here is the result:
You can play with the offset coordinates to position the label text "foo" where you want.
To change the size of the point, change the value of ps in the line style command at the beginning of your script.
I am not sure what the purpose of the two different files is (1_1910.txt and 2_1920.txt), so I used the same file in my example. You may need to add to the plot command if you are plotting two different things at once.
As for your other questions, I think looking closely at the code in the example should give you an idea of what is going on. In the example, the range every ::1::ii plots points from 1 to ii, giving a line; the range every ::ii::ii plots just one point. Note that the abbreviations w l and w p in the example expand to with lines and with points, respectively.
In the example the line disappears due to the action of the second loop, which runs in reverse (for [ii=99:1:-1]). If you are copying that example, make sure also that your indices run over the correct values (you may have a number other than 99 of data points). Also note that indices in gnuplot start at 0, so if the first line of your data file contains data (not a header) you want to start your loop at ii=0 instead of ii=1.
I am having nearly the same problem here. I found a really nice hack! Instead of making multiple png's and then merge them, you just use set multiplot and then unset multiplot inside ONE loop. Being specific I used it this way:
do for [i=0:k]
{
set multiplot
plot function1
plot function2
unset multiplot
}
But unfortunately this won't work unless the data set you have is synchronized!, but yet can be solve this using if-statement!. Good Luck

Resources