# 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 '13 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. – Predrag Punosevac Jun 7 '13 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. – Foo Bar Jun 8 '13 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. ;) – Foo Bar Jun 8 '13 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. – Alexander Jun 8 '13 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. – Manuel Jun 8 '13 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 '13 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. – Manuel Jun 9 '13 at 19:01
I did choose the gnuplot method. Thank you. :) – Foo Bar Jun 24 '13 at 15:53
@FooBar: Hehe, good thing Manuel asked for it, then. Glad it helps! – Jake Jun 24 '13 at 15:55

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.