如何在不使用contours函数的情况下绘制平滑曲线边缘的数组图
问题:平滑imshow可视化的区域边缘,避免contours的默认插值问题
我用Python的numpy和matplotlib的imshow函数可视化二维数组,生成的图表不同颜色区域边缘呈锯齿状。想把边缘改成平滑曲线,但contours函数默认会在1(对应x)和3(对应z)之间生成2(对应y)的轮廓,和原始数据不符。有没有其他实现方法?
现有代码如下:
import numpy as np import matplotlib.pyplot as plt # Define the input data as a 2D NumPy array arr = np.array([ ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'y', 'z', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z'], ]) # Convert input data to numerical values num_arr = np.zeros(arr.shape) num_arr[arr == 'x'] = 1 num_arr[arr == 'y'] = 2 num_arr[arr == 'z'] = 3 # Create a colormap cmap = plt.get_cmap('viridis', 3) # Plot the data plt.imshow(num_arr, cmap=cmap) plt.colorbar(ticks=[1, 2, 3], format=plt.FuncFormatter(lambda val, loc: {1: 'x', 2: 'y', 3: 'z'}[val])) plt.title('Graphical Representation of Data') plt.show()
解决方案:插值提升分辨率+自定义真实边界绘制
核心思路是先对原始低分辨率数据做插值提升清晰度,再只提取真实存在的类别边界(x-y和y-z),避免contours自动生成不存在的中间边界。
完整实现代码
import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import griddata # 原始数据 arr = np.array([ ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'y', 'y', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'y', 'z', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z'], ['x', 'x', 'x', 'x', 'x', 'x', 'z', 'z', 'z', 'z', 'z'], ]) # 转数值数组 num_arr = np.zeros(arr.shape) num_arr[arr == 'x'] = 1 num_arr[arr == 'y'] = 2 num_arr[arr == 'z'] = 3 # -------------------------- # 1. 生成高分辨率网格并插值 # -------------------------- # 原始网格坐标 y_original, x_original = np.mgrid[0:num_arr.shape[0], 0:num_arr.shape[1]] # 高分辨率网格(这里把分辨率提升10倍,可调整) resolution_scale = 10 y_high, x_high = np.mgrid[0:num_arr.shape[0]:1j*(num_arr.shape[0]*resolution_scale), 0:num_arr.shape[1]:1j*(num_arr.shape[1]*resolution_scale)] # 用立方插值提升分辨率 interpolated_data = griddata((y_original.flatten(), x_original.flatten()), num_arr.flatten(), (y_high, x_high), method='cubic') # -------------------------- # 2. 提取真实存在的边界 # -------------------------- # 只绘制x-y(1和2之间)和y-z(2和3之间)的边界,跳过x-z的虚假边界 fig, ax = plt.subplots() # 绘制插值后的图像 im = ax.imshow(interpolated_data, cmap=plt.get_cmap('viridis', 3), extent=[0, num_arr.shape[1], num_arr.shape[0], 0]) # 绘制x-y边界(值为1.5的等高线) ax.contour(x_high, y_high, interpolated_data, levels=[1.5], colors='white', linewidths=2) # 绘制y-z边界(值为2.5的等高线) ax.contour(x_high, y_high, interpolated_data, levels=[2.5], colors='white', linewidths=2) # -------------------------- # 3. 调整图表样式 # -------------------------- cbar = plt.colorbar(im, ticks=[1, 2, 3]) cbar.ax.set_yticklabels(['x', 'y', 'z']) plt.title('Smooth Edge Visualization') plt.show()
关键细节说明
- 插值提升分辨率:使用
scipy.interpolate.griddata的立方插值,把原始低分辨率数据放大到更高精度的网格,让边缘过渡更平滑,避免锯齿。 - 自定义边界提取:手动指定只绘制
1.5(x和y的中间值)和2.5(y和z的中间值)的等高线,完全跳过1.5到2.5之外的虚假边界,保证和原始数据的类别对应关系一致。 - 样式调整:用白色粗线绘制边界,既突出区域划分,又保持整体视觉协调。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

