核心结论

清洗数据不是把不顺眼的数字删掉,也不是想办法让每个格子都有值。它要做的是:确认每一行究竟是谁,检查字段有没有按事先的规则产生,发现错误时回到证据修改,并把仍然不确定的地方保留下来。

描述分析也不只是算一个平均数。研究者要先交代样本怎样组成、数据质量怎样、数值集中在哪里、散得有多开,再把主要比例与不确定区间一起报告。只有先看清数据实际发生了什么,后面的模型才知道自己在解释什么。

本篇开始使用 1,200 行模拟数据。为了让任何人重新运行生成程序时都得到同一批数,程序预先固定了随机生成的起始编号,技术上常把这个编号叫随机种子。所有店铺、早餐和结果都只用于科研方法教学,不代表现实中式早餐,也不能据此给出饮食建议。

打开文件以后,先数行、数店、数空白

第一步不是点击“描述统计”,而是核对数据结构。冻结后的模拟库有 1,200 个不重复的早餐编号 combo_id,对应 240 个店铺编号 shop_id,每家店正好 5 行。六个城市各 200 行。四类店铺分别为早餐摊点 300 行、独立门店 420 行、连锁门店 270 行和食堂窗口 210 行,与抽样方案中的 10/14/9/7 家每城相符。

这一轮检查回答的是“文件是不是我们原本准备分析的那一份”。如果某家店只有 4 行,可能是漏同步,也可能是第 5 份后来被判定不合格;若一个 combo_id 出现两次,可能是重复提交。不能看到总行数恰好 1,200 就默认内部结构正确,因为漏一行再重复另一行,总数依然对得上。

关键身份、店铺和营养字段在这套模拟数据中没有普通缺失。不过,90 份早餐的份量没有现场实测,占 7.5%;148 份配方使用了参照估算,占 12.3%。它们并非“坏数据”,但测量来源与其他记录不同,后面需要排除这些记录再算一次,看看主要结果会不会明显改变。

120 份早餐进入参照测量子样本,其余 1,080 份在“参照蛋白质”列 reference_protein_g 和“参照钠”列 reference_sodium_mg 本来就不应有值。这 1,080 个空白不能拿来计算“参照值缺失率 90%”。先判断为什么空,再决定怎样统计,正是数据字典存在的意义。

STROBE要求观察性研究交代研究对象、变量、缺失和分析样本。把这些信息留到论文阶段临时回忆通常太晚;结构核对表就是将来样本流程和缺失说明的底稿。

清洗时,我们真的发现了 25 行算不回去

首次审计模拟库时,程序用公开的数据表文件(CSV)里的能量、蛋白质、纤维、钠和食物类别重新计算评分,发现 25 行与总分列 nutrition_score 不一致。没有哪一行差得离谱,多数只是处在计分边界附近。

追查生成过程后,原因很清楚:旧版生成器先用未四舍五入的内部数值计分,再把营养值保留一位小数写进 CSV。比如内部蛋白质可能是 17.96 克,按旧顺序得到 1 分,公开表里却显示 18.0 克,读者按冻结规则会得到 2 分。总分看起来合法,却无法由已发布的数据重建。

正确修复不是手动把 25 个总分改到一致。我们修改生成顺序,先把将要公开的营养值固定到规定精度,再用这些值运行同一评分函数,随后重新生成全库和全部汇总。修订后 1,200 行评分错配为 0,旧问题和修改理由保留在版本记录中。

这个错误很小,却会影响阈值两侧的数量。若总分从 5 变 6,“是否低于 6 分”这一列 imbalanced_lt6 就会从“是”变成“否”。这说明“公式算出的总分落在 0—10”只通过了范围检查,没通过来源检查。清洗最有价值的部分,往往不是删掉几个极端值,而是找到一条计算链为何不能重放。

极端值也接受了不同的检查。模拟库中最高钠为 1,862.2 mg,最高能量为 841.5 kcal。它们高于样本大多数记录,却仍在生成规则允许的范围内,身份和单位没有矛盾,因此保留。若因为“看起来不像正常早餐”就删掉,研究者会在没有现场证据的情况下主动缩窄分布。

中位数告诉你站在哪里,四分位数告诉你周围有多宽

早餐的营养值并不保证左右对称,少数很高的组合会拉动平均数。因此先用中位数描述典型位置,再用第 25 到第 75 百分位之间的四分位范围描述中间一半数据。

变量中位数第 25—75 百分位
能量532.8 kcal463.9—604.8
碳水化合物70.2 g58.7—81.1
蛋白质15.9 g11.4—20.3
脂肪16.6 g11.2—22.6
膳食纤维3.5 g2.2—5.0
725.6 mg513.1—951.4
食物类别数4 类3—5
价格10.2 元8.6—11.9

