news 2026/9/18 21:05:04

Stata处理CNRDS绿色专利数据:从导入清洗到合并的完整实操指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Stata处理CNRDS绿色专利数据:从导入清洗到合并的完整实操指南

最近总有师弟师妹来问我同一个问题:在Stata里处理CNRDS绿色专利数据库时,为什么下载下来直接打开就乱码,为什么统计出来的绿色专利申请量跟论文里对不上,为什么merge之后样本翻了好几倍。我意识到,这个数据库虽然下载门槛低、字段也齐全,但真正要拿到能进回归的“公司—年份”面板,中间还是有不少隐性坑的。索性把这几年的踩坑经历整理成一篇完整的操作笔记,从数据表结构、读取方式、统计口径,到匹配合并的常见报错,一条线走下来,希望能帮正在做绿色创新、ESG、环境经济相关研究的朋友少走弯路。

这篇内容不是基础菜单教学,而是围绕“CNRDS绿色专利数据到底怎么才能变成Stata里可用的研究数据”这个核心问题展开。无论你是刚接触Stata的新手,还是已经会跑基础命令但被数据清洗折磨过的老手,这篇文章应该都能提供一些直接可用的脚本和思路。我会把所有操作步骤写清楚,包括具体的Stata代码、字段名称、处理逻辑和判断理由,方便你直接对照自己的数据做修改。

1. CNRDS绿色专利数据库里究竟躺着哪些表

1.1 绿色专利识别的底层逻辑:先从IPC绿色分类说起

要理解CNRDS绿色专利数据库,必须先搞清楚一个问题:什么样的专利算“绿色专利”?CNRDS并不是自己拍脑袋定义了一套绿色专利标准,它的识别逻辑基于WIPO(世界知识产权组织)发布的环境友好技术分类清单,也就是业内常说的“WIPO Green Inventory”。

这份清单把与环境保护、资源节约、清洁能源相关的IPC分类号(国际专利分类号)单独摘了出来,比如太阳能、风能、废水处理、固体废物管理、节能照明等方向都有对应的IPC分类号段。CNRDS做的工作,本质上是在全量专利申请数据中,按照这些绿色IPC号做了一次筛选,再以公司为主体做了关联匹配。理解了这一点,你在用数据时就不会犯“把所有专利都当绿色专利”这种错误,也不会困惑为什么绿色专利数总比专利总数小一大截。

1.2 CNRDS绿色专利数据库的常用数据表及字段速查

根据研究场景不同,你大概率会用到这几个表中的几个。我用一张表把最常用的表名和核心字段列出来,你在下载的时候先认清楚自己需要哪些,不要一上来全选下载,文件又大又容易把自己绕晕。

数据表主要用途关键字段(常见命名)
公司专利基本信息表获取全量专利明细,含申请号、公开号、专利类型、IPC分类号、申请日、公开日Stkcd、证券简称、专利名称、公开公告号、专利类型、IPC分类号、申请日期、公开日期
公司绿色专利表已按绿色IPC识别好的绿色专利明细,核心数据Stkcd、证券简称、公开公告号、专利类型、IPC分类号、申请日期、公开日期
公司绿色专利引用表刻画专利质量,计算被引次数公开公告号、被引专利号、引用日期等
公司绿色专利分类号表专利与IPC分类号的多对多关系明细公开公告号、IPC分类号、分类号层级等

这四张表是大多数实证论文的数据源头。其中公司绿色专利表是最常用的一张,它已经完成了“绿色识别”这一步,能直接用来统计公司每个年度的绿色专利申请量。但正是这张表里藏着几个不小的坑,后面我会详细说。

在动手下载之前,我强烈建议你先在CNRDS系统里看清楚自己学校的授权权限。有些学校只订购了“绿色专利”模块,有些学校连“专利引用”模块也一起订了。权限直接决定你能下载哪些表。没有引用表的话,后面做专利质量指标时就只能另想办法,比如用申请量做稳健性检验。

2. Stata读取CNRDS数据:编码、格式与变量类型清理

2.1 下载后的文件形态:Excel直接拖进Stata为什么会出问题

