名师讲堂|渐进性创新与突破性创新:使用 Stata 计算上市公司跨年专利技术相似度

「Incremental vs. Breakthrough Innovation: The Role of Technology Spillovers」一文中提出了几个关于衡量渐进性创新与突破性创新的指标:

  1. 重复引用次数(Repeatcitations):企业重复引用过去 5 年内专利的引用次数占总引用次数的比例;

  2. 自引次数(Self-citations):企业自引占总引用次数的比例;

  3. 已知领域(known areas):企业熟悉的技术分类下的专利数量占企业专利申请总数的比例;

  4. 技术相似性(Tech Proximity):企业在 t 年的专利技术领域与 t-1 年专利技术领域之间的相似性。该值越小意味着更多的突破性创新。具体的指标为:

  5. 被引次数(Citations):采用 Citations90、Citations99 分别衡量被引次数在前 10% 和 1% 的专利数量除以企业当年专利申请总数。

其中前三个指标反映的是企业的渐进性创新,后两个指标反映的是企业的突破性创新。

今天我们先讲解专利相似性的计算,这个涉及到了公式,稍微比其他几个复杂点。

之前也给大家讲解过类似的课程:

使用 Stata 计算专利技术空间相似度、企业层面的知识宽度、新技术空间专利申请及IPC号新增数:https://rstata.duanshu.com/#/brief/course/535f9284c31d47c2861b8cd1384394d0

使用 Stata 计算企业技术相似度:交叉数据集、矩阵运算和 Mata 方法:https://rstata.duanshu.com/#/brief/course/11ec07a861384c218f5f77582875b0a2

第一个课程里面其实讲解过跨年的技术相似度,第二个计算的其实是企业间的技术相似度。虽然第一个课程中讲解过类似的内容,不过方法感觉还不是很好用,今天再给大家介绍一种更好用的方法。

读取专利数据并去除重复的

本次课程我们将以上市公司专利数据为例进行讲解。上市公司专利数据来自这里:

1985~2024 年上市公司与专利数据匹配结果(版本3, 含申请、授权信息):https://rstata.duanshu.com/#/brief/course/04100321f88b411f90429be934934bff

由于数据非常巨大,就没在附件中提供。使用专利数据的时候要特别注意专利数据里面有很多申请和授权同时存在的专利,在统计专利数量的时候应该先去除这种重复情况:

使用前可以先去除公开公告号里面的 A、B、U、S。其中 A 代表发明专利的申请公开,B 代表发明专利的授权公告,U 代表实用新型专利的授权公告,S 代表外观设计专利的授权公告:

cd ~/Desktop/渐进性创新与突破性创新:使用 Stata 计算上市公司跨年专利技术相似度
use 1985~2024年上市公司与专利数据匹配结果.dta, clear

*- 去除重复的专利
replace 公开公告号 = subinstr(公开公告号, "A", "", .)
replace 公开公告号 = subinstr(公开公告号, "B", "", .)
replace 公开公告号 = subinstr(公开公告号, "U", "", .)
replace 公开公告号 = subinstr(公开公告号, "S", "", .)
replace 专利类型 = "发明" if index(专利类型, "发明")
duplicates drop 股票代码 公开公告号, force

*- 保留需要的变量
keep 股票代码 年份 newipzlid IPC主分类
save rawdata, replace

rawdata.dta 在附件中提供了:

use rawdata, clear
*> (数据处理:微信公众号 RStata)
list in 1/10
*> +----------------------------------------+
*> | 股票代码 newipz~d 年份 IPC主分类 |
*> |----------------------------------------|
*> 1. | 000960 19850031 1985 F16H1/34 |
*> 2. | 000709 19850705 1985 B03C3/02 |
*> 3. | 600416 19850975 1985 H01M2/10 |
*> 4. | 600099 19852578 1985 F24C3/08 |
*> 5. | 000538 19853310 1985 A61K31/35 |
*> |----------------------------------------|
*> 6. | 200530 19853984 1985 F16F15/28 |
*> 7. | 000530 19853984 1985 F16F15/28 |
*> 8. | 200530 19853985 1985 B63B9/08 |
*> 9. | 000530 19853985 1985 B63B9/08 |
*> 10. | 600416 19854105 1985 H01M10/44 |
*> +----------------------------------------+

