
1. 从上一篇文章的断点说起EpiData 数据库的第二层功夫做现场流行病学调查的人几乎都绕不开 EpiData 这个老牌工具。第一篇文章里我聊的大多是“怎么把一张纸质问卷变成一堆 .rec 文件”——建变量、定字段类型、设取值范围属于把地基打平。但真正干过项目的人都知道一个能用的数据库和一堆能跑的 .rec 文件中间差了整整一层功夫逻辑校验、变量间约束、跳转关系、双录入比对、数据导出规范。这些才是决定你后期清理数据要熬几个通宵的关键。这篇就接着上一回往下说主题是EpiData 数据库建立方法二——从“能录入”进化到“录得对、错得少、查得快”。核心关键词围绕CHK 校验文件、逻辑关系设置、双录入比对、数据导出与清洗准备这几块展开。不管你用的是 EpiData Entry 3.x 还是 4.x 系列思路是通用的只是菜单位置略有差异我下面会以 3.1 为主、顺带提一下 4.x 的差异点。先说清楚这篇文章适合谁看。如果你刚学会建 .rec 文件能手动录数据但一录错就得回头翻纸质问“这 2 到底是不是 12”那这篇就是给你的如果你已经是项目里的数据管理员负责给十几号调查员发数据库那这篇更适合当 checklist 用——我踩过的坑基本都写在里面了。EpiData 最大的价值不在于存数据而在于录入那一刻就把错误挡在门外这一点想明白了后面的活能省一大半。提示本篇所有操作都基于一个虚构场景——某高校一个关于慢性病危险因素的横断面调查问卷约 60 个变量包含跳转题、多选题、日期题和数值题。你遇到的真实问卷千差万别但对应的处理手法可以对号入座。2. CHK 校验文件让数据库自己“挑错”的核心机制2.1 为什么一定要单独建 CHK 文件而不是在 REC 里写死刚上手的人常问.rec 里不已经能设变量类型和范围了吗为什么还要多此一举搞个 .chk这个问题的答案决定了你后面维护数据库的痛苦程度。.rec 定义的是“数据长什么样”.chk 定义的是“数据之间讲不讲道理”。前者是单变量约束——年龄在 0 到 120 之间、性别只能填 1 或 2后者是多变量联动——如果性别填 1男那么“是否怀孕”这一题必须留空如果跳转题“近一年是否体检”选了“否”那后面一串体检项目都必须跳过。分开建还有一个非常现实的理由CHK 文件是相对独立的文本文件出问题好改、好对比、好交接。我在项目里遇到过这样的情况同一份问卷三个中心各自微调过录入规则最后合并数据时发现校验逻辑不一致靠的就是把三份 .chk 拉出来逐行 diff 才定位到问题。如果这些逻辑全塞在 .rec 里改一处就可能牵动字段结构风险高得多。从操作层面讲EpiData 里面向 .rec 文件生成 .chk 的入口通常在菜单栏的“文件 → 新建 → 校验文件”或者直接在打开 .rec 后点“生成 CHK 文件”。生成的骨架长这样LABEL BLOCK DEFINE 年龄 DEFINE 性别 END 年龄 RANGE 0 120 TYPE COMMENT 请输入实际年龄不满一岁填 0 END END 性别 RANGE 1 2 MUSTENTER END这里最关键的概念是BLOCK。EpiData 的 CHK 以“块”为单位组织校验逻辑一个块对应一个变量或一组变量。块与块之间是顺序执行还是条件执行靠IF ... THEN、GOTO、HIDEBLOCK之类的控制语句来调度。很多人第一次写 CHK 就是在这里犯晕把一堆RANGE全堆在一个块里结果校验根本不生效因为实际录入时变量根本不在同一个块上下文中。2.2 单变量约束的粒度选择宁松勿紧还是宁紧勿松这个问题没有标准答案但有个判断口径我用了很多年——看这个变量录错之后能不能被外部信息纠回来。举个例子年龄这种变量理论上 0 到 120 都合法但在一项成年人慢性病调查里你会看到 14 岁的记录吗如果调查对象明确是 18 岁以上那么RANGE 18 110是合理的而且能立刻抓住录入员把“48”打成“4”这种情况。反过来血压这种变量如果你设RANGE 60 250看起来合理但实际上会有调查对象收缩压 58 这种真实存在的情况尤其老年人设太窄就会把真数据挡在外面逼着调查员去改数。我的一般原则是逻辑上绝不可能出现的值设硬范围生理上少见但可能存在的值只做警告提示用TYPE WARNING而不是直接拒绝。约束类型适用变量设置方式风险硬范围拒绝性别、选项编码、日期年份RANGE 1 2、TYPE默认设错会挡真数据软警告年龄、身高体重、血压RANGETYPE WARNING提示被忽略则无效必填关键标识变量ID、日期MUSTENTER无自定义表达式派生变量、一致性检查IF ... THEN ...逻辑写错难排查这里补一个实操细节EpiData 的TYPE WARNING只是弹提示录入员按回车仍可继续录入。如果你想让提示真正起作用得在项目培训时明确讲清楚“看到黄色提示必须回查原始问卷”否则它就是一个被习惯性忽略的弹窗。我在一个项目里统计过未经过提醒的警告被忽略率超过六成讲清楚之后降到一成以下。工具是死的流程得是活的。2.3 变量命名与块组织为三个月后的自己着想CHK 文件写多了会有一种错觉——反正就是几十行随手写写得了。三个月后你回来改看到IF a1 2 THEN b2 .这种代码保证想不起来 a1 是啥。所以我在项目里强制两条规矩变量名用有意义的英文或拼音缩写绝不使用 v1、v2、q1 这类纯序号。比如age_y、sex、has_exam、exam_date。拼音也可但同一项目内必须统一别一半拼音一半英文。每个块前加LABEL说明把题目原话抄一部分进去。CHK 文件支持注释*开头的是注释行别省这点字数。* 变量名: has_exam 题目: 近一年是否参加过健康体检 * 来源: 问卷第 12 题 DEFINE has_exam END has_exam RANGE 1 2 MUSTENTER TYPE COMMENT 1是 2否 END END这一段看着啰嗦但当你手上同时有五个项目的 CHK 在改时这个习惯能救命。我见过一个真实案例某项目交接时原负责人已离职CHK 里全是无注释的q12、q13新接手的人花了整整两周对着纸质问卷逐条反推逻辑效率损失非常大。3. 逻辑关系与跳转控制复杂问卷的真正战场3.1 跳转题的三种实现路径与取舍问卷里的跳转题是最容易录错的地方。比如“近一年是否参加过体检”选“否”的人后面日期、机构、项目数三题都应该跳过。EpiData 里实现跳转大致有三条路第一条靠GOTO硬跳。在 has_exam 块里写IF has_exam 2 THEN GOTO skip_exam然后在后续变量块前面放一个LABEL skip_exam占位。这是最直观的做法缺点是问卷一改跳转点所有 GOTO 标签都得跟着挪维护成本高。第二条靠HIDEBLOCK/SHOWBLOCK动态隐藏。根据前面的值决定后续块显示还是隐藏。这种方式看起来优雅但 EpiData 3.x 对块的显示控制支持有限实测中容易出现“隐藏了但焦点还在里面”的尴尬情况需要配合JUMP才有好效果。4.x 版本在这块好一些。第三条不跳转但用IF ... THEN强制置空。也就是选“否”时直接在 CHK 里把后续变量置为缺失值同时在这些变量自己的块里加条件校验IF has_exam 2 THEN ... 必须为空。这条路最笨但最稳尤其适合多人协作、问卷版本多的项目。我个人的选择是第三条为主、第一条为辅。原因很实际跳转做复杂了录入员一旦按错键退回去改前一题跳转状态就可能错乱出现“明明选了否后面还是被要求填”的情况。强制置空 条件校验虽然录入时要多按几次回车但行为确定、可预测不会出玄学问题。3.2 多变量一致性校验把“矛盾数据”挡在录入阶段真正体现 CHK 价值的是跨变量校验。我列几个横断面调查里高频的规则你可以直接改成自己的变量名用校验场景逻辑表达示意说明性别与孕产史IF sex 1 AND preg 1 THEN ERROR男性不应有怀孕记录年龄与出生年份由出生年推算年龄差异 2 岁则警告抓录入错位体检日期与调查日期exam_date survey_date则报错未来日期是典型错误吸烟量与吸烟状态状态为“从不吸”但日吸烟量 0 则报错跳转漏填多选题互斥项“无”与其他选项同时被选则报错逻辑互斥收入与支出极值比较超阈值给警告抓小数点错位具体到语法EpiData 的 CHK 支持在块内写条件判断也可以写一个独立的“虚拟块”专门做跨变量检查。后一种做法我强烈推荐因为它的执行时机可控——通常放在一组相关变量之后作为一个“检查点”。* 跨变量一致性检查点性别与孕产史 IF sex 1 AND preg 1 THEN TYPE ERROR 男性不应有怀孕记录请核对第 5 题与第 21 题 END ENDIF注意EpiData 3.x 的 CHK 语法里IF语句的位置有讲究写在块外通常作为全局检查写在块内则跟随该块的执行时机。写错位置会导致校验根本不触发这是新手最高频的“写了等于没写”问题。建议每写完一段就进 Data Entry 手动录几条测试数据验证。3.3 派生变量的处理自动算还是录完再算EpiData 支持在 CHK 里对变量做赋值运算也就是派生变量比如由出生日期自动算年龄或者把身高体重直接换算 BMI。这块我的建议是谨慎使用能用导出后计算就别在录入端算。理由有三条。第一录入端的计算一旦写错错误会被静默写入数据集而且很难事后发现——你没法从数据里看出这个 BMI 是算的还是录的。第二派生逻辑改动后历史数据不会自动重算会出现新旧数据口径不一致。第三CHK 里的表达式语法相对简陋复杂计算写起来费劲且不易调试。如果确实要在录入端派生比如需要现场即时反馈给调查对象那至少做到两点派生结果存到独立变量、命名带_calc后缀并且保留原始输入变量方便日后重算与核查。3.4 日期与时间变量的校验陷阱日期是 EpiData 里最容易出问题的类型。常见坑包括分隔符不统一有人打2024-5-1有人打2024/05/01、年份只打两位导致世纪误判、农历公历混填。CHK 里可以做的约束有用RANGE限定合理日期区间比如调查日期限定在项目执行期内。用TYPE强制格式EpiData 的日期类型本身会做格式校验但不同版本对分隔符宽容度不同最好在培训文档里写死一种格式。对“今天”类变量做上限约束未来日期一律报警。实测下来最省事的组合是日期类型 合理区间 一条“未来日期即报错”的全局检查。别看简单光这一条就能挡掉录入阶段相当比例的日期错误而且不增加录入员任何操作负担。4. 双录入与数据比对把错误率压到千分位以下4.1 双录入的必要性与成本权衡数据管理圈里有句老话单录入的错误率大概在千分之几双录入比对后能压到万分之一以下。但双录入意味着双倍人工很多小项目会因为成本放弃。我的看法是分情况样本量小、变量少、问卷简单的项目单录入 关键变量二次抽查就够了。涉及关键结局指标、样本量上千、问卷复杂的项目双录入是底线省这一点人工后期清数据的代价更大。无论哪种录入前的培训与录入中的抽检都必不可少工具替代不了管理。EpiData 自带双录入比对功能流程大概是建一个“双录入模式”的数据库两个录入员分别录同一批问卷录完后用“数据录入 → 双录入比对”或者工具菜单里的比对功能指定两份数据文件生成差异报告。4.2 双录入的实际操作流程与注意事项具体步骤我按实际操作顺序捋一遍建立双录入目录结构。比如\project\entry_A\和\project\entry_B\各自放一份 .rec。别偷懒共用一份否则后期比对文件和原始数据混在一起理都理不清。统一双录入的文件编号规则。问卷 ID 必须手工录入且唯一这是比对的锚点。ID 录错会导致整份问卷对不上号比对报告里全是“缺失记录”。录入前对齐 CHK 文件。两份数据库必须用同一个 .chk这一点非常重要。我见过一边用了更新的 CHK、一边用旧的结果两边校验逻辑不同比对时差异报告里混进大量“本应被拦下的错误”白费功夫。控制录入节奏。两个录入员不要同时看到同一份问卷避免互相影响判断。通常会分成两个批次交错录入。执行比对。EpiData 会生成一份差异清单通常包含记录编号、变量名、两侧取值。这份清单要打印出来由第三个人拿着原始问卷逐条核对裁决——注意裁决不是录入员自己改必须回到纸质问卷确认哪个是对的然后由管理员在指定侧修正。修正后重跑比对直到差异清零或剩余差异均有明确解释比如已知的问卷字迹不清。注意比对报告里的“差异”不全是错的有些是两份录入各自合理但取值不同典型如多选顺序不同。所以比对前要先约定哪些变量参与比对、哪些属于“允许差异”这一步不做差异清单会非常噪杂。4.3 双录入常见问题速查表现象可能原因处理方式比对显示大量“记录缺失”两侧 ID 录入不一致或顺序错乱先单独核对 ID 列必要时导出排序后比对差异集中在同一变量该变量题面歧义或选项顺序不清回看问卷设计与培训材料统一口径修正后差异反复出现两人对同一题理解不同召开短会明确规则改培训文档比对功能无法启动文件路径含特殊字符或数据被占用关掉所有数据窗口换简单路径重试日期变量差异极多分隔符习惯不同统一格式并在 CHK 中约束这张表是我这几年攒下来的几乎每个项目都会撞上其中两三条。尤其是第一条和最后一条出问题的时候人会本能地怀疑软件坏了其实八成是数据层面的规则没对齐。4.4 单录入项目的替代方案抽检与逻辑审核如果项目实在做不了双录入至少补上两道防线。一是关键变量 100% 复核把所有直接影响分析结论的变量结局、暴露、年龄性别单独导出由第二人对着问卷核一遍。二是逻辑审核清单把 CHK 里没法覆盖的复杂规则通常涉及三四个变量联动列成表导出数据后用统计软件跑一遍把违规记录挑出来回查。这两道防线加起来能把错误率压到可以接受的水平虽然不如双录入彻底但至少不会在分析阶段才发现数据烂尾。5. 数据导出与交接规范数据库的“最后一公里”5.1 导出格式的选择与编码问题EpiData 的录入文件不能直接进统计分析软件中间需要导出。常见路径是.rec → 导出为文本/Excel/SPSS 格式。这里有几个实操要点导出文本.txt / .csv最稳兼容性最好但需要注意分隔符和编码。中文变量标签在导出时容易乱码建议统一用 UTF-8并在导出前确认标签字段没有超长或含特殊字符。导出 Excel 便于交接和人工核对但数值型变量的前导零、日期格式会被 Excel 自作主张改掉导出后必须抽几行核对。直接导出 SPSS 格式能保留变量标签和值标签省去后期重复定义但不同版本 SPSS 的兼容性偶有问题导出后要拿统计软件打开验证。我个人的习惯是主数据用 CSV、交接给合作方用 SPSS 格式、人工核对用 Excel三份同时生成互相印证。别嫌麻烦一次导出多花十分钟能省下后期对不上的两天。5.2 数据字典与变量清单的同步维护数据库建完一定要同步产出两样东西变量清单变量名、类型、取值范围、对应的问卷题号和值标签表每个编码的含义。这两样东西建议直接从 .rec 和 .chk 里整理而不是另起一份 Excel 手打避免不一致。维护时记住一条原则任何字段变更必须同时更新三处——.rec、.chk、变量清单。我在项目里推行过“变更登记表”每改一次字段就登记一行谁改的、何时改的、为什么改。听起来像官僚流程但数据交接时这份登记表的价值极高尤其当项目周期跨年、人员流动频繁的时候。5.3 交接前的自检清单数据交付给分析团队之前我会跑一遍固定清单摊在这里供你参考抽查 5 到 10 份问卷逐个变量与原始纸质记录比对确认录入与导出无损。确认所有逻辑校验在测试数据上都能正常触发没有“写了但无效”的块。确认导出文件的记录数与问卷回收数一致无多无少。确认缺失值编码统一EpiData 的缺失与统计软件的空值语义要对上别把“9不详”和“空白”混成一类。确认变量名、标签、值标签与变量清单完全一致。备份原始 .rec/.chk 文件与导出数据至少两份介质命名带日期。这套流程跑下来一个中等规模项目大概多花半天到一天但能极大降低“数据交出去后被退回来返工”的概率。返工的代价做过的人都懂。提示备份这件事我单独强调一次。EpiData 文件体积小备份成本几乎为零但丢了就是灾难。我见过不止一个项目把数据库放在单台电脑上硬盘故障后只能用导出的半成品数据重建前功尽弃。6. 我个人在实际操作中的几点体会写到这里技术上该说的基本说完了最后分享几条不写在手册里、但真正影响项目成败的经验。第一CHK 写得漂亮不如写得稳定。我早期喜欢把校验逻辑写得非常密恨不得每个变量都加五条约束结果录入员怨声载道遇到弹窗直接无脑按回车校验形同虚设。后来我改成“关键路径紧、边缘变量松”弹窗数量降下来录入员的注意力反而集中了效果更好。工具设计要考虑人的行为这一点比语法本身重要得多。第二培训录入门槛比优化数据库更划算。同样一套数据库培训半小时和培训两小时出来的数据质量差距肉眼可见。培训里重点讲三件事就够ID 怎么编、遇到弹窗怎么办、哪些题容易看错。其他都是次要的。第三留出“已知问题清单”。每个项目做到后期都会有那么几条无法彻底解决的数据问题比如某几份问卷字迹不清、某个变量口径在项目中期调整过。把这些明确列出来交给分析团队比藏着掖着强。分析阶段的人最怕的不是数据有瑕疵而是不知道瑕疵在哪。第四别迷信工具的自动化。EpiData 的校验再全也挡不住问卷设计本身的歧义双录入比对再细也挡不住两个录入员共同的误读。数据库只是流程的一环问卷设计、培训、抽检、复核哪一环松了都会在数据里体现出来。建库这件事功夫在库外。如果后面还要继续往下扩展我大概会聊聊怎么把 EpiData 导出的数据接入统计分析流程、缺失值的处理策略以及多中心项目里数据库版本的管理办法。这些内容都能单独成篇等遇到合适的项目再整理。