Search code examples
pythonmatplotlibplot3dplotly

How to plot spheres in 3d with plotly (or another library)?


I am struggling with plotting something, that I thought should be really simple. I have a dataframe/table df that contains the coordinates x,y,z of spheres. Additionally, in a separate column, it contains the radius of these spheres. How can I make with plotly a 3d plot of the spheres in space with their correct radius being used?

The closest I have come to making this plot reality is this code:

import plotly.express as px
import plotly.graph_objects as go

x =y=z = np.arange(1,4)

fig = go.Figure()
fig.add_trace(go.Scatter3d(
        mode='markers',
        x=x,
        y=y,
        z=z,
        marker=dict(color=px.colors.qualitative.D3,size=[50, 5, 25],sizemode='diameter'
        )
    )
)

This produces almost what I want (see below), but notice that the sizes of the spheres are not correct - the ratios between the sphere sizes are correct, but I want size to provide the absolute size of the spheres in the plot. How can I make this plot work with plotly (or worst case with another library)? Tnx!

enter image description here


Solution

  • When you use markers, the size is usually specified in pixels (either area or diameter). The docs state precisely that.

    I have never used plotly, but I am pretty sure it will be easy to convert this script using matplotlib to use plotly (see here.)

    import numpy as np
    import matplotlib.pyplot as plt
    from itertools import repeat  # just for the example
    
    def makesphere(x, y, z, radius, resolution=10):
        """Return the coordinates for plotting a sphere centered at (x,y,z)"""
        u, v = np.mgrid[0:2*np.pi:resolution*2j, 0:np.pi:resolution*1j]
        X = radius * np.cos(u)*np.sin(v) + x
        Y = radius * np.sin(u)*np.sin(v) + y
        Z = radius * np.cos(v) + z
        return (X, Y, Z)
    
    fig = plt.figure("Spheres")
    ax = fig.add_subplot(projection='3d')
    
    for x, y, z, radius, in zip(*repeat(np.arange(1,4),3), [.1, .5, .75]):
        X, Y, Z = makesphere(x, y, z, radius)
        ax.plot_surface(X, Y, Z, color="r")
    

    Result:

    Result of the script

    If you want smoother spheres, increase the resolution parameter (and check the cstride and rstride parameters to plot_surface).