gnuplot reading data to an array or referencing point coordinates in an external file - gnuplot

Is there a way in gnuplot to loop through data points in a data file? Let's say I have a file with the data
# x y
1 2
2 5
3 1
4 5
5 6
And I am looking how to draw lines in a loop from some x, y to some other x, y from the files. Something like:
j=0
x3 = data[1][1]
y3 = data[1][2]
do for [i=1:4] {
x1 = data[i][1]
y1 = data[i][2]
x2 = data[i+1][1]
y2 = data[i+1][2]
j=j+1
set arrow j from first x3,y3 to first x2,y1
j = j+1
set arrow j from first x2,y1, to first x2, y2
x3 = x2
y3 = y2
}
where data would be some array or matrix representation of data file. I think I could live with the ability to read the datafile into, say, 4 separate arrays. But i have not found anything even close to this.
Note that vector plotting is probably not general enough to cover this case.

If I interpreted your intention correctly, you want to plot a stepwise function.
There is a gnuplot plotting style with steps (check help steps).
However, if you want arrows it's probably getting a bit more complicated and you have to use the style with vectors or with arrows, check help vectors and help arrows.
Script:
### plotting steps with and without arrowheads
reset session
$Data <<EOD
# x y
1 2
2 5
3 1
4 5
5 6
EOD
set offsets 1,1,1,1
set key noautotitle
set multiplot layout 2,1
plot $Data u 1:2 w steps lw 2 lc "web-green"
plot x1=y1=NaN $Data u (x0=x1,x1=$1,x0):(y0=y1,y1=$2,y0):(x1-x0):(0) \
w vec head lw 2 lc "red", \
x1=y1=NaN '' u (x0=x1,x1=$1):(y0=y1,y1=$2): (0): (y0-y1) \
w vec backhead lw 2 lc "red"
unset multiplot
### end of script
Result:

Related

Is there any way to visualize the field on adaptive mesh with gnuplot?

I am a beginner in gnuplot. Recently I tried to visualize a pressure field on adaptive mesh.
Firstly I got the coordinates of nodes and center of the cell and the pressure value at the center of the cell.
And, I found something difficult to deal with. That is the coordinates in x and y directions are not regular, which made me feel hard in preparing the format of source data. For regular and equal rectangular case, I can do something just like x-y-z format. But is there any successful case in adaptive mesh?
I understand that you have some x,y,z data which is in no regular grid (well, your adaptive mesh).
I'm not fully sure whether this is what you are looking for, but
gnuplot can grid the data for you, i.e. inter-/extrapolating your data within a regular grid and then plot it.
Check help dgrid3d.
Code:
### grid data
reset session
# create some test data
set print $Data
do for [i=1:200] {
x = rand(0)*100-50
y = rand(0)*100-50
z = sin(x/15)*sin(y/15)
print sprintf("%g %g %g",x,y,z)
}
set print
set view equal xyz
set view map
set multiplot layout 1,2
set title "Original data with no regular grid"
unset dgrid3d
splot $Data u 1:2:3 w p pt 7 lc palette notitle
set title "Gridded data"
set dgrid3d 100,100 qnorm 2
splot $Data u 1:2:3 w pm3d
unset multiplot
### end of code
Result:
If you have the size of each cell, you can use the "boxxyerror" plotting style. Let xdelta and ydelta be half the size of a cell along the x-axis and y-axis.
Script:
$datablock <<EOD
# x y xdelta ydelta pressure
1 1 1 1 0
3 1 1 1 1
1 3 1 1 1
3 3 1 1 3
2 6 2 2 4
6 2 2 2 4
6 6 2 2 5
4 12 4 4 6
12 4 4 4 6
12 12 4 4 7
EOD
set xrange [-2:18]
set yrange [-2:18]
set palette maxcolors 14
set style fill solid 1 border lc black
plot $datablock using 1:2:3:4:5 with boxxyerror fc palette title "mesh", \
$datablock using 1:2 with points pt 7 lc rgb "gray30" title "point"
pause -1
In this script, 5-column data (x, y, xdelta, ydelta, pressure) is given for "boxxyerror" plot. To colorize the cells, the option "fc palette" is required.
Result:
I hope this figure is what you are looking for.
Thanks.

How to interpolate data with Gnuplot for further calculations

