I would like to plot points in a 3d plot and create a mesh that could handle non rectangular data. To be more specific my data is
data.dat
-1 0 0
-1 1 0
0 -1 0
0 0 0
0 1 0
1 -1 0
1 0 0
and my gnuplot file
plot.plt
set dgrid3d 3,3
splot 'data.dat' w l
pause -1
Unfortunately dgrid3d seems to make my data rectangular and a rectangular grid (actually a square grid) is plotted. Is there a way to plot a restricted envelope of my data ? Ideally I would like to plot the orthogonal convex hull of my set of points.
You have a few options for doing something like this -- unfortunately they all require some work on your part.
First, it is important to note that gnuplot can handle non-rectangular meshes for surface plotting (even though you only need a rectangular surface for this example). For surface plotting, the datafile looks like (Notice the blank records):
x11 y11 z11
x21 y21 z21
x31 y31 z31
...
x12 y12 z12
x22 y22 z22
x23 y23 z23
...
It then forms quadrilaterals. In this case, the first quadrilateral would be formed from the points (x11,y11),(x21,y21),(x12,y12),(x22,y22). The second quadrilateral would be formed from (x21,y21),(x31,y31),(x22,y22),(x23,y23) and so forth. So, given your set of points, you can easily create a "rectangular mesh" which will hold all of your datapoints. It won't be evenly spaced, but that's no problem as all. Now we need to figure out how to remove points so that gnuplot won't plot them. This is the "trick". You can mark a particular string as "missing data" in the datafile (set datafile missing "string"). In that case, gnuplot won't plot that point, but it will still keep track of the coordinates of the missing data for the sake of generating the surface.
So at the end of the day, your datafile will look something like:
x11 y11 ?
x21 y21 ?
x31 y31 z31
...
x12 y12 ?
x22 y22 z22
x23 y23 z23
...
and the script to plot it would be:
set datafile missing '?'
set surf
set view map #Not sure about this...depends on the view you want
splot "mydata.dat" u 1:2:3 w lines
If you want gnuplot to compute the "orthogonal convex hull", I think you're out of luck there.
THE EASY WAY
Also note that if you want a solid colored object in 2d space (like the picture on the wikipedia link you posted), this problem becomes significantly easier. If you can create a datafile with just the vertices of the object you want to draw (in order such that (x1,y1) connects to (x2,y2) connects to (x3,y3)...), then you can plot that datafile as:
set fillstyle #However you want the object to appear
plot "datafile.dat" u 1:2 with filledcurves closed
Related
The gnuplot doc for set offsets is extremely terse and I cannot find how offsets interact with multiple x-axes or y-axes.
I have 2 plots and they use x1y1 and x1y2, so there are 2 y-axes one on the left one on the right. Right now, when I add some top offset it applies to the plot which uses y1. How can I make it affect the plot which uses y2?
That's what gnuplot help offsets says:
Offsets provide a mechanism to put an empty boundary around the data
inside an autoscaled graph. The offsets only affect the x1 and y1
axes, and only in 2D plot commands.
So it looks like, it's not (directly) possible.
By the way, do you want offsets affecting...
only y2 axis or
y1 and y2 axes in the same way or
y1 and y2 axes differently?
Maybe you can edit your question and add an example for illustration.
Addition:
Maybe the following is helpful. As you already did, you can use the GPVAL_ variables.
Important to know, that these values are only set after plotting.
So, you have to plot, then modify your y1 and y2 ranges as desired and then replot. You could also use the variables GPVAL_Y_MIN, GPVAL_Y_MAX, GPVAL_Y2_MIN, and GPVAL_Y2_MAX which gnuplot's autoscaling algorithm suggests as ranges.
Code:
### different "offsets" for y1 and y2 axes
reset session
set xlabel "x1-axis"
set ylabel "y1-axis"
set ytics nomirror
set y2label "y2-axis"
set y2tics nomirror
plot 100*(sin(x)+1) axes x1y1 w l, \
10*cos(x) axes x1y2 w l
Y1FromBottom = 0.40 # y1 data will use 40% space from bottom
Y2FromBottom = 0.80 # y2 data will use 80% space from bottom
set yrange[:(GPVAL_DATA_Y_MAX-GPVAL_DATA_Y_MIN)/Y1FromBottom+GPVAL_DATA_Y_MIN]
set y2range[:(GPVAL_DATA_Y2_MAX-GPVAL_DATA_Y2_MIN)/Y2FromBottom+GPVAL_DATA_Y2_MIN]
replot
### end of code
Result:
I have a data file, the data for y axis are in the third column. I would like to have the scale given by the first column on the x1 and by the second column on the x2. The standard way would be to:
plot data u 1:2 axes x1y1, data u 1:3 x2y1
But that creates two plots which is something I want to avoid. Of course one could make the above work with colours or with some other dirty tricks. It makes the whole plot code very cumbersome. Another nice way is to use multiplot as suggested here. But this is not really my goal, as I want to have the the real x2 axis.
Another way that came to my mind was to set x2range but that means going to the source file and figuring out the min and max or using some statistics in gnuplot (which feels like a waste of time for such a simple thing).
Is there any more simple and elegant way than the above ones? (I am especially concerned about the solution to be short to write, the plot can consist of several (>5) datasets and doing and I want to avoid plotting each dataset twice.
This can be done in this way, by telling gnuplot to re-scan file with 2nd column as x2 values but only invalid y-values for this second plot:
set xtics nomirror
set xrange [:] noextend
set x2tics
set x2range [:] noextend
plot '/tmp/f.gdat' u 1:3 w l, '' u 2:(1/0) ax x2y1
As an example, you can plot this data with Celsius on x and Fahrenheit on x2:
0 32 0
30 86 1
60 140 2
90 194 3
Note that this will only be sensible if column 2 is affinely linked with column 1. If you know the affine relation, using set link is much better.
I'm looking for a way to plot histograms in 3d to produce something like this figure http://www.gnuplot.info/demo/surface1.17.png but where each series is a histogram.
I'm using the procedure given here https://stackoverflow.com/a/19596160 and http://www.gnuplotting.org/calculating-histograms/ to produce histograms, and it works perfectly in 2d.
Basically, the commands I use are
hist = 'u (binwidth*(floor(($2-binstart)/binwidth)+0.5)+binstart):(1) smooth freq w boxes
plot 'data.txt' #hist
Now I would just like to add multiple histograms in the same plot, but because they overlap in 2d, I would like to space them out in a 3d plot.
I have tried to do the following command (using above procedure)
hist = 'u (1):(binwidth*(floor(($2-binstart)/binwidth)+0.5)+binstart):(1) smooth freq w boxes
splot 'data.txt' #hist
But gnuplot complains that the z values are undefined.
I don't understand why this would not put a histogram along the value 1 on the x-axis with the bins along the y-axis, and plot the height on the z-axis.
My data is formatted simply in two columns:
Index angle
0 92.046
1 91.331
2 86.604
3 88.446
4 85.384
5 85.975
6 88.566
7 90.575
I have 10 files like this, and since the values in the files are close to each other, they will completely overlap if I plot them all in one 2d histogram. Therefore, I would like to see 10 histograms behind each other in a sort of 3d perspective.
This second answer is distinct from my first. Whereas the first addresses what the OP was trying to accomplish, this second provides an alternative approach which address the underlying problem the OP was trying to overcome.
I have posted an answer that addresses the ability to do this in 3d. However, this isn't usually the best way to do this with multiple histograms like this. A 3d graph like that will be difficult to compare.
We can address the overlap in 2D by stagnating the position of the boxes. With default settings, the boxes will spread out to touch. We can turn that off and adjust the position of the boxes to allow more than 1 histogram on a graph. Remember, that the coordinates you supply are the center of the boxes.
Suppose that I have the data you have provided and this additional data set
Index Angle
0 85.0804
1 92.2482
2 90.0384
3 99.2974
4 87.729
5 94.6049
6 86.703
7 97.9413
We can set the boxwidth to 2 units with set boxwidth 2 (your bins are 4 units wide). Additionally, we will turn on box filling with set style fill solid border lc black.
Then I can issue
plot datafile1 u (binwidth*(floor(($2-binstart)/binwidth)+0.5)+binstart):(1) smooth freq w boxes, \
datafile2 u (binwidth*(floor(($2-binstart)/binwidth)+0.5)+binstart+1):(1) smooth freq w boxes
The second plot command is identical to the first, except for the +1 after binstart. This will shift this box 1 unit to the right. This produces
Here, the two series are clear. Keeping track of which box is associated with each is easy because of the overlap, but it is not enough to mask the other series.
We can even move them next to each other, with no overlap, by subtracting 1 from the first plot command:
plot datafile1 u (binwidth*(floor(($2-binstart)/binwidth)+0.5)+binstart-1):(1) smooth freq w boxes, \
datafile2 u (binwidth*(floor(($2-binstart)/binwidth)+0.5)+binstart+1):(1) smooth freq w boxes
producing
This first answer is distinct from my second. This answer address what the OP was trying to accomplish whereas the second addresses the underlying problem the OP was trying to overcome.
Gnuplot isn't going to be able to do this on it's own, as the relevant styles (boxes and histograms) only work in 2D. You would have to do it using an external program.
For example, using your data and your 2d command (your first command), we get (using your data and the linked values of -100 and 4 for binstart and binwidth)
To draw these boxes on the 3d grid, we will need to use the line style and have four points for each: lower left, upper left, upper right, and lower right. We can use the previous command and capture to a table, but this will only gives the upper center point. We can use an external program to pre-process, however. The following python program, makehist.py, does just that.
from sys import argv
import re
from math import floor
pat = re.compile("\s+")
fname = argv[1]
binstart = float(argv[2])
binwidth = float(argv[3])
data = [tuple(map(float,pat.split(x.strip()))) for x in open(fname,"r").readlines()[1:]]
counts = {}
for x in data:
bn = binwidth*(floor((x[-1]-binstart)/binwidth)+0.5)+binstart
if not bn in counts: counts[bn] = 0
counts[bn]+=1
for x in sorted(counts.keys()):
count = counts[x]
print(x-binwidth/2,0)
print(x-binwidth/2,count)
print(x+binwidth/2,count)
print(x+binwidth/2,0)
print(max(counts.keys())+binwidth/2,0)
print(min(counts.keys())-binwidth/2,0)
Essentially, this program does the same thing as the smooth frequency option does, but instead of getting the upper center of each box, we get the four previously mentioned points along with two points to draw a line along the bottom of all the boxes.
Running the following command,
plot "< makehist.py data.txt -100 4" u 1:2 with lines
produces
which looks very similar to the original graph. We can use this in a 3d plot
splot "< makehist.py data.txt -100 4" u (1):1:2 with lines
which produces
This isn't all that pretty, but does lay the histogram out on a 3d plot. The same technique can be used to add multiple data files onto it spread out. For example, with the additional data
Index Angle
0 85.0804
1 92.2482
2 90.0384
3 99.2974
4 87.729
5 94.6049
6 86.703
7 97.9413
We can use
splot "< makehist.py data.txt -100 4" u (1):1:2 with lines, \
"< makehist.py data2.txt -100 4" u (2):1:2 with lines
to produce
I am creating a program that solves a 3D partial differential equation using finite difference methods. This is surprisingly not the hard part, and it is technically finished.
At the end of the program, I am writing the numerical solutions to the PDE in the following format to some file (for later processing)
X Y Z C
0 0 0 0.1
0 0 1 etc etc
Where X Y and Z are spatial coordinates and C is the intensity at each location.
I found one a lot of information on plotting 3D data with 2 spatial dimensions and 1 intensity. So "technically" I have 4D data ... 3 spacial, 1 intensity.
The one piece of information I found was using this command:
splot 'datafile' u 1:2:3:4 w pm3d
Which does do the job, but since it is a rectangular prism, you can't easily see the concentration at the center of the prism.
I was imagining that the best way to do this would be to take a "chunk" out of the rectangular prism, so that you can see the intensity layers. The best analogy I could think of was the how text books represent the layers of the earth, where they take a chunk of the earth out to show all the way down to the core.
Another way I saw in research papers was to plot and XY cross section, YZ cross section and XZ cross section all on the same graph.
I have tried to search for both of these but it is very hard (for me) to concisely articulate.
Any advice would be great on the best way to represent this data!
I always find color maps to be the most helpful when interpreting data. You basically plot slices though planes with sensible information. If you have gridded data this is very easy to do in gnuplot even without preprocessing the data file. For instance, if your data looks like this:
# x y z c
0 0 0 0.15
1 0 0 0.14
2 0 0 0.16
0 1 0 0.11
1 1 0 0.19
2 1 0 0.12
0 2 0 0.15
1 2 0 0.19
2 2 0 0.13
0 0 1 0.10
1 0 1 0.09
2 0 1 0.17
# etc
then you can make a conditional plot with gnuplot for a fixed value of x, y or z. For the z = 0 plane, this could be achieved with splot "data" u 1:2:($3 == 0 ? $4 : 1/0), that is, if 3rd column's value is 0 then plot the 4th column's value, else ignore that point. For the simple example above:
set pm3d map
splot "data" u 1:2:($3 == 0 ? $4 : 1/0)
Note that pm3d does some interpolation between data points.
If you preprocess your data or have it nicely structured like in my example, you can also use the with image style, that might be preferred over pm3d for several reasons, including smaller file sizes:
plot "data" u 1:2:4 every :::0::2 with image
Where there is no interpolation but the actual point values. every :::0::2 above selected data blocks 0 to 2, which are the ones that belong to z = 0 in my example.
Finally, if your data is non gridded, you cannot use with image and should use pm3d instead. In this case the command should take into considerations points that are at an acceptable distance from the plane where you want to plot. This could be achieved as follows:
set pm3d map
plane_z = 0
splot "data" u 1:2:( abs($3 - plane_z) < 0.1 ? $4 : 1/0)
Above I include in the plot all the points whose z values are less than a distance 0.1 away from the plane (z = 0) I'm interested in.
i'm trying to plot an heatmap on a triangular surface, the coordinates and "heat values" are obtained with the methog shown on page staff.aist.go.jp/a.noda/programs/ternary/ternary-en.html.
so, i process the data and obtain a data file in the form:
x y val
where x and y are values between 0 and 1, and val is an integer representing the frequency i need to show.
the data file is this: http://tinyurl.com/lqsqtvv
and the plot script is this:
#!/usr/bin/gnuplot
reset
set terminal pngcairo size 640,480
set output 'heat_map_triangle.png'
set border linewidth 0
unset tics
set bmargin 3
set lmargin 3
set rmargin 3
set tmargin 3
set dgrid3d
set pm3d map
#set pm3d ftriangles
set pm3d interpolate 0,0
set pm3d at bs
set label 'Y' at 0, -0.03 center
set label 'Z' at 1, -0.03 center
set label 'X' at 0.5, 0.886 center
set style line 1 lt 1 lw 3 pt -1 ps 1
# x
set arrow 1 from 0,0 to 1, 0.0 nohead linestyle 1
# z
set arrow 11 from 1, 0 to 0.50, 0.866 nohead linestyle 1
# y
set arrow 21 from 0.50, 0.866 to 0,0 nohead linestyle 1
splot "./triangle.out" using 1:2:3
so, i'm getting out this plot
that is not exactly what i wanted...
i can't understand how to tell pm3d not to fill zones that are not in the data file (e.g. outside the triangle) and why the triangle top edge is taller than the heatmap.
it there a way to plot the data the way i want it?
in pm3d documentation it says that it can leave empty spaces, but how?
thanks
Ultimately, for pm3d to work, gnuplot requires that the data be on some sort of grid "mesh". The mesh needs to composed of quadrillaterals, but that is the only stipulation. e.g., your gridpoints could be arranged like this:
1
2
3
4 5 6 10
9
8
7
In this case, gnuplot will create a quadrilateral from points 1-2-4-5 and points 2-3-5-6, etc. etc. Gnuplot will color the quadrilateral depending on the corners2color option of pm3d. By default, it uses the average of the 4 values on the corners of the cells.
To put this in a datafile, you'd want to the datafile coordinates like this:
x1 y1 z1
x2 y2 z2
x3 y3 z3
x10 y10 z10
x4 y4 z4
x5 y5 z5
x6 y6 z6
x10 y10 z10
x7 y7 z7
x8 y8 z8
x9 y9 z8
x10 y10 z10
Notice how I left a blank line between horizontal "scans" across the data. (Of course, we could have structured the datafile to take vertical "scans" across the data as well). I also repeated a point at the right vertex of my triangle to give it a sharp point. This isn't strictly necessary, but I wanted to demonstrate it was possible.
Your data isn't in that form and normally, gnuplot would give you an error complaining that your data wasn't gridded. however, you've added the line set dgrid3d which tells gnuplot that your data isn't on a grid and that gnuplot should use an inverse distance weighting function to interpolate your data onto a grid. Unfortunately, gnuplot creates a regular (rectangular) grid and there is no way to tell it to create some other kind of grid. Ultimately, you need to figure out how to beat your data into this form.
If you were prepared to use R, and the ggtern library, the following could be achieved:
Which was done with the following code:
#Load library
library(ggtern)
#Load the data
df <- read.table("./data.txt")
colnames(df) = c("x","y","Value")
#Put in ternary coordinates
df.new <- data.frame(transform_cart_to_tern(data=df),Value=df$Value)
df.new <- df.new[order(df.new$Value),]
df.new <- df.new[which(df.new$Value > 0),]
#Plot the diagram
ggtern(data=df.new,aes(y=T,x=L,z=R)) +
geom_point(aes(color=Value,alpha=Value)) +
scale_color_gradient(low="transparent",high="red") +
guides(alpha="none") +
theme_rgbw() +
theme(legend.position=c(0,1),legend.justification=c(0,1)) +
labs(title="Example Density Plot",color="Frequency")