CNRDS下载下来的Excel文件,大多数情况下是能正常读取的,但如果你用的是老版本Stata(尤其是14及以下),直接import excel会有不少变量类型识别错误。最典型的表现是:股票代码那一列明明是文本,但Stata自动读成了数值,前导的0被吃掉;日期列要么读成序列值,要么读成字符串,后面还得手动转换。

比如股票代码“000001”,如果以数值格式读入,就会变成1。别笑,这个问题真的非常高频。解决方法是在导入前就告诉Stata哪些列是文本:

import excel using "公司绿色专利表.xlsx", firstrow clear destring Stkcd, replace force

但这里又会引出另一个问题:如果Stkcd里包含类似“000001.SZ”这种带后缀的代码,destring会直接报错。所以更稳妥的做法是导入后先tostring或者用正则表达式处理。

2.2 import excel时值得保留的几项设置

我自己在实际操作中,一直用的是一套固定导入模板,基本能避开大部分格式问题:

import excel using "CNRDS_GreenPatent.xlsx", /// sheet("公司绿色专利表") firstrow clear * 中文变量名转英文,避免后续输入麻烦 rename Stkcd stkcd rename 证券简称 name rename 公开公告号 publication_no rename 专利类型 patent_type rename 申请日期 apply_date rename 公开日期 publish_date

这段代码里有两个细节值得注意。sheet()一定要写,因为一个工作簿里可能有好几个表单,Stata默认读取第一个表,如果位置不对就会读错数据。另一个是中文变量名,新版Stata支持中文变量名,但每次输入变量名都要切换输入法,效率很低,建议还是导入后统一改成英文。

2.3 日期字段的统一:从Excel序列值到Stata日期

CNRDS导出的日期,有的版本是“2020-05-12”这样的文本,有的是“2020/5/12”,还有的老版本导出后Excel会直接显示为数值,比如43963这种序列值。这三种形态都需要统一处理。

处理文本日期格式:

* 第一种情况:标准格式 gen apply_date2 = date(apply_date, "YMD") format apply_date2 %td * 第二种情况:含斜杠 replace apply_date = subinstr(apply_date, "/", "-", .) gen apply_date2 = date(apply_date, "YMD") format apply_date2 %td

处理Excel序列值,逻辑是:Excel默认的起始日期是1900年1月1日,但要扣掉一个闰年bug造成的偏差,在Stata里用date("1899-12-30","YMD") + 序列值就能转换。

gen apply_date2 = date("1899-12-30", "YMD") + apply_date_numeric format apply_date2 %td

在处理日期之前,我建议先对原始字符串列做一次tab检查,看看到底有几种格式,不要上来就批量替换。万一某一行是“2020.05.12”,直接替换“/”和“-”都会漏掉它。

3. 公司绿色专利申请量的标准化统计流程

3.1 为什么不能直接把“绿色专利数”字段拿来用

很多同学以为下载下来的公司绿色专利表里都带了一列“绿色专利数量”,直接加总就行。但实际上,CNRDS提供的明细数据通常是“一行为一条专利记录”的格式,而不是“公司—年份—数量”的汇总格式。你需要自己根据Stkcd和申请年份做分组统计。

如果你在下载页面看到有“公司绿色专利年度统计表”之类的汇总表,也要谨慎使用。因为不同版本的汇总口径可能不一致,有的按申请年统计,有的按公开年统计。用明细表自己跑一遍统计是最稳妥的,既能控制口径,又能检查原始数据质量。

3.2 从明细表到公司—年份面板:一个标准化do文件模板

下面这套脚本是我每次处理CNRDS绿色专利数据都要跑的流程,基本可以复用于绝大多数研究场景:

* 第一步:导入 import excel using "公司绿色专利表.xlsx", sheet("Sheet1") firstrow clear * 第二步:清洗关键字段 rename Stkcd stkcd rename 专利类型 patent_type rename 申请日期 apply_date rename 公开公告号 publication_no * 将stkcd规范为6位字符串 tostring stkcd, replace format("%06.0f") force * 生成申请年份 gen apply_year = year(date(apply_date, "YMD")) * 第三步:处理重复值。同一专利可能包含多个IPC分类号,明细表会出现同一公开公告号多条记录 duplicates drop stkcd publication_no apply_year, force * 第四步:按专利类型分类统计 gen is_green_invention = (patent_type == "发明专利") gen is_green_utility = (patent_type == "实用新型") collapse (count) green_patent_total = publication_no /// (sum) green_invention = is_green_invention /// (sum) green_utility = is_green_utility, by(stkcd apply_year) * 第五步:补全面板(从最小年份到最大年份) xtset stkcd apply_year tsfill, full

