GNUPlot: Animating Labels - gnuplot

My problem, following from my previous question, goes like this now: I have a particle moving in a plane, and now I want a sort of box at the side which says the particle's position in XY and its velocity. I tried using labels, but they end up overlapping each other.
Here's a really crude "sketch" of what I want to see:
+------------------------------------------+
| +-----------+ |
| | | t = 2 |
| | PLOT HERE | x = 0 y = 1 |
| | | vx = 2 vy = 3 |
| +-----------+ |
+------------------------------------------+
where those numbers would be changing at each frame. I've been able to animate a title, but a label seems different.
My current code looks very much like the answer in here GNUPlot - Plot 2D datapoints as MPEG but with some minor stylistic modifications, and that I removed the title. I can produce data that are just the XY coordinates, which is what I use right now.
I can also produce something like (these are random points, just for the sake of illustration)
#X Y t vx vy
0.00 1.00 0.0 0.0 6.28
0.01 0.01 0.01 1.0 6.00
for the animating labels.

A nice way for placing labels from a data file is with the labels plotting style. However, there are some difficulties for placing labels outside of the actual plotting area, since these points and labels usually are clipped.
Since you are using stats anyway to fix the x- and yrange, here is how I would do this:
Set a fixed right margin with e.g. set rmargin 20. This uses a right margin of 20 character widths. You could also use absolute coordinates like set rmargin at screen 0.8, but since you need the margin to place labels, character units seem appropriate.
Use the upper right corner of your plot area as reference point (STATS_max_x, STATS_max_y) and shift the labels using the offset parameter and shifting again by some character widths.
So, a complete script might look as follows:
# calculate the number of points
stats 'file.txt' using 1:2 nooutput
# if you want to have a fixed range for all plots
set xrange [STATS_min_x:STATS_max_x]
set yrange [STATS_min_y:STATS_max_y]
set terminal pngcairo size 800,400
outtmpl = 'output%07d.png'
v_label(x, y) = sprintf('vx = %.2f vy = %.2f', x, y)
c_label(x, y) = sprintf('x = %d y = %d', x, y)
t_label(t) = sprintf('t = %.2f', t)
set rmargin 20
do for [i=0:STATS_records-1] {
set output sprintf(outtmpl, i)
plot 'file.txt' every ::::i with lines title sprintf('n = %d', i),\
'' every ::i::i using (STATS_max_x):(STATS_max_y):(t_label($3)) with labels offset char 11,-5 notitle,\
'' every ::i::i using (STATS_max_x):(STATS_max_y):(c_label($1, $2)) with labels offset char 11,-6.5 notitle,\
'' every ::i::i using (STATS_max_x):(STATS_max_y):(v_label($4, $5)) with labels offset char 11,-8 notitle
}
set output
Note, that the rmargin and offset settings depend on the terminal, terminal size, font and font size. For nicer labels placement you might consider placing the vx and vy labels separately and maybe changing their alignment.
Alternatively, in each iteration you can extract the current line from your data file and set the labels manually. This however requires you to use an external tool to extract the line:
do for [i=0:STATS_records-1] {
line = system(sprintf("sed -n %dp file.txt", i+2))
set label 1 at screen 0.9, screen 0.9 sprintf("t = %.2f", real(word(line, 3)))
set label 2 at screen 0.9, screen 0.88 sprintf("x = %.2f y = %.2f", real(word(line, 1)), real(word(line, 2)))
plot 'file.txt' every ::::i with lines title sprintf('n = %d', i)
}
I don't know which variant fits you better. I used i+2 as line number to skip the commented header line, which isn't detected automatically. By using tag for the label (set label 1) you make sure, that old labels are overwritten.

Related

gnuplot histogram bins divided by volume

