Let us suppose that we have the following data file, called 'bar_matrix.dat', with 4 by 4 real numbers
1 0.5 2.2 0.5 1.2 0.6 -2.4 0.2 0.1 0.5 -1.8 -1.5 1.2 0.3 0.6 1.9
and we want to generate a plot similar to this:
Following the recipe from yesterday, we have to read out the numbers in the matrix, one by one, and plot the cuboids one by one. In order to give the impression of a 3D object, the three visible sides of the cuboids must be painted with different shades of the same colour, but that should not be a problem, we have already seen how to do that. However, there is one difficulty here: we will have to use multiplot, which means that in order to keep the relative size of the cuboids, we have to fix all three ranges, xrange, yrange, and zrange. In addition, determining the xrange and yrange serves not only some aesthetic purpose, but they will also control our 'for' cycle. So, what can we do to crack this nut?
In order to answer the question, we have got to think about how gnuplot plots: first, if no ranges are specified, data are read in, then based on certain values, the ranges are calculated. So, if we issue
plot 'something.dat' using 1:2
the first, and second columns of 'something.dat' will be read, and based on the minimum and maximum values of the first column, the xrange is calculated, and likewise for the yrange. But then these values are stored as a special variable in gnuplot. So, if you are interested in the minimum and maximum of your xrange, you can do
print GPVAL_DATA_Y_MIN print GPVAL_DATA_Y_MAX
You can also print out all variables, both user-defined and internal, by typing
show variables all
Now, this means that we will have access to some properties of our data file, provided that the data file has been plotted at least once. This is what we will use in the following script, called 'bar_matrix.gnu'.
reset filename="bar_matrix.dat" w=0.4 FIT_LIMIT=1e-8 mult=1.05; cd=1.4 f(x,a)=(abs(x-a)<0.5?d:0) R(x)=abs(2*x-0.5); G(x)=sin(x*pi); B(x)=cos(x*pi/2.0) set view 60, 20 set parametric; set isosample 2, 2 unset border; unset tics; unset key; unset colorbox set ticslevel 0 set urange [0:1]; set vrange [0:1] splot 'bar_matrix.dat' mat set zrange [mult*GPVAL_DATA_Z_MIN:mult*GPVAL_DATA_Z_MAX] Y=GPVAL_DATA_Y_MAX+1 X=GPVAL_DATA_X_MAX+1 set xrange [1-w:X+2*w] set multiplot C=0; xx=1; yy=Y call 'bar_matrix_r.gnu' set palette model RGB functions 0.9, 0.9,0.95 splot -w+u*(X+3*w), -w+v*(Y+w), 0 w pm3d C=1; xx=1; yy=Y call 'bar_matrix_r.gnu' unset multiplot
In addition to this file, we will need two more. Remember, in order to plot a matrix, we have to use two 'for' cycles, each requiring an external gnuplot script. So, we also have 'bar_matrix_r.gnu'
yy=Y call 'bar_matrix_r2.gnu' xx=xx+1 if(xx<X+1) reread
and 'bar_matrix_r2.gnu'
unset parametric yy=yy-1 d=0.3 set yrange [*:*] fit [0:Y] f(x,yy) filename u 0:xx via d set yrange [-w:Y+2*w] set parametric r=R(yy/Y); g=G(yy/Y); b=B(yy/Y) if(C==0 && d<0) \ set palette defined (0 r g b, 1 r g b);\ splot w*u+xx, w*v+yy, d w pm3d;\ r=r/cd; g=g/cd; b=b/cd;\ set palette defined (0 r g b, 1 r g b);\ splot w*u+xx, yy, v*d w pm3d;\ r=r/cd; g=g/cd; b=b/cd;\ set palette defined (0 r g b, 1 r g b);\ splot w+xx, yy+w*u, v*d w pm3d; if(C==1 && d>0) \ set palette defined (0 r g b, 1 r g b);\ splot w*u+xx, yy+w*v, d w pm3d;\ r=r/cd; g=g/cd; b=b/cd;\ set palette defined (0 r g b, 1 r g b);\ splot w*u+xx, yy, v*d w pm3d;\ r=r/cd; g=g/cd; b=b/cd;\ set palette defined (0 r g b, 1 r g b);\ splot w+xx, yy+w*u, v*d w pm3d; if(yy>0) reread
Having seen the scripts, let us discuss what the three files do. The beginning of 'bar_matrix.gnu' should be familiar by now, there is nothing new in it. The first interesting thing happens where we do a dummy plot of the matrix, just to extract the number of rows and columns, and the range of the data values. We use GPVAL_DATA_Z_MIN and GPVAL_DATA_Z_MAX to set the common zrange of all consequent figures, and GPVAL_DATA_X_MAX and GPVAL_DATA_Y_MAX to determine the number of iterations (and the xrange, and yrange, of course). Note that we use a multiplier, 'mult', defined at the beginning of the file, so that the top and bottom of the plot will definitely not be clipped. Then we clear our plot by setting the multiplot, and calling 'bar_matrix_r.gnu'. This file controls the 'for' cycles over rows. It does nothing, but re-sets the value of the inner 'for' cycle, increments its own variable, and calls 'bar_matrix_r2.gnu'. In this second 'for' loop is where the actual plotting takes place. In that file, we call our fitting routine, set the palette, and plot the 3 sides of the cuboids. Note that we want to paint the sides in different colours, so after plotting the top, we produce a darker shade by dividing all RGB values by 'cd', and after plotting the front side, we reduce the RGB values once more, to draw the right hand side.
Now, this script contains an 'if' statement, with the variable 'C' and 'd'. The reason for this is that we call 'bar_matrix_r.gnu' twice: first we plot all bars that represent a negative value, then draw the z=0 plane, and finally plot the positive values. This is what happens in the last couple of lines of 'bar_matrix.gnu'. If you are certain that your matrix contains positive values only, you can get rid of the 'if' statement, and also of the first call of 'bar_matrix_r.gnu'.
If you now call 'bar_matrix.gnu' from gnuplot, you will get the figure at the beginning of this plot. You can then add labels and so on, as you wish. I would also like to point out that human intervention is required only at the very beginning of 'bar_matrix.gnu', where we specify the name of the file that we want to plot. Otherwise, everything is automatic, and will just happen by the stroke of the key. You should also keep in mind that we had a dummy plot, so if you want to plot into a file, you have got to set the terminal after this plot, lest you should not have the dummy plot in your file.
Dear Gnuploter,
ReplyDeleteThis is really beautiful graph. I have a question concerning small modification of this graph.
I would like to plot bargraphs (cuboids) in a such a way that you specify (in your data file) the position of each bargraph. Example of data file would be the following (3 columns): #x-coordinate of bargraph #y-coordinate of bargraph #magnitude of bargraph.
Is there an easy way to this ?
Thanks in advance for the answer.
Jarek
Greetings Jarek,
ReplyDeleteI think that would require data processing, and I don't believe that it is easily done in gnuplot.
Cheers,
Zoltán