
用 Stata 处理 CSMAR 数据库的朋友应该都体会过从下载到真正能跑回归之间的那段“灰色地带”。我最初接触的就是 CSMAR 里最小但非常实用的一个子库——资质认定数据库代码叫 QUA。当时我拿到压缩包以为把文件读进 Stata 就完事了结果跟财务主表一 merge满屏的_merge 1和_merge 2差点让我怀疑人生。后来花了整整两天才弄清楚问题出在股票代码的格式、日期变量的编码还有那张表里看似重复实则不同的观测记录上。这篇东西就是把我处理 QUA 数据的完整流程、踩过的坑和判断逻辑写出来给正在啃 CSMAR 或者打算用资质认证数据做实证的同学一份可以直接照着操作的参照系。1. QUA数据库在研究里的用途先说清楚它到底装了些什么很多第一次听说“资质认定数据库”的人第一反应是“这是个什么冷门库”。其实它在 CSMAR 里的定位很清晰收录上市公司及其重要的子公司获得的各种资质认证信息包括高新技术企业认定、软件企业认定、ISO 系列体系认证、行业资质、生产许可等。名字叫 QUA估计是 Qualification 的缩写。这种数据在研究里的价值不在于它本身多复杂而在于它是一个天然的“政策时点”。比如某个公司哪一年首次拿到了高新技术企业资质这意味着从该年度起公司可能享受 15% 的所得税优惠技术研发的边际收益变了后续的研发投入、产出、人力结构都可能跟着变。这个逻辑天然适合 DID、事件研究、动态效应分析。很多做创新经济学的论文就是把高企认定当成一个外生冲击来识别政策效果。ISO 认证则更偏向管理效率、出口行为、供应链关系这些话题。1.1 资质数据适合回答哪几类问题从实证设计角度我把 QUA 最常见的用途分成三种。第一种是政策效应评估最典型的是“高新技术企业认定对企业研发投入的影响”。这里认证时点的外生性不一定完美但通常还能用 PSM-DID 或者事件研究法来缓解内生性。第二种是异质性分析也就是把资质认证当作企业特征。比如研究融资约束时有资质的企业可能更容易获得银行授信或政府补助资质变量可以作为企业质量或政策支持力度的一个代理变量。第三种是事件史分析比如研究企业获得认证后多久会发生 IPO、多久会获得下一轮融资、认证失效对公司股价是否造成冲击。QUA 里通常有认证的开始日期、结束日期和状态字段这给生存分析提供了很好的原材料。1.2 一条观测记录里到底有什么信息以我自己打开过的 QUA 表为例常见字段大概这么几类。企业识别类证券代码、证券简称、公司全称。认证属性类认证类型、证书编号、资质名称或认定项目名称。时间字段类发证日期、开始日期、结束日期、有效截止日期。状态与机构类认证状态有效/撤销/过期、认定机构、审批机关。一条记录的含义通常是某公司在某个时间段内拥有某项资质。举个例子证券代码 300750认证类型为“高新技术企业”发证日期 2017-11-30有效期从 2017-01-01 到 2019-12-31状态为“有效”。这里要注意CSMAR 不同年份的导出文件字段名可能不一样同一个概念有时候叫CertifType有时候又变成Atype所以打开文件第一步不是急着写代码而是先看数据字典或字段说明。1.3 别忽略 QUA 的隐藏价值很多同学只知道拿 QUA 做“是否高企”这个虚拟变量其实还可以挖得更细。比如认证有效期长度、是否连续通过复审、有没有认证撤销记录这些信息都能构造出更有意思的变量。我后来在做一个关于上市公司“质量信号”的项目时就把“认证类型数量”和“认证是否有复审记录”当作企业认证强度的指标效果比单纯用 0/1 变量好很多。QUA 看似简单但字段背后可挖掘的地方不少建议别只盯着表面那几个变量。2. 从下载的CSMAR目录到Stata内存导入编码与文件结构那些坑很多问题其实从下载完压缩包那一刻就开始了。CSMAR 网页导出数据时常见格式有 xlsx、csv、txt甚至有时候是一个包含多个文件的文件夹。最稳妥的路径是先看清楚里面有什么再决定用什么导入命令。2.1 解压之后第一件事先看表头我见过不少同学拿到压缩包就直接双击打开 CSV看到中文列名能正常显示就以为万事大吉然后拖进 Stata 里import delimited结果变量名乱成一团或者数值全变成字符串。这是因为 CSV 文件在 Windows 下用 Excel 打开时会按本地系统的默认编码猜测看起来正常不代表 Stata 也能自动识别。所以我的建议是解压后先用import delimited读前几行或者直接用 Stata 里的import delimited ... varnames(1)把第一行作为变量名读进来然后describe看看每个变量的存储类型和格式。这里最容易忽略的是列名可能是中文Stata 默认会把中文字符变量名转换成带下划线的拼音或数字标签后续引用会很痛苦。我一般会先把中文列名rename成英文比如证券代码改成Stkcd发证日期改成CertIssueDate这样后面写代码不会经常因为引号问题报错。2.2 编码问题乱码并不代表文件坏了CSMAR 提供的 CSV 文件常见编码是 UTF-8 或 GBK具体取决于你下载时的设置。如果你用import delimited读出来发现所有中文字段全是或者无法识别的方块别急着删文件往往是编码没指定对。解决套路很简单。先试 UTF-8import delimited QUA.csv, varnames(1) encoding(utf-8) clear如果出来的中文还是不对就换成 GB18030import delimited QUA.csv, varnames(1) encoding(gb18030) clear还有一个办法是先用外部命令file或者编辑器VSCode、Notepad打开原始文件看它自动识别出的编码是什么再回头指定。通常 UTF-8 带 BOM 的文件Excel 打开没问题但 Stata 里需要encoding(utf-8)而 GBK 文件则常用gb18030兼容性比gbk更好。这个细节我踩过不止一次特别是从不同年份下载的文件编码风格可能都不一样。2.3 大数据文件不要反复读原始CSVQUA 这个库的文件体积一般不会特别夸张但也看筛选条件。如果你把全部 A 股上市公司几十年的认证记录一次性导出来CSV 可能上百兆甚至更大。每天跑论文代码时都从头读一遍 CSV既慢又容易碰到编码问题。我的习惯是第一遍导入清洗完成后直接保存成QUA_raw.dta之后所有代码都从这里开始。这样做还有一个好处就是 dta 文件会把变量的存储类型、格式、标签都保留好不会因为每次导入的默认类型不同而出现“这次Stkcd是字符串下次变成数值”这种鬼问题。别忘了compress一下compress save QUA_raw.dta, replace数据量太大会让 Stata 内存吃紧compress能有效降低存储占用后面跑起来轻快很多。3. 建面板先过键值关股票代码、日期和重复观测的清洗顺序真正决定你能不能顺利把 QUA 和财务数据合在一起的不是导入本身而是“键值”干不干净。这里说的键值主要是股票代码和日期。这两个变量看起来简单但 CSMAR 里格式经常不统一。3.1 股票代码前导零的教训CSMAR 的证券代码字段有时导出成数值型有时导出成字符串型。如果你用 Excel 打开看到的是1、2、600036这样的数值实际上 A 股代码应该是000001、000002、600036前导零全部被吃掉了。等你去 merge 财务数据时一边是000001另一边是数值1Stata 自然对不上。我的标准处理方式是这样的* 假设导入后 Stkcd 是字符串但可能存在 1 而不是 000001 replace Stkcd string(real(Stkcd), %06.0f) if length(Stkcd) 6如果你读进来发现它是数值型就先tostring Stkcd, format(%06.0f) replace再处理。这里的原则是所有股票代码最终统一成 6 位字符串不要用数值型保存。原因很简单数值型会丢掉前导零而证券代码本身没有数值意义完全不该作为数值参与运算。合并之前再做一次核验isid Stkcd year如果提示有重复说明你还没有处理好键不要急着 merge。3.2 日期变量先统一成 Stata 日期再谈年份CSMAR 的日期字段很任性可能是2017-06-01、2017/06/01、20170601甚至有的单元格是 Excel 序列号。如果不做清洗直接gen year year(CertStartDate)一定会报错因为CertStartDate还是字符串。我常用的处理模板* 先创建一个干净的日期变量 gen double StartDate_temp . replace StartDate_temp date(CertStartDate, YMD) if regexm(CertStartDate, -) replace StartDate_temp date(CertStartDate, YMD) if regexm(CertStartDate, /) replace StartDate_temp date(CertStartDate, YMD) if regexm(CertStartDate, .) format StartDate_temp %td gen year year(StartDate_temp) drop StartDate_temp还有一个容易忽略的细节CSMAR 里有些日期字段会用1900-01-01表示“未知”或者用2099-12-31表示“长期有效”。如果你不处理生成year时会凭空多出一些 1900 年和 2099 年的极端观测后面匹配上会出现大量异常。碰到这种情况建议先和数据库说明文件核对如果确实是占位符就把它们替换成缺失值或者单独标记一个变量。3.3 重复观测先分清“该删”和“该留”QUA 里出现重复记录很多时候不是数据错误而是同一家公司有多个资质、多个证书、多次复审。比如宁德时代 2017 年拿了高企证书2019 年又换了一次新证书两张记录都出现在表里但实际只是一家企业延续同一个资质。处理重复前先想清楚你的分析单元是什么。如果你想做“企业层面是否拥有某类资质”那就该按Stkcd 资质类型去重如果你想做“资质事件”那不同时间段的证书可能应该保留下来。一个能救命的检查命令是duplicates report Stkcd CertifType StartDate EndDate如果存在完全相同的行那大概率是下载时重复导出可以放心删除duplicates drop Stkcd CertifType StartDate EndDate, force但更麻烦的是“看起来重复、实际不重复”的情况也就是主键里少了某个区分字段比如证书编号或者状态字段。这时候最好排序后手工检查一小部分sort Stkcd StartDate EndDate duplicates list Stkcd StartDate别在没搞懂数据逻辑之前就粗暴地去重否则很容易把有价值的观测删掉。4. 匹配财务面板与生成政策变量从merge结果检查到DID样本构造等 QUA 清洗成一张干净的事件表之后下一步就是把它整合到以Stkcd year为标识的面板里去。这一步往往是全流程里最容易出错、也最需要动脑子的地方。4.1 合并方向与一对多关系先把 QUA 表整理成Stkcd year级别。最简单的做法是gen year year(StartDate) collapse (max) ever_cert flag_cert, by(Stkcd year)但collapse会把其他字段丢掉如果你需要保留认证类型、证书编号这些信息通常有两种思路。一种是把 QUA 按Stkcd yearmerge 到主财务面板里然后允许一对多匹配另一种是先构造出“公司是否在某年拥有某项资质”的宽表再和财务数据匹配。我个人更推荐先在 QUA 内部把粒度定义好。比如你想做“是否高新技术企业”那就先keep if CertifType 高新技术企业然后按Stkcd year去重再和主面板做merge m:1。千万不要在主面板和 QUA 都有多个匹配键、但还没去重的情况下直接merge 1:1那样 Stata 会直接报错或者产生大量交叉匹配记录。4.2 生成政策时点相关的变量拿到合并后的数据最常见的实证需求是构造 DID 变量。逻辑上要做三件事识别处理组、识别政策时点、构造交互项。* 先标记企业是否在样本期内获得过高企认定 by Stkcd: egen treat max(ever_cert) * 再找到每个企业第一次获得认定的年份 by Stkcd (year), sort: gen first_cert_year year if ever_cert 1 by Stkcd: egen cert_year min(first_cert_year) * 政策后时期 gen post (year cert_year) if cert_year ! . * 交互项 gen did treat * post这里有个容易被忽视的细节高企证书通常有有效期比如三年后需要复审。如果你用的数据里只有“获得年份”没有证书截止年份那就没法区分“已经过期”和“持续有效”。这时候两个办法一是从 QUA 中把EndDate拆出来构造“当年是否处于有效期内”的状态变量二是干脆在稳健性检验里只保留认证后第一期到第三期避免过期问题干扰结果。如果你想做事件研究还可以生成相对政策时点gen rel_year year - cert_year if treat 1生成之后记得检查一下tab rel_year看看每个相对期的样本量是否足够避免后续估计中某些年份没有观测值。4.3 合并后的质量核查很多同学merge一个tab _merge就完事了我觉得远远不够。至少还要看两个东西第一_merge 1的多不多说明主面板里有很多公司年没匹配到认证信息第二_merge 2的多不多说明 QUA 里有些记录在主面板中找不到对应公司或年份。一个典型的排查流程merge m:1 Stkcd year using QUA_collapsed.dta tab _merge list Stkcd year if _merge 2 in 1/20如果是证书数据和财务主表合并_merge 2多半是 ST 股、退市股或 B 股代码在主面板里没有。这时候要回头核对股票代码的格式是否统一而不是默认数据有错。合并完以后再用isid Stkcd year确认面板主键唯一。我几乎每次做都发现有人在这个环节翻车因为 QUA 事件表里一个公司同一年可能有多条认证记录如果没在 QUA 内部先处理好合并结果必然重复。5. 实测总结我每次打开QUA都会做的几项检查这部分是纯经验存款不写大道理只写我实际操作中反复用到的小检查。你可以当成一份排查清单来用。5.1 合并结果异常时第一反应是检查键值格式如果merge之后_merge 2的比例特别高别急着怀疑 CSMAR 数据缺失先看股票代码。用命令tab _merge Stkcd大概率会发现_merge 2的股票代码全是1、2这种短字符串而主面板里是000001、000002。这就是前导零问题没处理好。遇到这种问题重新回到第 3 节做一次格式统一比对着几千条记录手工改高效得多。5.2 日期缺漏不要自己想当然QUA 里EndDate缺失时有人会默认“长期有效”这非常危险。有些认证确实没有明确截止日期但也有些数据缺失只是录入问题。我建议对照 CSMAR 的原始说明或者下载页面的更新日志确认缺失值背后的业务含义。如果实在没法断定那就标记成缺失并在稳健性检验里单独处理。另外要注意“发证日期”和“生效日期”不一定相同政策效应评估应该用哪个日期取决于你研究的政策时点到底是什么。比如高企认定的税收优惠通常从认定当年开始但证书上列出的有效期可能从年初或发证日算起。这一点在构造post变量时需要特别留心。5.3 中间文件命名和说明别偷懒我早期处理 QUA 的时候经常生成一堆temp.dta、new.dta过了一个月自己都忘了哪个是清洗后的版本。后来强迫自己养成几个习惯中间文件统一叫QUA_clean.dta、fin_panel.dta、did_sample.dta每次save之前加notes写清楚这个文件的生成时间、变量筛选标准、是否已经去重重要处理步骤用 log 保存下来方便论文答辩或审稿人质疑时快速回溯不要在同一个文件名后面反复加_final、_final2我看到太多人最后用了错误版本的中间文件却不自知。给文件一个清晰的版本结构比临时抱佛脚可靠得多。回到最初那个让我抓狂的_merge问题现在回头看一切都很简单不过是字符串格式、编码和重复值的综合作用。QUA 这个库的数据量在 CSMAR 里不算大但麻雀虽小五脏俱全把它的清洗流程跑通你对面板数据键值管理、事件时点构造、政策变量生成的整个套路都会上一个台阶。下次拿到类似的认证数据不管是环保处罚还是专利授权处理思路都是一样的。希望这篇东西能帮你少走几步弯路。