以蛋白质为例,一半早餐落在 11.4 到 20.3 克之间,中位位置是 15.9 克。这不是说每份早餐都接近 15.9,也不是说区间外的四分之一加四分之一有错。最小值 3.0 克、最大值 36.7 克仍属于分布的一部分,图形和范围检查可以帮助判断尾部是否由录入错误、真实差异或生成机制产生。

表格之外还要看图。直方图能显示数值是否集中在一个主要峰附近、是否有一侧拖得特别长,或者可能混着几种模式;箱线图方便比较城市或店铺层,同时把远离中间区域的记录标出来。图上的孤立点只是“请回去核对”的线索,不是“应当删除”的命令。若核对后值真实,它反而可能提示某一类组合与大多数早餐不同。

四分位范围也不是置信区间。它描述样本中间一半记录实际散在哪里;置信区间描述研究者对总体参数估计有多不确定。两个区间都可能写成“下限—上限”,回答的问题完全不同。

WHO 的基础统计与研究方法资料把测量、描述问题大小和比较放在连续的方法路线中。实际选择均值、中位数或比例,要看变量类型和分布,而不是让每张表都固定输出同一套统计量。

55.8% 不是一个没有误差的事实

按冻结的主要定义,总分低于 6 的早餐有 669 份。计算为 669 ÷ 1,200 = 55.8%。总分本身的均值为 5.20,中位数为 5;从 0 到 10 分都有记录,最多的是 5 分,共 204 份,其次是 4 分 196 份和 6 分 179 份。

若暂时把 1,200 份早餐当成彼此互不相关,用常见的比例区间算法得到的 95% 估计范围是 52.9%—58.5%。但每家店贡献 5 份,同店菜单和制作方式会让记录更相似。把同一家店的早餐看作一组,重新计算后,区间变成 52.6%—58.9%,稍微更宽。后文把这种做法简称为按店铺聚类调整。

数据估计的店内相关系数为 0.067。这个数用来表示同店早餐比不同店早餐多出多少相似性,数值越大,五份同店早餐包含的重复信息越多。它高于第 6 篇样本量计划使用的 0.02。计划参数没有被数据“证明正确”;它只是开工前的假设。实际同店相似更强,独立信息比原计划略少,区间自然要诚实变宽。不能因为原本承诺 ±3 个百分点,就继续选择更窄的简单区间。

在这套模拟数据、六座城市所占比重相同的教学样本和低于 6 分的主要定义下,区间下限仍高于 50%,因此“低分早餐超过半数”这项主要假设得到支持。这个结论的限定条件必须和结果一起出现。它不是“中国多数早餐营养失衡”的现实证据。

分组描述也显示数据并非完全均一。六城的低分比例从海川 47.0% 到北宁 63.5%;四类店铺从食堂窗口 44.3%、连锁门店 45.2%,到独立门店 59.0%、早餐摊点 68.7%。这些比例只是各组直接算出的结果,还没有同时考虑城市与店铺构成的差别。本篇只把差异摆出来,不宣布某类店铺“造成”低分,也不逐组计算所谓“显著不显著”,再从中挑最好看的比较。

店铺类型早餐数总分中位数低于 6 分
早餐摊点3004.568.7%
独立门店4205.059.0%
连锁门店2706.045.2%
食堂窗口2106.044.3%

这张表至少提出两个后续问题:差异有多少来自各类店铺出售的食物组成,城市构成是否又混在里面;同一家店的五份早餐会不会把表面差异放大。描述表不负责一次回答完,但它让第 9 篇的模型有了明确任务,而不是把所有字段一股脑塞进去寻找显著结果。

合格线移动一分,故事会换一个版本

主要阈值低于 6 分时,低分比例为 55.8%。改成低于 5 分,比例降到 38.8%;改成低于 7 分,又升到 70.7%。三个数字都由同一套早餐和总分产生,差异完全来自分界线。

这种更换一个合理规则、观察结论变化多大的检查,叫敏感性分析。主结果仍按事先冻结的 6 分线回答主要假设,不能看完三组数字后改选最有传播力的 70.7%。另外两个比例告诉读者:关于“是否普遍”的判断高度依赖教学阈值,0—10 分和各营养维度不能被一条合格线取代。

清洗与描述分析结束时,我们得到的不是一张毫无问题的完美数据表,而是一张知道问题在哪里的表:90 份未实测、148 份配方估算、实际店内相关高于计划、总体比例对阈值敏感,同时关键结构和派生计算能够复核。这比一张看上去没有空白、却不知道数字从哪里来的表可靠得多。

下一篇会在这些条件上继续。先按营养特征探索早餐模式,再用能够同时考虑食物标记、价格、城市和店铺类型的统计模型检查三条关联预期。之后轮流拿掉一座城市,看看用其余五城得到的模型能否预测被拿掉的城市,并更换阈值和数据质量条件检查结果是否稳定。模型必须解释这张已经看清的表,不能把这些质量与设计问题重新藏起来。