参考文献中使用的技术分类信息来自美国国家经济研究局(NBER)的专利数据库。该数据库提供了从1980年到2006年由美国专利商标局(USPTO)授予的超过300万项专利的详细信息。不过根据前述两个课程的介绍,进来年份的参考文献通常使用 IPC 主分类号的第一位(部)、前三位(大类)、前四位(小类)三种方法作为类别的划分标准。

下面我们依次使用这三种方式进行计算。

方法一:使用部作为技术分类

提取部:

use rawdata, clear
*> (数据处理:微信公众号 RStata)
drop if mi(IPC主分类)
*> (337,069 observations deleted)
gen class = substr(IPC主分类, 1, 1)
drop IPC主分类

每年每家企业的总专利申请量:

destring newipzlid, replace
*> newipzlid: all characters numeric; replaced as double
bysort 股票代码 年份: egen totalcount = count(newipzlid)

分类别的数量:

bysort 股票代码 年份 class: egen subcount = count(newipzlid)

计算比率:

gen ratio = subcount / totalcount
drop *count newipzlid
duplicates drop _all, force
*> Duplicates in terms of 股票代码 年份 class ratio
*> (1,729,414 observations deleted)
spread class ratio
foreach i of varlist A-H {
*> 2. replace `i' = 0 if mi(`i')
*> 3. }
*> (47,783 real changes made)
*> (28,405 real changes made)
*> (40,095 real changes made)
*> (56,505 real changes made)
*> (51,634 real changes made)
*> (40,850 real changes made)
*> (32,031 real changes made)
*> (37,492 real changes made)

排个序:

gsort 股票代码 年份

如果企业只有一年的数据,无法跨年计算,因此删除:

bysort 股票代码: egen count = count(年份)
drop if count == 1
*> (214 observations deleted)
drop count

现在的数据是这样的:

list in 1/10
*> +-----------------------------------------------------------------------------+
*> | 股票代码 年份 A B C D E F G H |
*> |-----------------------------------------------------------------------------|
*> 1. | 000001 2003 0 0 0 0 0 0 1 0 |
*> 2. | 000001 2010 0 0 0 0 0 0 1 0 |
*> 3. | 000001 2013 0 0 0 0 0 0 .5 .5 |
*> 4. | 000001 2016 0 0 0 0 0 0 1 0 |
*> 5. | 000001 2017 0 0 0 0 0 0 .8333333 .1666667 |
*> |-----------------------------------------------------------------------------|
*> 6. | 000001 2018 0 0 0 0 0 0 1 0 |
*> 7. | 000001 2019 0 0 0 0 0 0 .9300699 .0699301 |
*> 8. | 000001 2020 0 0 0 0 0 0 .9007353 .0992647 |
*> 9. | 000001 2021 0 .0084746 0 0 0 0 .9350283 .0564972 |
*> 10. | 000001 2022 .0012715 .0006357 0 0 0 0 .8900191 .1080737 |
*> +-----------------------------------------------------------------------------+

下面我们需要循环企业进行计算,最高效的方式就是使用 Mata 代码了。不过在正式写循环之前,我们先选择平安银行的为例:

