名师讲堂|如何处理上市公司 ESG 评级数据:使用 Mata 加快处理速度

最近有个小伙伴遇到了这样一个问题,他想把从 Wind 上下载的上市公司历年 ESG 评级数据处理成面板数据。原始数据是这样的:

import excel using "Sample_20240406.xlsx", clear first

首先对数据进行一些简单的清理:

*- 去除数据中的换行符和制表符
gen v1 = ustrregexs(0) if ustrregexm(历史评级, "\n")
replace 历史评级 = subinstr(历史评级, v1, ";", .)

gen v2 = ustrregexs(0) if ustrregexm(历史评级, "\r")
replace 历史评级 = subinstr(历史评级, v2, ";", .)

replace 历史评级 = subinstr(历史评级, ";;", ";", .)
replace 历史评级 = subinstr(历史评级, " ", "", .)
drop v1 v2
tostring 评级日期, format(%tdCY-N-D) replace force
replace 历史评级 = 历史评级 + 评级日期 + "," + 评级
drop 评级日期 评级
drop if mi(序号)
drop in 1
save data2, replace

然后下面我们需要做的就是把这里的评级拆分开。

传统的方法是使用 split + gather:

use data2, clear
split 历史评级, parse(;)
drop 历史评级
gather 历史评级*
drop if mi(value)
drop var
split value, parse(,)
drop value
ren value1 评级日期
ren value2 评级

不过如果观测值数据很多、历史评级很多,这个方法会非常低效,甚至无法实现。

今天我们再介绍一种使用 Mata 的方法。

附件中的 _gipc_count.ado 是我编写的一个 egen 函数,可以用来统计字符串使用特定字符拆分后得到的子字符串数量:

use data2, clear
*- 统计可拆分的数量:
egen n = ipc_count(历史评级), parse(;)

然后我们把历史评级数据读取到 Mata 里面处理:

mata:
mata clear
// 把历史评级数据读入 mata
v1 = st_sdata(., "历史评级")
v1

// 转换成行向量
v2 = rowshape(v1, cols(v1))
v2

// 连接起来
v3 = invtokens(v2, ";")
v3

// 使用分号拆分
v4 = ustrsplit(v3, ";")
v4

// 转换成列向量
v5 = colshape(v4, rows(v4))
v5
end

v5 就是拆分的结果,为了让 v5 能够直接插入 Stata 的变量中,我们可以用 expand 命令把观测值扩充下:

*- 拓展数据
expand n
gsort 序号

然后就可以直接把 v5 创建成变量了:

*- 从 mata 的 v5 再创建变量
mata:
stata("cap drop newvar")
st_addvar("strL", "newvar")
st_sstore(., "newvar", v5)
end

然后再简单处理下:

drop 历史评级
drop if mi(newvar)
split newvar, parse(,)
drop newvar
gen date = date(newvar1, "YMD")
format date %tdCY-N-D
drop newvar1 n
ren date 评级日期
ren newvar2 评级
append using data
drop 历史评级
destring 序号, replace
gsort 万得代码 评级日期

list in 1/10

*> +-------------------------------------------------+
*> | 序号 万得代码 公司名称 评级 评级日期 |
*> |-------------------------------------------------|
*> 1. | 2 000002.SZ 万科 AA 2021-04-07 |
*> 2. | 2 000002.SZ 万科 AA 2020-03-25 |
*> 3. | 2 000002.SZ 万科 AA 2019-04-02 |
*> 4. | 2 000002.SZ 万科 A 2018-04-03 |
*> 5. | 2 000002.SZ 万科 A 2022-04-07 |
*> |-------------------------------------------------|
*> 6. | 3 000004.SZ 国华网安 BBB 2021-08-01 |
*> 7. | 3 000004.SZ 国华网安 BBB 2020-08-01 |
*> 8. | 3 000004.SZ 国华网安 BBB 2020-04-29 |
*> 9. | 3 000004.SZ 国华网安 B 2019-08-01 |
*> 10. | 3 000004.SZ 国华网安 B 2018-08-01 |
*> +-------------------------------------------------+

为了方便后续的处理,我们再把评级因子化:

* 查看所有可能的评级
drop if mi(序号)
codebook 评级

gen 评级num = 1 if 评级 == "CCC"
replace 评级num = 2 if 评级 == "B"
replace 评级num = 3 if 评级 == "BB"
replace 评级num = 4 if 评级 == "BBB"
replace 评级num = 5 if 评级 == "A"
replace 评级num = 6 if 评级 == "AA"
replace 评级num = 7 if 评级 == "AAA"

label define pj 1 "CCC" 2 "B" 3 "BB" 4 "BBB" 5 "A" 6 "AA" 7 "AAA", replace
label val 评级num pj
save data3, replace

不过有些公司在某一年中可能有多个评级结果,因此我们可以使用两种方法把这个数据汇总成面板数据:

  1. 分公司保存每年的最后一次评级:
use data3, clear
gen 评级年份 = yofd(评级日期)
bysort 万得代码 评级年份 (评级日期): keep if _n == _N
drop 评级日期
  1. 分公司保留每年最高的评级结果:
use data3, clear
gen 评级年份 = yofd(评级日期)
bysort 万得代码 评级年份 (评级num): keep if _n == _N
drop 评级日期

点击这里跳转到 RStata 短书平台获取附件:名师讲堂|如何处理上市公司 ESG 评级数据:使用 Mata 加快处理速度

评论