1
votes

I have a dataframe:

  surfacex surfacey surfacez
0   -0.50   0.00    0.00
1   -0.48   -0.14   0.00
2   -0.48   -0.12   -0.06
3   -0.48   -0.12   0.06
4   -0.48   -0.10   -0.08
... ... ... ...
3897    0.48    0.10    0.08
3898    0.48    0.12    -0.06
3899    0.48    0.12    0.06
3900    0.48    0.14    0.00
3901    0.50    0.00    0.00

where each row represents a 3D point. I want to plot a surface that bounds these points. In this case, the points all lie on the surface of a sphere, and hence I want to plot a spherical surface. Currently this is what I am trying:

import numpy as np
import plotly.graph_objects as go
X=df['surfacex'].values
Y=df['surfacey'].values
Z=df['surfacez'].values
trace= go.Surface(x=np.reshape(X,(1951,2)),y=np.reshape(Y,(1951,2)),z=np.reshape(Z,(1951,2)))
#I am reshaping as online tells me it needs to be a 2D array, and my df is of length 3902
fig2=go.Figure(data=[trace])
fig2.show()

However, the resulting plot looks like:

Plot

which is clearly not what I want. What I want would be something similar to:

Plot2

How can I achieve the desired plot? Matplotlib or plotly solutions are both ok

Thanks

1

1 Answers

1
votes

Using Maplotlib, calculate the convex hull of the datapoints and then either

Step 1: Create some test data (random points within a sphere, using this link, section 6.08):

import numpy as np
from math import pi

n=500 #number of data points
z=np.random.uniform(low=-1.0, high=1.0, size=n)
t=np.random.uniform(low=0.0, high=2*pi, size=n)
r=np.sqrt(1-z*z)
x=r*np.cos(t)
y=r*np.sin(t)
sphere=np.vstack([x,y,z]).T

Step 2: Get all points that lie on the surface of the sphere:

from scipy.spatial import ConvexHull
hull = ConvexHull(sphere)
surface=sphere[hull.vertices]

Step 3: create a 3d scatter plot using the points on the surface:

import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_box_aspect((1,1,1))
ax.scatter(surface[:,0], surface[:,1], surface[:,2])

scatter plot

Step 4: use Delaunay triangulation to calculate and draw triangles that represent the surface. The method to draw the single elements of the surface is taken from this answer:

from scipy.spatial import Delaunay
deln = Delaunay(surface) 

def plot_tri_simple(ax, points, tri):
    for tr in tri.simplices:
        pts = points[tr, :]
        ax.plot3D(pts[[0,1],0], pts[[0,1],1], pts[[0,1],2], color='g', lw='0.1')
        ax.plot3D(pts[[0,2],0], pts[[0,2],1], pts[[0,2],2], color='g', lw='0.1')
        ax.plot3D(pts[[0,3],0], pts[[0,3],1], pts[[0,3],2], color='g', lw='0.1')
        ax.plot3D(pts[[1,2],0], pts[[1,2],1], pts[[1,2],2], color='g', lw='0.1')
        ax.plot3D(pts[[1,3],0], pts[[1,3],1], pts[[1,3],2], color='g', lw='0.1')
        ax.plot3D(pts[[2,3],0], pts[[2,3],1], pts[[2,3],2], color='g', lw='0.1')


plot_tri_simple(ax, surface, deln)

triangulation surface