With a set of data files. I would like to performs series of operations on each file (such as fitting) and stack the resulting curves continiously along with my analysis (to see how each curves fit on the bigger picture). I wrote the following code snippet
reset
PATH = 'XRP_'
nmin = 1
nmax = 20
f(x) = log10(x); h(x) = a*x + b
name(i) = sprintf(PATH.'%04d/data_main_ddnls_twod_mlce.dat', i)
set xrange [0:7]
start = 0
set fit
do for [i=nmin:nmax]{
fit [4:] h(x) name(i) using (f($1)):(f($4)) via a, b
if (start==0){
plot name(i) using (f($1)):(f($4)) w l title sprintf("%04d", i)
} else {
replot name(i) using (f($1)):(f($4)) w l title sprintf("%04d", i)
}
start = start + 1
pause -1
}
# Add the slope
replot (1./5.)*x + 0.5 lc 'black' lw 3 dt 2
unset fit
# pause -1
Instead of stacking all the previous curves + the current one, it plots only the current curve i-times (see loop of code). For instance, after 10 iterations it plots only the 10th datafile, 10 times (see legends on picture)
How can I fix this?
The reason your plot behaves the way it does, and example (1) from theozh does also, is that "replot f(x)" acts by tacking ", f(x)" onto the end of the previous plot command. By putting it in a loop you are basically creating the successive commands
plot f(x,i)
plot f(x,i), f(x,i)
plot f(x,i), f(x,i), f(x,i)
...
Yes the value of i might change each time, but nevertheless each plot command produces multiple copies of the same thing.
Alternative solution: I don't normally recommend multiplot mode for creating a single output, but in this case it may be the best option.
# force identical margins even if the range changes
set margins screen 0.1, screen 0.9, screen 0.1, screen 0.9
# ... same prelimary stuff as shown in the question
# revised loop using multiplot rather than replot
set multiplot
do for [i=nmin:nmax]{
fit [4:] h(x) name(i) using (f($1)):(f($4)) via a, b
plot name(i) using (f($1)):(f($4)) w l \
title sprintf("%04d", i) at screen 0.9, screen 1.0 - 0.02*i
unset tics
}
unset multiplot
Note that you cannot use auto-generated title placement because each of the multiplot iterations will put the title in the same place. So instead we use the form "title foo at ". Similarly it is better to turn off tic generation after the first pass so that you don't redraw the tics and labels each time through the loop.
Indeed, a strange behaviour which I also would not have expected. See the minimal examples below.
Version 1: basically your attempt. Not the expected result. I also don't know
why.
Version 2: the expected result. Basically the same but not in a loop.
Version 3: the expected result, although in a loop but using eval.
Not very satisfying but at least some solution. Hopefully, others will have better solutions or explanations.
### plotting in a loop
reset session
set colorsequence classic
# Version 1
set title "Version 1"
do for [i=1:5] {
if (i==1) { plot x**i }
else { replot x**i noautoscale }
}
pause -1
# Version 2
set title "Version 2"
plot x**1
replot x**2 noautoscale
replot x**3 noautoscale
replot x**4 noautoscale
replot x**5 noautoscale
pause -1
# Version 3
set title "Version 3"
do for [i=1:5] {
if (i==1) { cmd = sprintf("plot x**%d",i) }
else { cmd = sprintf("replot x**%d noautoscale",i) }
eval cmd
}
### end of code
Related
I have used the stats command to store the x-postion of absolute maxima in my plot of seven datasets in seven variables, grN_pos_max_y with N that goes from 1 to 7. Can I place the tics in the x-axis at the positions specified by these variables?
I tried using
$maxima << EOD
gr1_pos_max_y
gr2_pos_max_y
gr3_pos_max_y
gr4_pos_max_y
gr5_pos_max_y
gr6_pos_max_y
gr7_pos_max_y
EOD
and then
plot ..., \
$maxima u 1:(NaN):xticlabel(1) notitle
but I don't know how to read variables into a data block (if I replace the variable names by their values, however, it works).
Edit: This is what I want (I plotted it using Ethan's answer)
I'm not entirely sure I understand what you want, but this may get you partway there:
set xtics add (gr1_pos_max_y, gr2_pos_max_y, gr3_pos_max_y, gr4_pos_max_y, gr5_pos_max_y, gr6_pos_max_y, gr7_pos_max_y)
plot 'whatever'
That will get you plain (unlabeled) tic marks in addition to whatever tic marks and labels are being generated automatically.
If you want only these marks and no auto-generated marks, remove the keyword add.
If you want to place labels to go with these new tics, change it to:
set xtics add ( "Max 1" gr1_pos_maxy, "Max 2" gr2_pos_maxy, ...
This is all assuming you want these tics to label a plot that contains something other than the tics themselves. If you want only a plot of these y values, perhaps as impulses?, please re-phrase the question or show a sketch of what you want it to look like.
There is no need for awk, you can do it all in gnuplot.
put stats into a loop and write the STATS values into a datablock $Maxima
plot your data and $Maxima as Ethan suggested with impulses
you can also plot the maxima y-value as labels in the graph
The script needs to be adapted depending on your file naming scheme.
Script:
### extract maxima from several files
reset session
N = 7
myFile(n) = sprintf("SO72750257_%d.dat",n)
# create some "random" test data
do for [n=1:N] {
set table myFile(n)
f(x) = -a*(x-x0)**2 +y0
x0 = (n-1)*10./N + rand(0)*10./N
a = rand(0)*50+10
y0 = rand(0)*80+20
plot [0:10] '+' u 1:(f(x))
unset table
}
# extract maxima
set print $Maxima
do for [n=1:N] {
stats myFile(n) u 1:2 nooutput
print sprintf("%.1f %.1f", STATS_pos_max_y, STATS_max_y)
}
set print
set yrange[0:]
set offsets graph 0.05, graph 0.05, graph 0.1, 0
set xtics () # remove all xtics
set key out noautotitle
plot for [i=1:N] myFile(i) u 1:2 w l ti sprintf("Set %d",i), \
$Maxima u 1:2:($0+1):xtic(1) w impulses lc var dt 2, \
$Maxima u 1:2:2 w labels offset 0, char 1
### end of script
Result:
I would like to plot a smoothed curve based on a dataset which spans over 13 orders of magnitude [1E-9:1E4] in x and 4 orders of magnitude [1E-6:1e-2] in y.
MWE:
set log x
set log y
set xrange [1E-9:1E4]
set yrange [1E-6:1e-2]
set samples 1000
plot 'data.txt' u 1:3:(1) smooth csplines not
The smooth curve looks nice above x=10. Below, it is just a straight line down to the point at x=1e-9.
When increasing samples to 1e4, smoothing works well above x=1. For samples 1e5, smoothing works well above x=0.1 and so on.
Any idea on how to apply smoothing to lower data points without setting samples to 1e10 (which does not work anyway...)?
Thanks and best regards!
JP
To my understanding sampling in gnuplot is linear. I am not aware, but maybe there is a logarithmic sampling in gnuplot which I haven't found yet.
Here is a suggestion for a workaround which is not yet perfect but may act as a starting point.
The idea is to split your data for example into decades and to smooth them separately.
The drawback is that there might be some overlaps between the ranges. These you can minimize or hide somehow when you play with set samples and every ::n or maybe there is another way to eliminate the overlaps.
Code:
### smoothing over several orders of magnitude
reset session
# create some random test data
set print $Data
do for [p=-9:3] {
do for [m=1:9:3] {
print sprintf("%g %g", m*10**p, (1+rand(0))*10**(p/12.*3.-2))
}
}
set print
set logscale x
set logscale y
set format x "%g"
set format y "%g"
set samples 100
pMin = -9
pMax = 3
set table $Smoothed
myFilter(col,p) = (column(col)/10**p-1) < 10 ? column(col) : NaN
plot for [i=pMin:pMax] $Data u (myFilter(1,i)):2 smooth cspline
unset table
plot $Data u 1:2 w p pt 7 ti "Data", \
$Smoothed u 1:2 every ::3 w l ti "cspline"
### end of code
Result:
Addition:
Thanks to #maij who pointed out that it can be simplified by simply mapping the whole range into linear space. In contrast to #maij's solution I would let gnuplot handle the logarithmic axes and keep the actual plot command as simple as possible with the extra effort of some table plots.
Code:
### smoothing in loglog plot
reset session
# create some random test data
set print $Data
do for [p=-9:3] {
do for [m=1:9:3] {
print sprintf("%g %g", m*10**p, (1+rand(0))*10**(p/12.*3.-2))
}
}
set print
set samples 500
set table $SmoothedLog
plot $Data u (log10($1)):(log10($2)) smooth csplines
set table $Smoothed
plot $SmoothedLog u (10**$1):(10**$2) w table
unset table
set logscale x
set logscale y
set format x "%g"
set format y "%g"
set key top left
plot $Data u 1:2 w p pt 7 ti "Data", \
$Smoothed u 1:2 w l lc "red" ti "csplines"
### end of code
Result:
Using a logarithmic scale basically means to plot the logarithm of a value instead of the value itself. The set logscale command tells gnuplot to do this automatically:
read the data, still linear world, no logarithm yet
calculate the splines on an equidistant grid (smooth csplines), still linear world
calculate and plot the logarithms (set logscale)
The key point is the equidistant grid. Let's say one chooses set xrange [1E-9:10000] and set samples 101. In the linear world 1e-9 compared to 10000 is approximately 0, and the resulting grid will be 1E-9 ~ 0, 100, 200, 300, ..., 9800, 9900, 10000. The first grid point is at 0, the second one at 100, and gnuplot is going to draw a straight line between them. This does not change when afterwards logarithms of the numbers are plotted.
This is what you already have noted in your question: you need 10 times more points to get a smooth curve for smaller exponents.
As a solution, I would suggest to switch the calculation of the logarithms and the calculation of the splines.
# create some random test data, code "stolen" from #theozh (https://stackoverflow.com/a/66690491)
set print $Data
do for [p=-9:3] {
do for [m=1:9:3] {
print sprintf("%g %g", m*10**p, (1+rand(0))*10**(p/12.*3.-2))
}
}
set print
# this makes the splines smoother
set samples 1000
# manually account for the logarithms in the tic labels
set format x "10^{%.0f}" # for example this format
set format y "1e{%+03.0f}" # or this one
set xtics 2 # logarithmic world, tic distance in orders of magnitude
set ytics 1
# just "read logarithm of values" from file, before calculating splines
plot $Data u (log10($1)):(log10($2)) w p pt 7 ti "Data" ,\
$Data u (log10($1)):(log10($2)) ti "cspline" smooth cspline
This is the result:
I would like to generate a three dimensional mesh or net with variable length at each of three sides. How to achive that in gnuplot?
Thanks a lot for help.
Your question is not very detailed... furthermore, no code and no research effort visible (By the way, that's what people expect here on SO). In gnuplot console, e.g. check help do, help print, help sprintf or other keywords.
A guess what you might want could be the following:
Code:
### creating a 3D grid
reset session
set view equal xyz
dx = 0.7
dy = 0.8
dz = 0.6
set print $Data
do for [z=0:3] {
do for [y=0:4] {
do for [x=0:5] {
print sprintf("%g %g %g",x*dx,y*dy,z*dz)
}
print ""
}
print ""
}
set print
set xtics 1
set ytics 1
set ztics 1
set view 73,53
splot $Data u 1:2:3 w lp pt 7
### end of code
Result:
I have a datafile with position of a moving point in the following format.
x1 y1
x2 y2
x3 y3
.
.
.
I wish to make an animated trajectory with this data in gnuplot. How can I do that?
I tried
do for [i=1:20] {
plot "temp.dat" every ::i using 1 : 2 w p
}
But it plots all the points in a single image, not an animation. What is the way of doing this?
While I was coding and got interrupted... #Ethan's answer already contains all the necessary ingredients, I post my answer nevertheless, with a little visual demo...
Check help gif, help stats and help every, these are the main "components".
In the following example you hopefully find what you are looking for.
Code:
### trajectory animated
reset session
# create some test data
v = 40
a = 45
g = 9.81
set print $Data
do for [i=0:86] {
t = i/10.
sx(t) = v*cos(a)*t
sy(t) = v*sin(a)*t - 0.5*g*t**2
print sprintf("%.3f %.3f",sx(t),sy(t))
}
set print
set xrange[0:200]
set yrange[0:80]
set term gif size 400,300 animate delay 5 optimize
set output "Trajectory.gif"
stats $Data nooutput
N = STATS_records
do for [i=0:N-1] {
plot $Data u 1:2 every ::::i w l notitle, \
'' u 1:2 every ::i::i w p pt 7 lc rgb "red" notitle
}
set output
### end of code
Result:
If you just want the animation to show on the screen, then your code is fine except that you need to add a delay between the successive frames:
do for [i=1:20] {
plot "temp.dat" every ::i using 1 : 2 w p
pause 0.1 # 1/10 second between frames
}
If you are making an animated gif file then the pause doesn't go into the loop itself, it becomes a parameter to the set term command:
set term gif animate delay 10 # 10 = 10 units of 0.01 seconds
set output 'animation.gif'
do for [i=1:20] {
plot "temp.dat" every ::i using 1 : 2 w p
}
I'm plotting some data and I want to use dashed grid lines.
Any dashed grid line would suffice, but I prefer a "long dash, short dash, long dash" format.
For example, given the following code
set grid lc rgb "#000000" lt 1 dt (50, 25, 20, 25)
plot x**2
I get this result
But I would rather the grid lines intersection to happen always at the middle of two dashes, like this
If I could make horizontal grid lines different to vertical grid lines and I could add some offset to each one, then I'd imagine there's a way to accomplish this. But I can't seem to do that either.
It looks like gnuplot cannot have two different dashstyles for x-grid and y-grid.
One workaround I see currently is to plot two identical plot on top of each other. One with appropriate x-grid lines and the other with appropriate y-grid lines.
If you want a dash pattern with proportions of (50-25-20-25), this correspond to (25-25-20-25-25-0) or (5-5-4-5-5-0) between two tics.
Furthermore, the dash and gap length numbers, e.g. in dt (50,25,20,25), seem to be in a fixed relation to the graph size. The "empirical" factor is 11 with good approximation (at least for the wxt terminal which I tested under gnuplot 5.2.6).
Edit: actually, the code below gives different results with a qt terminal. And it's not just a different factor. It's more complicated and probably difficult to solve without insight into the source code. So, the fact that the following seems to work with wxt terminal (maybe even just under Windows?) was probably a lucky strike.
With this you can create your dash lines automatically resulting in crosshairs at the intersections of the major grid lines.
Assumptions are:
your first and last tics are on the borders
you know the number of x- and y-intervals
You also need to know the graph size. These values are stored in the variables GPVAL_TERM..., but only after plotting. That's why you have to replot to get the correct values.
This workaround at least should give always crosshairs at the intersection of the major grid lines.
Edit 2: just for "completeness". The factors to get the same (or similar) looking custom dashed pattern on different terminals varies considerably. wxt approx. 11, qt approx. 5.6, pngcairoapprox. 0.25. This is not what I would expect. Furthermore, it looks like the factors slightly depend on x and y as well as graph size. In order to get "exact" crosshairs you might have to tweak these numbers a little further.
Code:
### dashed grid lines with crosshairs at intersections
reset session
TERM = "wxt" # choose terminal
if (TERM eq "wxt") {
set term wxt size 800,600
FactorX = 11. # wxt
FactorY = 11. # wxt
}
if (TERM eq "qt") {
set term qt size 800,600
FactorX = 5.58 # qt
FactorY = 5.575 # qt
}
if (TERM eq "pngcairo") {
set term pngcairo size 800,600
set output "tbDashTest.png"
FactorX = 0.249 # pngcairo
FactorY = 0.251 # pngcairo
}
set multiplot
set ticscale 0,0
Units = 24 # pattern (5,5,4,5,5,0) are 24 units
# set interval and repetition parameters
IntervalsY = 10
RepetitionsY = 1
IntervalsX = 4
RepetitionsX = 3
# initial plot to get graph size
plot x**2
gX = real(GPVAL_TERM_YMAX-GPVAL_TERM_YMIN)/IntervalsY/Units/FactorY/RepetitionsY
gY = real(GPVAL_TERM_XMAX-GPVAL_TERM_XMIN)/IntervalsX/Units/FactorX/RepetitionsX
# first plot with x-grid lines
set grid xtics lt 1 lc rgb "black" dt (gX*5,gX*5,gX*4,gX*5,gX*5,0)
replot
unset grid
# second plot with y-grid lines
set grid ytics lt 1 lc rgb "black" dt (gY*5,gY*5,gY*4,gY*5,gY*5,0)
replot
unset multiplot
set output
### end of code
Result:
Not really. The closest I can think of is
set grid x y mx my
set grid lt -1 lc "black" lw 1 , lt -1 lc bgnd lw 16
set ticscale 1.0, 0.01
set mxtics 4
plot x**2 lw 2
But that leaves the vertical grid lines solid.