Broken z-axis in splot - gnuplot

There are plenty of resources online to teach you how to draw 2d plots with broken axis, e.g. http://www.phyast.pitt.edu/~zov1/. Basically what strategy used was to draw two plots using multiplot mode, and combine them together.
However, what I wished to do is to break the Z axis, consider these two surfaces:
Because of the large energy gap between two surfaces, the ground energy surface is almost "flat" in this plot, yet if we plot the ground energy surface alone, we can see it is not "flat" at all:
Is there a way of breaking the Z axis to make Gnuplot display more details of the surface? multiplot does not work here because it is a 3d plot.

You can shift the upper surface downwards and relabel the z tics manually. Take this figure as an example:
Let's work out some gnuplot magic:
# Make sure that there are no data points exactly at the corners
# of the xy plane (it affects the vertical borders)
set xrange [-1.001:1.001]
set yrange [-1.001:1.001]
zmin = -2
zmax = 5
dz = zmax - zmin
set zrange [zmin:zmax]
# Remove vertical borders
set border 15
# Some functions to plot
f(x,y)=x**2+y**2+10.
g(x,y)=-x**2-y**2
# Draw vertical borders by hand leaving empty space where the
# axis is broken. I have used variables zmin etc. for transparency
set for [i=-1:1:2] for [j=-1:1:2] arrow \
from i,j,zmin-dz*0.5 to i,j,1 lw 1 nohead
set for [i=-1:1:2] for [j=-1:1:2] arrow \
from i,j,2 to i,j,zmax lw 1 nohead
# Draw zig-zag line to denote broken axis
set for [i=-1:1:2] for [j=-1:1:2] arrow \
from i,j,1 to i-0.05,j,1+0.25 lw 1 nohead
set for [i=-1:1:2] for [j=-1:1:2] arrow \
from i-0.05,j,1+0.25 to i+0.05,j,1+0.75 lw 1 nohead
set for [i=-1:1:2] for [j=-1:1:2] arrow \
from i+0.05,j,1+0.75 to i,j,2 lw 1 nohead
# Add ztics by hand. Use "for" if you have many tics
set ztics (-2, 0)
# We print the z value - 7, which is the amount we are shifting the
# upper surface
set ztics add ("10" 3, "12" 5)
# Plot shifting the surface
splot f(x,y)-7, g(x,y)
Note that the new borders defined with set arrow will be drawn behind the surface. If you want a particular one to be in the front, then take it out of the set for loop and add the front keyword to it.

Related

multiplot of isolines and points creates two axis frames

