插值生成更多数据点后还原阶梯状绘图的技术问题
阶梯图插值后图形不一致的解决方法
问题背景
我用以下代码绘制出了阶梯状图:
import numpy as np from scipy.interpolate import interp1d import matplotlib.pyplot as plt x = np.array([3.45, 3.88, 3.99, 4.33]) y = np.array([14.0, 11.0, 14.0, 31.0]) y = np.cumsum(y) y0 = np.array([0]) x0 = np.interp([0], y, x) x = np.concatenate([x0, x]) y = np.concatenate([y0, y]) # Plot fig, ax = plt.subplots() ax.step(x, y, color='r', where='post') ax.set_xlim(2, 5) ax.set_ylim(0, y.max()) ax.invert_yaxis() plt.show()

现在我需要通过插值函数生成更多x、y数据点,同时保持阶梯图和原图完全一致。但尝试以下代码后,生成的图形和原图不符:

import numpy as np from scipy.interpolate import interp1d import matplotlib.pyplot as plt x = np.array([3.45, 3.88, 3.99, 4.33]) y = np.array([14.0, 11.0, 14.0, 31.0]) y = np.cumsum(y) y0 = np.array([0]) x0 = np.interp([0], y, x) x = np.concatenate([x0, x]) y = np.concatenate([y0, y]) # 生成更多数据点 NUM = 100 # 修改点的数量 funct = interp1d(x, y, kind='next') x_cont = np.linspace(x[0], x[-1], NUM) y_cont = funct(x_cont) # 绘图 fig, ax1 = plt.subplots() ax1.step(x_cont, y_cont, color='r', where='post') ax1.set_xlim(2, 5) ax1.set_ylim(0, y.max()) ax1.invert_yaxis() plt.show()
问题原因
问题出在插值方向和step函数的where='post'参数不匹配:
- 原代码中,
ax.step(x, y, where='post')表示在每个x节点之后才跳跃到对应的y值,即阶梯在x节点处保持左侧y值,过了节点才切换到下一个y。 - 你用
interp1d(x, y, kind='next')做插值,kind='next'会取当前x右侧的第一个y值,这和step的post逻辑冲突,导致插值后的y值提前跳跃,最终图形变形。
解决方案
要保持图形一致,需要让插值逻辑和step的post逻辑匹配,同时确保插值后的点包含所有原始节点(避免丢失阶梯的关键跳跃点)。以下是两种可行方法:
方法一:使用kind='previous'插值
把插值方法换成kind='previous',它会取当前x左侧的最后一个y值,正好对应step的post逻辑:
import numpy as np from scipy.interpolate import interp1d import matplotlib.pyplot as plt x = np.array([3.45, 3.88, 3.99, 4.33]) y = np.array([14.0, 11.0, 14.0, 31.0]) y = np.cumsum(y) y0 = np.array([0]) x0 = np.interp([0], y, x) x = np.concatenate([x0, x]) y = np.concatenate([y0, y]) NUM = 100 # 改用previous插值,匹配step的post逻辑 funct = interp1d(x, y, kind='previous') x_cont = np.linspace(x[0], x[-1], NUM) y_cont = funct(x_cont) fig, ax1 = plt.subplots() ax1.step(x_cont, y_cont, color='r', where='post') ax1.set_xlim(2, 5) ax1.set_ylim(0, y.max()) ax1.invert_yaxis() plt.show()
方法二:保留原始节点的插值(更稳妥)
如果担心插值方法的适配问题,可以直接在生成的插值点中强制包含所有原始x节点,既保证阶梯的关键跳跃点,又能增加中间点:
import numpy as np from scipy.interpolate import interp1d import matplotlib.pyplot as plt x = np.array([3.45, 3.88, 3.99, 4.33]) y = np.array([14.0, 11.0, 14.0, 31.0]) y = np.cumsum(y) y0 = np.array([0]) x0 = np.interp([0], y, x) x = np.concatenate([x0, x]) y = np.concatenate([y0, y]) NUM = 100 # 生成包含原始节点的插值点:先生成均匀点,再合并原始点并去重排序 x_uniform = np.linspace(x[0], x[-1], NUM) x_cont = np.unique(np.concatenate([x_uniform, x])) # 用previous插值保证阶梯逻辑 funct = interp1d(x, y, kind='previous') y_cont = funct(x_cont) fig, ax1 = plt.subplots() ax1.step(x_cont, y_cont, color='r', where='post') ax1.set_xlim(2, 5) ax1.set_ylim(0, y.max()) ax1.invert_yaxis() plt.show()
效果验证
两种方法生成的图形都会和原图完全一致,仅数据点数量更多。它们都保证了在原始x节点处的y值跳跃逻辑和原代码一致,不会出现提前或延迟跳跃的情况。
内容的提问来源于stack exchange,提问作者pro
相关产品推荐
相关产品推荐

