C00 · 第 6 章 · 第三编 工程基础与综合复习

编程与科学计算综合项目

以落体测量数据估计重力加速度为可复算小项目,串联输入输出契约、函数分解、集合与文件、异常处理、数组加权拟合、浮点精度、残差可视化、实验记录、测试、版本和环境交付。

报告页面错误
预备知识测试、版本控制与环境管理程序、类型、控制流与函数集合数据、文件与错误处理数组、向量化与数值精度数据可视化与计算实验

本章目标

  1. 把科学问题写成带单位、schema、失败行为和产物的输入输出契约。
  2. 将解析、验证、分组、拟合、制图和写出拆成可独立测试函数。
  3. 使用集合与数组表达记录数据,并检查形状、dtype、有限值和条件数。
  4. 由加权最小二乘估计参数、不确定度和残差,区分随机与系统误差。
  5. 设计能揭示模型失配且不夸大证据的图表和数据摘要。
  6. 用测试、版本、环境、输入哈希和复现命令交付完整项目。
页面阅读位置0% · 仅保存在此浏览器
章节未开始
本册完成进度0/6 章 · 0%
本页目录

项目问题:从高度—时间数据估计重力加速度

目标是读取多次落体实验的测量文件,对每个试次拟合

h(t)=h0+v0t12gt2,h(t)=h_0+v_0t-\frac12gt^2,

输出 gg、标准误、样本数和残差诊断,再汇总试次。高度 hh 用 m,时间 tt 用 s,ggms2\mathrm{m\,s^{-2}}。该模型假设测量区间内重力近似恒定、竖直坐标向上、阻力可忽略、计时与高度误差满足拟合假设。程序得到一个精确小数,不会自动证明这些物理假设。

项目完成标准预先固定:合法输入可从干净环境用一条命令生成结构化汇总、图件和 manifest;非法输入返回非零退出状态且不留下被误认作成功的最终产物;合成基准恢复已知 gg;真实数据结果带数值与模型边界。这样“能运行”与“可交付”有清楚区别。

输入、输出与失败契约

输入为 UTF-8 CSV,列固定为 trial_id,time_s,height_m,sigma_height_mtrial_id 是非空字符串;后三列必须可解析为有限浮点数;时间非负,同一试次严格递增;高度不预设正负,但单位固定为 m;标准不确定度必须大于零。生产拟合每试次至少五点,以留下残差自由度。文件头缺失、重复时间、非有限值、负不确定度和点数不足都是输入错误,而不是自动丢行。

输出目录包含:summary.csv,每试次一行并给 g_m_s2,se_g_m_s2,n,rmse_m,status;每试次的拟合与残差图;manifest.json,保存输入哈希、参数、代码标识、环境标识、运行命令和产物哈希;人类可读说明。列名携带单位,防止后续把 m 与 mm 混用。原始输入只读,不在原地“清洗”。

默认策略是全有或全无:任何试次失败,最终汇总不发布。若明确启用部分结果模式,每个失败试次必须在汇总中有状态和原因,报告标题也标明不完整,不能静默跳过。所有产物先写临时目录,全部成功并校验后再原子重命名到最终目录。

例 1:一条坏记录如何穿过失败路径

文件前两行属于试次 A:

A,0.00,1.000,0.002

A,0.10,0.951,0.000

第二行不确定度为零,违反“严格大于零”。解析器应产生含文件名、物理行号、字段名和原始值的输入异常;顶层入口捕获后写 stderr、返回非零状态,并删除临时目录。它不应把零换成一个很小正数,也不应继续生成看似完整的 summary.csv。修正输入后重新运行会产生新的输入哈希和 run ID。

函数分解让数据流可检查

顶层 main 只负责参数、调用顺序和退出状态。read_rows(path) 管理文件资源并返回原始记录;parse_record 做类型转换;validate_records 检查跨行不变量;group_trials 建立 trial_id → records 映射;fit_trial 只接收数组并返回不可变结果;make_figures 从结果与观测生成图;write_outputs 负责临时写出和提交;build_manifest 汇总来源。

每个函数的输入和返回值都不依赖隐式全局目录、当前随机状态或上次运行缓存。路径和参数由入口显式传入,日志带 run ID。底层函数不吞异常再返回空列表,因为“无数据”和“读取失败”语义不同;它可添加字段上下文后继续抛出,由最有能力决定策略的边界层处理。

