名师讲堂|使用 Python 语言测算专利的创新突破度:ACD指数和mACD指数

本次课程在原始 CD 指数(Funk & Owen-Smith, 2017)的基础上,介绍徐照宜等(2023)在《金融研究》上发表的改进算法:ACD 指数和 mACD 指数。改进的核心是解决”从未被引用专利”的歧义问题,以及调整分母以更好地反映专利的突破性。

本课程提供 Python 版本 的完整实现,通过 reticulate 包在 R Markdown 中调用 Python,与 R 语言版本计算结果完全一致。

附件中提供了相关论文 PDF 和计算代码。

一、背景:原始 CD 指数及其问题

1.1 CD 指数公式

CD 指数由 Funk & Owen-Smith (2017) 提出,用于衡量专利的创新突破程度:

1.2 三种引用情况的 CD 值

类型 条件 CD 值 含义
圆形 引用目标专利,不引用后向引用 +1 突破性
正方形 既引用目标专利又引用后向引用 -1 渐进性
三角形 只引用后向引用专利 0 中性

论文中的示意图如下:

1.3 原始 CD 指数的问题

  1. 从未被引用的专利 CD = 0:这与”渐进性专利”的 CD = -1 混淆
  2. 正负值相抵:多个专利的 CD 值正负相抵,汇总时相互抵消

二、改进的 ACD 和 mACD 指数

2.1 ACD 公式(论文原文)

2.2 mACD 公式(论文原文)

2.3 关键变量定义

变量 定义
fit 1 如果专利 i 引用了目标专利,否则 0
bit 1 如果专利 i 既引用了目标专利又引用了目标专利的后向引用专利,否则 0
nt 引用过目标专利的专利数(parta + partb)
mt 引用过目标专利或后向引用专利的专利总数(parta + partb + partc)
wit 权重(设为 1)

2.4 三种引用情况的分子值

类型 f b −2f⋅b+f 含义 计入 nt 求和
parta 1 1 -1 既引用目标专利又引用后向引用专利 ✓
partb 1 0 +1 只引用目标专利 ✓
partc 0 0 0 只引用后向引用专利 ✗

2.5 公式简化

2.6 ACD 值含义

此处内容需下载讲义材料查看~

2.7 mACD 的特殊意义

mACD = m_t × ACD,体现了专利在整体知识网络中的累积效应:

  • m_t 越大,说明目标专利及其后向引用专利被越多专利引用
  • mACD 可以大于 2,反映该专利在知识网络中的影响力越大

2.8 三个指数的关系

指数 分母 公式 特点
CD m_t (partb - parta) / m_t 原始值,可正可负
ACD n_t 2 × partb / n_t 只关注直接引用,范围 [0, 2]
mACD m_t ACD × m_t 考虑整体网络,可大于 2

三、使用 reticulate 创建与管理 Python 虚拟环境

在 R 中通过 reticulate 包来调用 Python,最好的实践是为项目创建一个专属的 Python 虚拟环境,将所需依赖隔离到独立空间,避免与系统 Python(如 Anaconda)发生版本冲突。

重要说明(避免”已初始化”报错):reticulate 在 R 会话中只能绑定一次 Python——一旦某个 {python} 代码块运行,Python 解释器就被锁定,之后再调用 use_virtualenv() 会报错:

ERROR: The requested version of Python cannot be used, as another version has already been initialized.

因此,虚拟环境的激活必须在所有 {python} 代码块之前完成。本文档的解决方案是在 setup chunk 中通过 Sys.setenv(RETICULATE_PYTHON = ...) 提前锁定 Python 路径,这是 reticulate 选取 Python 的最高优先级入口。

3.1 安装 reticulate(仅首次)

# 设置 CRAN 镜像(knit 时 R 处于非交互模式,不会自动选择镜像)
options(repos = c(CRAN = "https://mirrors.tuna.tsinghua.edu.cn/CRAN/"))

# 仅在尚未安装时才安装,避免每次 knit 都重装
if (!requireNamespace("reticulate", quietly = TRUE)) {
install.packages("reticulate")
message("reticulate 安装完成!")
} else {
message("reticulate 已安装,版本:", packageVersion("reticulate"))
}

3.2 虚拟环境初始化原理(已在 setup chunk 中完成)

本文档的 setup chunk(隐藏运行)包含如下逻辑:

library(reticulate)

.venv_name <- ".venv"
.venv_python <- virtualenv_python(.venv_name)

# 虚拟环境不存在时自动创建
if (!file.exists(.venv_python)) {
virtualenv_create(.venv_name)
.venv_python <- virtualenv_python(.venv_name)
}