这里面的关键一步是duplicates drop。因为同一个专利如果有多个IPC分类号,明细表里就会有同一publication_no的多条记录,如果不先去重,后面collapse统计出的专利申请量就会虚高。我见过有人在统计时忘了这步,结果绿色专利数直接翻倍,回归结果也完全失真。

3.3 申请年vs公开年:两类年份的取舍逻辑

做绿色创新实证研究时,核心解释变量通常用专利申请量。那应该用申请年份还是公开年份?我的看法是:优先用申请年份。

专利的申请日代表技术方案提交的时点,更贴近企业的创新决策时间节点,而公开日通常是申请日后18个月甚至更晚的时点,用它构造的变量在时间上会整体滞后,且滞后长度不均匀(不同类型专利不一样,发明公开慢、实用新型公开相对快)。在做因果识别时,申请日通常被认为是相对外生的时间锚点。

但也有人会做稳健性检验,把公开年的统计量也跑一遍。你完全可以在do文件里同时生成两个年份变量:

gen apply_year = year(date(apply_date, "YMD")) gen publish_year = year(date(publish_date, "YMD"))

后面想用哪个就用哪个。

4. 绿色专利质量刻画:授权量与引用次数怎么用

4.1 授权量计算的细节:授权公告日不等于公开日

如果只看申请量,很多时候会受到“策略性绿色创新”的质疑——企业申请一堆绿色专利只是为了“漂绿”,并不代表真实的技术进步。所以现在审稿人普遍要求补充绿色专利授权量来做稳健性检验。授权量的计算逻辑和申请量类似,但关键日期是授权公告日,而不是公开日。

CNRDS的公司专利基本信息表里通常会有“授权公告日”字段,绿色专利表有时候也会带。但要注意的是,有些表中的授权日字段在专利还没授权时为空值,直接按授权日年份统计时,空值年份会被自动排除,这没问题;但有些表中授权日缺失可能恰好对应“被驳回”的专利,这类专利本来就不该计入授权量,所以空值导致的排除是合理的,不用担心“丢失样本”。

授权量的统计脚本:

* 假设已导入绿色专利明细表 gen grant_year = year(date(授权公告日, "YMD")) * 仅保留授权年份非缺失的记录 keep if !missing(grant_year) * 按公司+授权年份统计 collapse (count) green_grant = publication_no, by(stkcd grant_year)

这里有一个容易忽略的细节:授权量一般需要滞后几期再用。因为从申请到授权通常需要1到3年,如果拿当期授权量做被解释变量,估计出来的效应在经济意义上可能解释不清。常见的做法是做核心解释变量滞后一期、两期。

4.2 引用次数:构建“公司—年份”专利质量变量的路径

专利被引次数是衡量专利质量和高技术影响力的常用指标。CNRDS的绿色专利引用表记录了每件绿色专利的被引用情况,但它是“专利—引用记录”级别的数据,不能直接用来做回归。你需要把它汇总到“公司—年份”层面上。

一个合理的处理流程是:

* 导入引用表 import excel using "公司绿色专利引用表.xlsx", firstrow clear rename Stkcd stkcd rename 公开公告号 publication_no rename 引用日期 cite_date * 生成引用年份 gen cite_year = year(date(cite_date, "YMD")) * 有的引用表是引用的“被引证”记录,一行表示一次引用事件 collapse (count) green_cite = publication_no, by(stkcd cite_year)

如果你想计算的是“某公司某年申请的绿色专利,在之后若干年被引的总次数”,那逻辑就更复杂一些:首先要拿到公司年度申请的专利清单,然后和引用表按publication_no关联,再按引用年份加总。这本质上是一个多表关联任务,建议分两步走,先在公司—年份层面上把申请专利的publication_no汇总宏列表保存下来,再与引用表匹配,最后collapse。别试图一个命令解决,中间变量多了容易出错。

我自己的经验是,引用数据在汇总前先做一次“引用年份是否在申请年份之后”的判断,剔除那些引用日早于申请日的异常记录。虽然理论上不会发生,但数据清洗时经常能遇到数据录入错误,这种异常值不处理会让质量变量产生微小但明显的污染。