这种分解还隔离副作用。解析与拟合可以在内存中测试,文件系统测试只覆盖读取、原子提交和权限失败。制图不重新读取原始 CSV 或重新拟合,以免同一 run 的表格和图使用不同过滤逻辑。

集合、文件与确定顺序

逐行记录最初用列表保留文件顺序;分组使用映射按试次索引;集合可检查重复主键,但集合无业务顺序。输出排序规则显式规定为按 trial_id 的规范化文本顺序,而不依赖哈希遍历或文件偶然顺序。每个试次内部按已验证时间排列;若契约禁止乱序,就应报错而不是偷偷排序掩盖采集问题。

CSV 解析使用标准字段规则处理引号、逗号和换行,不用字符串简单切分。读取时指定编码和换行策略;写出时固定小数序列化、缺失表示和列顺序。大文件可流式解析,但跨行验证与按试次拟合需要状态;内存方案的允许文件大小也应写入契约。

文件操作使用上下文管理保证异常时关闭。最终目录若已存在,默认拒绝覆盖;显式覆盖也先备份或以新 run ID 写入。对产物计算哈希发生在文件关闭后,避免缓冲未落盘时记录错误摘要。

数组形状、加权拟合与单位

对单个试次构造数组 t,h,σt,h,\sigma,都为一维、长度 nn、dtype 至少为约定浮点精度。先令 τ=ttˉ\tau=t-\bar t 降低大时间原点导致的条件问题,再构建设计矩阵

X=[1τ1τ121τnτn2],W=diag(σi2).X=\begin{bmatrix} 1&\tau_1&\tau_1^2\\ \vdots&\vdots&\vdots\\ 1&\tau_n&\tau_n^2 \end{bmatrix}, \qquad W=\operatorname{diag}(\sigma_i^{-2}).

加权最小二乘最小化 i[(hiXiβ)/σi]2\sum_i[(h_i-X_i\beta)/\sigma_i]^2。教学公式

β^=(XTWX)1XTWh\hat\beta=(X^TWX)^{-1}X^TWh

说明数学结构,程序应使用 QR、SVD 或稳定线性求解而不显式求逆。系数单位依次为 m、ms1\mathrm{m\,s^{-1}}ms2\mathrm{m\,s^{-2}},估计 g^=2β^2\hat g=-2\hat\beta_2

σi\sigma_i 是可信独立标准差,参数协方差近似 (XTWX)1(X^TWX)^{-1};若只知道相对权重而共同噪声未知,还要由残差估计尺度。时间误差、相关高度误差或模型失配不满足这一简单公式,需要正交距离、广义最小二乘或显式系统误差分析。

例 2:三点合成数据恢复 $g$

作为最小内核 oracle,取等间隔 t=(0,0.1,0.2)st=(0,0.1,0.2)\,\mathrm s,高度 h=(1.000,0.951,0.804)mh=(1.000,0.951,0.804)\,\mathrm m。二阶有限差分给

h22h1+h0(0.1s)2=0.8042(0.951)+1.0000.01=9.8ms2.\frac{h_2-2h_1+h_0}{(0.1\,\mathrm s)^2} =\frac{0.804-2(0.951)+1.000}{0.01} =-9.8\,\mathrm{m\,s^{-2}}.

h=gh''=-g,得到 g=9.8ms2g=9.8\,\mathrm{m\,s^{-2}}。这三点足以验证二次系数符号和单位,但没有残差自由度,不是生产不确定度估计;生产契约仍要求至少五点。

浮点精度、条件数与失败而非猜测

若绝对时间戳约为 106s10^6\,\mathrm s,试次只跨越 0.2s0.2\,\mathrm s,直接使用 1,t,t21,t,t^2 会让列尺度相差巨大,并在相减中损失有效位。中心化时间、按量级缩放列,再用稳定分解可改善条件。输出参数要反变换回原单位,并通过合成数据测试。

程序计算设计矩阵的秩和条件数。秩不足、时间跨度过小或条件数超过预设可靠范围时抛出 FitError,错误包含试次、点数、时间范围和诊断量;不能继续返回巨大 gg 再让用户凭眼睛删掉。浮点溢出、nan 和线性代数失败都在拟合边界转为带上下文异常。