I've created a multiplot containing an isoline plot f(x,y) and a point, (0,0) on the zero level set of the isoline plot. Unfortunately the point plot appears to create a second axis frames as shown below
f(x,y)=2*x**2 - x + 2*y**2 - y - 2
set multiplot
set xrange [-3:3]
set yrange [-3:3]
set isosamples 250
set contour
unset surface
set view map
set key out
set cntrparam levels incremental 0,1,5
splot f(x,y)
plot "< echo '1 1'"
set nomultiplot
What can I do to solve this problem?
Update
A bit more context. In the larger problem I am using multiplot to superimpose two isoline plots, as hinted below. Both isoline plots share a common axis frame.
...
set cntrparam levels incremental 0,1,5
splot f(x,y)
set cntrparam levels discrete 0
g(x,y)=(x - 1)**2 + y**2 - 1
splot g(x,y)
Unless there are additional requirements that mandate use of multiplot, the simplest solution is to draw your single point as a label instead. The label comes with a point for free. You only want the point, so the label text is an empty string.
If this was a proxy question for a more complicated requirement, please edit the question to give more detail.
f(x,y)=2*x**2 - x + 2*y**2 - y - 2
set label 1 "" at 1,1,0 point pt 7 lc "blue"
set xrange [-3:3]
set yrange [-3:3]
set isosamples 250
set contour
unset surface
set view map
set key out
set cntrparam levels incremental 0,1,5
splot f(x,y) with lines
Edit
The multiplot version:
f(x,y)=2*x**2 - x + 2*y**2 - y - 2
g(x,y)=(x - 1)**2 + y**2 - 1
set label 1 "" at 1,1,0 point pt 7 lc "blue"
set xrange [-3:3]
set yrange [-3:3]
set isosamples 250
set contour
unset surface
set view map
set multiplot
set cntrparam levels incremental 0,1,5
set key out top
splot f(x,y) with lines
set cntrparam levels discrete 0
set key out bottom
splot g(x,y) with lines
unset multiplot
This would be my suggestion:
you don't need multiplot (I would use it only if absolute necessary)
plot the contour data into a table and instead of 3D splot and "set view map" use a 2D plot. Because after set contour you cannot plot a single datapoint as non-grid data.
there are a lot of ways to plot a single data point. My preference would be without system call.
you might want to set size ratio -1, then a circle appears as a circle
Update:
Here is the extended example after you mentioned your second contour function g(x,y).
Just to mention the differences between a multiplot solution and a single plot solution:
Script (multiplot): (with fixed margins and legend box positions relative to screen)
easy handling of the legend entries and boxes (but manual positioning if more than 2)
you need to set fixed margins to get reliable overlap independent of the legend. At least, fixed top/bottom margins are enough if you set size ratio -1. Additionally, you can set the legend to a fixed position relative to the screen.
I recommended to unset border, tics and labels for the second plot, otherwise these elements get a slight "bold" appearance if you plot them several times on top of each other (you will notice if you compare the numbers on Ethan's first and second plot)
zooming is not possible with multiplot in interactive terminals, e.g. wxt, qt
requires another multiplot if you want to plot single/multiple points from another file/datablock
so far, I haven't found out how to freely control the color of the contour lines. But I'm sure there is a way.
### plot several contour plots and add a single point (multiplot)
reset session
f(x,y) = 2*x**2 - x + 2*y**2 - y - 2
g(x,y) = (x-1)**2 + y**2 - 1
set xlabel "x-label"
set xrange [-3:3]
set ylabel "y-label"
set yrange [-3:3]
set samples 100
set isosamples 100
set contour
unset surface
set view map
set size ratio -1
set multiplot
xPos = 0.95
set key at screen xPos,0.9 title "Main title" font ",12"
set bmargin screen 0.15
set tmargin screen 0.92
set cntrparam levels incremental 0,1,5
splot f(x,y) w l
set cntrparam levels discrete 0
unset border
unset tics
unset xlabel
unset ylabel
set key at screen xPos,0.5 notitle
splot g(x,y) w l
set key at screen xPos,0.35 notitle
plot '+' u (1):(1) every ::::0 w p pt 7 lc "red" ti "You are here"
unset multiplot
### end of script
Result:
Script (single plot):
zooming possible in interactive terminals, e.g. wxt, qt
extra tables for contours
a bit more effort with the legend (but auto positioning)
easy customization of contour line color
For the single plot, the extraction of the contour level labels is a bit tricky and needs some explanations.
The contour data in the datablocks looks like this:
# Surface 0 of 1 surfaces
# Curve title: "f(x,y)"
# Contour 0, label: 5
-1 -1.15789 5
-1.15789 -1 5
-1.54386 -0.333333 5
...
-0.333333 -1.54386 5
-1 -1.15789 5
# Contour 1, label: 4
-0.333333 -1.38596 4
...
The data is separated by two empty lines. So, you can address the subblocks by using index (check help index).
So, now, after skipping the first 5 lines, you need to extract the contour label value from each the first line (header) of each (sub)block from the 5th column. For this, you need to tell gnuplot that # is not a comment character anymore (check help datafile commentschars).
Furthermore, the legend entry for the functions names are realized by keyentries (check help keyentry) with a fully transparent point according to the color scheme 0xaarrggbb where alpha is `0xff´.
### plot several contour plots and add a single point (single plot)
reset session
f(x,y) = 2*x**2 - x + 2*y**2 - y - 2
g(x,y) = (x-1)**2 + y**2 - 1
set xlabel "x-label"
set xrange [-3:3]
set ylabel "y-label"
set yrange [-3:3]
set samples 100
set isosamples 100
set contour
unset surface
set table $ContourF
set cntrparam levels incremental 0,1,5
splot f(x,y)
set table $ContourG
set cntrparam levels discrete 0
splot g(x,y)
unset table
set size ratio -1
set key noautotitle left at graph 1.05,1 title "Main title" font ",12"
set datafile commentschar ''
plot keyentry w p lc rgb 0xff123456 ti "f(x,y)", \
for [i=0:*] $ContourF u 1:2:3 skip 5 index i w l lc i ti columnhead(5), \
keyentry w p lc rgb 0xff123456 ti "g(x,y)", \
for [i=0:*] $ContourG u 1:2:3 skip 5 index i w l lc "blue" ti columnhead(5), \
'+' u (1):(1) every ::::0 w p pt 7 lc "red" ti "You are here"
### end of script
Result:

Plotting intersecting lines in GNUplot

I haven't been able to find any example of what I'm trying to do in GNUplot from raking docs and demos.
Essentially I want to plot the Blue, Green, and Red lines I manually drew on this output (for demonstration) at the 10/50/90% marks.
EDIT: For clarity, I'm looking to determine where the distribution lines hit the cumulative distribution at 0.1/0.5/0.9 to know which co-ordinates to draw the lines at. Thanks!
set terminal png size 1600,800 font "Consolas" 16
set output "test.png"
set title "PDF and CDF - 1000 Simulations"
set grid y2
set ylabel "Date Probability"
set y2range [0:1.00]
set y2tics 0.1
set y2label "Cumulative Distribution"
set xtics rotate by 90 offset 0,-5
set bmargin 6
plot "data.txt" using 1:3:xtic(2) notitle with boxes axes x1y1,'' using 1:4 notitle with linespoints axes x1y2
Depending on the number of points in your cumulative data curve you might need interpolation. The following example is chosen such that no original data point will be at your levels 10%, 50%, 90%. If your data is not steadily increasing, it will take the last value which matches your level(s).
The procedure is as follows:
plot your data to a dummy table.
check when Level is between to successive y-values (y0,y1).
remember the interpolated x-value in xp.
draw arrows from the borders of the graph to the point (xp,Level) (or instead use the partly outside rectangle "trick" from #Ethan).
Code:
### linear interpolation of data
reset session
set colorsequence classic
set key left
# create some dummy data
set sample 10
set table $Data
plot [-2:2] '+' u 1:(norm(x)) with table
unset table
Interpolate(yi) = x0 + (x1-x0)*(yi-y0)/(y1-y0)
Levels = "0.1 0.5 0.9"
do for [i=1:words(Levels)] {
Level = word(Levels,i)
x0 = x1 = y0 = y1 = NaN
set table $Dummy
plot $Data u (x0=x1,x1=$1,y0=y1,y1=$2, (y0<=Level && Level<=y1)? (xp=Interpolate(Level)):NaN ): (Level) w table
unset table
set arrow i*2 from xp, graph 0 to xp,Level nohead lc i
set arrow i*2+1 from xp,Level to graph 1,Level nohead lc i
}
plot $Data u 1:2 w lp pt 7 lc 0 t "Original data"
### end code
Result:
It is not clear if you are asking how to find the x-coordinates at which your cumulative distribution line hits 0.1, 0.5, 0.9 (hard to do so I will leave that for now) or asking how to draw the lines once you know those x values. The latter part is easy. Think of the lines you want to draw as the unclipped portion of a rectangle that extends off the plot to the lower right:
set object 1 rectangle from x1, 0.1 to graph 2, -2 fillstyle empty border lc "blue"
set object 2 rectangle from x2, 0.1 to graph 2, -2 fillstyle empty border lc "green"
set object 3 rectangle from x3, 0.1 to graph 2, -2 fillstyle empty border lc "red"
plot ...

Gnuplot: fill area bounded by curves left/right?

I have a dataset that defines two curves, and I want to fill the area between them. However, contrary to the standard situation, the abscissa is to be plotted on the vertical axis and the ordinates on the horizontal one; the abscissa indicates depth, this is a common plotting format in geophysics. In other words, I want something like
plot 's.dat' u 1:2:3 w filledcurves
but with swapped axes so that the filled area is bounded not at the top and bottom but to the left and right by the curves as seen in
plot 's.dat' u 2:1,'s.dat' u 3:1
My dataset is like this:
0. -1.776 -0.880
160. -1.775 -0.882
160. -1.692 -0.799
320. -1.692 -0.800
320. -1.531 -0.634
480. -1.534 -0.637
480. -1.286 -0.394
Is this possible in Gnuplot?
Thomas
This is a totally different solution using 3D plot style "with zerror".
You will need current gnuplot (version 5.2) for this. The plot style was really not designed for this so there are some difficulties (e.g. x tic marks invisible because drawn perpendicular to the plane of the plot, all tic labels requiring an offset for readability).
#
# [mis]use 3D plot style "with zerror" to create a plot of the xz
# plane with area fill between two sets of data points with
# equal coordinates on the vertical axis (x) but contrasting
# values on the horizontal axis (z).
#
set view 270, 0
set view azimuth -90
set xyplane at 0
unset ytics
set ztics offset 4, -2 out
set xtics offset 4
splot 's.dat' using 1:(0):(0.5*($2+$3)):2:3 with zerror notitle
If there is some value of x which is guaranteed to lie between the two curves then you can plot in two halves. For the data you show, x=-1 would be a suitable value and the plot command would be:
plot 's.dat' u 2:1 with filledcurve x=-1 lt 3, \
's.dat' u 3:1 with filledcurve x=-1 lt 3
If the requirement for a constant intermediate x value can only be
satisfied piece-wise, e.g.
x=-1 for (0<y<500), x=0 for (500<y<1000)
then it may nevertheless be possible to construct a graph by stacking
the piecewise sections.
A simple way would be to define a closed line and fill it. For this, you take column 2 and add the reversed column 3. You probably need gnuplot >=5.2 for this.
Code:
### fill between vertical curves
reset session
$Data <<EOD
0. -1.776 -0.880
160. -1.775 -0.882
160. -1.692 -0.799
320. -1.692 -0.800
320. -1.531 -0.634
480. -1.534 -0.637
480. -1.286 -0.394
EOD
set print $Outline
do for [i=1:|$Data|] {
print sprintf("%s %s", word($Data[i],2), word($Data[i],1))
}
do for [i=|$Data|:1:-1] {
print sprintf("%s %s", word($Data[i],3), word($Data[i],1))
}
set print
plot $Outline w filledcurve lc rgb "green"
### end of code
Result:

splot axis or border description

in the following code I try to achieve the following:
1) As can be seen, the description 'talkative' does not appear. Is this really the y2axis? Or does y2axis not account for splot but only for plot?
Or do I have to label a border ?
2) How do I get tics only at the axis of interest?
3) A small detail: A close look reveals that the blue dot is transparent, but not the red and green dot. How to also make the blue dot filled?
set ticslevel 0
set xrange [0:1]
set yrange [0:1]
set zrange [0:1]
set object 1 polygon from \
0, 0, 1 to \
0, 1, 0 to \
1, 0, 0 to \
0, 0, 1
set view 56, 77
set style line 1 lc rgb "blue" pt 7 ps 2
set style line 2 lc rgb "red" pt 7 ps 2
set style line 3 lc rgb "green" pt 7 ps 2
unset xtics
unset ytics
unset ztics
set border 1+2+16
set xlabel "listening" offset +4,0
set y2label "talkative"
set zlabel "sleeping" rotate offset -1,-2
splot \
'-' with points ls 1 title "",\
'-' with points ls 2 title "",\
'-' with points ls 3 title ""
1 0 0
e
0 1 0
e
0 0 1
e
Unfortunately not a solution, but some explanations:
Yes, splot doesn't have an x2 and y2 equivalent. You can set a ylabel and shift it:
set ylabel 'talkative' offset graph -1.5
You mean getting tics only at the 'talkative' axis? That isn't possible out-of-the-box. You can have tics only on the y-axis (which gnuplot selects to always the the one in the front), or on both front and back y-axis.
That seems to be a bug to me. Usually you can set the border to be behind everything with set border back, but that doesn't work in this situation (tested with 4.6.5 and 5.0RC2). And it's also strange that it involves only one axis.
There are no second axes for splots. You can see this when you rotate the plot with the mouse: The drawn border, the tics and the label are jumping from one side to the other. You can also see the intention: If the volume is quite full of plot data, the axes in the rear would be hidden by your plots. So as soon as this can happen, gnuplot switches to axes in the front. There seems to be no way around.
(btw: when drawing the full border box, you have 4 axes in each direction. And there is definitively no y3tics or y4tics...)
set xtics nomirror
It's not really transparent, as only the "front border" is visible on the dot. The other lines (and also tick labels) are behind the dot. set border back should help, but it doesn't. The setting has an effect only for plot , not for splot. The data is plottet between front and rear axes, and so, the data hides the rear axes, but the front axes are drawn in front of the data.
In general, this all makes sense, but for special cases, there are options to tweak the plot as you want. However, only for plot ...

Add a single point at an existing plot

I am using the following script to fit a function on a plot. In the output plot I would like to add a single value with etiquette on the fitting curve lets say the point f(3.25). I have read that for gnuplot is very tricky to add one single point on a plot particularly when this plot is a fitting function plot.
Has someone has an idea how to add this single point on the existing plot?
set xlabel "1000/T (K^-^1)" font "Helvetica,20"
#set ylabel "-log(tau_c)" font "Helvetica,20"
set ylabel "-log{/Symbol t}_c (ns)" font "Helvetica,20"
set title "$system $type $method" font "Helvetica,24"
set xtics font "Helvetica Bold, 18"
set ytics font "Helvetica Bold, 18"
#set xrange[0:4]
set border linewidth 3
set xtic auto # set xtics automatically
set ytic auto # set ytics automatically
#set key on bottom box lw 3 width 8 height .5 spacing 4 font "Helvetica, 24"
set key box lw 3 width 4 height .5 spacing 4 font "Helvetica, 24"
set yrange[-5:]
set xrange[1.5:8]
f(x)=A+B*x/(1000-C*x)
A=1 ;B=-227 ; C=245
fit f(x) "$plot1" u (1000/\$1):(-log10(\$2)) via A,B,C
plot [1.5:8] f(x) ti "VFT" lw 4, "$plot1" u (1000/\$1):(-log10(\$2)) ti "$system $type" lw 10
#set key on bottom box lw 3 width 8 height .5 spacing 4 font "Helvetica, 24"
set terminal postscript eps color dl 2 lw 1 enhanced # font "Helvetica,20"
set output "KWW.eps"
replot
There are several possiblities to set a point/dot:
1. set object
If you have simple points, like a circle, circle wedge or a square, you can use set object, which must be define before the respective plot command:
set object circle at first -5,5 radius char 0.5 \
fillstyle empty border lc rgb '#aa1100' lw 2
set object circle at graph 0.5,0.9 radius char 1 arc [0:-90] \
fillcolor rgb 'red' fillstyle solid noborder
set object rectangle at screen 0.6, 0.2 size char 1, char 0.6 \
fillcolor rgb 'blue' fillstyle solid border lt 2 lw 2
plot x
To add a label, you need to use set label.
This may be cumbersome, but has the advantage that you can use different line and fill colors, and you can use different coordinate systems (first, graph, screen etc).
The result with 4.6.4 is:
2. Set an empty label with point option
The set label command has a point option, which can be used to set a point using the existing point types at a certain coordinate:
set label at xPos, yPos, zPos "" point pointtype 7 pointsize 2
3. plot with '+'
The last possibility is to use the special filename +, which generates a set of coordinates, which are then filtered, and plotted using the labels plotting style (or points if no label is requested:
f(x) = x**2
x1 = 2
set xrange[-5:5]
set style line 1 pointtype 7 linecolor rgb '#22aa22' pointsize 2
plot f(x), \
'+' using ($0 == 0 ? x1 : NaN):(f(x1)):(sprintf('f(%.1f)', x1)) \
with labels offset char 1,-0.2 left textcolor rgb 'blue' \
point linestyle 1 notitle
$0, or equivalently column(0), is the coordinate index. In the using statement only the first one is taken as valid, all other ones are skipped (using NaN).
Note, that using + requires setting a fixed xrange.
This has the advantages (or disadvantages?):
You can use the usual pointtype.
You can only use the axis values as coordinates (like first or second for the objects above).
It may become more difficult to place different point types.
It is more involved using different border and fill colors.
The result is:
Adding to Christoph's excellent answers :
4. use stdin to pipe in the one point
replot "-" using 1:(f($1))
2.0
e
and use the method in 3rd answer to label it.
5. bake a named datablock
(version > 5.0) that contains the one point, then you can replot without resupplying it every time:
$point << EOD
2.0
EOD
replot $point using 1:(f($1)):(sprintf("%.2f",f($1))) with labels
6. A solution using a dummy array of length one:
array point[1]
pl [-5:5] x**2, point us (2):(3) pt 7 lc 3
7. Or through a shell command (see help piped-data):
pl [-5:5] x**2, "<echo e" us (2):(3) pt 7 lc 3
pl [-5:5] x**2, "<echo 2 3" pt 7 lc 3
8. Special filename '+'
pl [-5:5] x**2, "+" us (2):(3) pt 7 lc 3
It seems to be the shortest solution. But note that while it looks like a single point, these are like 500 points (see show samples) plotted on the same position.
To have only one point the sampling needs to be temporarily adjusted (see help plot sampling)
pl [-5:5] x**2, [0:0:1] "+" us (2):(3) pt 7 lc 3
9. Function with zero sampling range length
Shortest to type, but plotting as many points on top of each other as many specified with samples
pl [-5:5] x**2, [2:2] 3 w p pt 7 lc 3

Resources