5. 合并匹配时的三个“坑死人不偿命”的细节

5.1 重复合并带来的样本膨胀

这个坑我印象太深了。有一次处理绿色专利数据和公司财务数据合并,merge完成后发现样本量从3000多直接变成12000多,我还以为是自己财务数据下载多了,反复检查才发现是绿色专利明细表在publication_no层面有多条重复记录。

原因和前面说的一样:一条专利可能对应多个IPC分类号,如果合并前没有做去重,专利表和财务表做1:1合并时,系统就会自动变成多对多,一条公司年度观测值被复制成多条。解决办法就是在合并之前,确保绿色专利表已经在stkcd + year + publication_no层面去重,或者先用collapse把专利表变成“一个公司一年一行”的汇总表,再去和财务数据合并。

正确的合并路径:

* 第一步:把绿色专利明细collapse到公司-年份层面 preserve use "绿色专利明细_清洗.dta", clear collapse (count) green_patent = publication_no, by(stkcd apply_year) rename apply_year year save "绿盼专利_公司年度.dta", replace restore * 第二步:与公司财务数据合并 use "公司财务数据.dta", clear merge 1:1 stkcd year using "绿色专利_公司年度.dta" keep if _merge == 3

preservemerge的好处是,财务数据永远作为主表,专利数据作为辅助信息匹配进去,不会因为专利数据的问题破坏主表结构。

5.2 股票代码格式不一致:000001到底该是文本还是数值

CNRDS表的股票代码有两种形态,一种是不带交易所后缀的6位代码,比如“000001”“600519”;另一种是带后缀的,比如“000001.SZ”“600519.SH”。在你的研究样本里,如果只用A股上市公司,6位代码就够用;但如果你合并的数据里有港股、B股或者其他市场标识,就必须带着后缀区分。

问题在于,Stata读取时如果直接destring,带后缀的代码会全部变成缺失值。我的建议是:在合并之前,先统一股票代码格式。

* 统一去掉所有后缀,只保留6位 gen stkcd_clean = substr(stkcd_raw, 1, 6)

如果原始代码是数值型,就先转字符串再取前6位:

tostring stkcd, generate(stkcd_str) format("%06.0f") gen stkcd_clean = substr(stkcd_str, 1, 6)

这一步的核心目的在于:让合并双方的主键完全一致。你花十分钟把代码格式统一,就能避免后面merge后几千条_merge == 1_merge == 2对不上的痛苦。

5.3 时间区间错位:申请年度与财务年度的对齐问题

绿色专利的申请年度和公司财务年度都是自然年,所以stkcd + year可以直接做1:1合并。但如果你用的财务数据是“会计年度”且披露日和报告期不一致,或者有些数据库里的year变量用的是“公告年份”而非“报告期年份”,就一定要先核对两边的时间口径。

我可以分享一个简单的检查逻辑:合并之前,分别对两个数据源做tab stkcd year,格式和年份范围各看一遍。如果发现财务数据里有2024年的记录,而绿色专利数据只到2023年,那合并后2024年样本的绿色专利变量就全是缺失值,后续回归会自动剔除这些样本,样本区间就莫名其妙缩短了。

另一个常见问题发生在做滞后项时。如果你构造了L.green_patent,那么第一期的观测值会自动缺失,这是正常的,但很多新手会以为是自己数据丢了,反复检查。实际上只要面板设定正确,xtset stkcd year之后,L.前缀生成的缺失值是正常的统计结果。

6. 从数据到回归:一个最简可复现的变量构造全流程

6.1 一份可以“抄作业”的完整do文件示例

前面讲了那么多零散步骤,这里我把它们串起来,给出一份从零到回归前变量的完整do文件。你拿到CNRDS绿色专利表之后,可以直接套用这个框架。

