Creating 4D Surface Graph

I am trying to create a 4D graph of data to try to represent certain values of a specific point in an XYZ plane. In context, I was trying to graph the amount of snow at different points of a mountain. Currently, I have the data in the form of x coordinates, y coordinates, z coordinates, and the amount of snow at the given point. The data looks like the following:

[ 0.05497894 -0.11160859  0.37084371], 992634.25
[-0.00817349 -0.14016391  0.39896593], 22319520.0
[-0.04405962 -0.13445015  0.41198156], 23621576.0
[-0.02392213 -0.1297217   0.39447151], 22518240.0
[-0.00622379 -0.12973436  0.39639086], 19963640.0
[ 0.01604251 -0.12920773  0.40366026], 20256056.0
[ 0.06505235 -0.13291964  0.40368367], 33873776.0
[ 0.07263324 -0.13199505  0.40244909], 19216264.0
[ 0.03655646 -0.13674183  0.40442567], 17755528.0
[ 0.00245951 -0.13406454  0.40627548], 4434950.0

The first array on the left-hand side is as follows, [x y x] and the number on the right-hand side is the amount of snow in micrometers. Is there a way I could potentially plot a graph to show the x, y, z coordinates and then represent the amount of snow with a different color? Ideally, the goal would be to do anomaly detection to find specific clusters of points with outliers. Is there a particular way I could do this with Plotly, I am kinda stuck at a standstill at the moment and would greatly appreciate any help!

Hi @Alpheron,

You can illustrate the fourth dimension by color. The working algorithm checked with your posted data is as follows:

  • set up the array of coordinates and the array of snow height at each point.
  • Theoretically you have to project the points (x,y,z) to (x,y) and triangulate the set of 2d points (x,y). Delaunay triangulation returns the array of 2d points, and the array of triangles (triangulation faces).
  • With x, y, z as 3D coordinates and snowh defined below you can draw a triagulated surface such that the color (in a given colormap) of each point is assigned according to the corresponding value in snowh:
import plotly.graph_objects as go
import numpy as np
from scipy.spatial import Delaunay

point_coords= np.array([[ 0.05497894, -0.11160859,  0.37084371], 
                        [-0.00817349, -0.14016391,  0.39896593], 
                        [-0.04405962, -0.13445015,  0.41198156], 
                        [-0.02392213, -0.1297217,   0.39447151], 
                        [-0.00622379, -0.12973436,  0.39639086], 
                        [ 0.01604251, -0.12920773,  0.40366026], 
                        [ 0.06505235, -0.13291964,  0.40368367], 
                        [ 0.07263324, -0.13199505,  0.40244909], 
                        [ 0.03655646, -0.13674183,  0.40442567], 
                        [ 0.00245951, -0.13406454,  0.40627548]]) 


x = point_coords[:, 0]
y = point_coords[:, 1]
z = point_coords[:, 2]
points2D = np.vstack([x,y]).T
tri = Delaunay(points2D) #triangulate the 2d points
I, J, K = (tri.simplices).T
print("simplices:", "\n", tri.simplices)
fig=go.Figure(go.Mesh3d(x=x, y=y, z=z,
                        i=I, j=J, k=K, 
                       intensity=snowh, colorscale="ice" )) #these two last attributes, intensity and colorscale, assign a color according to snow height

Hey @empet

Does it only work with go.Mesh3d?

I plotted a graph with go.Surface and want to add a 4th dimension by color. I see the possibility as written above, but it does not seem intuitive to me. That’s why I would like to know, how (or if) the “color”-dimension can be added to this for instance:

x_test_d1 = np.linspace(-4,4,100)
x_test_d2 = np.linspace(-3,3,100)

grid_d1, grid_d2 = np.meshgrid(x_test_d1,x_test_d2)

z = 1.5*np.sin(grid_d1) - 0.1*(grid_d1-3)**2 +10 - 0.5*(grid_d2**2-2) + np.sin(grid_d2)*2

fig = go.Figure(data=[go.Surface(x=grid_d1, y=grid_d2, z=z, colorscale="ice", opacity=0.8)])
fig.update_layout(margin=dict(l=20, r=20, t=20, b=20),
    height = 600, width =500, scene_aspectmode="cube")

Hi @robl01,
Your code is plotting a surface colored according to z- values. If you want to be colored following another rule, you should define an array, surfcolor, that records the values of a scalar field defined at the points of the surface, i e. surfcolor.shape = z.shape.
Once the surfcolor has been defined, insert in the go.Surface definition, ` surfacecolor=surfcolor’.