You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

3D散点图中心光源设置及点强度计算报错排查

3D散点图中心光源设置与光照强度计算问题解决

问题描述

需求:

  • 将光源设置在3D散点图的中心位置
  • 获取图中所有点在该光源下的光照强度

运行代码时触发TypeError,代码及报错信息如下:

原代码

import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.colors import LightSource
df=pd.read_csv('wr.csv')
col_name='pH'
xmin=df.index.min()
xmax=df.index.max()
ymin=df[col_name].min()
ymax=df[col_name].max()
zval=np.mean(np.square(df[col_name]))
x = df.index
y = df[col_name].values
z = np.square(df[col_name].values)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.scatter(df.index, df[col_name], np.square(df[col_name]), s=0.5,color='black')
ax.scatter(np.mean(df.index),np.mean(df[col_name]),zval,color='orange',s=300)
ax.plot_surface([[xmin, xmax], [xmin, xmax]], [[ymin, ymin], [ymax, ymax]], np.array([[zval, zval], [zval, zval]]), alpha=0.5,color='blue')
ax.set_xlabel('X-axis')
ax.set_ylabel('Y-axis')
ax.set_zlabel('Z-axis')
 
ls = LightSource()
shading_array = ls.shade(z, cmap=plt.get_cmap('cool'))
print(shading_array)

报错信息

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[49], line 20
     17 ax.set_zlabel('Z-axis')
     19 ls = LightSource()
---> 20 shading_array = ls.shade(z, cmap=plt.get_cmap('cool'))

File ~\anaconda3\envs\py11\Lib\site-packages\matplotlib\colors.py:2428, in LightSource.shade(self, data, cmap, norm, blend_mode, vmin, vmax, vert_exag, dx, dy, fraction, **kwargs)
   2425     norm = Normalize(vmin=vmin, vmax=vmax)
   2427 rgb0 = cmap(norm(data))
-> 2428 rgb1 = self.shade_rgb(rgb0, elevation=data, blend_mode=blend_mode,
   2429                       vert_exag=vert_exag, dx=dx, dy=dy,
   2430                       fraction=fraction, **kwargs)
   2431 # Don't overwrite the alpha channel, if present.
   2432 rgb0[..., :3] = rgb1[..., :3]

File ~\anaconda3\envs\py11\Lib\site-packages\matplotlib\colors.py:2483, in LightSource.shade_rgb(self, rgb, elevation, fraction, blend_mode, vert_exag, dx, dy, **kwargs)
   2437 """
   2438 Use this light source to adjust the colors of the *rgb* input array to
   2439 give the impression of a shaded relief map with the given *elevation*.
   (...)
   2480     An (m, n, 3) array of floats ranging between 0-1.
   2481 """
   2482 # Calculate the "hillshade" intensity.
-> 2483 intensity = self.hillshade(elevation, vert_exag, dx, dy, fraction)
   2484 intensity = intensity[..., np.newaxis]
   2486 # Blend the hillshade and rgb data using the specified mode

File ~\anaconda3\envs\py11\Lib\site-packages\matplotlib\colors.py:2302, in LightSource.hillshade(self, elevation, vert_exag, dx, dy, fraction)
   2299 dy = -dy
   2301 # compute the normal vectors from the partial derivatives
-> 2302 e_dy, e_dx = np.gradient(vert_exag * elevation, dy, dx)
   2304 # .view is to keep subclasses
   2305 normal = np.empty(elevation.shape + (3,)).view(type(elevation))

File <__array_function__ internals>:200, in gradient(*args, **kwargs)

File ~\anaconda3\envs\py11\Lib\site-packages\numpy\lib\function_base.py:1184, in gradient(f, axis, edge_order, *varargs)
   1182         dx[i] = diffx
   1183 else:
-> 1184     raise TypeError("invalid number of arguments")
   1186 if edge_order > 2:
   1187     raise ValueError("'edge_order' greater than 2 not supported")

TypeError: invalid number of arguments

错误原因

  1. LightSource的使用场景错误:LightSource.shade()是为2D高程数据(比如地形高度图,二维数组)设计的,你传入的z是一维散点坐标,导致后续np.gradient计算梯度时参数不匹配,触发类型错误。
  2. 对光源类型的误解:LightSource模拟的是平行光(固定方位角/高度角),无法实现你需要的中心点光源效果。

解决方案

要实现中心点光源,需要手动计算每个散点的光照强度:

  • 基于平方反比定律(点光源光照衰减规律)
  • 结合漫反射分量(光源向量与散点法线的夹角余弦值)

修改后的代码

import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

# 读取数据
df = pd.read_csv('wr.csv')
col_name = 'pH'

# 计算中心光源坐标
center_x = df.index.mean()
center_y = df[col_name].mean()
center_z = np.mean(np.square(df[col_name]))

# 获取所有散点的三维坐标
x = df.index.values
y = df[col_name].values
z = np.square(df[col_name]).values

# 计算每个点到中心光源的向量
vec_x = x - center_x
vec_y = y - center_y
vec_z = z - center_z

# 计算点到光源的距离,避免中心点除以0
distance = np.sqrt(vec_x**2 + vec_y**2 + vec_z**2)
distance[distance == 0] = 1e-8

# 计算光照强度:平方反比 + 漫反射(假设散点法线指向z轴)
# 漫反射分量:光源单位向量与法线(0,0,1)的点积
dot_product = vec_z / distance
# 平方反比衰减
intensity = dot_product / (distance**2)
# 归一化到0-1区间,方便颜色映射
intensity = (intensity - intensity.min()) / (intensity.max() - intensity.min())

# 绘图
fig = plt.figure()
ax = fig.add_subplot(projection='3d')

# 用光照强度作为颜色绘制散点
scatter = ax.scatter(x, y, z, s=0.5, c=intensity, cmap='cool')
# 标记中心光源
ax.scatter(center_x, center_y, center_z, color='orange', s=300, label='Center Light Source')
# 绘制原代码中的蓝色平面
xmin, xmax = df.index.min(), df.index.max()
ymin, ymax = df[col_name].min(), df[col_name].max()
ax.plot_surface([[xmin, xmax], [xmin, xmax]], 
                [[ymin, ymin], [ymax, ymax]], 
                np.array([[center_z, center_z], [center_z, center_z]]), 
                alpha=0.5, color='blue')

ax.set_xlabel('X-axis')
ax.set_ylabel('Y-axis')
ax.set_zlabel('Z-axis')
plt.colorbar(scatter, label='Light Intensity')
plt.legend()
plt.show()

# 输出每个点的光照强度
print("每个点的光照强度:")
print(intensity)

关键说明

  • 代码中默认散点法线指向z轴,如果你需要模拟不同朝向的散点,可修改法线向量重新计算点积
  • 光照强度的归一化是为了适配颜色映射,如果你需要真实物理强度值,可去掉归一化步骤

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 09:47:02