Skip to content

SciPy - ODR

正交距离回归 (ODR) 是一种统计技术,用于在自变量(预测变量)和因变量(响应变量)都存在测量误差时,将模型拟合到数据。这与普通最小二乘法 (OLS) 回归形成对比,OLS 假设自变量已知精确,并且只最小化平方垂直距离之和(因变量的误差)。

在 ODR 中,目标是最小化从数据点到拟合曲线或曲面的平方正交(垂直)距离之和。这使得 ODR 在所有变量测量都存在不确定性的实验科学中特别有用。

scipy.odr 包提供了执行 ODR 的工具。

想象将一条直线拟合到 (x, y) 数据点的散点图上:

  • OLS(普通最小二乘法): 最小化从每个点 $(x_i, y_i)$ 到直线 $hat{y}_i = f(x_i)$ 的平方垂直距离之和。它假设 $x_i$ 无误差。
  • 图示将显示点和垂直线,代表 OLS 最小化的误差。
  • ODR(正交距离回归): 最小化从每个点 $(x_i, y_i)$ 到直线的平方垂直距离之和。它可以考虑 $x_i$ 中的误差 $delta_i$ 和 $y_i$ 中的误差 $epsilon_i$。
  • 图示将显示点和垂直线,代表 ODR 最小化的误差。

当您有理由相信 X 的测量也存在不确定性,或者当您不想任意偏袒一个变量的误差而不是另一个变量的误差时,ODR 更适合使用。

让我们通过将线性模型 $y = mx + c$ 拟合到 x 和 y 都可能存在误差的数据来演示 ODR。我们将基于二次关系生成一些合成数据,但尝试使用 ODR 对其进行线性模型拟合。如果基础模型未知或需要更简单的近似时,可能会出现这种情况。

import numpy as np
import matplotlib.pyplot as plt
from scipy.odr import Model, Data, ODR, RealData
# 1. Define the model function for ODR
# The function should take a tuple of parameters (beta) and the input x.
# For a linear model y = m*x + c, beta = [m, c].
# 1. 定义 ODR 的模型函数
# 函数应接受一个参数元组 (beta) 和输入 x。
# 对于线性模型 y = m*x + c,beta = [m, c]。
def linear_func(beta, x):
m, c = beta
return m * x + c
# 2. Generate some sample data
# Let's create data that roughly follows y = 0.5*x^2 + noise, but we'll try to fit a line.
# 2. 生成一些样本数据
# 创建大致遵循 y = 0.5*x^2 + noise 的数据,但我们尝试拟合一条直线。
np.random.seed(0) # For reproducibility # 为了可重现性
N_points = 20
x_true = np.linspace(0, 5, N_points)
y_true = 0.5 * x_true**2
# Add some noise to both x and y to simulate measurement errors
# 向 x 和 y 都添加一些噪声以模拟测量误差
x_observed = x_true + np.random.normal(0, 0.3, N_points)
y_observed = y_true + np.random.normal(0, 1.0, N_points)
# (Optional) Specify standard deviations of errors if known (sx, sy)
# If not known, ODR can estimate them or assume they are equal.
# For this example, let's assume we don't know them perfectly.
# (可选)如果已知误差的标准差 (sx, sy),则指定
# 如果未知,ODR 可以估计它们或假设它们相等。
# 对于此示例,假设我们对其了解不完全。
# sx = np.full_like(x_observed, 0.3)
# sy = np.full_like(y_observed, 1.0)
# data = RealData(x_observed, y_observed, sx=sx, sy=sy)
# If errors are unknown or assumed to be weighted implicitly:
# 如果误差未知或假定为隐式加权:
data = RealData(x_observed, y_observed)
# 3. Create a Model object
# We're fitting a linear function
# 3. 创建一个 Model 对象
# 我们正在拟合一个线性函数
linear_model = Model(linear_func)
# 4. Set up ODR
# beta0 provides initial guesses for the parameters [m, c].
# 4. 设置 ODR
# beta0 提供参数 [m, c] 的初始猜测。
initial_guess_m = 1.0
initial_guess_c = 0.5
odr_instance = ODR(data, linear_model, beta0=[initial_guess_m, initial_guess_c])
# 5. Run the regression
# 5. 运行回归
odr_output = odr_instance.run()
# 6. Print the results
# 6. 打印结果
print("ODR Fit Results:") # ODR 拟合结果:
odr_output.pprint() # Pretty print of results # 漂亮打印结果
# Extract fitted parameters
# 提取拟合参数
best_fit_params = odr_output.beta
m_fit, c_fit = best_fit_params
print(f"\nFitted slope (m): {m_fit:.4f}") # 拟合斜率 (m):
print(f"Fitted intercept (c): {c_fit:.4f}") # 拟合截距 (c):
# Plot the results
# 绘制结果
plt.figure(figsize=(8, 6))
plt.errorbar(x_observed, y_observed, xerr=0.3, yerr=1.0, fmt='o',
label='Observed Data (with conceptual error bars)', capsize=3, alpha=0.6)
# label='Observed Data (with conceptual error bars)' # 观测数据(带概念误差条)
# capsize=3 # 误差条帽大小
# alpha=0.6 # 透明度
plt.plot(x_true, y_true, 'g--', label='True Underlying Quadratic Relation') # 真实的底层二次关系
# Plot the ODR fitted line
# 绘制 ODR 拟合直线
x_fit = np.linspace(min(x_observed), max(x_observed), 100)
y_fit_odr = linear_func(best_fit_params, x_fit)
plt.plot(x_fit, y_fit_odr, 'r-', label=f'ODR Linear Fit: y={m_fit:.2f}x + {c_fit:.2f}') # ODR 线性拟合:
plt.xlabel('X variable') # X 变量
plt.ylabel('Y variable') # Y 变量
plt.legend() # 图例
plt.title('Orthogonal Distance Regression (ODR) Example') # 正交距离回归 (ODR) 示例
plt.grid(True) # 网格
# plt.show()
# Description: This code generates synthetic data with errors in both x and y from an
# underlying quadratic relationship. It then fits a linear model using ODR. The plot
# shows the observed data points, the true quadratic curve, and the ODR fitted straight line.
# The ODR line tries to minimize perpendicular distances to the points.
# 描述:此代码生成在 x 和 y 中都包含误差的合成数据,这些数据来自
# 底层的二次关系。然后使用 ODR 拟合线性模型。图表
# 显示了观测数据点、真实的二次曲线以及 ODR 拟合的直线。
# ODR 直线尝试最小化到点的垂直距离。

