Plotlly Isosurface being cut off in Dash App

Hi,

I am creating a dash app that has an 3D isosurface plot. The isosurface is effectively a perfect sphere and I am able to confirm / view this when running in plotly using fig.show(). However, when I transfer my code and build a dash app a small part of the isosurface gets cut off. I have kept all of the figure layouts etc and the data the same. If anyone has run into this before, any help would be greatly appreciated.

Figure in plotly:

Figure in dash app:

Thanks,
Dan

@Dseal
The Isosurface chart has among others, the attribute caps, that seems to be set on True, in your code. To ensure that no cap (cutoff) is displayed set in your isosurface definition:

caps= dict(
                      x=dict(show=False),
                      y=dict(show=False),
                      z=dict(show=False))

Is your sphere defined by a function:

f = lambda x, y, z, r: x**2+y**2+z**2-r**2

and
N=100
x, y, z = np.mgrid[-d:d:75j, -d:d: 100j, -d:d: 100j] # with N=100 the number of points in the interval [-d, d]
vals = f(x, y, z, r0)

?????
If this is a case, then r0 (the sphere radius) must be less than d, otherwise you'll get caps.

More precisely, if d=2, than the radius of your sphere must be less than 2.

Then your trace definition should be:

isosurf = go.Isosurface(
surface=dict(show=True, fill= 0.65, pattern= “all”, count=1), #your particular settings here
colorscale=your_colorscale,

           x=x.flatten(),
           y=y.flatten(),
           z=z.flatten(),
           value=vals.flatten(),       
  
           caps= dict(
                  x=dict(show= False),
                  y=dict(show=False),
                  z=dict(show= False)),
           
           isomin= 0,
           isomax=  0)

@empet thanks for getting back to me.

For context, my sphere is the probability density of a 3D multi-variate distribution such that X, Y and Z are all mu = 0 and the covariance matrix is [[1,0,0], [0,1,0], [0,0,1]] returning the function, pdf(x,y,z):

def _probability_density(mu, coordinates):
    """Calculate the probability density of a 3D normal gaussian distribution.
    3 variables uncorrelated so covariance matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]"""
    pdf = []
    for X in coordinates:
        pX = (1 / sqrt(2 * pi)) * exp(-(1 / 2) * (X[0] - mu[0]) ** 2) * (1 / sqrt(2 * pi)) * exp(
            -(1 / 2) * (X[1] - mu[1]) ** 2) * (1 / sqrt(2 * pi)) * exp(-(1 / 2) * (X[2] - mu[2]) ** 2)
        pdf.append(pX)

    return pdf

pdf_analytical = _probability_density(mu=[0,0,0], coordinates) 

Where the coordinates are basically all of the (x,y,z) coordinates in a (64, 64, 64) grid.

I am then plotting a constant probability density. ** In the screenshots below I am also showing an additional surface which is a KDE pdf density estimate.

In terms of my current code (giving the “cut off” surface on x-axis) here it is:

        isosurface_analytical = go.Isosurface(x=x_,
                                              y=y_,
                                              z=z_,
                                              value=pdf_analytical,
                                              opacity=0.1,
                                              isomin=levels[0],
                                              isomax=levels[0],
                                              surface=dict(count=1,
                                                           fill=1,
                                                           pattern="all",
                                                           show=True),
                                              colorscale=[[0, "rgb(220,20,60)"], [1.0, "rgb(220,20,60)"]],
                                              caps=dict(x_show=True,
                                                        y_show=True,
                                                        z_show=True),
                                              showscale=False,
                                              showlegend=False
                                              )

I tried changing x_show, y_show and z_show to False as mentioned but I got this:

re the radius of the isosurface, given that when plotting it in normal plotly it is fine Id assume that it is within the [-d, d].

It does just seem that the surface is being cut off at the x-axis, here is a better screenshot:

I also tried manually making all of my axes [-5, 5] and it was still cut off.

@Dseal

When you evaluate the pdf function at the points of the grid, you get an array fvals.

fvals has a max value, and your isosurface must be the level set f(x,y,z)=a <max f- value.
I suspect that your chosen a is such that it leads to a cap or your pdf function has a drawback.

I experimented defining the pdf function via scipy.stats as follows:

import plotly.graph_objects as go
from scipy.stats import multivariate_normal as mn
import numpy as np
d=3
x, y, z = np.mgrid[-d:d:120j, -d:d:120j, -d:d:120j] #mgrid returns an array of shape (3, 120, 120, 120)
pos = np.stack((x, y, z), axis=-1)# pos of shape (120, 120, 120, 3) needed to evaluate the pdf
cov = np.eye(3)
rv = mn(mean= [0,0,0], cov=cov)
fvals  = rv.pdf(pos)
#fvals.max()  is  0.06345398132788951
a = 0.055
fig = go.Figure(go.Isosurface(
               surface=dict(show=True, fill= 0.65, pattern= "all", count=1), 
               colorscale=[[0, "rgb(220,20,60)"], [1.0, "rgb(220,20,60)"]],
               showscale=False,
              
               x=x.flatten(),
               y=y.flatten(),
               z=z.flatten(),
               value=(fvals-a).flatten(),       
               isomin= 0,
               isomax=  0,
    lighting=dict(ambient=0.8),
    lightposition=dict(x=8500, y=8500, z=100)
        ))
fig.update_layout(width=800, height=800)
fig.show()

f-max value is so small that it’s impossible to get a cap within the chosen volum.
LE: The plot corresponds to case d=1, and the same a. For d=3, the sphere is much smaller.

1 Like

I completely agree with this condition: “isosurface must be the level set f(x,y,z)=a <max f- value”. With my data, I get:

a = 0.005633899603219131
max f-value = 0.0630784028561267

I also can verify that by reducing the constant with fvals-a, the “cut off” doesnt happen. However, for my data the isosurface is represntative of a physical phenomenan and so I can’t just reduce the f-vals as the isosphere at f(x,y,z) = a (= levels[0] in my code) needs to be interpreted as such. For instance, take the two options:

Don’t substract a and have a cut off:


Subtract a and no cut off:

The isosphere is smaller (as expected) meaning that it contains less of my data and the volume it takes up is smaller which is not what I am allowed to do.

Beacuse of this, I essentially need to find a way to show the surface without cut off.

I really appreciate your help on this btw.

Have you tried with the scipy.stats definition of pdf? Does it lead to a cut off, too?

To give you an effective help I need your complete code, not only a summary presentation.

@empet unfortunately it doesnt work when using the scipy.stats definition of pdf either, same cut off.

I am going to creae a dash app for just this isosurface plot and I will see if I still get the same problem to confirm that it is either the html layout or the actual plot. I am still adamant that it is the former as the plot works fine when plotting in isolation in plotly.

Will also try and send the code across tomorrow!