# 通过环境变量抢先锁定 Python(优先级最高,早于任何 {python} chunk)
Sys.setenv(RETICULATE_PYTHON = .venv_python)
use_virtualenv(.venv_name, required = TRUE)

3.3 在虚拟环境中安装 Python 包(仅首次)

# 检查关键包是否已安装,缺失的才安装
py_pkgs <- c("numpy", "pandas")
installed <- py_list_packages(".venv")$package
need_install <- setdiff(py_pkgs, installed)

if (length(need_install) > 0) {
virtualenv_install(".venv", packages = need_install)
message("已安装缺失的包:", paste(need_install, collapse = ", "))
} else {
message("所有 Python 包已就绪,无需安装")
}

3.4 验证激活状态

# 验证当前绑定的 Python 路径(应指向 .venv 目录)
py_config()

3.5 虚拟环境管理常用命令

# 查看所有已创建的虚拟环境
virtualenv_list()

# 删除虚拟环境(当不再需要时)
# virtualenv_remove(".venv")

# 升级某个包
# virtualenv_install(".venv", packages = "pandas", ignore_installed = TRUE)

四、数据来源与处理过程

4.1 原始数据

本课程使用的数据来源于中国专利数据库,包含 1985-2024年 上市公司申请的专利及其引用关系数据。

4.2 数据文件说明

本 Python 版本使用从 R 版本 RDS 文件转换的 CSV 格式数据:

处理方法可以参考 R 语言版本的课程。另外这三个文件已经可以用来计算全部上市公司 ACD 和 mACD 指数了,无需额外再处理了。

文件名 行数 列名 说明
patent_citations2.csv 46,102,368 newipzlid, newipzlid2, date, date2 5年窗口筛选后的引用数据
nobackrefs.csv 8,277,416 newipzlid2, cd_index 没有后向引用的专利(cd_index = 1)
patents_with_citations.csv 23,330,294 newipzlid 所有具有引用与被引用信息的专利ID列表

变量说明:

  • newipzlid:引用专利的ID(申请日的专利)
  • newipzlid2:被引用专利的ID(被引用的专利)
  • date:引用专利的申请日期
  • date2:被引用专利的申请日期

4.3 数据读取

读取本课程提供的处理后数据:

import pandas as pd
import numpy as np
from pathlib import Path

# 设置数据路径
DATA_DIR = Path(".")

# 读取专利引用数据(5年窗口)
print("正在读取数据...")
patent_citations2 = pd.read_csv(DATA_DIR / "patent_citations2.csv")
print(f"专利引用数据行数: {len(patent_citations2):,}")

# 转换日期列
patent_citations2['date'] = pd.to_datetime(patent_citations2['date'])
patent_citations2['date2'] = pd.to_datetime(patent_citations2['date2'])

# 读取上市公司专利数据
sspat = pd.read_csv(DATA_DIR / "2010年上市公司与专利数据匹配结果.csv")
print(f"上市公司专利数: {len(sspat):,}")

# 读取无后向引用专利
nobackrefs = pd.read_csv(DATA_DIR / "nobackrefs.csv")
print(f"无后向引用专利数: {len(nobackrefs):,}")

print("\n专利引用数据预览:")
print(patent_citations2.head())

专利引用数据的变量说明:

  • newipzlid:引用专利的ID(引用方)
  • newipzlid2:被引用专利的ID(被引用方)
  • date:引用专利的申请日期
  • date2:被引用专利的申请日期

五、计算示例

5.1 单个专利的计算逻辑

以某个专利为例,详细展示 ACD 和 mACD 的计算过程:

# 以某个专利为例
patent_id = 20190809613

# 获取目标专利信息
target_patent = patent_citations2[patent_citations2['newipzlid2'] == patent_id]
print("目标专利信息:")
print(target_patent)

步骤 1:获取后向引用专利

此处代码需下载讲义材料查看~

步骤 4:计算三种情况的专利集合

  • parta:既引用目标专利又引用后向引用专利
  • partb:只引用目标专利但不引用后向引用专利
  • partc:只引用后向引用专利但不引用目标专利
# 三种情况的专利集合
part1_set = set(part1)
part2_set = set(part2)

parta = len(part1_set & part2_set) # 既引用目标又引用后向引用
partb = len(part1_set - part2_set) # 只引用目标专利
partc = len(part2_set - part1_set) # 只引用后向引用专利

print(f"parta (既引用目标又引用后向引用): {parta}")
print(f"partb (只引用目标专利): {partb}")
print(f"partc (只引用后向引用专利): {partc}")

步骤 5:计算 n_t 和 m_t

  • n_t = parta + partb(引用目标专利的专利数)
  • m_t = parta + partb + partc(引用目标专利或后向引用专利的专利总数)
