名师讲堂|使用 Python 测算上市公司技术专业化与技术多样化

今天给大家分享如何使用 Python 测算上市公司技术专业化与技术多样化程度。该方法参考自王慧扬等《知识搜索与科技领军企业关键核心技术突破》,通过变异系数和Shannon 熵指数来分别衡量企业的技术专业化与多样化水平。

附件中提供了该参考文献的 PDF 文件,感兴趣的小伙伴可以阅读原文。

指标来源与计算原理

技术专业化(TSP)

技术多样化(TDI)

计算步骤概述

整个计算过程分为以下几个步骤:

  1. 数据读取:读取 2020 年上市公司与专利数据匹配结果
  2. 专利去重:清洗公开公告号并按股票代码+年份+公告号/申请号去重
  3. IPC 分类提取:提取 IPC 大类(前 3 位)和小类(前 4 位),移除 IPC 为空的记录
  4. 计算 TSP:基于 IPC 小类专利占比的变异系数
  5. 计算 TDI:基于 IPC 大类专利占比的 Shannon 熵指数
  6. 结果保存:输出 CSV 文件

使用 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 的最高优先级入口。

安装 reticulate(仅首次)

# 设置 CRAN 镜像
options(repos = c(CRAN = "https://mirrors.tuna.tsinghua.edu.cn/CRAN/"))

if (!requireNamespace("reticulate", quietly = TRUE)) {
install.packages("reticulate")
message("reticulate 安装完成!")
} else {
message("reticulate 已安装,版本:", packageVersion("reticulate"))
}

在虚拟环境中安装 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 包已就绪,无需安装")
}

验证激活状态

py_config()

虚拟环境管理常用命令

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

详细计算代码

数据读取与预处理

import pandas as pd
import numpy as np

# 读取 2020 年上市公司与专利数据匹配结果
# dtype=str:全部读为字符串,与 R 的 read_csv(cols(.default = "c")) 一致
df_2020 = pd.read_csv("2020年上市公司与专利数据匹配结果.csv", dtype=str)
print(f"原始数据: {len(df_2020)} 行")

专利去重

按照以下步骤进行专利去重(与 R 代码逻辑完全一致):

  1. 清洗公开公告号(去掉末尾字母)
  2. 按股票代码 + 年份 + 公开公告号去重
  3. 按股票代码 + 年份 + 申请号去重
# Step 1: 清洗公开公告号(去掉末尾字母)
# R: str_remove(公开公告号, "[A-Z]$")
df_2020["公开公告号_clean"] = df_2020["公开公告号"].str.replace(r"[A-Z]$", "", regex=True)

# Step 2: 按 股票代码 + 年份 + 公开公告号_clean 去重
# R: distinct(股票代码, 年份, 公开公告号_clean, .keep_all = TRUE)
df_2020 = df_2020.drop_duplicates(subset=["股票代码", "年份", "公开公告号_clean"], keep="first")
print(f"按公告号去重后: {len(df_2020)} 行")

# Step 3: 按 股票代码 + 年份 + 申请号 去重
# R: distinct(股票代码, 年份, 申请号, .keep_all = TRUE)
df_2020 = df_2020.drop_duplicates(subset=["股票代码", "年份", "申请号"], keep="first")
print(f"按申请号去重后: {len(df_2020)} 行")

关键函数对照:

R 函数 Python 函数 说明
str_remove(x, "[A-Z]$") str.replace(r"[A-Z]$", "", regex=True) 正则替换
distinct(..., .keep_all=TRUE) drop_duplicates(subset=..., keep="first") 去重保留首行

提取 IPC 分类信息

# IPC分类层级说明:
# 部(Section): A-H(8个大部)
# 大类(Class): 如 G06, H04(前3位,含部字母+2位数字)
# 小类(Subclass): 如 G06F, H04L(前4位,含部字母+3位数字+可能字母)
#
# 文献定义:
# 技术专业化 → 使用IPC小类(前4位,如G06F)
# 技术多样化 → 使用IPC大类(前3位,如G06)

