I'm trying to figure out how to do fence plots in gnuplot, but I'm having a hard time understanding what's going on in the examples I find on the internet.
I have a (varying) number of data sets from different points in time in my simulation, in a datafile organized as a matrix of values1:
t1 x11 y11 // indices here indicate that (x1,y1) are a data point which
t1 x21 y21 // I'd plot on a regular 2D plot for this timestep, with the
... // additional index noting which time step the values are for.
t1 xN1 yN1
[blank line]
t2 x12 y12
t2 x22 y22
...
t2 xN2 yN2
[etc...]
tM xNM yNM
I want to plot this with one fence for each time value. I can plot do simply splot 'data.txt' and get something which is quite similar to what I want - + markers along the "top edges" of the fences, with time on x axis, x-data on y axis and y-data on z axis. However, if I add something like w lines to the splot command I just get a surface with all the data series connected.
I've tried to adapt the examples from the demo script collection (about halfway down), but they both rely on a dummy variable, and I can't figure out how to combine that with my data series. I've found some other exampels as well, but they are all quite elaborate and I don't understand what they do at all.
What is a good way to create fence plots from data using gnuplot?
1 If it's necessary, it is possible to change this - I am in control of the code that generates the data. It's a hassle, though...
This does require a bit of a change to the data unfortunately. The change is pretty minor though and could probably be handled with a simple awk1,2 script:
Here's a copy/paste of my interactive gnuplot session:
gnuplot> !cat test.dat
1 2 3
1 2 0
1 3 4
1 3 0
1 4 5
1 4 0
2 2 3
2 2 0
2 3 4
2 3 0
2 4 5
2 4 0
3 2 3
3 2 0
3 3 4
3 3 0
3 4 5
3 4 0
!
gnuplot> splot 'test.dat' u 1:2:3 w lines
The thing to note here is that there are 2 blank lines between "fences" and each x,y data point appears twice with a blank line afterward. The second time it appears, the z-coordinate is 0.
To get each fence to have a different color:
gnuplot> splot for [i=0:3] 'test.dat' index i u 1:2:3 w lines
The awk script can even be done inline:
splot "< awk {...} datafile"
But that can get a little tricky with quoting (to include a single quote in a single quoted string, you double it) ...
AWKCMD='awk ''{if(!NF){print ""}else if(index($0,"#")!=1){printf "%s %s %s\n%s %s 0\n\n", $1,$2,$3,$1,$2}}'' '
splot '<'.AWKCMD.'datafile.dat' u 1:2:3 w lines
As far as efficiency is concerned, I'm believe that the iteration I used above will call the awk command for each time it iterates. The workaround here is to pull the color from the index number:
splot '<'.AWKCMD.' test.dat' u 1:2:3:(column(-2)) w l lc variable
I believe that this will only do the awk command once as desired so with only a million entries it should still respond relatively quickly.
1awk '{if(!NF){print ""}else{printf "%s %s %s\n%s %s 0\n\n", $1,$2,$3,$1,$2}}' test.dat
2awk '{if(!NF){print ""}else if(index($0,"#")!=1){printf "%s %s %s\n%s %s 0\n\n", $1,$2,$3,$1,$2}}' test.dat (version which ignores comments)
Related
I have two different .txt files with x and y coordinates of equal number of samples in both.
File 1
x y
1 2
5 4
4 6
File 2
x y
5 6
3 4
2 3
I want to connect each of these points inFile 1 with the corresponding points in File 2. I know to draw an arrow between two points it is
set arrow from (x,y) to (c,d)
But how do I get the coordinates of these points from two different files to draw connecting lines/ arrows?
Something like this:
plot "< paste file1.data file2.data" with vectors
Edit: I came across this old answer and was amazed (actually shocked) how inefficient and complicated solutions I suggested.
A much better approach: Since gnuplot 5.2.0 you can index datablocks. Prerequisite is that your data is in a datablock already. See here: gnuplot: load datafile 1:1 into datablock.
If you can be sure that your data files have identical number of lines, you can "mimic" the Linux paste command using a gnuplot-only, hence, platform-independent solution.
Basically, you join the two lines having the corresponding index after removing the last character (newline) of each line.
Script: (works with gnuplot>=5.2.0, Sept. 2017)
### plot arrows across two files, i.e. merge lines with gnuplot only
reset session
$Data1 <<EOD
File 1
x y
1 2
5 4
4 6
EOD
$Data2 <<EOD
File 2
x y
5 6
3 4
2 3
EOD
set print $Combined
do for [i=1:|$Data1|] {
print $Data1[i][1:strlen($Data1[i])-1].' '.$Data2[i][1:strlen($Data2[i])-1]
}
set print
plot $Combined u 1:2:($3-$1):($4-$2) w vec lc rgb "red"
### end of script
Result:
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:
This question is an extension of a previously answered one.
I want a graph using gnuplot with two data in mixed scheme. This is a data consisting of 3 columns:
#x y1 y2
1 0 1
2 0 1
3 0 1
4 0 1
5 0 1
6 0 1
7 0 1
8 0 1
9 0.1 1.2
10 0.1 1.23
What I want is that one line draws without seam. e.g.
From x=1 to x=5, use y1 value, then from x=6 to x=10 use y2 value.
And, all the points are connected with one single line. Does any body know how to make it with simple gnuplot command?
One more related question is there. If the data in 3rd column is separated to other file, say sheme2.dat, how can i draw a same graph with pure gnuplot commands?
If you absolutely want to use pure Gnuplot you can cheat a little with variables(, but I suggest you to rethink the problem):
set term unknown
plot 'sheme.dat' u 1:2 every ::::4
x0=GPVAL_DATA_X_MAX
y0=GPVAL_DATA_Y_MAX
plot 'sheme2.dat' u 1:2 every ::5
x1=GPVAL_DATA_X_MIN
y1=GPVAL_DATA_Y_MIN
set term wxt
# or png or qt or...
set arrow 1 from x0,y0 to x1,y1 nohead lc 1
plot 'sheme.dat' u 1:2 every ::::4 w l lc 1
replot 'sheme2.dat' u 1:2 every ::5 w l lc 1
This is a tricky way to connect the last and first data points with an arrow, but the conditions of this problem are so specific.
For a file with columns 1/2 and a second file with column 3 you can use paste as well:
plot "<paste -d ' ' data1.dat data2.dat" using 1:($1<=5?$2:$3) with lines
With the -d ' ' meaning that data1.dat and data2.dat will be separated by a single space.
For three columns in one file (as answered before)
plot "data.dat" using 1:($1<=5?$2:$3) with lines
I have a file where my data are separated into several indexes. I would like to plot some or all of the indexes as stacked filledcurves by adding the values of selected previous indexes to the values of the current index. I could not find a way to use the sum function as in the case of data arranged as columns in a single index (as in this question), even using the pseudocolumn(-2) as the index number.
Important note: every index as strictly identical sets of x values, only the y values differ.
Is there a way to do something like
p 'data.dat' index (sum(ind=1,3,4,5) ind) u 1:2 w filledcurve x1 t 'Sum(1,3,4,5)', '' index (sum(ind=1,2,5) ind) u 1:2 w filledcurve x1 t 'Sum(1,2,5)'
within gnuplot or do I have to resort to a script (maybe a variation of the one in this answer)?
You can do this with some help outside gnuplot (invoked within gnuplot). Imagine you have the following data file with 4 indices (0 to 3):
1 2
2 3
1 5
2 5
1 0
2 3
1 4
2 3
Now say that we want to sum 1 and 2 and 0 and 3. The first sum should return:
1 5
2 8
while the second sum should return
1 6
2 6
We can select the blocks we want using set table:
set table "sum1"
plot for [i in "1 2"] "data3" index 0+i pt 7 not
set table "sum2"
plot for [i in "0 3"] "data3" index 0+i pt 7 not
unset table
Now use sed piping to remove the empty lines and smooth freq to sum for equal x values:
plot "< sed '/^\s*$/d' sum1" smooth freq t "sum1", \
"< sed '/^\s*$/d' sum2" smooth freq t "sum2"
Although you may be able to do it using functions and variables of gnuplot 4.4+, this won't be very efficient as you want to perform an operation on several distant lines in your file, which is in fact an operation on arrays. Gnuplot is not meant for this, the datafiles should have a structure reasonably close to what you want to plot. I advise that you try to produce a file with such a structure, e.g. have the values you want to sum on the same line in different columns.
My data file is as-
2 3 4 1 5 2 0 3 4 5 3 2 0 3 4 0 5 4 3 2 3 4 4 0 5 3 2 3 4 5 1 3 4
My requirement is to plot normal PDF in gnuplot.
I could do it by calculating f(x)
f(x) = \frac{1}{\sqrt{2\pi\sigma^2} } e^{ -\frac{(x-\mu)^2}{2\sigma^2} }
for each x using shell script.
Then I plot it in gnuplot using the command-
plot 'ifile.txt' using 1:2 with lines
But whether is it possible to plot directly in gnuplot?
gnuplot provides a number of processing options under the smooth keyword (try typing help smooth for more info). For your specific case, I would recommend a fit though.
First, note that your data points are in a row, you need to convert it to columns for gnuplot to use it. You can do it with awk:
awk '{for (i=1;i<=NF;i++) print $i}' datafile
which can be invoked from within gnuplot:
plot "< awk '{for (i=1;i<=NF;i++) print $i}' datafile" ...
Now assume that datafile has the right format for simplicity.
You can use the smooth frequency option to see how many occurrences of each value you have:
plot "datafile" u 1:(1.) smooth frequency w lp pt 7
To get the normalized distribution, you divide by the number of values. This can be done automatically within gnuplot with stats:
stats "datafile"
This will store the number of values in variable STATS_records, which in you case has value 33:
gnuplot> print STATS_records
33.0
So the normalized distribution (the probability of getting a value at x) is:
plot "datafile" u 1:(1./STATS_records) smooth frequency w lp pt 7
As you can see, your distribution doesn't really look like a normal distribution, but anyway, let's go on. Create a Gaussian for fitting and fit to your data, and plot it. You need to fit to the probability, rather than to the data itself. To do so, we plot to a table to extract the data generated by smooth frequency:
# Non-normalized Gaussian
f(x)= A * exp(-(x-x0)**2/2./sigma**2)
# Save probability data to table
set table "probability"
plot "datafile" u 1:(1./STATS_records) smooth frequency not
unset table
# Fit the Gaussian to the data, exclude points from table with grep
fit f(x) "< grep -v 'u' probability" via x0, sigma, A
# Normalize the gaussian
g(x) = 1./sqrt(2.*pi*sigma**2) * f(x) / A
# Plot
plot "datafile" u 1:(1./STATS_records) smooth frequency w lp pt 7, g(x)
set table generates some points which you should exclude, that's why I used grep to filter the file. Also, the Gaussian needs to be normalized after the fitting is done with a variable amplitude. If you want to retrieve the fitting parameters:
gnuplot> print x0, sigma
3.40584703189268 1.76237558717934
Finally note that if the spacing between data points is not homogeneous, e.g. instead of x = 0, 1, 2, 3 ... you have values at x = 0, 0.1, 0.5, 3, 3.2 ... then you'll need to use a different way to do this, for example defining bins of regular size to group data points.