例 3:时间中心化保留有效变化

试次时间为 1000000.001\,000\,000.001000000.101\,000\,000.101000000.20s1\,000\,000.20\,\mathrm s。直接平方约为 1012s210^{12}\,\mathrm{s^2},而决定曲率的差来自 102s210^{-2}\,\mathrm{s^2} 量级。取均值 tˉ=1000000.10s\bar t=1\,000\,000.10\,\mathrm s 后,

τ=(0.10,0,0.10)s,τ2=(0.01,0,0.01)s2.\tau=(-0.10,0,0.10)\,\mathrm s, \qquad \tau^2=(0.01,0,0.01)\,\mathrm{s^2}.

模型预测不变,但矩阵列尺度更合理。测试应比较中心化前后的物理预测在误差内一致,并要求中心化实现的条件数更低;不能只比较内部系数字节。

残差图是模型检查,不是装饰

每个试次第一幅图显示观测高度、垂直不确定度和拟合曲线,坐标轴写 时间 / s高度 / m。第二幅显示标准化残差 ri=(hih^i)/σir_i=(h_i-\hat h_i)/\sigma_i 对时间,并有零线。残差若呈抛物之外的系统弯曲,可能有空气阻力或计时偏差;方差随时间增长提示误差模型不恒定;单个极端点提示采集问题,但不能只因不好看就删除。

图的标题包含 trial ID 而非主观结论,颜色不是唯一编码,还用点形、线型和图例。汇总图显示各试次 gg 与误差条,并另给机器可读数据。轴范围覆盖全部合法数据;若使用截断轴需显式标记,不能夸大微小差异。图生成函数接收已经验证的结果对象,失败试次不画伪空图。

例 4:加权汇总与共同系统误差

三个独立试次给 g=(9.78,9.82,9.80)ms2g=(9.78,9.82,9.80)\,\mathrm{m\,s^{-2}},标准误 (0.04,0.02,0.03)ms2(0.04,0.02,0.03)\,\mathrm{m\,s^{-2}}。按 wi=1/si2w_i=1/s_i^2 的加权均值为

gˉw=iwigiiwi=9.809ms2,sgˉ=1iwi=0.015ms2.\bar g_w=\frac{\sum_iw_ig_i}{\sum_iw_i} =9.809\,\mathrm{m\,s^{-2}}, \qquad s_{\bar g}=\frac1{\sqrt{\sum_iw_i}}=0.015\,\mathrm{m\,s^{-2}}.

若三个试次共享同一高度标尺校准误差,该系统项不会被加权平均缩小,必须另列或写入协方差;只画较小的 0.0150.015 误差条会过度精确。

测试矩阵覆盖数据流和失败流

解析测试含正常、单试次、Unicode ID、字段缺失、重复时间、非有限值和权限失败。拟合测试含已知合成二次曲线、加常数高度不改变 gg、时间平移不改变预测、噪声下置信覆盖和秩不足异常。输出测试检查列、单位、确定排序、原子提交及已有目录策略。图测试先检查底层数据系列、轴标签和残差,再少量检查渲染。

端到端基准使用一个很小、可审阅的输入目录,运行公开入口并核对退出状态、产物清单、关键数值和 manifest 关系。测试不与真实研究数据路径绑定。修复任何生产缺陷时保存最小失败输入;随机 bootstrap 若启用,记录生成器、种子和副本规则,并用固定小样本回归加独立统计测试。

容差来自合成数据精度和线性求解误差。测试不能直接调用生产 fit_trial 来生成期望值;至少一个 oracle 用手算或独立高精度路径。对真实数据不写“必须得到 9.81”这样的测试,因为模型、地点和实验误差决定结果。

版本、环境和实验身份

提交顺序保留项目因果:先加入契约与最小输入,再实现解析和异常测试,再实现拟合 oracle,随后制图、原子输出和端到端入口。重构与行为改变分开。每次提交只包含相关源文件和测试,不纳入临时图、环境缓存或原始私有数据;审查重点是单位、异常、数组形状、误差公式和产物兼容。

依赖清单表达直接需求,锁文件固定解析版本;环境清单再记录运行时、操作系统和架构、数值库、线程设置、区域与时区、绘图库字体或后端。输入文件、代码提交、未提交差异、锁文件和配置都有哈希或明确标识。任何凭据不进入仓库或 manifest。

