Normally, when one plots a function or file, the command has the following structure
plot 'foo' using 1:2 with line, f(x) with line
That was the old syntax. In the new version of gnuplot, we can insert arithmetic expressions in the plot command as follows
f(x) = a*sin(x) plot a = 1.0, f(x), a = 2.0, f(x)
Now, this has some implications. First, one has to be a bit careful, because the arithmetic expressions are separated from the actual function by a comma. However, the 'for' loop that we discussed a week ago, reads statements up to the comma, and then returns to the beginning of the statement. In other words,
plot for [i=1:10] a = i, f(x)will evaluate the expression a = i ten times, and then plots f(x). At that point, the value of 'a' will be 10, therefore, we have only one plot, and that will be 10*sin(x).
The second implication is that the notion of a function has completely changed. What we do in a plot command now is no longer a mapping of the form
x -> f(x)but rather, the evaluation of a set of instructions, one of which is the above-mentioned mapping. But the crucial point here is that the mapping is not the only allowed statement. The upshot is that "functions" have become a set of operations, and the following statement is completely legal
f(x) = (a = a+1.0, a*sin(x)) a = 0.0 plot for [i=1:10] f(x)
(It is a complete different question, whether this plot makes any sense...)
What we should notice is the fact that now a function can have the form of
f(x) = (statement1, statement2, statement3, return value)and when the function is called, statement1, statement2, statement3 are evaluated, and 'return value' is returned. We should not underestimate the significance of this! Many things can be done with this. I will show a few of them below.
The first thing I would like to dive into is calculating some statistics of a file. Let us see how this works!
reset set table 'inline.dat' plot sin(x) unset table num = 0 sum = 0.0 sumsq = 0.0 f(x) = (num = num+1, sum = sum+x, sumsq = sumsq+x*x, x) plot 'inline.dat' using 1:(f($2)) print num, sum, sumsqwhich will print out
100 -1.77635683940025e-15 0.295958848441We expected this, for the number of samples is 100 by default, and the sum should be 0 in this case.
So, what about finding the minimum and its position in a data file? This is quite easy. All we have to do is to modify our function definition, and insert a statement that determines whether a value is minimal or not.
reset set table 'inline.dat' plot sin(x) unset table num = 0 min = 1000.0 min_pos = 0 min_pos_x = 0 f(x,y) = ((min > y ? (min = y, min_pos_x = x, min_pos = num) : 1), num = num+1, y) plot 'inline.dat' using 1:(f($1, $2)) print num, min, min_pos_x, min_poswhich prints
100 -0.999385 4.74747 73i.e., the minimum is at the 73rd record (we count from 0), at x = 4.74747, and its value is -0.999385. Note that instead of an 'if' statement, we use the ternary operator to decide whether min, min_pos_x, and min_pos should be updated.
The implementation of calculating the standard deviation, e.g., should be trivial:
sum = 0.0 sumsq = 0.0 f(x) = (num = num+1, sum = sum + x, sumsq = sumsq + x*x, x) plot 'inline.dat' using 1:(f($1)) print num, sqrt(sumsq/num - (sum/num)*(sum/num))We have thus seen how the "inline" arithmetic can be used for calculating quantities, e.g., various moments, minima/maxima and their respective positions. These involve the sequential summing or inspection of the data set. But this trick with the function definition can be used for back-referencing, too. This is what we will discuss next.
The trick is to use a construct similar to this
backshift(x) = (prev = pres, pres = x, prev)
which will store the last but one value in the variable 'prev', and return it. That is, the following code shift the whole curve to the right by one
reset set table 'inline.dat' plot sin(x) unset table pres = 0.0 backshift(x) = (prev = pres, pres = x, ($0 > 0 ? prev : pres)) plot 'inline.dat' using 1:(backshift($2)) with line, '' u 1:2 with line(In cases like this, we always have to decide what to do with the first/last data record. In this particular case, I opted for duplicating the first record, - this is what happens in the ternary operator - but this is not the only possibility.) If, for some reason, you have to shift the curve by more, you do the same thing, but multiple times. E.g., the following code shifts by 3 places.
backshift(x) = (prev1 = prev2, prev2 = prev3, prev3 = x, prev1)
Once we have this option of back-referencing, we should ask the question what it can be used for. I show two examples for this.
The first example is drawing arrows along a line given by the data set. Drawing arrows one by one is done by using
set arrow from x1,y1 to x2,y2but we have to use a different method, if we want to plot the arrows from a file. Incidentally, there is a plotting style, 'with vectors', that works as
plot 'foo' using 1:2:3:4 with vectorswhere the first two columns specify the coordinates of the beginning, and the second two columns specify the relative coordinates of the vectors. So, it works on four columns. What should we do, if we want to plot vectors from the points in a file. Well, we use the back shift that we defined above. Our script is as follows:
reset unset key set sample 30 set table 'arrowplot.dat' plot [0:3] sin(x)+0.2*rand(0) unset table px = NaN py = NaN dx(x) = (xd = x-px, px = ($0 > 0 ? x : 1/0), xd) dy(y) = (yd = y-py, py = ($0 > 0 ? y : 1/0), yd) plot 'arrowplot.dat' using (px):(py):(dx($1)):(dy($2)) with vectorwhich results in the following figure:
In the second example, we will turn this around. In my post in last August, plotting the recession, I showed how the background of a plot can be changed, based on whether the the curve is dropping, or increasing. Let us take the following script
reset set sample 20 set table 'inline.dat' plot [0:10] exp(-x)+1.0+rand(0) unset table unset key px = 0 py = 1000 dx(x) = (xd = x-px, px = x, xd) dyn(y) = (yd = y-py, py = y, (yd < 0 ? yd : 1/0)) dyp(y) = (yd = y-py, py = y, (yd >= 0 ? yd : 1/0)) plot 'inline.dat' using (px):(py):(dx($1)):(dyp($2)) with vector nohead lt 1 lw 3, \ px = 0, py = 0, '' using (px):(py):(dx($1)):(dyn($2)) with vector nohead lt 3 lw 3which creates the following graph
Another utilisation of the back reference can be found on gnuplot's web site, under running averages.
Next time I will try to go a bit further, and demonstrate some other uses of the inline data processing.
Cheers,
Gnuplotter