I am simulating points in a sphere volume with radius 1. I generated 1.000.000 monte-carlo based points in this volume. To make a gnuplot histogram i calculated the length of each vector (every vector length is between 0 and 1). With 100 bins the histogram looks like:
gnuplot data histogram.
If someone is wondering why there no points greater than 0.91 are generated, i also dont know, but this is not the question here.
This is my gnuplot Code:
n=100 #number of intervals
max=1.0 #max value
min=0.0 #min value
width=(max-min)/n #interval width
#function used to map a value to the intervals
hist(x,width)=width*floor(x/width)+width/2.0
#settings
set xlabel "Radius"
set ylabel "Primarys/Intervall"
set xrange [-0.1:1.1]
set yrange [0:32000]
set boxwidth width*0.8
set style fill solid 0.5 #fillstyle
set tics out nomirror
#plot
plot "primaryPosition(1).csv" u (hist($1,width)):(1.0) smooth freq w boxes lc rgb"green"
In general: A Volume grows by r^3 to Radius r.
In my histrogram every spherical shell is one bin and the bin number is 100. So, as the bin number increases, the volume of each sperical shell grows cubically (with r^3). From this point of view, the histogram looks good.
But what i want to do is to plot the density of points per volume: points/shellvolume.
This should be a linear distribution from the center of the sphere to its border.
How can i tell gnuplot to divide each bin by its corresponding volume, which depends on the outer and the inner radius of each spherical shell?
The formula is: (4/3)pi(R^3-r^3) with R outer and r inner radius a shell.
The following example creates some random test data (should be 20'000 equally distributed random points).
One possibility would be that you first you create your histogram data via binning into a table and then you divide it by the volume of the shell.
By the way, the volume of a sphere shell is (4./3)*pi*(R**3-r**3), not the formula you've given. And why are you setting max < min? Maybe you want to fine tune the binning to your exact needs.
Code:
### histogram normalized by sphere shell volume
reset session
set view equal xyz
# create some test data
set print $Data
do for [i=1:20000] {
x = rand(0)*2-1
y = rand(0)*2-1
z = rand(0)*2-1
r = sqrt(x**2 + y**2 + z**2)
if (r <= 1) { print sprintf("%g %g %g %g",x,y,z,r) }
}
set print
n = 100 # number of intervals
min = 0.0 # max value
max = 1.0 # min value
myWidth=(max-min)/n # interval width
bin(x)=myWidth*floor(x/myWidth)
ShellVolume(r) = (4./3)*pi*((r+myWidth)**3-r**3)
set boxwidth myWidth absolute
set table $Histo
plot $Data u (bin($4)):(1) smooth freq
unset table
set multiplot layout 2,1
plot $Histo u 1:2 w boxes ti "Occurrences"
plot $Histo u 1:($2/ShellVolume($1)) w boxes ti "Density"
unset multiplot
### end of code
Result:

How can I make a filled region in the x direction in gnuplot?

I know that gnuplot has the great type of plot that is filledcurve, which you can make a filled region between two curves that are presented like 1:2:3, it will make a curve between columns $2 and $3 for the same x value $1. But how can I fill this region in the graph below in gnuplot? The range is in x direction like x1:x2:y, same value of y.
My data it's in form like:
# rho1 rho2 C
0.8022651311239721 0.8299444680667378 0.00005011872336272725
0.8022624676512962 0.8299464715046031 0.00004466835921509635
0.8022618998639025 0.8299490455369624 0.000039810717055349695
0.8022533810411624 0.8299390462160209 0.000035481338923357534
...
But I can separate that in two archives too.
Here is a useful trick that uses the 3D plotting style with zerror and then sets the view angle so that it looks like a 2D x/y plot. I don't have enough of your data to replicate the plot you show so I use a junk data file just for the purpose of showing how the plot works:
# 3D plot style "with zerror" takes 5 columns of input
# x y z zlow zhigh
# We will choose a view angle such that "z/zlow/zhigh" is the horizontal axis
# "x" is the vertical axis
# "y" is unused because it is along the line of sight
# For your data as described
rho1 = 1 # column 1
rho2 = 2 # column 2
c = 3 # nominal y value, we use it for X
junk = 0 # unused constant coordinate
rhomean(c) = (column(rho1) + column(rho2)) / 2.
set view 270, 0
set view azimuth -90
unset ytics
set zlabel "ρ" # horizontal axis in this projection
set xlabel "C" # vertical axis in this projection
set zrange [0:50] # Note how this affects the horizontal axis!
splot "data" using c:(junk):(rhomean(c)):rho1:rho2 with zerror lt black fc "gold"
The with zerror plot style and the set view azimuth command both require a reasonbly current version of gnuplot.

Discrete heat map with GNUPLOT

I'm trying to make something as a heat map with GNUPLOT but I need that my palette takes discrete colors for defined values.
I mean, my data file has three columns, for example:
x y value
0.0 0.0 10
0.0 0.5 2
0.0 1.0 2
0.5 1.0 10
1.0 0.0 -1
1.0 1.0 -1
I need that each point has one color depending of its value. Traditional heat map mixes point making regions of continuos colors, but I need it in a discrete form.
If your data forms a "matrix", i.e., there are M x-samples, N y-samples, and you have the data for all MxN points, then probably the easiest solution is to use
plot ... w rgbimage u 1:2:(r($3)):(g($3)):(b($3))
and supply the r,g,b values as three additional columns as shown above.
However, if your data is "sparse" (only some of the samples are available as shown in your question) and there are not many points, one might be tempted to generate the elementary squares forming the plot manually. To this end, one could proceed as:
set terminal png enhanced
set output 'plot.png'
#custom value -> color mapping
rgb(r, g, b) = 65536 * int(r) + 256 * int(g) + int(b)
fn(val) = rgb(100 + val*10, 0, 0)
#square size
delta = 0.5
set xr [-delta/2:1+delta/2]
set yr [-delta/2:1+delta/2]
set xtics 0,delta/2,1 out nomirror
set ytics 0,delta/2,1 out nomirror
set format x "%.2f"
set format y "%.2f"
set size ratio 1
unset key
fName="test.dat"
load sprintf("<gawk -v d=%f -f parse.awk %s", delta, fName)
plot fName u 1:2:3 w labels tc rgb 'white'
This script assumes the presence of auxiliary gawk script parse.awk in the same directory:
{
printf "set object rectangle from %f,%f to %f,%f fc rgb fn(%d) fs solid\n",
$1-d/2, $2-d/2, $1+d/2, $2+d/2, $3
}
This scripts accepts the required square size (-v d=%f in the invocation of gawk) and generates for each point a statement generating the corresponding square. These statements are consequently executed by the load command.
Mapping of the colors is done via the function fn defined in the main Gnuplot script. It takes the passed value and generates a rgb value which is then used with fc rgb in the rectangle specification.
Together, this then produces:
This might do what you want, after some fiddling:
set view map
set style fill transparent solid noborder
splot 'data' u 1:2:3:(100+200*$3) pt 5 lc rgbcolor var ps 14
The pt 5 will plot a square (at least in the x11 term) at each point in the datafile, colored according to a transformation on the last column.

gnuplot: How to increase the width of my graph

I am using 'gnuplot' to plot a line graph with point:
set style data linespoints
set xlabel "number"
set ylabel "Dollars"
set yrange [0:250]
how can I increase the width of my graph, so that as I have more 'x', i want my graph to more of a rectangle instead of a square?
And how can I increase the interval of my 'y-axis'? now, it just draw a mark for every 50 in my y axis?
It sounds like you want your output to dynamically adjust in size to the data being plotted. Here is a script that does that:
#!/usr/bin/env gnuplot
# don't make any output just yet
set terminal unknown
# plot the data file to get information on ranges
plot 'data.dat' title 'My Moneys'
# span of data in x and y
xspan = GPVAL_DATA_X_MAX - GPVAL_DATA_X_MIN
yspan = GPVAL_DATA_Y_MAX - GPVAL_DATA_Y_MIN
# define the values in x and y you want to be one 'equivalent:'
# that is, xequiv units in x and yequiv units in y will make a square plot
xequiv = 100
yequiv = 250
# aspect ratio of plot
ar = yspan/xspan * xequiv/yequiv
# dimension of plot in x and y (pixels)
# for constant height make ydim constant
ydim = 200
xdim = 200/ar
# set the y tic interval
set ytics 100
# set the x and y ranges
set xrange [GPVAL_DATA_X_MIN:GPVAL_DATA_X_MAX]
set yrange [GPVAL_DATA_Y_MIN:GPVAL_DATA_Y_MAX]
# set the labels
set title 'Dollars in buckets'
set xlabel 'number'
set ylabel 'Dollars'
set terminal png size xdim,ydim
set output 'test.png'
set size ratio ar
set style data linespoints
replot
For these example data:
0 50
50 150
100 400
150 500
200 300
I get the following plot:
It is about square, as it should be (I defined 100 units in x to be equal to 250 units in y, and the data span the range [(0,200),(50,500)]). If I add another data point (400,300), the output file is wider, as expected:
To answer your other question, you can set the y tic increment thus:
set ytics <INCREMENT>
The script above gives an example.
To add to the discussion here, there's also set size ratio ... so that you can set the aspect ratio of your plot.
Here's an excerpt from help set size:
ratio causes gnuplot to try to create a graph with an aspect ratio of
(the ratio of the y-axis length to the x-axis length) within the portion of
the plot specified by <xscale> and <yscale>.
The meaning of a negative value for is different. If =-1, gnuplot
tries to set the scales so that the unit has the same length on both the x
and y axes (suitable for geographical data, for instance). If =-2, the
unit on y has twice the length of the unit on x, and so on.
For this to really work, you'll probably need to set the output driver to some reasonable size:
set term png size 800,400 #800 pixels by 400 pixels
or:
set term post size 8,4 #8 inches by 4 inches
This is all terminal dependent, so its worth it to look up the terminal's help to see what units it uses, etc.
set xrange[:]
set yrange[:]
Use those 2 commands to define the 'size' of your graph ;)

