ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Stata数据清洗实战:计量回归前的编码、缺失值与可复现流程

Stata数据清洗实战:计量回归前的编码、缺失值与可复现流程 1. 为什么计量前的数据清洗决定了结果的天花板1.1 计量里最贵的一课垃圾进垃圾出在 Stata 里跑回归最不缺的就是能出结果。reg y x敲下去系数、标准误、星号全都出来了看着挺像那么回事。但真正让人难受的是同一份原始数据清洗方式不同核心解释变量的系数能从 0.05 变成 -0.03三颗星变成一颗都没有。你去找导师或者审稿人解释人家第一句话往往是你的数据是怎么处理的而不是你的模型设定对不对。这就是计量工作里最贵的一课。模型是公开的、方法是可以抄的唯独数据清洗这一段全靠手上功夫。Stata 不会替你判断一个 -99 到底是缺失还是真的亏了 99 块也不会替你发现两个变量合并时出现了重复匹配。它只会老老实实地按你给的规则算然后给你一个看起来无懈可击、实际上千疮百孔的结果。我见过太多人把 80% 的精力花在研究设计和模型选择上剩下 20% 随手应付数据最后卡在稳健性检验过不了、审稿意见问为什么样本量从 5000 掉到 3200。所以这篇东西不聊花哨的方法就聊一件事在做回归、做匹配、做亚组分析之前数据到底该怎么一步步洗干净。1.2 一条完整的清洗链路长什么样很多新手对数据清洗的理解是删删空值、去掉重复行这远远不够。一套站得住脚的 Stata 清洗流程大致要经过五个层次每一层解决的问题都不一样层次解决的问题典型命令导入层编码乱码、变量名缺失、类型错位import excel、unicode、rename结构层重复观测、多表错配、面板不平衡duplicates、merge、xtset变量层字符串转数值、缺失值、重编码、日期destring、mvdecode、recode派生层构造新变量、量纲统一、缩尾gen、egen、winsor2验证层逻辑一致性、可复现、留痕assert、codebook、log这五个层次不是随便排的顺序一旦打乱就会反复返工。比如你没做去重就直接合并merge报出_merge3的数量远超预期你却以为是数据本身有问题再比如你没统一量纲就做缩尾把万元和元混在一起处理1% 分位点直接算废。提示清洗顺序的原则是先结构后内容、先唯一后派生。表格唯一性没确定之前任何合并和面板声明都是空中楼阁。我个人的习惯是写一个 do-file从导入一直做到验证中间不手动修数据。原因很实在一份数据你今天清洗完三个月后补充了新样本要重跑一遍如果过程中有手动改单元格的动作你根本复现不出来。而审稿人、合作者、甚至三个月后的你自己都会需要这个可复现性。2. 导入环节把数据干净地请进 Stata2.1 中文数据最头疼的编码问题只要你的数据里带中文省份名、行业名、企业名称编码问题几乎一定会出现。表现是导入后中文变成问号、方块或者干脆显示成一段乱码。根子在于 Stata 早期版本默认用 GB 系列编码后来 14 版以后转向 Unicode两套体系一碰就出事。处理思路分两种情况。第一种文件还是老编码先分析再转码unicode analyze raw_data.dta unicode encoding set gb18030 unicode translate raw_data.dta, invalid(mark)unicode analyze会告诉你这份文件用的是哪种编码unicode encoding set指定目标编码unicode translate执行转换。加invalid(mark)是让无法识别的字符被标记出来而不是被悄悄替换成乱码这样你能知道哪些内容出了问题。第二种直接从 Excel 或 CSV 导入这个问题反而更好办因为import excel在新版 Stata 里对编码的兼容性不错import excel using raw_data.xlsx, sheet(data) firstrow clear注意能用 CSV 就别用 Excel。xlsx 里经常藏着合并单元格、多行表头、隐藏列firstrow只认第一行当变量名多行表头会导致整列变成字符串。我个人经验是拿到 Excel 原始文件先另存为 CSV再用import delimited导入可控性最高import delimited using raw_data.csv, encoding(UTF-8) clear如果中文还是乱就把UTF-8换成GB18030再试一次。这两种基本覆盖了国内数据的绝大部分场景。2.2 变量名、标签与清洗前的起手式导入完成后的第一件事不是急着跑描述统计而是把变量名和标签整理利落。变量名不规范后面写代码全是坑带空格、带中文、带括号的变量名在 Stata 里根本用不了会自动被改成v1、v2这种毫无意义的名字。rename (v1 v2 v3 v4) (id year province income) label variable id 个体编号 label variable year 年份 label variable province 省份名称 label variable income 年收入元变量名统一用英文小写加下划线这是 Stata 里最省心的写法。标签用中文完全没问题注释性信息放在标签里反而更清楚。接着是数据类型检查。这一步最容易被跳过但后果很严重。数据里只要有一列混进了一个 N/A 或 未知整列就被 Stata 判定成字符串后面所有数值运算都做不了。describe codebook, compactdescribe看类型和存储格式codebook看取值分布。两个命令连着跑你就能快速判断哪些变量该是数值却变成了字符串。2.3 缺失值识别Stata 里的.不是零这是新手最容易犯、后果最隐蔽的一个错误。Stata 里数值型缺失值的内部编码是一个极大的数显示为.。它在排序时会被排到最后在计算均值和回归时会被自动剔除这一点和 0 完全不是一回事。但麻烦在于很多原始数据不用.表示缺失而是用 -99、-999、0、未知、空字符串这些伪缺失值。Stata 根本不认识这些会把 -999 当成一个真实观测参与计算。一个收入变量里混了 200 个 -999均值直接往下拉一大截。misstable summarize misstable patternsmisstable summarize只统计真正的缺失值也就是已经存成.的那些。对于伪缺失值得靠你自己先识别summarize income, detail tab income if income 0看一下最小值和分位点如果出现 -99、-999 这种扎堆的极端负数基本可以确定是缺失值编码。识别之后再统一转换mvdecode income wealth consumption, mv(-99 -999)mvdecode会把指定的编码值全部变成真正的缺失值。这一步做完你后面的所有统计才是有意义的。提示mvdecode建议在清洗初期就做别拖到建模时才想起来。缺失值拖得越久污染的范围越大你就越难判断某个异常到底是数据错误还是真实的经济行为。3. 结构层重复值、合并与面板结构重建3.1 重复观测先搞清是完全重复还是逻辑重复数据重复分两种。一种是完全重复行整行一模一样另一种是逻辑重复比如同一个个体-年份出现了两次记录但两次的其他变量有细微差别。第一种直接删第二种必须查清楚原因。先用duplicates report看全局duplicates report id year duplicates tag id year, gen(dup) tab dup如果dup 0的观测数量很少先列出来人工看一眼list id year income if dup 0, sepby(id)这时候通常会看到三种情况一是录入时重复提交两条完全一样删掉一条二是同一主体在同一年有多次测量比如企业一年内报了两次表这种情况要么取平均、要么取年末值取决于你的研究设计三是主键本身有问题比如 id 相同但明显是两个人那是编码错误得回原始表核对。确定逻辑之后才动手删duplicates drop id year, forceforce是保留第一条、删掉其余加了它 Stata 才允许删除那些主键重复但其他变量不完全相同的行。用它之前你要先确认被保留的那条确实是正确的。注意删重复之前一定先save一份带dup标记的副本。删错了想恢复靠记忆是恢复不回来的。3.2 merge 与 append多表拼接最容易翻车的两个地方先区分这两个命令的用途append是纵向堆叠把结构相同的表拼在一起比如把 2010—2015 年的数据逐年追加merge是横向连接靠主键把不同的变量接到同一批观测上比如把企业财务数据和地区 GDP 数据按省份-年份接起来。append的坑相对少但要注意变量类型一致。如果第一年 income 是数值型第二年导进来变成字符串append后整列会变字符串。做法是每年导入后立刻统一类型再追加。追加时用generate加一个年份来源变量方便日后追溯foreach y in 2010 2011 2012 { import delimited using data_y.csv, clear gen year y save panel_y.dta, replace } use panel_2010.dta, clear append using panel_2011.dta panel_2012.dtamerge的坑就深多了。核心问题只有一个主键必须唯一。如果 using 表里同一个主键出现了两次merge会生成笛卡尔积式的重复5000 行瞬间变成 8000 行而且不报错。use firm.dta, clear merge 1:1 province year using gdp.dta tab _merge_merge的取值含义要背下来1 是仅主表有、2 是仅 using 表有、3 是两边都有。如果_merge3的数量和主表观测数对不上说明主键有问题。count count if _merge 3两个数字应该相等对于 1:1 合并。不相等就回头查 using 表的主键唯一性。3.3 面板声明与平衡性检查面板数据分析之前必须声明面板结构否则xtreg、xtabond这些命令全都用不了xtset id year xtdescribextset会告诉你面板是强平衡还是非平衡。xtdescribe更细致能看出每个个体有多少期观测、整体缺哪些年份。很多实证研究默认用平衡面板但我建议别急着强制平衡。非平衡面板在计量上完全是被允许的强行删掉不完整观测会损失大量样本还可能引入选择偏差——那些数据不全的个体往往本身就有特殊性。判断是否需要平衡看两点一是缺失是否是随机的如果某一年的数据整体缺失那大概率是数据来源问题而不是个体问题二是你的方法是否要求平衡比如某些差分 GMM 的设定对时间连续性有要求。by id: gen n_obs _N tab n_obs看每个个体观测期的分布如果绝大多数个体都是满期只有少数不全那删掉少数反而更干净如果分布很散宁可用非平衡面板。3.4 异常值与量纲先看分布再动手异常值处理之前一定要先做两件事看分布、统一量纲。summarize income, detail histogram income, bin(50)detail会给出百分位点重点是看 1%、5%、95%、99% 这几个点。如果最大值是最小值的上万倍基本可以确定量纲没统一——比如有的记录是万元有的是元。这种情况必须先除以 10000 把单位对齐再谈异常值。量纲统一之后缩尾winsorize是最常用的处理方式ssc install winsor2, replace winsor2 income, cuts(1 99) replacecuts(1 99)表示把低于 1% 分位的值替换成 1% 分位的值高于 99% 分位的替换成 99% 分位的值。注意是替换不是删除这能保留样本量同时削弱极端值的影响。提示缩尾比例没有绝对标准1%/99% 最常见样本量小时可以用 2%/98%。关键是在论文或报告里明确写出来并且做一次不缩尾的稳健性检验两个结果方向一致才放心。我个人习惯是先备份一份原始变量缩尾生成新变量而不是replace覆盖。这样随时能对比winsor2 income, cuts(1 99) suffix(_w)生成的income_w就是缩尾后的版本原始income原封不动。做稳健性检验时两个变量换着用非常方便。4. 变量层类型转换、缺失值填补与派生变量4.1 字符串转数值destring、encode 与 real() 的区别这三个东西看着都能把字符变数字但用途完全不同用错了会出大问题。destring是把本来就是数字、只是被存成字符串的内容转回数值比如 1234 转成 1234destring income, replace destring income, replace force加force表示遇到无法转换的字符时把那个观测变成缺失值而不是整列报错。用force之前建议先看看有哪些非数字字符list income if missing(real(income)) !missing(income)encode是把文字分类转成带标签的数值编码比如把北京上海广东变成 1、2、3encode province, gen(prov_id)它生成的 prov_id 是数值型但背后挂着province的标签tab prov_id显示的还是中文。这适合做回归里的固定效应。real()是一个函数一般用在gen里做条件转换不直接改原变量。一个关键区别encode只能用在字符串变量上而且转换后的数值大小没有数学意义北京的编码 1 不代表它比编码 2 的上海大destring转换后的数值是有数学意义的。两者混用是常见的错误来源。4.2 缺失值处理的三种策略缺失值处理没有万能方案得看缺失比例和缺失机制。第一种缺失比例很低比如小于 5%且是随机的直接删。前提是你确认过这些缺失和核心变量无关drop if missing(income, education, age)用missing()一次判断多个变量比income .这种写法安全因为它同时能处理数值缺失和字符串缺失。第二种缺失比例中等用插补。Stata 里简单地可以用均值、中位数插补但这种方法会人为压缩方差只适合做敏感性分析。更规范的做法是用多重插补mi set mlong mi register imputed income education mi impute chained (regress) income (logit) education age gender, add(20)add(20)生成 20 个插补数据集最后用mi estimate:跑回归把插补的不确定性纳入标准误。这套流程现在做实证研究越来越常见。第三种缺失本身有信息比如收入拒答可能和低收入相关。这种情况可以把缺失当成一个单独的类别做建模或者用 Heckman 之类的选择模型处理。注意无论用哪种方法一定要先做misstable patterns看看缺失的模式。如果缺失是成片出现的比如某一年所有收入都缺那多半是数据采集环节出了问题硬插补反而会制造虚假结论。4.3 分类变量重编码与虚拟变量回归里绝大多数模型都需要虚拟变量。Stata 里生成和识别其实很方便但有几个坑。直接生成虚拟变量tab province, gen(prov_dum)这会生成 prov_dum1、prov_dum2 等一组变量。注意tab是按字母或标签顺序编号的你得弄清楚哪个编号对应哪个省份。做交互项或者需要基准组时用因子变量语法更省事reg wage i.education i.region c.age##c.agei.表示分类变量Stata 自动把它拆成虚拟变量并选一个基准组。c.表示连续变量##生成主效应加交互项。这套语法的好处是不用手动gen一堆变量模型干净、可读性强。对于有序分类比如教育程度小学、初中、高中、大学用i.会损失顺序信息。如果研究需要可以改成数值编码或做有序 logit。重编码本身用recoderecode age (0/171) (18/342) (35/593) (60/1204), gen(age_group)这种分段通常结合研究假设来定不要为了让结果好看去反复调整分段点那属于数据操纵。4.4 日期时间变量计量里最容易被忽视的坑做时间趋势、事件研究、政策评估的日期是核心变量。但原始数据里它往往是一列字符串比如 2010-03-15 或者 2010/3/15。转换用date()函数配合formatgen date date(datestr, YMD) format date %tddate()把字符串转成 Stata 内部的日序号%td让它以人类可读的日期形式显示。如果你只有年月可以用mdy()或者拼一个日gen ym ym(year, month) format ym %tm对于做月度、季度面板的%tm、%tq很常用。提示政策评估、事件研究里最怕日期错位。有代表性的一类错误是一个政策在 2015 年 7 月实施但数据只到年度有人就把整年都当成处理后。这种粗细处理会直接影响估计结果有条件的话尽量用月度甚至季度数据。5. 清洗后的体检与可复现留痕5.1 用 assert 和 count 做交叉验证清洗做完不代表就能用。assert是我在清洗阶段用得最多的命令它的作用是如果一个逻辑不成立就报错停下来assert age 0 age 120 assert year 1990 year 2023 assert !missing(id) assert income 0 if !missing(income)一旦某条不成立Stata 立刻报错并告诉你哪条观测出问题。这种断言式检查能帮你抓住那些肉眼很难发现的边缘错误。配合count做数量核对也很实用count count if _merge 3 count if missing(income)清洗前后各记一次数量凡是应该相等却没有相等的地方都是需要排查的线索。比如你去重前有 5000 行duplicates drop后应该剩多少合并前主表 3000 行1:1 合并后应该还是 3000 行把这些数字对齐数据质量就基本可控了。5.2 do-file 与 log让清洗过程可追溯这一步是拉开专业和业余差距的地方。很多人手动在数据编辑器里改数据改完保存过一段时间自己都说不清做过什么。正确做法是把所有清洗动作写进 do-file同时打开 log 记录运行过程log using cleaning_log.smcl, replace // 所有清洗代码 log closelog using会把屏幕上所有的命令和输出存成文件.smcl格式可以用 Stata 打开也可以导出成文本。合作者拿到这个文件能完整看到你的每一步操作和中间结果。do-file 编写有个小技巧把路径写在最前面用全局宏后面直接引用global raw D:/project/raw global clean D:/project/clean import delimited using $raw/data.csv, clear save $clean/data_cleaned.dta, replace这样换电脑、换目录时只需改一行其余代码照常运行。5.3 codebook 与数据字典交接时的救命文档codebook, compactcodebook会列出每个变量的类型、标签、取值数量、缺失数量、取值范围。清洗完成后跑一遍输出保存下来就是一份天然的变量清单。如果要做正式的数据交接建议再补一个数据字典用表格写清楚每个变量的名称、含义、单位、取值说明、缺失处理方式。这份文档在后续合作、论文附录、数据公开时都是必需的。我的习惯是清洗完立刻更新字典拖久了记忆就模糊了。5.4 清洗结果导出与版本管理清洗完成的数据建议一次性导出两种格式.dta给 Stata 用.csv做通用备份。save $clean/final.dta, replace export delimited using $clean/final.csv, replace版本管理上文件名加日期是最简单有效的做法final_20240315.dta。同时在 do-file 头部用注释写清版本变更* 版本: v1.2 * 变更: 补充 2022 年样本修正 income 量纲统一逻辑 * 日期: 2024-03-15别小看这几行注释等你三个月后想不通为什么样本量变了它会救你一命。6. 常见问题与排查速查表做完前面几步绝大多数问题都能解决。但实操中总有一些反复出现的疑难杂症我把它们整理成一张速查表方便你对照排查。现象最可能的原因处理方式中文显示为乱码编码不匹配unicode analyzeunicode translate或import时指定encoding()一列本该是数字却存成字符串混入 N/A、未知、空格等非数字字符先定位非数字字符处理后再destring均值异常低或异常高伪缺失值 -99/-999 被当成真实值mvdecode转成真正缺失值合并后观测数翻倍using 表主键不唯一合并前检查 using 表主键用duplicates report核对xtset报错面板不唯一或主键有重复先做duplicates drop再xtset回归样本量骤减存在缺失值或共线变量被自动剔除检查哪些变量含缺失用misstable定位缩尾后结果大幅变化极端值本身携带重要信息对比缩尾前后做稳健性检验说明日期计算出现负数日期转换格式写错核对date()的格式字符串检查%td格式合并方向的_merge全是 2主键根本没对上检查主键类型是否一致数值 vs 字符串导出后中文又乱码csv 导出未指定编码export delimited时加encoding(UTF-8)这张表里的每一条基本都是我踩过或者看别人踩过的坑。其中排在第一位和第二位的——编码问题和伪缺失值——占比最高几乎每个做国内数据的项目都会撞上。再补两个容易被忽略的独家心得。第一清洗和建模之间的过渡要留一个中间版本。我通常会在清洗完成后save一个analysis_ready.dta然后从它开始做所有建模建模过程中任何临时变量、临时子样本都在这个版本的基础上派生不再回头改原始数据。这样一旦发现某个模型设定有问题随时能回到干净的起点重来。第二遇到犹豫不决的处理选择比如该不该缩尾、该不该删某年数据先全部保留在主分析之后用不同处理方式做敏感性检验两种结果方向一致时再在正文写主口径。清洗阶段就急着做决策回头想验证都没数据了。我前面提到的缩尾生成新变量而非 replace就是这个思路本质上是在清洗环节给自己留退路。数据清洗这件事说到底是把研究者的判断标准落到数据里。Stata 只是个执行工具它比的是谁对数据理解和检查得更细。手上多留一个版本、多写一行注释、多跑一次 assert最后体现在结果里的就是那个审稿人挑不出毛病、自己也能复现的扎实分析。
返回列表