复现命令不依赖交互点击,参数含输入路径、输出路径和配置。运行开始即生成 run ID,日志每条带 ID 与阶段;成功后 manifest 写退出状态、开始结束时间、命令、环境、输入和产物哈希。若硬件导致浮点末位不同,报告数值容差和允许差异,不承诺未经证明的位级一致。

例 5:从 manifest 判断产物能否复算

某结果目录记录输入哈希 D1、代码提交 C7、依赖锁哈希 L3、配置哈希 P2、命令、运行时版本和三个产物哈希。复算时先核对取得同一 D1/C7/L3/P2,在新目录运行,再比较 schema、数值容差和产物哈希。

若输入文件改一行,哈希变为 D2,即使输出数值碰巧相同,也不能把它标为原 run 的重现;应产生新 run ID。若代码工作树有未提交差异,manifest 必须保存差异摘要,否则 C7 无法唯一标识实际执行代码。

失败报告也是项目产物

入口将失败分为参数错误、输入错误、拟合错误、写出错误和意外内部错误,使用稳定退出码或机器可读状态。用户错误给可修复信息,不打印无关长堆栈;内部错误保留完整堆栈和阶段上下文。日志不复制整份敏感输入,但保留行号、字段和安全摘要。

部分模式下,成功与失败试次共享一个总 manifest,汇总含 statuserror_code,统计聚合默认排除失败并报告排除数量。若所有试次失败,不生成成功图表。磁盘写满时临时目录保留诊断或按策略清理,既有成功目录不被覆盖。

项目的最终结论应写成:“在给定模型、输入版本和误差假设下估计了什么”,附残差证据和系统边界;不把软件通过测试等同于物理模型正确。契约、代码、数据、图、测试和环境通过 manifest 连成一条可追溯数据流,才构成完整小项目。

练习

练习 1:契约设计
为另一种速度—时间实验写最小契约。
查看提示
列字段、单位、范围、失败行为和产物。
查看解答
明确 CSV 列与编码、时间和高度单位、有限值及顺序条件、最少点数;规定汇总 schema、图和 manifest,并说明异常退出与原子写出。
练习 2:函数边界
说明为何拟合函数不应自行读取 CSV。
查看提示
把纯计算与文件副作用分开。
查看解答
解析、验证、分组、拟合、制图数据和写出分别成函数;拟合只接收数组并返回结果,不读取文件或修改全局状态。
练习 3:拟合单位
核对二次模型参数的单位和符号。
查看提示
检查设计矩阵三列与高度的单位。
查看解答
常数、时间、时间平方列对应系数 m、m/s、m/s2m/s^{2};二次系数为 g/2-g/2,因此 g=2β2g=-2\beta_{2},单位 m/s2m/s^{2}
练习 4:异常策略
设计某试次秩不足时的处理流程。
查看提示
区分默认全有或全无与显式部分模式。
查看解答
默认任一失败不发布最终目录;部分模式必须标记失败试次和不完整状态。两种模式都不能静默丢行。
练习 5:残差诊断
解释三种残差图模式的可能原因。
查看提示
观察趋势、方差和极端点。
查看解答
系统弯曲提示模型缺项,漏斗形提示异方差,孤立大残差提示采集异常;图形只能提出诊断,需结合实验记录验证。
练习 6:完整复算
列出第三方从空目录复算项目所需信息。
查看提示
连接输入、代码、环境、命令和产物。
查看解答
保存输入与配置哈希、代码提交和差异、依赖锁与环境、非交互命令、日志退出状态、summary 和图的 schema/哈希及数值容差。

关系与资源

课程 · 2016

Introduction to Computer Science and Programming in Python

Ana Bell, Eric Grimson, John Guttag

用于核对 C00 的基础程序语义、函数分解、集合与文件处理、异常、测试和可执行例题。

打开官方来源
课程 · 2024

Computational Methods of Scientific Programming

Thomas Herring, Chris Hill

用于核对 P11 的程序验证、并行性能测量、环境记录、结果传播和可复现计算实验要求。

打开官方来源

MIT OpenCourseWare 6.0001 用于核对函数、集合、文件和异常的基础实现边界,12.010 用于核对数值拟合、图表、实验记录和可复现交付。示例数据仅用于验证数据流,不声称测得某地的标准重力值。