n_t = parta + partb
m_t = parta + partb + partc

print(f"n_t = {n_t}")
print(f"m_t = {m_t}")

步骤 6:计算 ACD 和 mACD

此处代码需下载讲义材料查看~

5.2 封装计算函数

将上述逻辑封装成函数,方便批量计算:

# 此处代码需下载讲义材料查看~

# 测试函数
test_result = calc_patent_acd_macd(20190809613, patent_citations2)
print("测试结果:")
print(test_result)

六、批量计算与多线程优化

6.1 筛选需要计算的专利

并非所有专利都需要计算。如果某个专利没有后向引用,则所有引用它的专利都落在 partb,ACD = 2:

# 没有后向引用的专利
nobackrefs_ids = set(nobackrefs['newipzlid2'].tolist())
all_cited_patents = set(patent_citations2['newipzlid2'].unique())
patents_with_backrefs = all_cited_patents - nobackrefs_ids

print(f"无后向引用专利数: {len(nobackrefs_ids):,}")
print(f"有后向引用专利数: {len(patents_with_backrefs):,}")

6.2 批量计算

读取上市公司专利数据,只计算需要计算的专利:

# 筛选上市公司中有后向引用的专利
sspat_ids = set(sspat['newipzlid'].unique())
need_calc_ids = list(sspat_ids & patents_with_backrefs)

print(f"需要计算的有后向引用的上市公司专利数: {len(need_calc_ids):,}")

# 测试:计算前10个专利
test_ids = need_calc_ids[:10]

results = []
for pid in test_ids:
result = calc_patent_acd_macd(pid, patent_citations2)
results.append(result)

results_df = pd.DataFrame(results)
print("\n批量计算结果:")
print(results_df.to_string(index=False))

6.3 多线程计算

由于计算量较大,使用多线程加速(需在独立脚本中执行):

注意:在 Rmd 文档中,由于 reticulate 的限制,多线程代码需要在独立的 Python 脚本中执行。以下是多线程脚本的关键代码示例。

此处代码需下载讲义材料查看~

七、结果汇总

7.1 合并计算结果

将计算结果与上市公司专利数据合并:

# 合并到上市公司专利数据
patent_with_backrefs = sspat.merge(
results_df,
on='newipzlid',
how='inner'
)

print(f"有后向引用的上市公司专利数: {len(patent_with_backrefs):,}")

7.2 结果展示

print("计算结果预览:")
print(results_df.to_string(index=False))

7.3 统计汇总

print("\n=== ACD 统计 ===")
print(results_df['ACD'].describe())

print("\n=== mACD 统计 ===")
print(results_df['mACD'].describe())

7.4 公司层面汇总

将专利层面的 ACD/mACD 汇总到公司-年份层面:

# 添加公司信息
# 此处代码需下载讲义材料查看~

八、附件说明

本课程提供了以下附件:

8.1 数据文件

文件名 行数 说明
patent_citations2.csv 46,102,368 推荐使用:5年窗口筛选后的引用数据
nobackrefs.csv 8,277,416 无后向引用的专利(ACD=2)
patents_with_citations.csv 23,330,294 具有引用与被引用信息的专利ID列表

8.2 代码文件

文件名 说明
acd_macd.py 核心计算模块
计算ACD_mACD.py 单线程计算脚本
计算ACD_mACD_多线程.py 多线程计算脚本
合并汇总ACD_mACD.py 结果合并和汇总脚本

8.3 参考文件

文件名 说明
金融科技、数字化转型与企业突破性创新——基于全球专利引用复杂网络的分析_徐照宜.pdf 参考文献
pic1.png 论文中三种引用情况示意图

8.4 直接运行 Python 脚本

如果不想在 Rmd 中运行,也可以直接使用终端执行 Python 脚本:

# 进入项目目录
cd "使用 Python 语言测算专利的创新突破度:ACD指数和mACD指数"

# 激活虚拟环境(使用独立 venv)
python3 -m venv venv
source venv/bin/activate
pip install pandas numpy

# 运行计算脚本
python 计算ACD_mACD.py
python 计算ACD_mACD_多线程.py
python 合并汇总ACD_mACD.py

九、参考文献

  1. Funk, R. J., & Owen-Smith, J. (2017). A dynamic network measure of technological change. Management Science, 63(12), 4174-4187.
  2. 徐照宜, 巩冰, 陈彦名, 成程. 金融科技、数字化转型与企业突破性创新——基于全球专利引用复杂网络的分析[J]. 金融研究, 2023(10): 47-65.

点击这里跳转到 RStata 短书平台获取附件:名师讲堂|使用 Python 语言测算专利的创新突破度:ACD指数和mACD指数

评论