I am (somehow) familiar with the smooth/interpolation techniques in Gnuplot. It seems to me that these interpolations work only for plotting the interpolated values. However, I need the interpolated values for further calculations.
A simple example may illustrate this:
Let’s say we are selling a specific item on four days and have the number of sales stored in input_numbers.dat:
# days | number_of_sold_items
1 4
2 70
3 80
4 1
Now, I want to plot my income for each day. But the relation between the price per item and the number of sold items is not a simple linear relation, but something complicate which is only known for a few examples – stored in input_price.dat:
# number_of_sold_items | price_per_item
1 5.00
3 4.10
10 3.80
100 3.00
How can I do something like this (pseudocode):
make INTERPOLATED_PRICE(x) using "input_price.dat"
plot "input_numbers.dat" using 1:($2*INTERPOLATED_PRICE($2))
I can do it by fitting but it is not what I want. The relation of the data is too complicated.
P.S.: I know that the price per item vs the number of items in such an example is more like a step-like function and not smooth. This is just an example for some interpolation in general.
It’s hard to prove the non-existence of something but I am pretty confident that this cannot be done with Gnuplot alone, as:
I am under the illusion to be sufficiently familiar with Gnuplot that I would know about it if it existed.
I cannot find anything about such a feature.
It would completely go against Gnuplot’s paradigm to be a one-purpose tool for plotting (fitting is already borderline) and not to feature data processing.
Gnuplot can do something like this:
text = "%f*x + %f"
a = 2
b = 10
eval("f(x) = ".sprintf(text,a,b))
set grid x y
plot f(x)
which basically means that complicated functions can be defined dynamically: The sprintf command converts the text "%f*x + %f" into "2.0*x + 10", the dot operator . concatenates the strings "f(x) = " and "2.0*x + 10", and the eval command defines the function f(x) = 2.0*x + 10. The result can be plotted and gives the expected diagram:
This behavior can be used for creating a piecewise interpolation function as follows:
ip_file = "input_price.dat"
stats ip_file nooutput
n = STATS_records - 1
xmin = STATS_min_x
xmax = STATS_max_x
ip_f = sprintf("x < %f ? NaN : ", xmin)
f(x) = a*x + b # Make a linear interpolation from point to point.
do for [i=0:n-1] {
set xrange [xmin:xmax]
stats ip_file every ::i::(i+1) nooutput
xmintemp = STATS_min_x
xmaxtemp = STATS_max_x
set xrange [xmintemp:xmaxtemp]
a = 1
b = 1
fit f(x) ip_file every ::i::(i+1) via a, b
ip_f = ip_f.sprintf("x < %f ? %f * x + %f : ", xmaxtemp, a, b)
}
ip_f = ip_f."NaN"
print ip_f # The analytical form of the interpolation function.
eval("ip(x) = ".ip_f)
set samples 1000
#set xrange [xmin:xmax]
#plot ip(x) # Plot the interpolation function.
unset xrange
plot "input_numbers.dat" using 1:($2*ip($2)) w lp
The every in combination with stats and fit limits the range to two successive datapoints, see help stats and help every. The ternary operator ?: defines the interpolation function section by section, see help ternary.
This is the resulting analytical form of the interpolation function (after some formatting):
x < 1.000000 ? NaN
: x < 3.000000 ? -0.450000 * x + 5.450000
: x < 10.000000 ? -0.042857 * x + 4.228571
: x < 100.000000 ? -0.008889 * x + 3.888889
: NaN
This is the resulting interpolation function (plotted by plot ip(x)):
This is the resulting plot using the interpolation function in another calculation (plot "input_numbers.dat" using 1:($2*ip($2))):
I don't know the limits on how many ternary operators you can nest and on how long a string or a function definition can be, ...
Tested with Gnuplot 5.0 on Debian Jessie.
Linear interpolation is not available, but how about this:
set xr [0:10]
set sample 21
# define an inline example dataset
$dat << EOD
0 1
2 2
4 4
6 5
8 4
10 3
EOD
# plot interpolated data to another inline dataset
set table $interp
plot $dat us 1:2 with table smooth cspline
unset table
plot $dat w lp, $interp w lp
As I understand your question, you are not looking for interpolation but for a lookup-table, i.e. depending on the number of sold items you have a different price.
What you can do with gnuplot is:
(mis)using stats to create a lookup-string (check help stats)
(mis)using sum to create a lookup-function (check help sum)
Comment: I assume it will be a difference if you for example sell 3 times 1 item on a single day or 1 time 3 items on a single day, because of the graduation of prices.
So, I would suggest a different input data format, i.e. with a date.
(However, not yet implemented in the example below, but can be done. Then, you can make use of the smooth frequency option.) Some data format, e.g. like this:
# date sold_items
2022-09-01 1
2022-09-01 1
2022-09-01 1
2022-09-02 3
Script: (works with gnuplot 5.0.0, Jan. 2015)
### implement lookup table
reset session
$SALES <<EOD
# days | number_of_sold_items
1 4
2 70
3 80
4 1
EOD
$PRICE <<EOD
# number_of_sold_items | price_per_item
1 5.00
3 4.10
10 3.80
100 3.00
EOD
LookupStr = ''
stats $PRICE u (LookupStr=LookupStr.sprintf(" %g %g",$1,$2)) nooutput
Lookup(v) = (p0=NaN, sum [i=1:words(LookupStr)/2] (v>=real(word(LookupStr,i*2-1)) ? \
p0=real(word(LookupStr,i*2)) : 0), p0)
set grid x,y
set key noautotitle
set multiplot
plot $SALES u 1:2 w lp pt 6 lc "dark-grey" ti "sold items", \
'' u 1:($2*Lookup($2)) w lp pt 7 lc "red" ti "total income"
# price table as graph inset
set origin x0=0.41, y0=0.42
set size sx=0.30, sy=0.28
set obj 1 rect from screen x0,y0 to screen x0+sx,y0+sy fs solid noborder lc "white" behind
set margins 0,0,0,0
set xrange [:150]
set yrange [2.5:5.5]
set xlabel "pieces" offset 0,0.5
set ylabel "price / piece"
set logscale x
plot $PRICE u 1:2 w steps lc "blue", \
'' u 1:2 w p pt 7 lc "blue"
unset multiplot
### end of script
Result:

How to remove line between "jumping" values, in gnuplot?

I would like to draw a line with plots that contain "jumping" values.
Here is an example: when we have plots of sin(x) for several cycles and plot it, unrealistic line will appear that go across from right to left (as shown in following figure).
One idea to avoid this might be using with linespoints (link), but I want to draw it without revising the original data file.
Do we have simple and robust solution for this problem?
Assuming that you are plotting a function, that is, for each x value there exists one and only one corresponding y value, the easiest way to achieve what you want is to use the smooth unique option. This smoothing routine will make the data monotonic in x, then plot it. When several y values exist for the same x value, the average will be used.
Example:
Data file:
0.5 0.5
1.0 1.5
1.5 0.5
0.5 0.5
Plotting without smoothing:
set xrange [0:2]
set yrange [0:2]
plot "data" w l
With smoothing:
plot "data" smooth unique
Edit: points are lost if this solution is used, so I suggest to improve my answer.
Here can be applied "conditional plotting". Suppose we have a file like this:
1 2
2 5
3 3
1 2
2 5
3 3
i.e. there is a backline between 3rd and 4th point.
plot "tmp.dat" u 1:2
Find minimum x value:
stats "tmp.dat" u 1:2
prev=STATS_min_x
Or find first x value:
prev=system("awk 'FNR == 1 {print $1}' tmp.dat")
Plot the line if current x value is greater than previous, or don't plot if it's less:
plot "tmp.dat" u ($0==0? prev:($1>prev? $1:1/0), prev=$1):2 w l
OK, it's not impossible, but the following is a ghastly hack. I really advise you add an empty line in your dataset at the breaks.
$dat << EOD
1 1
2 2
3 3
1 5
2 6
3 7
1 8
2 9
3 10
EOD
plot for [i=0:3] $dat us \
($0==0?j=0:j=j,llx=lx,lx=$1,llx>lx?j=j+1:j=j,i==j?$1:NaN):2 w lp notit
This plots your dataset three times (acually four, there is a small error in there. I guess i have to initialise all variables), counts how often the abscissa values "jump", and only plots datapoints if this counter j is equal to the plot counter i.
Check the help on the serial evaluation operator "a, b" and the ternary operator "a?b:c"
If you have data in a repetitive x-range where the corresponding y-values do not change, then #Miguel's smooth unique solution is certainly the easiest.
In a more general case, what if the x-range is repetitive but y-values are changing, e.g. like a noisy sin(x)?
Then compare two consecutive x-values x0 and x1, if x0>x1 then you have a "jump" and make the linecolor fully transparent, i.e. invisible, e.g. 0xff123456 (scheme 0xaarrggbb, check help colorspec). The same "trick" can be used when you want to interrupt a dataline which has a certain forward "jump" (see https://stackoverflow.com/a/72535613/7295599).
Minimal solution:
plot x1=NaN $Data u 1:2:(x0=x1,x1=$1,x0>x1?0xff123456:0x0000ff) w l lc rgb var
Script:
### plot "folded" data without connecting lines
reset session
# create some test data
set table $Data
plot [0:2*pi] for [i=1:4] '+' u 1:(sin(x)+rand(0)*0.5) w table
unset table
set xrange[0:2*pi]
set key noautotitle
set multiplot layout 1,2
plot $Data u 1:2 w l lc "red" ti "data as is"
plot x1=NaN $Data u 1:2:(x0=x1,x1=$1,x0>x1?0xff123456:0x0000ff) \
w l lc rgb var ti "\n\n\"Jumps\" removed\nwithout changing\ninput data"
unset multiplot
### end of script
Result:

Fill several sections below a curve of data in Gnuplot

I have a set of points "data" defining a curve that I want to plot with bezier smooth.
So I want to fill the area below that curve between some pairs of x values.
If I only had one pair of x values it's not that difficult because I define a new set of data and plot it with filledcu. Example:
The problem is that I want to do that several times in the same plot.
Edit: Minimal working example:
#!/usr/bin/gnuplot
set terminal wxt enhanced font 'Verdana,12'
set style fill transparent solid 0.35 noborder
plot 'data' using 1:2 smooth sbezier with lines ls 1
pause -1
Where the structure of 'data' is:
x_point y_point
And I realized that my problem is that in fact I can't fill not even one curve, it seems to be filled because the slope is almost constant there.
To fill parts below a curve, you must use the filledcurves style. With the option x1 you fill the part between the curve and the x-axis.
In order to fill only parts of the curve, you must filter your data, i.e. give the x-values a value of 1/0 (invalid data point) if they are outside of the desired range, and the correct value from the data file otherwise. At the end you plot the curve itself:
set style fill transparent solid 0.35 noborder
filter(x,min,max) = (x > min && x < max) ? x : 1/0
plot 'data' using (filter($1, -1, -0.5)):2 with filledcurves x1 lt 1 notitle,\
'' using (filter($1, 0.2, 0.8)):2 with filledcurves x1 lt 1 notitle,\
'' using 1:2 with lines lw 3 lt 1 title 'curve'
This fills the range [-1:0.5] and [0.2:0.8].
To give a working example, I use the special filename +:
set samples 100
set xrange [-2:2]
f(x) = -x**2 + 4
set linetype 1 lc rgb '#A3001E'
set style fill transparent solid 0.35 noborder
filter(x,min,max) = (x > min && x < max) ? x : 1/0
plot '+' using (filter($1, -1, -0.5)):(f($1)) with filledcurves x1 lt 1 notitle,\
'' using (filter($1, 0.2, 0.8)):(f($1)) with filledcurves x1 lt 1 notitle,\
'' using 1:(f($1)) with lines lw 3 lt 1 title 'curve'
With the result (with 4.6.4):
If you must use some kind of smoothing, the filter may affect the data curve differently, depending on the filtered part. You can first write the smoothed data to a temporary file and then use this for 'normal' plotting:
set table 'data-smoothed'
plot 'data' using 1:2 smooth bezier
unset table
set style fill transparent solid 0.35 noborder
filter(x,min,max) = (x > min && x < max) ? x : 1/0
plot 'data-smoothed' using (filter($1, -1, -0.5)):2 with filledcurves x1 lt 1 notitle,\
'' using (filter($1, 0.2, 0.8)):2 with filledcurves x1 lt 1 notitle,\
'' using 1:2 with lines lw 3 lt 1 title 'curve'

Plot different set of Y for a single set of X

I have a single set of value for X. Otherwise I have three set of Y values let it be y1,y3,y4.
Now, I am unable to plot X versus y1,y3,y4 in the same plot in GNUPLOT.
Can anyone help me out to solve this problem?
If you have your data in a file, assuming data.dat
X1 Y1 Y2 Y3
1 0.1 0.2 0.3
... ... ... ...
you can plot Y1, Y2 and Y3 versus X with
plot 'data.dat' using 1:2, '' u 1:3, '' u 1:4
u being the shortcut for using.
If all y values are of the same units, then one y axis would suffice. If not, you can plot x against up to 2 y-axes (here's how).
You use the 'axes' option to plot. Here is an example drawing 2 plots using the same X values:
set xrange [-4:4]
plot cos(x) axes x1y1 title "cos" with linespoints lt 1 pt 7 ps 0.0,\
sin(x) axes x1y2 title "sin" with linespoints lt 2 pt 8 ps 0.0
pause mouse any "Click the mouse or hit any key to terminate"
If you run that with gnuplot it should look like the following image

Resources