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
错误原因
LightSource的使用场景错误:LightSource.shade()是为2D高程数据(比如地形高度图,二维数组)设计的,你传入的z是一维散点坐标,导致后续np.gradient计算梯度时参数不匹配,触发类型错误。- 对光源类型的误解:
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
相关产品推荐
相关产品推荐

