问卷清洗与加权 · 无注册表版
本技能由 survey-weight 独立派生;原 survey-weight 保持不变。本目录包含独立清洗入口、人口学识别、IPF脚本和城市分级资源,不含注册数据、注册表参数、主库路径或关联步骤。不依赖其它技能的目录完成清洗和加权。
硬边界
- 只从用户问卷及其实际答案提取人口学信息,不从姓名、昵称、消费偏好或他人属性猜测。
- 没有相关字段就跳过相关检查/加权,不能寻找注册表,不将缺信息的全体样本删光。
- 不修改用户原始文件;准备工作副本,重跑始终从副本重新处理。读取与执行遵循宿主表格路由;工具失败不能假装已执行。
- 每阶段先展示信息、确认、执行、汇报。只在用户明确已清洗/直接加权时跳过清洗并注明。
- 选项标题必须包含具体规则、数值或题号题干,不能写“默认”“全部”等代称。选项解释放到问题文本,不依赖界面可能隐藏的说明。
- 目标比例必须来自本次用户确认或可靠总体资料,不沿用其它项目的比例;目标命中不等于消除全部偏差。
运行环境
使用已安装 pandas>=2.2、openpyxl>=3.1、numpy 的 Python。脚本位于本技能 scripts/,按真实安装绝对路径定位,不递归搜索用户个人目录。
python -m pip install -r <本技能目录>/requirements.txt
下文 PY 为已确认解释器,CLEAN 为 <本技能目录>/scripts/clean_pipeline.py,WEIGHT 为 <本技能目录>/scripts/survey_weight.py。入口包含 convert、prepare、inspect、check、clean、questions;结果最后一行 @@RESULT@@ {json}。解析完整JSON,不能截断表头后根据答案猜题干。清洗/统计长任务按宿主要求后台运行,取最终结果后再交付。
0. 准备副本与人口学识别
工作目录建议 <原文件同级>/问卷加权输出_YYYYMMDD_HHMM/清洗/,必须是新的独立目录。
$PY $CLEAN prepare --dir <工作目录> --survey <问卷.csv或xlsx> \
[--sheet <工作表名>] [--city-file <城市分级表>] [--reference-year <调查参考年>]
$PY $CLEAN inspect --dir <工作目录>
CSV兼容UTF-8(含BOM)及GB18030/GBK,正确处理引号内逗号、引号、CRLF及换行;行数按逻辑记录统计。xlsx默认第一工作表,指定sheet会保存配置。重复/空表头、列数不齐或引号语法错误报错,不擅自改名、补字段掩盖问题。
CSV→Excel:先转换,再识别与清洗
输入CSV时,prepare会在工作目录中自动生成:
- 问卷数据表.csv:原始CSV的审计副本,内容不变。
- 未清洗问卷数据表.xlsx:纯格式转换,默认工作表 data;没有人口学标准列、剔除或新增权重。
- conversion_report.json:编码、逻辑记录数、列数、跳过的物理空行数及源/Excel校验信息。
完整题干、表头顺序和全部答案按文字保存,保留前导零、超长数字标识、NA/NULL原文、空字段及等号开头答案;不把问卷答案写成公式。明确有分隔符或引号的全空记录仍保留,纯物理空行跳过并计数。问卷平台导出常见每行末尾多一个逗号,形成末尾无列名列:仅当这些末尾无名列在所有记录中都为空时自动忽略,并在 dropped_trailing_empty_columns 报告列数;末尾无名列有任何数据、或无名列出现在开头/中间时仍报错停止。转换后回读Excel逐单元格核对行列与原文,成功才供后续处理。CSV字段全为文字,年龄/数值等仅在后续已确认的标准字段中规范化。
prepare结果中的 conversion.output 是完整未清洗Excel路径,conversion.sheet/rows/cols/encoding/verified 是定位与核验信息;不能把已转换当成已清洗。inspect/questions/clean在核验源CSV、转换Excel与报告hash、实际数据一致后读取该Excel,不读取上次merged叠加处理。任何副本被改动则停止,重新建立工作目录。同源同配置再次prepare复用原转换文件,不覆盖;想改变转换sheet须使用新工作目录。旧v1CSV目录可再次prepare补转换,未迁移时仍读取CSV并注明未转换。
若用户只要转换而不要清洗,使用独立入口:
$PY $CLEAN convert --survey <原CSV绝对路径> --output <新的Excel绝对路径.xlsx> \
[--sheet data] [--encoding auto]
convert编码可选 auto、utf-8-sig、utf-8、gb18030、gbk。auto先UTF-8、后GB18030(兼容GBK);编码有疑问使用已确认编码。目标文件必须不存在,拒绝输出/父目录软链和输入输出冲突,没有强制覆盖参数。所有输出仅为新文件,原CSV不修改。超过Excel行列容量、单元格32767文字长度、非法XML字符或工作表名时停止,不静默截断。转换使用内存,超大文件须关注资源;未做百万行压力验证。文件创建的安全实现适用于本机macOS及具有相同POSIX能力的环境,Windows运行须另行适配验证。
实际问卷转换、解析、清洗仍遵循宿主表格执行通道。具备经授权的转换通道时,可先使用本入口准备Excel再交表格执行体读取;没有允许的通道或调用失败时如实报告,不能绕过执行纪律或宣称已修复当前环境的读取能力。
识别与模式
detail:有核心人口学信息或待确认候选。核心为性别、数值年龄、出生年份、年龄段、学历、职业,不要求六项全部齐全。questionnaire:只有城市、城乡、婚姻、收入、行业等可选字段;基础清洗照常,可对确实可用且有目标的维度加权。basic:无人口学信息;仍可做确认的唯一ID去重、专注度、时长和题目筛选,只不能做缺失维度的人口学加权。后续可衔接总体统计。
| 题干/原列示例 | 识别 |
|---|---|
| 42.您的性别是?、Q42.性别(单选) | 按实际答案男/女编码,不按A/B顺序猜 |
| 您今年多少岁? | 仅精确岁数转为年龄,0岁有效 |
| 第43题您的年龄范围是? | 保留年龄档位,不取中点/下限编造年龄 |
| 您的出生年份是? | 按reference-year换算;默认当前年,提示生日/调查时点限制 |
| 您的最高学历是什么? | 规范明确细档;本科及以上、研究生等合并口径不拆分 |
| 您目前从事的职业是? | 保留真实职业文本,不能把“身份”自动猜成职业 |
同时支持明确省份、城市、地区、城乡、婚姻、个人月收入、行业等题目。原题列保留,新增标准列;标准同名列被规范化时其原值存为 原始_<列名>。规范化备份列与源列同名时阻断;重分段/城市分级保留旧值时可用 _2 等无冲突备份名。源数据若含 _source_row_ref、_removed_stage、_removed_reason 审计保留列则阻断,不覆盖原值。多个候选、未知编码、年龄口径矛盾需先确认,clean退出2时数据产物不变。明确单选年龄题的裸数字可能是选项码,须提供确认后的值映射;标准年龄列、填空数值和明确“岁/周岁”的答案不受此限制。
展示识别表:标准字段、源题号和完整题干、实际原值→标准值映射、缺失、未识别值。报告取 demographics.fields/candidates/available_core_fields/warnings,不在回复中展示个人标识原值。
题干漏识别或码值无含义时,取得用户或码本确认后指定映射:
$PY $CLEAN prepare --dir <工作目录> --survey <原文件> \
--demo-map '{"性别":"Q42.您的性别是?"}' \
--demo-value-map '{"性别":{"1":"女","2":"男"}}'
示例1/2含义不能照抄;实际以本问卷码本为准。prepare持久化映射/年份/sheet,inspect与clean也接受这些映射和年份参数,但仅当次覆盖。原文件或源副本变动时拒绝复用,须新建工作目录。
1. 体检与一次确认清洗规则
展示样本数/列数、完整题目清单、可用人口字段、ID来源及可否去重、专注度题完整题干和正确答案候选、已有权重列、各缺失与歧义。不要把矩阵普通评价或数值评分误当专注度题。
结合实际可用项最多一次问4问(必要歧义确认另行处理):
1. 真实用户级ID来源及重复保留规则;没有可确认ID则跳过去重,不用编号/昵称/IP识别人。默认缺ID记录保留,不彼此去重;需要删空ID时单独确认。
2. 专注度是否剔除、正确答案全文与适用人群。不适用/跳题须指定保留选项;空值是否跳过明确确认。
3. 人口学异常及核心字段部分缺失是否剔除,只问具备必要字段的规则;没有数值年龄就跳过所有精确年龄异常规则。
4. 年龄分组、额外题目筛选或最小时长;没有精确年龄不问重新分组,原档位不可擅自拆分。
不默认剔除短时答卷。 只有用户明确给出秒数阈值才执行;时长缺失/无法解析保留并警告。不存在的题目或选项报错,不允许写错规则零命中却声称已执行。
10条人口学规则
| 编号 | 规则 | 必需字段 |
|---|---|---|
| 1 | 大学专科/本科<17岁 | 年龄、学历 |
| 2 | 硕士<21岁 | 年龄、学历 |
| 3 | 在校学生&初中及以下>17岁 | 年龄、学历、职业 |
| 4 | 在校学生&高中/中专/技校>21岁 | 年龄、学历、职业 |
| 5 | 在校学生&大学专科/本科/硕士>35岁 | 年龄、学历、职业 |
| 6 | 公务员<18岁 | 年龄、职业 |
| 7 | 专业人员<21岁 | 年龄、职业 |
| 8 | 工人/职员/职工<16岁 | 年龄、职业 |
| 9 | 退休人员:男<60岁 | 年龄、职业、性别 |
| 10 | 退休人员:女<50岁 | 年龄、职业、性别 |
具备字段但逻辑阈值不适合本项目时由用户关闭,不把经验规则当定律。合并学历不能当细档检查;仅在精确可判定的记录执行并报告跳过记录。只有已收集可用核心字段部分缺失才可能剔除;整列未收集/全空或可选字段为空不删全体。
2. 执行清洗
$PY $CLEAN clean --dir <工作目录> \
[--id-column <已确认ID列>] [--drop-empty-id] \
[--confirm-demo-completeness | --no-demo-completeness] \
[--confirm-attention-check --attention-column <完整题列> --attention-option <字母或选项全文>] \
[--attention-skip-option <不适用选项全文>] [--attention-skip-empty] [--no-attention-check] \
[--rules <规则原名或编号逗号分隔>] [--disable-rules <关闭的规则原名或编号>] \
[--reference-year <参考年>] [--age-groups "30岁以下,30-39岁,40-49岁,50岁及以上"] \
[--min-duration-seconds <用户确认秒数> --duration-column <时长列>] \
[--filter-rule "25.完整题干 == D.实际选项全文"]
- 专注度候选虽自动判定,执行剔除仍须
--confirm-attention-check;无专注度题就报告跳过。存在多个候选/正确选项不明先确认,不能猜。 - 核心字段部分缺失须
--confirm-demo-completeness才删,或--no-demo-completeness跳过完整性剔除。 - ID默认只识别显式openid/OpenID列或自定义字段内明确openid键;裸字符串/分号首段不作ID。多个ID列须指定。非空ID重复保留结束时间最新;无法解析时间则稳定保留该组最后一条并报告。去重先于时长剔除:若重复组中较晚提交恰是短时重提交,同时启用去重和时长阈值会把该受访者两份都删掉。执行前先核对重复组的提交先后与时长,向用户说明两种规则叠加的后果,必要时只选其中一种。
--rules ''或--rules none关闭全部规则;省略参数启用可执行的10条,仍需用户确认。--age-groups须先check验证:$PY $CLEAN check --age-groups "<实际分段>"。有精确年龄时显式指定可重新分组并保留原分段;省略则已有分段保留,无已有分段才按默认4档生成。没有精确年龄不能重分组,警告并保留档位。- 自定义条件按原题列原答案:
==剔除匹配者,!=只保留匹配者。先questions --dir <目录> --q <题号>核实选项。建议每条规则单独写、逐条确认,重复传多个filter-rule依次执行。复杂AND/OR沿用保留掩码语义(AND优先),必须先展示最终保留/剔除条件供人工确认,不能按通常命中集合误解。 - 所有重跑从源副本完整重新计算;兼容
--steps 4,5也不在上次已清洗数据上继续删。
汇报 stats.funnel:原始 → ID空值(如启用)→ 重复 → 核心字段缺失 → 时长(如启用)→ 专注度 → 各异常 → 自定义 → 有效。附 stats.skipped_rules、警告与实际产物。剔除人数多或小组剩余极少时主动提示。
产物:问卷数据表_merged.xlsx(data sheet)、UTF-8-SIG CSV、综合剔除记录、demographic_report.json、clean_report.json。剔除记录包含 _source_row_ref(原答卷逻辑行引用,不是CSV物理行)、环节和原因;不强行承诺固定两份剔除文件。
3. 清洗后确认加权目标
只对实际具备且目标可信的字段加权。无性别不能做性别加权,缺学历不能做学历加权,但缺职业不阻止其它维度加权。完全无人口信息则只交付清洗产物,按需进入总体统计。
向用户展示每维度档位人数/占比,以及本次目标来源。选择自定义、七普或CNNIC后先核对目标总体、调查年龄范围、类别口径并确认。各维度目标合计100%;四舍五入误差须明确归一化,不隐瞒。
- 自定义:以本次用户给定比例为准,不将示例冒充七普。
- 七普:本技能内置与原版相同目标数据(下节)。含非网民的全人口目标不能默认套用特定高校/平台人群。
- CNNIC:仅从官方主报告《中国互联网络发展状况统计报告》的网民结构取数,不能拿专题样本当总体;明确报告期/来源/滞后及分段差异。无法核实则改为人工配比,不猜数值。
- 问卷本科/硕士5档与七普4档不一致,先确认合并本科及以上或用户提供拆分数据;目标年龄段与问卷不符先确认,不能从汇总区间擅自拆分。
确认有效样本与目标后执行:
$PY $WEIGHT --file <工作目录>/问卷数据表_merged.xlsx --sheet data \
--dim "性别|A.男,B.女|50.4,49.6" \
--output-dir <独立加权输出目录>
增加维度则追加 --dim "列名|档位1,档位2|百分比1,百分比2";上例是自定义性别示例。--check-only预检映射、目标可行性和收敛,不写入数据。出生年已转换年龄;实际年龄分段标准列可直接使用。合并学历需要在新的加权输入副本新增辅助列,保留原列,不直接改原问卷。
已有 weight 时先确认是否重算;只有明确同意后增加 --replace-weight,旧值保留到 原始_weight 或无冲突编号备份列。未同意则不运行。所有加权输出目标文件必须不存在,遇到已有文件、输入同路径或软链时拒绝覆盖;重新加权使用新的输出目录。加权读写保留数字文本前导零、NA/NULL原文和等号开头答案,不把问卷文本写为XLSX公式;保留所选工作表的数据值,不保留整本工作簿版式或其它工作表。
目标须有限、非负、档位不重复;合计与100相差不超过0.1个百分点时脚本显式报告归一化,正式执行前仍须确认该口径。数字年龄区间使用整数左闭右开边界:50岁以上不含50,50岁及以上和50+含50;区间不可重叠或翻转,无法覆盖的记录会阻断。未映射或空值不得赋权1后继续;未收敛/不可行时退出2、不输出假成功。
零样本档位或未识别值时停下,给出人数和目标,由用户决定补采样本/删维度/合并合法档位/重设目标;不能自动把零样本目标归一化后称同一总体。
4. 质量检查、统计与交付
检查每档命中误差和警告。优先报告未命中/极端权重;非命中不必然是小样本,要核查编码、收敛和目标可行性,不能无证据归因。
- 报告权重范围、均值、权重>10人数及集中组合。
- ESS = (Σw)²/Σw²,设计效应=n/ESS;ESS<n的20%说明权重波动大,并解释Kish指标不是“删掉了这些样本”,不能据此保证真实设计方差。
- 全部命中也只能说所选边际已校准,不能宣称代表性必然充分。
- 交付清洗XLSX、剔除记录、加权XLSX/CSV、加权汇总与命中检查。不要把真实用户注册数据打包或发送。
- 本技能不内置逐题统计引擎。用户要统计,按宿主规则衔接survey-stats或表格统计通道,核对题号识别/人口字段/适用题分母。不加权只做总体也允许。原N.题干列保持;下游不支持Q/第N题时先确认后在统计副本统一题号,原文件不改。
- 量表不适用/无法回答不算分,多选跳题不一律当漏答;不足样本人群交叉须警告。
内置七普目标(沿用原版本,不联网取数)
2020年第七次全国人口普查,全国口径。性别:男51.12%、女48.88%;城乡:城镇63.55%、乡村36.45%。
学历为15岁及以上人口4档:初中及以下67.53%、高中/中专15.98%、大学专科8.53%、大学本科及以上7.95%(合计99.99%为舍入误差)。本科及以上不含可单独拆分的硕博目标;正式运行展示规范化方案供确认。
年龄为0—99岁单岁占比,保留原版舍入值(100岁及以上省略),按照用户实际目标年龄范围和分段加总、归一化,说明分母后确认;不是从样本最小最大年龄擅自裁剪总体。
| 岁 | 占比% | 岁 | 占比% | 岁 | 占比% | 岁 | 占比% |
|---|---|---|---|---|---|---|---|
| 0 | 0.85 | 1 | 1.02 | 2 | 1.08 | 3 | 1.31 |
| 4 | 1.26 | 5 | 1.17 | 6 | 1.32 | 7 | 1.27 |
| 8 | 1.37 | 9 | 1.26 | 10 | 1.23 | 11 | 1.26 |
| 12 | 1.23 | 13 | 1.18 | 14 | 1.15 | 15 | 1.09 |
| 16 | 1.08 | 17 | 0.97 | 18 | 1.00 | 19 | 1.02 |
| 20 | 1.03 | 21 | 0.99 | 22 | 1.08 | 23 | 1.09 |
| 24 | 1.13 | 25 | 1.22 | 26 | 1.19 | 27 | 1.30 |
| 28 | 1.35 | 29 | 1.46 | 30 | 1.84 | 31 | 1.81 |
| 32 | 1.71 | 33 | 1.84 | 34 | 1.61 | 35 | 1.39 |
| 36 | 1.38 | 37 | 1.35 | 38 | 1.55 | 39 | 1.35 |
| 40 | 1.24 | 41 | 1.37 | 42 | 1.32 | 43 | 1.26 |
| 44 | 1.40 | 45 | 1.44 | 46 | 1.58 | 47 | 1.65 |
| 48 | 1.69 | 49 | 1.75 | 50 | 1.85 | 51 | 1.73 |
| 52 | 1.85 | 53 | 1.49 | 54 | 1.67 | 55 | 1.64 |
| 56 | 1.60 | 57 | 1.85 | 58 | 1.36 | 59 | 0.75 |
| 60 | 0.92 | 61 | 0.86 | 62 | 1.10 | 63 | 1.22 |
| 64 | 1.11 | 65 | 1.16 | 66 | 1.15 | 67 | 1.04 |
| 68 | 1.03 | 69 | 0.87 | 70 | 0.82 | 71 | 0.80 |
| 72 | 0.68 | 73 | 0.64 | 74 | 0.58 | 75 | 0.51 |
| 76 | 0.48 | 77 | 0.43 | 78 | 0.40 | 79 | 0.39 |
| 80 | 0.35 | 81 | 0.29 | 82 | 0.30 | 83 | 0.26 |
| 84 | 0.24 | 85 | 0.21 | 86 | 0.18 | 87 | 0.16 |
| 88 | 0.12 | 89 | 0.10 | 90 | 0.09 | 91 | 0.06 |
| 92 | 0.05 | 93 | 0.04 | 94 | 0.03 | 95 | 0.02 |
| 96 | 0.01 | 97 | 0.01 | 98 | 0.01 | 99 | 0.01 |
维护验证
$PY <本技能目录>/scripts/test_lite_pipeline.py
$PY <本技能目录>/scripts/test_weight_safety.py
$PY <本技能目录>/scripts/test_csv_conversion.py
三套测试均只生成合成数据。转换测试覆盖自动/独立转换、长表头、编码/多行/空记录、原文及公式样文字保真、Excel限制、回读核验、文件冲突与篡改、旧配置补转换及转换→清洗→性别IPF。流程测试覆盖basic无人口41行、识别/歧义阻断/原题列、缺ID保留、专注度与时长确认、10异常规则、自定义筛选、重复重跑、CSV多行/编码、40行三维IPF及仅性别加权、拒绝旧注册接口。加权安全测试额外覆盖未映射/空值、非法目标、年龄边界/区间重叠、旧权重备份、原题与公式样文字保真、路径冲突及写入失败回滚。可设置SURVEY_LITE_TEST_OUTPUT为已存在的测试父目录;默认临时目录,各次合成产物保留。多文件输出不是跨文件原子事务,强制中断可能留下本次部分产物,须核对后在新目录重跑。原survey-weight仍可独立使用,更新本技能不应改它。