You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于球面点坐标X,Y,Z生成3D密度图的方法(无Mayavi环境)

3D球面密度图解决方案(无需Mayavi)

没问题,既然你的环境不支持Mayavi,咱们就用Python生态里更通用的工具来实现——Matplotlib+SciPy或者Plotly,这俩几乎在所有Python环境里都能轻松安装,而且和你现有的散点图工作流兼容。

方案一:Matplotlib + SciPy(基于散点颜色映射/球面曲面密度)

如果你已经在使用Matplotlib,这个方案衔接最顺畅。核心思路是先计算每个散点的局部密度,再把密度值映射到颜色上;或者把球面网格化,将密度插值到网格后渲染成带颜色的曲面。

步骤1:计算球面点的局部密度

因为数据在球面上,直接用笛卡尔坐标做密度估计会有边界偏差,建议先转成球坐标(极角θ、方位角φ),再在二维的(θ,φ)平面上做核密度估计(KDE):

import numpy as np
from scipy.stats import gaussian_kde

# 假设你的X,Y,Z是形状为(n,)的数组
r = np.sqrt(X**2 + Y**2 + Z**2)
theta = np.arccos(Z / r)  # 极角,范围[0, π]
phi = np.arctan2(Y, X)    # 方位角,范围[-π, π]

# 把(theta, phi)整理成KDE需要的格式
data = np.vstack([theta, phi])
kde = gaussian_kde(data)

# 计算每个原始点的密度值
density = kde(data)

步骤2:可视化方式选择

方式A:带密度颜色的散点图

直接在你现有的ax.scatter里加上颜色参数,用密度值控制颜色:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

# 用密度值作为颜色,搭配合适的颜色映射
scatter = ax.scatter(X, Y, Z, c=density, cmap='viridis', s=10)
plt.colorbar(scatter, label='Point Density')

# 可选:画一个透明的球面作为背景参考
u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]
x_sphere = np.cos(u)*np.sin(v)
y_sphere = np.sin(u)*np.sin(v)
z_sphere = np.cos(v)
ax.plot_surface(x_sphere, y_sphere, z_sphere, color='gray', alpha=0.1)

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()

方式B:球面曲面密度图

如果想要更平滑的密度分布效果,可以把球面网格化,插值密度后渲染成曲面:

# 生成球面网格的theta和phi
theta_grid, phi_grid = np.mgrid[0:np.pi:50j, -np.pi:np.pi:100j]
# 转成笛卡尔坐标
x_grid = r.mean() * np.sin(theta_grid) * np.cos(phi_grid)
y_grid = r.mean() * np.sin(theta_grid) * np.sin(phi_grid)
z_grid = r.mean() * np.cos(theta_grid)

# 计算网格点的密度值
grid_data = np.vstack([theta_grid.ravel(), phi_grid.ravel()])
density_grid = kde(grid_data).reshape(theta_grid.shape)

# 渲染曲面
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(x_grid, y_grid, z_grid, facecolors=plt.cm.viridis(density_grid/density_grid.max()), alpha=0.8)
plt.colorbar(surf, label='Area Density')

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()

方案二:Plotly(交互式3D密度图)

如果你想要交互式的可视化(可以旋转、缩放、hover看数值),Plotly是绝佳选择,代码也很简洁:

import plotly.graph_objects as go
import numpy as np
from scipy.stats import gaussian_kde

# 同样先转球坐标计算密度
r = np.sqrt(X**2 + Y**2 + Z**2)
theta = np.arccos(Z / r)
phi = np.arctan2(Y, X)
data = np.vstack([theta, phi])
kde = gaussian_kde(data)
density = kde(data)

# 绘制交互式散点密度图
fig = go.Figure(data=[go.Scatter3d(
    x=X, y=Y, z=Z,
    mode='markers',
    marker=dict(
        size=5,
        color=density,
        colorscale='Viridis',
        colorbar=dict(title='Density'),
        opacity=0.8
    )
)])

# 可选:添加球面背景
u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]
x_sphere = np.cos(u)*np.sin(v) * r.mean()
y_sphere = np.sin(u)*np.sin(v) * r.mean()
z_sphere = np.cos(v) * r.mean()
fig.add_trace(go.Surface(
    x=x_sphere, y=y_sphere, z=z_sphere,
    colorscale=[[0, 'gray'], [1, 'gray']],
    opacity=0.1,
    showscale=False
))

fig.update_layout(scene=dict(
    xaxis_title='X',
    yaxis_title='Y',
    zaxis_title='Z'
))
fig.show()

这两个方案都不需要Mayavi,而且能完美实现你要的“用颜色映射区分点密集/稀疏区域”的需求。如果你的数据量特别大,还可以对数据做下采样后再计算密度,提升运行速度。

内容的提问来源于stack exchange,提问作者James J

火山引擎 最新活动