如何在Python中去除异常值以实现更优曲线拟合?
如何处理图像曲线拟合中的异常值干扰?
我正尝试使用从图像中获取的数据进行曲线拟合,但有时图像中存在对应曲线之外的额外点,导致拟合结果不准确。例如右侧的数据点使拟合曲线出现偏离(见附图)。我当前使用的代码如下:
# CURVE trend = np.polyfit(x_list,y_list,2) trendpoly = np.poly1d(trend) new_x = np.linspace(min(x_list), max(x_list)) new_y = trendpoly(new_x) plt.plot(x_list, y_list, 'o', new_x, new_y,'*')
补充说明:处理的是相机采集的图像,经处理后获取曲线数据,理想状态为特定曲线(见理想场景图),期望得到对应的拟合效果(见预期拟合图)。但采集的图像出现偏差时,会有干扰点破坏拟合效果,且由于未知图形形态,无法确定更高次多项式的次数。请问是否有方法检测此类异常值并实现更优拟合?
解决方案
1. 用稳健拟合替代普通最小二乘拟合
普通np.polyfit基于最小二乘法,对异常值极度敏感。改用稳健拟合算法,通过鲁棒损失函数降低异常值的权重,能直接得到更可靠的拟合结果,无需手动剔除点。
示例代码:
import numpy as np from scipy.optimize import least_squares import matplotlib.pyplot as plt # 定义多项式残差计算函数 def poly_residuals(params, x, y): return y - np.polyval(params, x) # 初始参数猜测(以二次多项式为例) initial_guess = np.array([1, 0, 0]) # 使用Huber损失做稳健拟合,大残差点会被降权 result = least_squares(poly_residuals, initial_guess, args=(x_list, y_list), loss='huber') # 生成拟合多项式 trendpoly_robust = np.poly1d(result.x) # 对比绘图 new_x = np.linspace(min(x_list), max(x_list), 100) plt.plot(x_list, y_list, 'o', label='原始数据') plt.plot(new_x, np.polyval(trend, new_x), '*', label='普通拟合') plt.plot(new_x, trendpoly_robust(new_x), 'r-', label='稳健拟合') plt.legend() plt.show()
2. 先检测剔除异常值再拟合
如果希望先明确移除异常点,可通过以下两种方法实现:
- 残差阈值法:先做初步拟合,计算每个点的残差,将残差超过3倍标准差的点标记为异常值剔除;
- 聚类法:用DBSCAN聚类,远离主数据簇的点直接判定为异常值。
示例:残差阈值法
import numpy as np import matplotlib.pyplot as plt # 初步拟合 trend_initial = np.polyfit(x_list, y_list, 2) trendpoly_initial = np.poly1d(trend_initial) y_pred_initial = trendpoly_initial(x_list) # 计算残差与标准差 residuals = y_list - y_pred_initial std_residual = np.std(residuals) # 筛选正常点(阈值可根据实际场景调整) mask = np.abs(residuals) < 3 * std_residual x_clean = x_list[mask] y_clean = y_list[mask] # 用清洗后的数据重新拟合 trend_clean = np.polyfit(x_clean, y_clean, 2) trendpoly_clean = np.poly1d(trend_clean) # 绘图展示 new_x = np.linspace(min(x_list), max(x_list), 100) plt.plot(x_list, y_list, 'o', label='原始数据') plt.plot(new_x, trendpoly_initial(new_x), '*', label='初步拟合') plt.plot(new_x, trendpoly_clean(new_x), 'r-', label='清洗后拟合') plt.scatter(x_list[~mask], y_list[~mask], c='red', s=50, label='异常值') plt.legend() plt.show()
3. 非参数拟合(无需预设多项式次数)
如果不确定曲线的具体形态,可使用**LOESS(局部加权散点平滑)**这类非参数方法,它不需要预设多项式次数,且对局部异常值的鲁棒性更强。
示例代码:
import numpy as np import matplotlib.pyplot as plt import statsmodels.api as sm # LOESS要求x有序,先排序数据 sorted_indices = np.argsort(x_list) x_sorted = x_list[sorted_indices] y_sorted = y_list[sorted_indices] # 执行LOESS拟合,frac控制平滑程度(0-1,值越大拟合越平滑) lowess_result = sm.nonparametric.lowess(y_sorted, x_sorted, frac=0.3) # 提取拟合结果 x_fit = lowess_result[:, 0] y_fit = lowess_result[:, 1] # 绘图 plt.plot(x_list, y_list, 'o', label='原始数据') plt.plot(x_fit, y_fit, 'r-', label='LOESS拟合') plt.legend() plt.show()
内容的提问来源于stack exchange,提问作者Javier Piña Camacho
相关产品推荐
相关产品推荐

