# How to plot a surface from a set of data?

Disclaimer: I already read questions like 2D surface on a 3D surface plot external data in a file and 3D surface plots in TikZ but these did not solve my problem.

I have this set of data:

x       y       z
2.17    0.001   0.82044815
2.17    0.002   0.82345825
2.17    0.004   0.82679255
2.17    0.008   0.83334715
2.17    0.016   0.84395915
2.17    0.032   0.8584953
2.21    0.001   0.77582165
2.21    0.003   0.78520505
2.21    0.009   0.80205985
2.21    0.027   0.83085105
2.24    0.001   0.7227885
2.24    0.002   0.73391615
2.24    0.005   0.7543979
2.24    0.015   0.78798745
2.24    0.003   0.74176635
2.24    0.009   0.77064805
2.24    0.027   0.81042375
2.26    0.001   0.66545585
2.26    0.003   0.7012046
2.26    0.005   0.721067
2.26    0.009   0.7447984
2.26    0.015   0.76715245
2.26    0.027   0.794177
2.27    0.001   0.62916195
2.27    0.003   0.6774642
2.27    0.009   0.72961785
2.27    0.027   0.7861086
2.28    0.001   0.5750828
2.28    0.003   0.65059675
2.28    0.005   0.6802631
2.28    0.009   0.7145367
2.28    0.015   0.74447695
2.28    0.027   0.7774403
2.29    0.001   0.51357255
2.29    0.002   0.581053
2.29    0.003   0.6173075
2.29    0.009   0.6972096
2.29    0.027   0.76793225
2.31    0.001   0.36997965
2.31    0.002   0.474415
2.31    0.003   0.53649295
2.31    0.009   0.6587164
2.31    0.016   0.70870255
2.31    0.027   0.7482423
2.31    0.05    0.7912395
2.34    0.001   0.2204104
2.34    0.002   0.316308
2.34    0.003   0.39256745
2.34    0.004   0.45240835
2.34    0.009   0.5883453
2.34    0.016   0.6590771
2.34    0.027   0.71444205
2.34    0.05    0.7690014
2.38    0.001   0.13286995
2.38    0.002   0.1828288
2.38    0.004   0.2980268
2.38    0.008   0.4507145
2.38    0.016   0.58417075
2.38    0.032   0.6833616


And if I plot it in Mathematica (with ListPlot3D[data]) it looks, without further tweaking and fiddling around, like:

But if I try this pgfplots code

\begin{tikzpicture}
\begin{axis}
\end{axis}
\end{tikzpicture}


I only get this:

What do I have to do to get a surface like in Mathematica (color, smoothness, style, etc. does not matter, just a closed surface)?

• The data need to be arranged on a grid, so the number of rows and columns must not change. How did you generate that dataset?
– Jake
Jun 7, 2013 at 18:16
• So if the picture produced by Mathematica is acceptable why don't you save it as .epsfile and include in the paper? Why do you need to use pgf? Jake's question is very good:"How do you generate that dataset". If you generate with Python for example just use matplotlib to plot the set. Jun 7, 2013 at 19:32
• @Jake It's experimental data that was given to me. I did not generate it. The first two columns are varied parameters and the last column is the measured value in a physical experiment. Jun 8, 2013 at 8:24
• @PredragPunosevac I don't need to use pgfplots, but I'd like to. ;) Be it for learning or for fun or just because it generally looks better. ;) Jun 8, 2013 at 9:00
• Alternatively you can plot the surface in Mathematica and use the axis from pgfplots. This is nicely described in the manual. If you want to convert the data to a regular grid Originlab has several options to do that as well. Jun 8, 2013 at 16:51

If you have octave installed, you can triangulate the data from within your LaTeX document using the \addplot shell functionality and plot it using the patch plot style.

\documentclass[border= 5mm]{standalone}
\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}
tri=delaunay(data(:,1), data(:,2));
dlmwrite('triangles.txt',tri-1,' ');
disp(data)" | octave --silent};
\end{axis}
\end{tikzpicture}
\end{document}