# 检查 IPC主分类 的空值
na_count = df_2020["IPC主分类"].isna().sum()
print(f"IPC主分类的NA值数量: {na_count} (占比{na_count/len(df_2020)*100:.2f}%)")

# 提取 IPC 分类层级
# R: str_sub(IPC主分类, 1, 3) / str_sub(IPC主分类, 1, 4)
df_2020["IPC大类"] = df_2020["IPC主分类"].str[:3]
df_2020["IPC小类"] = df_2020["IPC主分类"].str[:4]

# 移除 IPC 分类为 NA 的观测值
# R: filter(!is.na(IPC大类) & !is.na(IPC小类))
df_2020 = df_2020.dropna(subset=["IPC大类", "IPC小类"])
print(f"移除IPC为NA后的数据维度: {len(df_2020)} 行")

计算技术专业化(TSP)

# 技术专业化:变异系数(Coefficient of Variation)
# TSP = 标准差(各技术小类专利占比) / 均值(各技术小类专利占比)
# TSP越大 → 技术越集中于少数小类 → 专业化程度越高

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

print(f"技术专业化(TSP)计算完成,共 {len(TSP)} 家企业")
print(f"TSP描述性统计:")
print(TSP["TSP"].describe())

关键函数对照:

R 函数 Python 函数 说明
group_by() %>% summarise(n()) groupby().agg(size) 分组计数
sd(x) x.std() 样本标准差(ddof=1)
case_when() np.where() 条件分支

计算技术多样化(TDI)

# 技术多样化:Shannon熵指数
# TDI = -Σ p_it_a * ln(p_it_a)
# TDI越大 → 技术分布越分散 → 多样化程度越高

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

print(f"技术多样化(TDI)计算完成,共 {len(TDI)} 家企业")
print(f"TDI描述性统计:")
print(TDI["TDI"].describe())

合并结果并保存

# 合并 TSP 和 TDI 结果
# R: inner_join(...) %>% arrange(股票代码)
result = (
TSP[["股票代码", "技术小类数量", "TSP"]]
.merge(TDI[["股票代码", "技术大类数量", "TDI"]], on="股票代码", how="inner")
.merge(firm_total_patents, on="股票代码", how="inner")
.sort_values("股票代码")
.reset_index(drop=True)
)

print(f"最终结果: {len(result)} 行, {len(result.columns)} 列")

# 描述性统计
print("\n--- 描述性统计 ---")
print(result[["TSP", "TDI", "总专利数"]].describe())

# 保存结果
result.to_csv("技术专业化与多样化_2020年_python.csv", index=False)
print("\n结果已保存到: 技术专业化与多样化_2020年_python.csv")

结果概览

print("=== 结果预览(前20行) ===")
print(result.head(20).to_string(index=False))
print("=== 详细描述性统计 ===")
for col in ["TSP", "TDI", "总专利数"]:
s = result[col]
print(f"\n{col}:")
print(f" 最小值: {s.min():.4f}")
print(f" 25%: {s.quantile(0.25):.4f}")
print(f" 中位数: {s.median():.4f}")
print(f" 均值: {s.mean():.4f}")
print(f" 75%: {s.quantile(0.75):.4f}")
print(f" 最大值: {s.max():.4f}")
#>
#> TSP:
#> 最小值: 0.0000
#> 25%: 0.3764
#> 中位数: 0.7071
#> 均值: 0.8015
#> 75%: 1.1326
#> 最大值: 5.6952
#>
#> TDI:
#> 最小值: -0.0000
#> 25%: 0.6365
#> 中位数: 1.1339
#> 均值: 1.1227
#> 75%: 1.6094
#> 最大值: 3.3103
#>
#> 总专利数:
#> 最小值: 1.0000
#> 25%: 6.0000
#> 中位数: 13.0000
#> 均值: 47.0864
#> 75%: 31.0000
#> 最大值: 11741.0000

点击这里跳转到 RStata 短书平台获取附件:名师讲堂|使用 Python 测算上市公司技术专业化与技术多样化

评论