How do I draw a set of vertical lines in gnuplot?

E.g. if I have a graph and want to add vertical lines at every 10 units along the X-axis.
From the Gnuplot documentation. To draw a vertical line from the bottom to the top of the graph at x=3, use:
set arrow from 3, graph 0 to 3, graph 1 nohead
Here is a snippet from my perl script to do this:
print OUTPUT "set arrow from $x1,$y1 to $x1,$y2 nohead lc rgb \'red\'\n";
As you might guess from above, it's actually drawn as a "headless" arrow.
alternatively you can also do this:
p '< echo "x y"' w impulse
x and y are the coordinates of the point to which you draw a vertical bar
You can use the grid feature for the second unused axis x2, which is the most natural way of drawing a set of regular spaced lines.
set grid x2tics
set x2tics 10 format "" scale 0
In general, the grid is drawn at the same position as the tics on the axis. In case the position of the lines does not correspond to the tics position, gnuplot provides an additional set of tics, called x2tics. format "" and scale 0 hides the x2tics so you only see the grid lines.
You can style the lines as usual with linewith, linecolor.
To elaborate on previous answers about the "every x units" part, here is what I came up with:
# Draw 5 vertical lines
n = 5
# ... evenly spaced between x0 and x1
x0 = 1.0
x1 = 2.0
dx = (x1-x0)/(n-1.0)
# ... each line going from y0 to y1
y0 = 0
y1 = 10
do for [i = 0:n-1] {
x = x0 + i*dx
set arrow from x,y0 to x,y1 nohead linecolor "blue" # add other styling options if needed
}

Resources