使用pyplot绘制3D图报错ValueError:形状不匹配问题排查
3D曲面图生成报错:shape mismatch问题排查与修复
问题概述
生成模糊逻辑模型的3D曲面图时触发维度不匹配错误,报错信息显示ValueError: shape mismatch: objects cannot be broadcast to a single shape,目标是生成类似示例的3D图表,当前视图窗口可正常打开,问题出在数据维度与赋值逻辑上。
报错回溯
ValueError Traceback (most recent call last) <ipython-input-79-bb38fe77253e> in <module>() 13 fig = plt.figure() 14 ax = fig.gca(projection='3d') ---> 15 surf = ax.plot_surface(s, v, pred_val, cmap=cm.coolwarm, linewidth=0, antialiased=False) 16 fig.colorbar(surf, shrink=0.5, aspect=5) 17 plt.show() 2 frames /usr/local/lib/python3.7/dist-packages/mpl_toolkits/mplot3d/axes3d.py in plot_surface(self, X, Y, Z, norm, vmin, vmax, lightsource, *args, **kwargs) 1502 1503 # TODO: Support masked arrays -> 1504 X, Y, Z = np.broadcast_arrays(X, Y, Z) 1505 rows, cols = Z.shape 1506 <__array_function__ internals> in broadcast_arrays(*args, **kwargs) /usr/local/lib/python3.7/dist-packages/numpy/lib/stride_tricks.py in broadcast_arrays(subok, *args) 536 args = [np.array(_m, copy=False, subok=subok) for _m in args] 537 -> 538 shape = _broadcast_shape(*args) 539 540 if all(array.shape == shape for array in args): /usr/local/lib/python3.7/dist-packages/numpy/lib/stride_tricks.py in _broadcast_shape(*args) 418 # use the old-iterator because np.nditer does not handle size 0 arrays 419 # consistently -> 420 b = np.broadcast(*args[:32]) 421 # unfortunately, it cannot handle 32 or more arguments directly 422 for pos in range(32, len(args), 31): ValueError: shape mismatch: objects cannot be broadcast to a single shape
原代码
d = 0.2 i_min = 1 / d i_max = pipe.max_slope(d=d) # float value slope = ctrl.Antecedent(np.arange(i_min, i_max + 1, 1), 'slope') v_min = 0 v_max = 5 velocity = ctrl.Antecedent(np.arange(v_min, v_max, 0.1), 'velocity') diameter = ctrl.Consequent(np.arange(1, 4, 1), 'diameter') # Populate slope with membership functions. slope['low'] = fuzz.trapmf(slope.universe, [i_min, i_min, i_min + 20, 50]) slope['medium'] = fuzz.trapmf(slope.universe, [25, 40, 120, 150]) slope['high'] = fuzz.trapmf(slope.universe, [100, 150, i_max, i_max]) # Populate velocity with membership functions. velocity['low'] = fuzz.trimf(velocity.universe, [v_min, v_min, 0.5 * v_max]) velocity['medium'] = fuzz.trimf(velocity.universe, [v_min, 0.5 * v_max, v_max]) velocity['high'] = fuzz.trimf(velocity.universe, [0.5 * v_max, v_max, v_max]) # Populate dimension diameter['reduction'] = fuzz.trimf(diameter.universe, [1, 1, 1]) diameter['optimal'] = fuzz.trimf(diameter.universe, [1, 2, 3]) diameter['increase'] = fuzz.trimf(diameter.universe, [2, 3, 3]) # Define rules r1 = ctrl.Rule(slope['low'] & velocity['low'] | velocity['medium'], diameter['reduction']) r2 = ctrl.Rule(slope['low'] & velocity['high'] | velocity['medium'], diameter['optimal']) r3 = ctrl.Rule(slope['medium'] & velocity['medium'], diameter['optimal']) r4 = ctrl.Rule(slope['high'] & velocity['high'] | slope['medium'], diameter['increase']) # Create model diameter_ctrl = ctrl.ControlSystem([r1, r2, r3, r4]) diameters = ctrl.ControlSystemSimulation(diameter_ctrl) # Model using diameters.input['slope'] = 150 diameters.input['velocity'] = 5 diameters.compute() print(diameters.output['diameter']) # Create 3D chart s = np.arange(i_min, i_max + 1, 1) v = np.arange(v_min, v_max + 0.1, 0.1) s, v = np.meshgrid(s, v) pred_val: np.ndarray = np.zeros(shape=(len(v), len(s))) for i in range(len(s)): for j in range(len(v)): diameters.input['slope'] = i_min + i diameters.input['velocity'] = v_min + j diameters.compute() pred_val[i][j] = diameters.output['diameter'] fig = plt.figure() ax = fig.gca(projection='3d') surf = ax.plot_surface(s, v, pred_val, cmap=cm.coolwarm, linewidth=0, antialiased=False) fig.colorbar(surf, shrink=0.5, aspect=5) plt.show()
问题分析
循环索引与赋值逻辑错误:
meshgrid生成的s和v是二维数组,维度为(len(v_original), len(s_original)),原循环用range(len(s))遍历的是二维数组的行数,而非原始s的元素数量- 赋值时用
i_min + i和v_min + j是错误的,应该直接使用meshgrid生成的实际坐标值,而非索引偏移 pred_val的赋值索引颠倒,原代码初始化维度为(len(v), len(s)),但循环中用pred_val[i][j]赋值,导致维度不匹配
数组范围不一致:
- 生成
v数组时用np.arange(v_min, v_max + 0.1, 0.1),超出了velocity变量的universe范围(原universe是到v_max=5),可能导致模糊逻辑计算异常
- 生成
修复方案
- 遍历
meshgrid生成的每个网格点,直接获取对应的slope和velocity数值 - 修正
pred_val的赋值索引,保证与s、v的维度一致 - 统一
v数组的范围,与velocity的universe保持一致
修复后代码
d = 0.2 i_min = 1 / d i_max = pipe.max_slope(d=d) # float value slope = ctrl.Antecedent(np.arange(i_min, i_max + 1, 1), 'slope') v_min = 0 v_max = 5 velocity = ctrl.Antecedent(np.arange(v_min, v_max, 0.1), 'velocity') diameter = ctrl.Consequent(np.arange(1, 4, 1), 'diameter') # Populate slope with membership functions. slope['low'] = fuzz.trapmf(slope.universe, [i_min, i_min, i_min + 20, 50]) slope['medium'] = fuzz.trapmf(slope.universe, [25, 40, 120, 150]) slope['high'] = fuzz.trapmf(slope.universe, [100, 150, i_max, i_max]) # Populate velocity with membership functions. velocity['low'] = fuzz.trimf(velocity.universe, [v_min, v_min, 0.5 * v_max]) velocity['medium'] = fuzz.trimf(velocity.universe, [v_min, 0.5 * v_max, v_max]) velocity['high'] = fuzz.trimf(velocity.universe, [0.5 * v_max, v_max, v_max]) # Populate dimension diameter['reduction'] = fuzz.trimf(diameter.universe, [1, 1, 1]) diameter['optimal'] = fuzz.trimf(diameter.universe, [1, 2, 3]) diameter['increase'] = fuzz.trimf(diameter.universe, [2, 3, 3]) # Define rules r1 = ctrl.Rule(slope['low'] & velocity['low'] | velocity['medium'], diameter['reduction']) r2 = ctrl.Rule(slope['low'] & velocity['high'] | velocity['medium'], diameter['optimal']) r3 = ctrl.Rule(slope['medium'] & velocity['medium'], diameter['optimal']) r4 = ctrl.Rule(slope['high'] & velocity['high'] | slope['medium'], diameter['increase']) # Create model diameter_ctrl = ctrl.ControlSystem([r1, r2, r3, r4]) diameters = ctrl.ControlSystemSimulation(diameter_ctrl) # Model using diameters.input['slope'] = 150 diameters.input['velocity'] = 5 diameters.compute() print(diameters.output['diameter']) # Create 3D chart s_original = np.arange(i_min, i_max + 1, 1) v_original = np.arange(v_min, v_max, 0.1) s, v = np.meshgrid(s_original, v_original) # 初始化pred_val维度与s、v一致 pred_val = np.zeros_like(s) # 遍历每个网格点 for j in range(s.shape[0]): for i in range(s.shape[1]): diameters.input['slope'] = s[j, i] diameters.input['velocity'] = v[j, i] diameters.compute() pred_val[j, i] = diameters.output['diameter'] fig = plt.figure() ax = fig.add_subplot(projection='3d') # 替代gca,更规范 surf = ax.plot_surface(s, v, pred_val, cmap='coolwarm', linewidth=0, antialiased=False) fig.colorbar(surf, shrink=0.5, aspect=5) plt.show()
内容的提问来源于stack exchange,提问作者Rafał
相关产品推荐
相关产品推荐