*- 以平安银行为例进行计算
mata:
mat = st_data(1::11, 3..10)
resmat = J(rows(mat), 1, .)
for (i = 2; i <= rows(mat); i++) {
resmat[i] = mat[i,.] * mat[i-1,.]' / (sqrt((mat[i,.] * mat[i,.]')) * sqrt((mat[i-1,.] * mat[i-1,.]')))
}
resmat
end

然后就可以循环所有公司的了:

mata:
code = st_sdata(., "股票代码")
mat = st_data(., 3..10)
// 所有互不相同的股票代码
codelist = uniqrows(code)

// 年份
year = st_data(., "年份")

// 保存到 res1 文件里面
stata("cap erase res1.csv")
filename = "res1.csv"
fh = fopen(filename, "rw")

// 循环公司
for (c = 1; c <= rows(codelist); c++) {
codelist[c, 1]

// 该公司对应的数据
index = selectindex(code[., 1] :== codelist[c, 1])
tempmat = mat[index, .]
tempyear = year[index, .]

resmat = J(rows(tempmat), 1, .)
for (i = 2; i <= rows(tempmat); i++) {
resmat[i] = tempmat[i,.] * tempmat[i-1,.]' / (sqrt((tempmat[i,.] * tempmat[i,.]')) * sqrt((tempmat[i-1,.] * tempmat[i-1,.]')))
}
for (i = 1; i <= rows(resmat); i++) {
fput(fh, codelist[c, 1] + "," + strofreal(tempyear[i]) + "," + strofreal(resmat[i, .]))
}
}
fclose(fh)
end

读取处理结果:

import delimited using res1.csv, clear stringcols(_all)
*> (encoding automatically selected: UTF-8)
*> (3 vars, 59,783 obs)
ren (v1 v2 v3) (股票代码 年份 跨年专利技术相似度_IPC1)
destring 年份 跨年专利技术相似度_IPC1, replace
*> 年份: all characters numeric; replaced as int
*> 跨年专利技术相似度_IPC1: all characters numeric; replaced as double
*> (4838 missing values generated)
save data1, replace
*> file data1.dta saved

方法二:使用大类作为技术分类

大类是 IPC 主分类号的前三个字符。然后代码和上面的类似:

use rawdata, clear
drop if mi(IPC主分类)
gen class = substr(IPC主分类, 1, 3)
drop IPC主分类

*- 每年每家企业的总专利申请量
destring newipzlid, replace
bysort 股票代码 年份: egen totalcount = count(newipzlid)

*- 分类别的数量
bysort 股票代码 年份 class: egen subcount = count(newipzlid)

*- 计算比率
gen ratio = subcount / totalcount
drop *count newipzlid
duplicates drop _all, force
spread class ratio
foreach i of varlist A01 - H10 {
replace `i' = 0 if mi(`i')
}

gsort 股票代码 年份

*- 删除只有一年数据的
bysort 股票代码: egen count = count(年份)
drop if count == 1
drop count

*- 循环所有公司的
mata:
code = st_sdata(., "股票代码")
mat = st_data(., 3..127)
// 所有互不相同的股票代码
codelist = uniqrows(code)

// 年份
year = st_data(., "年份")

// 保存到 res1 文件里面
stata("cap erase res2.csv")
filename = "res2.csv"
fh = fopen(filename, "rw")

// 循环公司
for (c = 1; c <= rows(codelist); c++) {
codelist[c, 1]

// 该公司对应的数据
index = selectindex(code[., 1] :== codelist[c, 1])
tempmat = mat[index, .]
tempyear = year[index, .]

resmat = J(rows(tempmat), 1, .)
for (i = 2; i <= rows(tempmat); i++) {
resmat[i] = tempmat[i,.] * tempmat[i-1,.]' / (sqrt((tempmat[i,.] * tempmat[i,.]')) * sqrt((tempmat[i-1,.] * tempmat[i-1,.]')))
}
for (i = 1; i <= rows(resmat); i++) {
fput(fh, codelist[c, 1] + "," + strofreal(tempyear[i]) + "," + strofreal(resmat[i, .]))
}
}
fclose(fh)
end

import delimited using res2.csv, clear stringcols(_all)
ren (v1 v2 v3) (股票代码 年份 跨年专利技术相似度_IPC3)
destring 年份 跨年专利技术相似度_IPC3, replace
save data2, replace

方法三:使用小类作为技术分类

小类是主分类号的前四位,代码也类似:

use rawdata, clear
drop if mi(IPC主分类)
gen class = substr(IPC主分类, 1, 4)
drop IPC主分类

*- 每年每家企业的总专利申请量
destring newipzlid, replace
bysort 股票代码 年份: egen totalcount = count(newipzlid)

*- 分类别的数量
bysort 股票代码 年份 class: egen subcount = count(newipzlid)

*- 计算比率
gen ratio = subcount / totalcount
drop *count newipzlid
duplicates drop _all, force
spread class ratio
foreach i of varlist A01B - H10N {
replace `i' = 0 if mi(`i')
}

gsort 股票代码 年份

*- 删除只有一年数据的
bysort 股票代码: egen count = count(年份)
drop if count == 1
drop count

*- 循环所有公司的
mata:
code = st_sdata(., "股票代码")
mat = st_data(., 3..660)
// 所有互不相同的股票代码
codelist = uniqrows(code)

// 年份
year = st_data(., "年份")

// 保存到 res1 文件里面
stata("cap erase res3.csv")
filename = "res3.csv"
fh = fopen(filename, "rw")

// 循环公司
for (c = 1; c <= rows(codelist); c++) {
codelist[c, 1]

// 该公司对应的数据
index = selectindex(code[., 1] :== codelist[c, 1])
tempmat = mat[index, .]
tempyear = year[index, .]

resmat = J(rows(tempmat), 1, .)
for (i = 2; i <= rows(tempmat); i++) {
resmat[i] = tempmat[i,.] * tempmat[i-1,.]' / (sqrt((tempmat[i,.] * tempmat[i,.]')) * sqrt((tempmat[i-1,.] * tempmat[i-1,.]')))
}
for (i = 1; i <= rows(resmat); i++) {
fput(fh, codelist[c, 1] + "," + strofreal(tempyear[i]) + "," + strofreal(resmat[i, .]))
}
}
fclose(fh)
end

import delimited using res3.csv, clear stringcols(_all)
ren (v1 v2 v3) (股票代码 年份 跨年专利技术相似度_IPC4)
destring 年份 跨年专利技术相似度_IPC4, replace
save data3, replace

合并三个结果

最后,合并三种结果:

use data1, clear
merge 1:1 股票代码 年份 using data2
*> Result Number of obs
*> -----------------------------------------
*> Not matched 0
*> Matched 59,783 (_merge==3)
*> -----------------------------------------
drop _m
merge 1:1 股票代码 年份 using data3
*> Result Number of obs
*> -----------------------------------------
*> Not matched 0
*> Matched 59,783 (_merge==3)
*> -----------------------------------------
drop _m
label data "数据计算:微信公众号 RStata"
save "1985~2024年各上市公司跨年专利技术相似度计算结果", replace
*> file 1985~2024年各上市公司跨年专利技术相似度计算结果.dta saved

最后可以绘制一幅图展示下计算结果:

collapse (mean) 跨年专利技术相似度_IPC1 (mean) 跨年专利技术相似度_IPC3 (mean) 跨年专利技术相似度_IPC4, by(年份)

tw conn 跨年专利技术相似度_IPC1 跨年专利技术相似度_IPC3 跨年专利技术相似度_IPC4 年份, ///
m(o ...) lp(solid ...) color("254 212 57" "112 154 225" "138 145 151") ///
leg(order(1 "跨年专利技术相似度(部)" 2 "跨年专利技术相似度(大类)" 3 "跨年专利技术相似度(小类)") pos(6) row(1)) ///
xti("") xla(1985(5)2020 2024) ///
yla(0(0.2)1, format(%6.1f)) ///
ti("1986~2024年各年上市公司与上年的平均技术相似度") ///
subti("数据处理:微信公众号 RStata") ///
caption("数据来源:RStata 数据中心:1986~2024年各年上市公司与上年的平均技术相似度. 2025." "https://tidyfriday.cn/rsdb2/")

gr export "1986~2024年各年上市公司与上年的平均技术相似度.png", replace width(4800)

点击这里跳转到 RStata 短书平台获取附件:名师讲堂|渐进性创新与突破性创新:使用 Stata 计算上市公司跨年专利技术相似度

评论