* 路径设置 global rawdata "D:/CNRDS" global workdata "D:/workdata" * 导入绿色专利明细 import excel using "$rawdata/公司绿色专利表.xlsx", /// sheet("Sheet1") firstrow clear * 变量重命名 rename Stkcd stkcd rename 证券简称 name rename 公开公告号 publication_no rename 专利类型 patent_type rename 申请日期 apply_date rename 公开日期 publish_date * 清理stkcd格式 tostring stkcd, replace format("%06.0f") * 生成申请与公开年份 gen apply_year = year(date(apply_date, "YMD")) * 去重:同一专利有多条IPC记录时只保留一条 duplicates drop stkcd publication_no apply_year, force * 构造类型虚拟变量 gen inv_dummy = (patent_type == "发明专利") gen util_dummy = (patent_type == "实用新型") * 汇总到公司-年份 collapse (count) green_all = publication_no /// (sum) green_inv = inv_dummy /// (sum) green_util = util_dummy, by(stkcd apply_year) rename apply_year year save "$workdata/green_patent_firm_year.dta", replace

用这个do文件跑出来的green_patent_firm_year.dta,每一行代表一个公司在某年的绿色专利申请情况,可以直接和公司财务数据做1:1合并。

6.2 合并后回归变量的常见选择与稳健性做法

变量构造好以后,具体回归模型根据研究主题可以灵活调整。这里仅说说绿色专利数据最常被用来做的几种变量形态:

  • 绝对量:green_allgreen_inv,代表绿色专利申请总量和绿色发明专利申请量
  • 相对量:green_all / 公司当年总专利申请量,代表绿色创新占比,用来刻画“绿色转型程度”
  • 对数化:ln(1 + green_all),消除右偏分布的影响,是实证中最常见的做法
  • 增量:green_all - L.green_all,刻画动态变化,在事件研究类论文中常用

我建议在do文件里把所有这些形态都生成好,后面做描述性统计和回归时来回切换,会节约大量时间。

use "$workdata/green_patent_firm_year.dta", clear merge 1:1 stkcd year using "$workdata/firm_finance.dta" keep if _merge == 3 xtset stkcd year * 生成常用变量形态 gen ln_green = ln(1 + green_all) gen green_ratio = green_all / total_patent_all gen d_green = green_all - L.green_all

6.3 数据质量的最后一道防线:描述性统计检查清单

变量生成完,不要着急跑主回归,花两分钟检查一下描述性统计,能避免很多低级问题。

tab year green_all tabstat ln_green green_ratio d_green, by(year) stats(n mean sd min max)

重点检查三件事:第一,每一年的样本量是否和财务数据的公司数基本一致,如果某一年样本量骤减,多半是合并时数据时间段没对齐。第二,green_all的最大值是否异常大,比如大于500甚至1000,如果出现,就要回溯到明细表,看是不是某个公司当年确实申请了大量专利,还是去重步骤出了问题。第三,green_ratio是否都落在0到1之间,如果大于1,说明分子统计用了绿色专利表,而分母用了全量专利表,两个数据源的识别口径不一致,比值自然出错。

这三项检查是数据清洗和回归分析之间的一道安全网。我自己吃过好几次亏,都是因为跳过检查直接跑回归,等到结果出来发现系数方向不对,回头查了半天才找到是数据层面出了问题。浪费的时间足够把整个do文件跑几十遍了。

最后再分享一个个人习惯:CNRDS数据下载后,我会第一时间把原始Excel另存一份,并备注下载日期和版本号。因为数据库会不定期更新版本,不同版本之间同一家公司的绿色专利数量可能存在差异。做研究时确保自己用的始终是同一版本的数据,后面写论文的时候也可以直接在脚注里写清楚数据版本和下载时间。这个习惯帮我在审稿人要求补充数据细节时省了很多功夫。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 21:03:23

RoboMaster电控实战:卡尔曼滤波在云台姿态与底盘估计中的落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 20:59:06

MySQL Explain执行计划详解:慢查询排查与索引优化实战

凌晨两点被电话叫起来,一条订单查询接口的 P99 从 80ms 直接冲到 4.2s,业务方在群里刷屏。我做的第一件事不是翻代码,而是连上库,把那条 SQL 原封不动复制出来,前面加上 EXPLAIN 敲回车。两秒钟后我看到 typeALL、rows…

作者头像 李华
网站建设 2026/9/18 20:57:22

IDEA打包Web项目war包的完整指南与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 20:55:49

STM32F407ZGT6深度解析:从引脚布局到工业级稳定运行

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 20:55:38

Cursor 改稿降 AI 率,Key 和 Base URL 走 TaoToken 行不行?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华