You can also grid the data:

\documentclass[border= 5mm]{standalone}
\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}
\addplot3 [surf, mesh/cols=50, z buffer=sort, restrict z to domain=0:inf, shader=faceted interp] shell {
echo "
res=\pgfkeysvalueof{/pgfplots/mesh/cols};
[xx,yy] = meshgrid(linspace(min(data(:,1)), max(data(:,1)),res), linspace(min(data(:,2)), max(data(:,2)),res));
zz=griddata(data(:,1),data(:,2),data(:,3),xx,yy);
zz(isnan(zz))=-999;
disp([xx(:) yy(:) zz(:)])
" | octave --silent};
\end{axis}
\end{tikzpicture}
\end{document}


Gnuplot could be used to interpolate scattered data, but the available interpolation functions don't work particularly well for this dataset.

\documentclass[border= 5mm]{standalone}
\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}
\addplot3 [surf] gnuplot [raw gnuplot] {
set dgrid3d 30,30 spline;
splot 'data.txt';
};
\end{axis}
\end{tikzpicture}
\end{document}

• Something similar that works with MATLAB? Or it's just octave because it works from terminal? And with gnuplot? I don't have octave and I would prefer not to install it. Great answer, by the way. Jun 8, 2013 at 20:08
• @Manuel: You can run the octave commands without adaptation in Matlab, but unfortunately it's really hard to run Matlab from a command line. I've added a gnuplot approach, but the octave solution really gives best results in this case.
– Jake
Jun 8, 2013 at 20:52
• Thank you for the gnuplot solution, may be not the best one, but it's great to know how to do it in that program. Jun 9, 2013 at 19:01
• I did choose the gnuplot method. Thank you. :) Jun 24, 2013 at 15:53
• @Jake Thank you for your great answer! I'm having issues, though. When I try the first example, only an empty 2D-chart is created. Second and third only compile the dots, not the actual surface. It says: ! Package pgfplots Error: Sorry, the shell-result file 'filename.pgf-plot.out' could not be found. Maybe you need to enable the shell-escape feature? or, respectively ! Package pgfplots Error: Sorry, the gnuplot-result file filename.pgf-plot.table could not be found. Maybe you need to enable the shell-escape feature? Feb 13, 2017 at 19:16

Check out this kind of a closed surface, produced with the Asymptote. A brief description:

• The data are stored in a file s.dat;
• since the data are not based on a regular grid, a 2d triangulation is used on x,y points;
• a surface is constructed from corresponding 3d triangles;
• z-value and two colors, pzmin and pzmax are used to color it.

irrsurf.tex

\begin{filecontents*}{s.dat}
#x       y       z
2.17    0.001   0.82044815
2.17    0.002   0.82345825
2.17    0.004   0.82679255
2.17    0.008   0.83334715
2.17    0.016   0.84395915
2.17    0.032   0.8584953
2.21    0.001   0.77582165
2.21    0.003   0.78520505
2.21    0.009   0.80205985
2.21    0.027   0.83085105
2.24    0.001   0.7227885
2.24    0.002   0.73391615
2.24    0.005   0.7543979
2.24    0.015   0.78798745
2.24    0.003   0.74176635
2.24    0.009   0.77064805
2.24    0.027   0.81042375
2.26    0.001   0.66545585
2.26    0.003   0.7012046
2.26    0.005   0.721067
2.26    0.009   0.7447984
2.26    0.015   0.76715245
2.26    0.027   0.794177
2.27    0.001   0.62916195
2.27    0.003   0.6774642
2.27    0.009   0.72961785
2.27    0.027   0.7861086
2.28    0.001   0.5750828
2.28    0.003   0.65059675
2.28    0.005   0.6802631
2.28    0.009   0.7145367
2.28    0.015   0.74447695
2.28    0.027   0.7774403
2.29    0.001   0.51357255
2.29    0.002   0.581053
2.29    0.003   0.6173075
2.29    0.009   0.6972096
2.29    0.027   0.76793225
2.31    0.001   0.36997965
2.31    0.002   0.474415
2.31    0.003   0.53649295
2.31    0.009   0.6587164
2.31    0.016   0.70870255
2.31    0.027   0.7482423
2.31    0.05    0.7912395
2.34    0.001   0.2204104
2.34    0.002   0.316308
2.34    0.003   0.39256745
2.34    0.004   0.45240835
2.34    0.009   0.5883453
2.34    0.016   0.6590771
2.34    0.027   0.71444205
2.34    0.05    0.7690014
2.38    0.001   0.13286995
2.38    0.002   0.1828288
2.38    0.004   0.2980268
2.38    0.008   0.4507145
2.38    0.016   0.58417075
2.38    0.032   0.6833616
\end{filecontents*}
\documentclass[10pt,a4paper]{article}
\usepackage{lmodern}
\usepackage[inline]{asymptote}
\begin{document}
\begin{figure}
\begin{asy}
settings.outformat="pdf";
settings.prc=false;
settings.render=0;

