使用 Stata 进行税调专利匹配:超高效的匹配流程

虽然之前给大家分享过很多数据匹配的结果,不过很久没有更新过数据匹配的课程了。平台上已有的一些匹配课程都是 2021 年左右讲解的了,用现在的视角来看已经不是那么的高效实用了。今天的课程中我们将以税调与专利数据的匹配为例介绍如何在 Stata 中高效的进行数据匹配。在数据处理、匹配中,我们不仅要针对当前数据处理,还应该下意识的构建自我数据库,这样才能让以后的数据处理更高效。

数据生成 ID 变量

如果大家经常使用我分享的数据,就会发现几乎所有的数据里面我都会生成一个 id 变量,例如税调数据里面有我生成的 sdid 变量,专利数据里面有我生成的 newipzlid:

2007~2016年税调数据与工商注册数据匹配结果(版本2):https://rstata.duanshu.com/#/brief/course/cb8992583a404b73bd01a0a5486ac640

1985~2024 年专利申请与授权数据(版本 3,含申请人所处的省市区县):https://rstata.duanshu.com/#/brief/course/2397451274c546d3a36e156ffc865988

在专利数据的介绍里面我分享了这种 id 变量的生成方法:

forval y = 1985/2024 {
di "`y'"
qui {
use "`y'patent.dta", clear
cap drop newipzlid
gen newipzlid = string(`y' * 10^strlen("`=_N'") + _n, "%12.0f")
order newipzlid
compress
foreach i of varlist _all {
cap format `i' %10s
}
save "`y'patent.dta", replace
}
}

这些 id 的变量都是用于标识观测值编号的,例如 2007276106 就表示 2007 年数据中的第 276106 个观测值。所谓的数据匹配其实就是建立 ID 对照表的过程,因为预先生成 id 变量,可以让我们在匹配过程中仅仅保留所需的变量,这样可以大大提升匹配的速度。

下图展示了税调变量的基本信息:

在完整的税调数据里面我也提供了这个 sdid 变量。

借助工商注册信息

企业与专利数据匹配过程中我们通常可以使用企业名称变量和申请人变量进行匹配。不过还可以借助工商注册数据来提升匹配数量。因为工商注册信息里面还包含了企业曾用名、统一社会信用代码等指标。

企业与工商注册信息的匹配通常也是用企业名称,当然如果有组织机构代码等变量也可以用。

这里匹配税调企业专利信息就可以借助税调企业的工商注册信息。

确定匹配思路

结合税调数据和专利数据包含的变量,我们选择企业名称、统一社会信用代码、纳税人识别号这三个变量进行匹配。

另外就是税调数据是个多年的面板数据,这种数据通常存在一个问题,就是例如 2007 年的数据中有某个企业 A 和它的组织机构代码,但是 2008 年的数据里面可能企业名称缺失。但是组织机构代码还在。因此我们可以首先进行跨年的数据填补。

匹配的时候可以分别使用企业名称、企业代码进行匹配。

使用企业名称进行匹配

处理税调数据的企业名称

按照上面的思路,我们合并税调和工商注册信息,保留所有可能用到的企业名称字段:

appendall.ado 是我编写的一个小命令,把该文件放到工作目录下面就可以使用了。该命令可以用来合并某个文件夹下面的所有 dta 文件。

cd "~/Desktop/使用 Stata 进行税调专利匹配"

*- 合并税调企业工商注册信息
appendall 税调数据与工商注册数据匹配结果(分年)
save "2007~2009年税调数据与工商注册数据匹配结果", replace

*- 合并税调基本信息语工商注册信息
use 税调基本信息.dta, clear
gen year = substr(sdid, 1, 4)
destring year, replace
keep if inrange(year, 2007, 2009)
ren 企业名称 企业名称_税调
merge 1:1 sdid using "2007~2009年税调数据与工商注册数据匹配结果.dta"

*- 根据法人代码填补企业名称
bysort 法人代码: fillmissing 企业名称_税调 if !missing(法人代码), with(any)

*- 保留匹配可能需要的变量
keep sdid year 企业名称_税调 企业名称 曾用名 英文名
gather 企业名称_税调 企业名称 曾用名 英文名
drop if missing(value)
drop var
ren value 企业名称
drop if 企业名称 == " "
save 税调基本信息2, replace

list in 1/10

*> +------------------------------------------------------------------------+
*> | sdid year 企业名称 |
*> |------------------------------------------------------------------------|
*> 1. | 2007000001 2007 北京京仪集团有限责任公司 |
*> 2. | 2007000001 2007 北京京仪集团有限公司 |
*> 3. | 2007000002 2007 中国煤炭海外开发有限公司 |
*> 4. | 2007000002 2007 中国煤炭海外开发有限公司 |
*> 5. | 2007000002 2007 中国煤炭海外开发公司中国成套设备出口公司煤炭分公司 |
*> |------------------------------------------------------------------------|
*> 6. | 2007000003 2007 中国种畜进出口有限公司 |
*> 7. | 2007000003 2007 中国种畜进出口有限公司 |
*> 8. | 2007000003 2007 中国种畜进出口公司 |
*> 9. | 2007000004 2007 中国图书进出口(集团)总公司 |
*> 10. | 2007000004 2007 中国图书进出口(集团)总公司 |
*> +------------------------------------------------------------------------+

专利数据预处理

由于一个专利的申请人可能存在多个,因此在匹配前我们需要先拆分申请人:

cap mkdir "专利申请人"
forval y = 2007/2009 {
use 专利原始数据/`y'.dta, clear
keep newipzlid 申请人
drop if mi(申请人)
export delimited using "专利申请人/`y'.csv", replace
}

这个 csv 文件是这样的:

newipzlid,申请人
2007237048,上海新跃仪表厂
2007018707,上海昱豪高压清洗机有限公司
2007039991,上海嘉迪机械有限公司
2007503190,上海南洋电机有限公司
2007346177,中涂化工(上海)有限公司
2007502524,上海华特汽车配件有限公司
2007194229,上海科世达 华阳汽车电器有限公司
2007270323,上海生大企业有限公司; 上海生大医保股份有限公司
2007422296,上海球明标准件有限公司
2007131238,上海众源燃油分配器制造有限公司
2007470817,中国科学院上海光学精密机械研究所
2007051307,中国科学院上海光学精密机械研究所

之所以导出为 csv 文件,是因为我们下面想使用 R 语言进行拆分。在 Stata 中,这种拆分可以使用 split + gather 或者循环实现。不过对于专利这种数据,这样的处理方法是非常低效的。所以这里我还是建议使用 R 语言。

对 split + gather 或者循环实现感兴趣的小伙伴可以学习这个课程:

使用 Stata 进行绿色专利的筛选:https://rstata.duanshu.com/#/brief/course/2fd6838527754c94a3f5ccb0e3ba7be1

下面的代码就是在 R 语言中运行了:

library(tidyverse)
dir.create("专利拆分申请人")
2007:2009 %>%
lapply(function(x){
readr::read_csv(paste0("专利申请人/", x, ".csv")) %>%
tidytext::unnest_tokens(input = "申请人", output = "申请人",
token = stringr::str_split,
pattern = ";", to_lower = F) %>%
mutate(申请人 = str_remove_all(申请人, " ")) %>%
haven::write_dta(paste0("专利拆分申请人/", x, ".dta"))
}) -> tempres

拆分好的结果就保存在”专利拆分申请人”文件夹里面:

use 专利拆分申请人/2007, clear
tostring newipzlid, replace format(%16.0f)
list in 1/10

*> +---------------------------------------------+
*> | newipzlid 申请人 |
*> |---------------------------------------------|
*> 1. | 2007237048 上海新跃仪表厂 |
*> 2. | 2007018707 上海昱豪高压清洗机有限公司 |
*> 3. | 2007039991 上海嘉迪机械有限公司 |
*> 4. | 2007503190 上海南洋电机有限公司 |
*> 5. | 2007346177 中涂化工(上海)有限公司 |
*> |---------------------------------------------|
*> 6. | 2007502524 上海华特汽车配件有限公司 |
*> 7. | 2007194229 上海科世达华阳汽车电器有限公司 |
*> 8. | 2007270323 上海生大企业有限公司 |
*> 9. | 2007270323 上海生大医保股份有限公司 |
*> 10. | 2007422296 上海球明标准件有限公司 |
*> +---------------------------------------------+

可以看到第 8、9 个观测值就是一个专利拆分出来的结果。

企业名称处理.do 文件是很多年前我在处理工企数据企业名称变量的时候写的,后来我发现这些纠正代码用在其他数据上也很好用,所以每次匹配的时候我都会用上。企业名称处理2.do 则是去除一些影响匹配的关键词:

gen z = 企业名称
order z
replace z = subinstr(z, "股份有限", "", .)
replace z = subinstr(z, "集团有限", "", .)
replace z = subinstr(z, "有限责任", "", .)
replace z = subinstr(z, "有限公司", "", .)
replace z = subinstr(z, "有限", "", .)
replace z = subinstr(z, "责任", "", .)
replace z = subinstr(z, "股份", "", .)
replace z = subinstr(z, "公司", "", .)
replace z = subinstr(z, "厂", "", .)
replace z = subinstr(z, " ", "", .)
replace z = subinstr(z, "(集团)", "", .)
replace z = subinstr(z, "(集团)", "", .)
replace z = subinstr(z, "(", "", .)
replace z = subinstr(z, ")", "", .)
replace z = subinstr(z, "(", "", .)
replace z = subinstr(z, ")", "", .)
replace z = subinstr(z, "回族自治区", "", .)
replace z = subinstr(z, "壮族自治区", "", .)
replace z = subinstr(z, "维吾尔自治区", "", .)
replace z = subinstr(z, "自治区", "", .)
replace z = subinstr(z, "省", "", .)
replace z = subinstr(z, "市", "", .)
replace z = subinstr(z, "区", "", .)
replace z = subinstr(z, "县", "", .)

依次使用这两个 do 文件处理企业名称就可以得到一个新变量 z,这个变量就是用来匹配的桥梁变量:

cap mkdir "专利数据待匹配_申请人"
forval y = 2007/2009 {
use 专利拆分申请人/`y', clear
ren 申请人 企业名称
do 企业名称处理.do
do 企业名称处理2.do
keep newipzlid z
save "专利数据待匹配_申请人/`y'", replace
}

处理好的文件就存放在 “专利数据待匹配_申请人” 文件夹里面。这个数据不仅仅可以用来匹配税调,用来匹配其他数据也是完全没有问题的。

使用 z 进行匹配

然后我们就可以用同样的方法处理税调的企业名称,进而匹配:

*- 根据企业名称匹配
use 税调基本信息2, clear
do 企业名称处理.do
do 企业名称处理2.do
duplicates drop _all, force
save 待匹配专利信息, replace

cap mkdir "res1"
forval y = 2007/2009 {
di "`y'"
qui {
use 待匹配专利信息, clear
keep if year == `y'
joinby z using "专利数据待匹配_申请人/`y'"
tostring newipzlid, replace format(%16.0f)
joinby newipzlid using "专利原始数据/`y'"
drop z 企业名称 year
save res1/`y', replace
}
}

合并匹配结果

appendall res1
destring *次数, replace force
foreach i of varlist _all {
cap format `i' %10s
}
save 根据企业名称匹配结果, replace

使用统一社会信用代码匹配

使用统一社会信用代码匹配的过程和上面的差不多。

预处理专利数据:

cap mkdir "专利数据待匹配_信用代码"
forval y = 2007/2009 {
use "专利原始数据/`y'", clear
gen code = 工商统一社会信用代码
keep newipzlid code
drop if mi(code)
split code, parse(;)
drop code
gather code*
drop if mi(value)
drop var
ren value code
replace code = subinstr(code, " ", "", .)
replace code = subinstr(code, ";", "", .)
drop if mi(code)
keep newipzlid code
save "专利数据待匹配_信用代码/`y'", replace
}

同样,这个数据也可以用来匹配其他数据。

统一社会信用代码、纳税人识别号、组织机构代码、工商注册号这几个变量在很多数据里面经常混用,因此匹配的时候可以考虑把这几个变量一起用来匹配:

use 税调基本信息.dta, clear
gen year = substr(sdid, 1, 4)
destring year, replace
keep if inrange(year, 2007, 2009)
ren 企业名称 企业名称_税调
merge 1:1 sdid using "2007~2009年税调数据与工商注册数据匹配结果.dta"
keep sdid 统一社会信用代码 纳税人识别号
gather 统一社会信用代码 纳税人识别号
drop if mi(value)
drop var
ren value code
replace code = strupper(code)
replace code = subinstr(code, "-", "", .)
drop if missing(code) | code == "None" | code == "未公开" | code == "-"
drop if code == " "
gen year = substr(sdid, 1, 4)
destring year, replace
duplicates drop _all, force
save 待匹配专利信息2, replace

匹配:

*- 使用统一社会信用代码匹配
cap mkdir "res2"
forval y = 2007/2009 {
di "`y'"
qui {
use 待匹配专利信息2, clear
keep if year == `y'
joinby code using "专利数据待匹配_信用代码/`y'"
tostring newipzlid, replace format(%16.0f)
joinby newipzlid using "专利原始数据/`y'"
save res2/`y', replace
}
}

*- 合并
appendall res2
destring *次数, replace force
foreach i of varlist _all {
cap format `i' %10s
}
drop code year
save 根据统一社会信用代码匹配结果, replace

合并两部分匹配结果

合并两部分匹配结果再去重即可。不过如果大家的电脑内存不是很足,其实上面的分年匹配结果就不要直接合并了。

*- 合并
use 根据企业名称匹配结果, clear
append using 根据统一社会信用代码匹配结果

*- 去除重复匹配的
duplicates drop sdid newipzlid, force
gsort sdid newipzlid
label data "数据处理:微信公众号 RStata"
save "2007~2009年税调与专利数据匹配结果", replace

*- 拆分成逐年的
use "2007~2009年税调与专利数据匹配结果", clear
cap mkdir "税调与专利数据匹配结果分年"
forval y = 2007/2009 {
preserve
keep if 年份 == `y'
save "税调与专利数据匹配结果分年/`y'", replace
restore
}

分年、分类型统计各企业的专利申请和授权数量

在计算专利申请数量时需要注意,专利数据里面有很多申请和授权同时存在的专利,在统计专利数量的时候应该先去除这种重复情况:

统计申请数量:

这部分代码需要下载讲义材料查看。

然后是授权数量的统计。这里说的授权通常是指申请并最终被授权的数量,因此去除未授权的即可统计:

这部分代码需要下载讲义材料查看。

最后合并两部分结果:

use data1, clear
merge 1:1 sdid using data2
drop _m

foreach i of varlist *量 {
replace `i' = 0 if mi(`i')
}
duplicates drop sdid, force
save "2007~2009年税调专利申请与授权数量统计", replace

这样就得到了分年、分类型统计各企业的专利申请和授权数量面板。

点击这里跳转到 RStata 短书平台获取附件:使用 Stata 进行税调专利匹配:超高效的匹配流程

评论