功能说明
这段代码只用标准库实现一元线性回归:输入若干 (x, y) 数据点,计算斜率、截距和决定系数 R²,并可用得到的参数预测新的 x。它适合无法引入 NumPy 的脚本、配置检查或小型报表,用来快速判断两组数值是否近似线性相关。
实现先求均值,再累加离差平方和与离差乘积和,避免直接展开大数相减带来的精度损失。整个过程是纯 Python 循环,逻辑透明,便于在受限环境中审计。
关键代码
fit_line 先检查样本数不少于 2,且 x 的离差平方和不为零;随后用 sxy / sxx 得到斜率,用 mean_y - slope * mean_x 得到截距。R² 由残差平方和与总平方和计算,总平方和为零时返回 1.0。
predict 只做一次乘加。main 用五个点演示并断言斜率约为 1.97、R² 大于 0.99。
使用注意
最小二乘对离群点非常敏感,一个极端值就可能显著改变斜率,必要时先做异常值检查。这里只支持一元线性模型,x 全部相同会抛出 ValueError。R² 接近 1 不代表因果关系,也不能说明模型适合外推。数据量很大时应改用数值库,避免纯 Python 循环的性能瓶颈。
验证方式
运行脚本会输出 slope=1.970、intercept=0.090 和接近 1 的 R²,并在 x=6 处给出约 11.910 的预测值。代码内的断言检查斜率误差小于 0.02、R² 大于 0.99。把数据点改成完全共线时 R² 应为 1;删到只剩一个点时应触发 ValueError。
代码示例
from __future__ import annotations
import math
def fit_line(points: list[tuple[float, float]]) -> tuple[float, float, float]:
count = len(points)
if count < 2:
raise ValueError("need at least two points")
mean_x = sum(x for x, _ in points) / count
mean_y = sum(y for _, y in points) / count
sxx = sum((x - mean_x) ** 2 for x, _ in points)
if sxx == 0:
raise ValueError("x values must not all be equal")
sxy = sum((x - mean_x) * (y - mean_y) for x, y in points)
slope = sxy / sxx
intercept = mean_y - slope * mean_x
ss_total = sum((y - mean_y) ** 2 for _, y in points)
ss_residual = sum((y - (slope * x + intercept)) ** 2 for x, y in points)
r_squared = 1.0 - ss_residual / ss_total if ss_total else 1.0
return slope, intercept, r_squared
def predict(slope: float, intercept: float, x: float) -> float:
return slope * x + intercept
def main() -> None:
points = [(1.0, 2.0), (2.0, 4.1), (3.0, 5.9), (4.0, 8.2), (5.0, 9.8)]
slope, intercept, r_squared = fit_line(points)
print(f"slope={slope:.3f} intercept={intercept:.3f} r2={r_squared:.4f}")
print(f"prediction at x=6: {predict(slope, intercept, 6.0):.3f}")
assert math.isclose(slope, 1.97, abs_tol=0.02)
assert 0.99 < r_squared <= 1.0
if __name__ == "__main__":
main()