import graph3;
size3(200,200,80,IgnoreAspect);
size(300,300,IgnoreAspect);

file fin=input("s.dat");
real[][] A=fin.dimension(0,3);
A=transpose(A);

real xmin=min(A[0]);
real xmax=max(A[0]);
real ymin=min(A[1]);
real ymax=max(A[1]);
real zmin=min(A[2]);
real zmax=max(A[2]);

currentprojection=orthographic(
camera=(18.8549512615229,-2.05615180783542,61.9196974622481),
up=Z,
target=0.5((xmin,ymin,zmin)+(xmax,ymax,zmax)),
zoom=0.7);

pair[] p=new pair[A[0].length]; // conversion to pairs x,y
for(int i=0;i<p.length;++i){    //   to get a triangulation
p[i]=(A[0][i],A[1][i]);       //
}                               //
int[][] trn=triangulate(p);     //

pen pzmin=rgb(0,1,0);           // pen for min z
pen pzmax=rgb(0.8,0.8,1);       // pen for max z

pen[] zpen(triple a,triple b,triple c){  // return z-pen for three vertices
pen[]zp=new pen[3];
real t;
t=(a.z-zmin)/(zmax-zmin);
zp[0]=(1-t)*pzmin+t*pzmax+opacity(0.9);
t=(b.z-zmin)/(zmax-zmin);
zp[1]=(1-t)*pzmin+t*pzmax+opacity(0.9);
t=(c.z-zmin)/(zmax-zmin);
zp[2]=(1-t)*pzmin+t*pzmax+opacity(0.9);
return zp;
}

triple a,b,c;
for(int i=0; i < trn.length; ++i) {
a=(A[0][trn[i][0]],A[1][trn[i][0]],A[2][trn[i][0]]);
b=(A[0][trn[i][1]],A[1][trn[i][1]],A[2][trn[i][1]]);
c=(A[0][trn[i][2]],A[1][trn[i][2]],A[2][trn[i][2]]);
draw(surface(a--b--c--cycle),zpen(a,b,c));        // draw i-th triangle
}

defaultpen(fontsize(10pt));

xaxis3(Label("$x$",align=-3Z),Bounds, xmin,xmax,InTicks(Step=0.05,step=0.01));
yaxis3(Label("$y$",align=-3Z),Bounds, ymin,ymax,InTicks(Step=0.01,step=0.002));
zaxis3(Label("$z$",align=-3Y),Bounds, zmin,zmax,InTicks());
\end{asy}
\caption{Surface constructed from an array of irregularly spaced points.}
\end{figure}
\end{document}


To process it with latexmk, create file latexmkrc:

sub asy {return system("asy '\$_[0]'");}

and run latexmk -pdf irrsurf.tex.
You can find details about both formats in the manual sections for the surfplot handler or the patch plot handler.