odr_output.pprint() 的输出将包括:

  • Beta:估计的参数(例如,[m_fit, c_fit])。
  • Beta Std Error:估计参数的标准误差。
  • Beta Covariance:参数的协方差矩阵。
  • Residual Variance:残差的方差。
  • Inverse Condition #:问题数值稳定性的度量。
  • Reason(s) for Halting:优化算法停止的原因(例如,收敛)。

pprint() 的示例(精简)输出:

Beta: [ 2.4591797 -1.12958544]
Beta Std Error: [0.30591196 0.86113156]
Beta Covariance: [[ 0.2266773 -0.59849207]
[-0.59849207 1.7912691 ]]
Residual Variance: 0.413024911864548
Inverse Condition #: 0.0940618500416838
Reason(s) for Halting:
Sum of squares convergence

这意味着 ODR 拟合找到一条直线 $y \approx 2.46x - 1.13$,作为考虑了 x 和 y 测量误差的噪声二次数据的最佳线性近似。

  • Model(fcn, ...):定义要拟合的模型函数 fcn(beta, x)。beta 是模型参数的序列,x 是自变量。
  • Data(x, y, wd=None, we=None, ...) 或 RealData(x, y, sx=None, sy=None, ...):保存输入数据。当您有标准差 (sx, sy) 的估计值时,RealData 很方便。如果未指定误差,可能会根据 ODR 设置进行估计或假定它们相等。
  • ODR(data, model, beta0, ...):设置 ODR 问题的主类。beta0 是参数的初始猜测。
  • odr_instance.run():执行 ODR 算法。
  • odr_output.beta:估计的最优参数。
  • odr_output.sd_beta:估计参数的标准误差。

当自变量中的误差很大时,scipy.odr 模块在拟合方面非常强大。它支持显式、隐式、单变量和多变量模型。更多详细信息和高级配置,请参阅官方 SciPy ODR 文档。