文本分析 | 管理层讨论信息含量原理与代码实现

前言

受读者建议,再次详细论述我们写的第一篇推文,讲讲管理层讨论信息含量这个指标如何构建。本文的主要内容分为管理层讨论信息含量的定义、计算原理、python和stata实现以及计量拓展

定义

参考孟庆斌等(中国工业经济,2017)的定义

一方面,所有上市公司都处于相同的宏观经济环境、风险因素和政治、政策背景之下;另一方面,同一行业中的各上市公司又面临着相似的产业政策、竞争环境和市场特征。由此可见,每个上市公司MD&A 信息不可避免地在某种程度上与同行业其他上市公司以及市场其他行业上市公司存在一定的相似性, 甚至某些公司可能直接参考其他公司MD&A 的表述。本文将这些与同行业其他公司或其他行业的公司重复或相似的信息定义为不具有信息含量的内容,同时将不同的信息定义为真正具有信息含量的内容,简称为信息含量

计算原理

词袋模型:举例来说,我们现在有10个文本,分别对这10个文本进行分词处理,然后将分词后所有词条(去重)进行编号,最后汇总词条得到一个基于这10个文本内容的词条库。

词频向量:接下来,我们需要统计每个文本分词后的词条数量,根据词条编号生成每份文本的词频向量,格式类似于[0, 1, 4, 0, 3,…0, 5],表示对于文本 i i i中,编号为1的词出现0次,编号为2的词出现1次,编号为3的词条出现4次,以此类推

孟庆斌等(中国工业经济,2017)的做法

第一步:基于所有上市公司的 M D & A MD\&A MD&A的文本内容生成词条库

第二步:基于词条库,生成每个公司的 M D & A MD\&A MD&A的文本内容的词频向量

第三步:对词频向量进行标准化处理得到个股标准化向量 N o r m i , t Norm_{i,t} Normit,即对词频向量除以该公司 M D & A MD\&A MD&A的总词数

第四步:基于公司 i i i的标准化词频向量计算行业标准化向量和市场标准化向量

行业标准化向量:将公司 i i i 所在行业除该公司之外其他所有公司的标准化向量的算术平均定义为行业标准化向量 N o r m I , t Norm_{I,t} NormIt

市场标准化向量:将公司 i i i 所在行业之外其他行业所有公司的标准化向量进行算术平均,得到市场标准化向量 N o r m M , t Norm_{M,t} NormM,t

第五步:利用行业标准化向量和市场标准化向量对个股标准化向量进行分离
N o r m i , t = α 0 + α 1 N o r m I , t + α 2 N o r m M , t + μ i , t Norm_{i,t}=\alpha_0+\alpha_1Norm_{I,t}+\alpha_2Norm_{M,t}+\mu_{i,t} Normit=α0+α1NormIt+α2NormM,t+μi,t
其中, α 1 \alpha_1 α1代表公司i 的MD&A 信息中能够被同行业其他公司所解释的部分, α 2 \alpha_2 α2代表该公司能够被市场其他行业公司所解释的部分,残差 μ i , t \mu_{i,t} μi,t为行业和市场信息所不能解释的部分。将残差向量各维度绝对值之和定义为信息含量

代码实现

Python代码实现

第一步:导入数据

import pandas as pd
# 2007-2019
mda = pd.read_excel('管理层讨论与分析.xls', sheet_name = 0)
# 读取行业数据
industry = pd.read_excel('证监会2012年版行业分类.xlsx',sheet_name = 0)
# 与行业数据进行合并
data = pd.merge(mda, industry,on=['股票代码','会计年度'], how = 'inner')

得到以下数据

data.info()

<class 'pandas.core.frame.DataFrame'>
Int64Index: 33269 entries, 0 to 33268
Data columns (total 6 columns):
股票代码            33269 non-null int64
会计年度            33269 non-null int64
经营分析时间          33269 non-null object
经营讨论与分析内容       33269 non-null object
shortname       33269 non-null object
industrycode    33269 non-null object
dtypes: int64(2), object(4)
memory usage: 1.8+ MB

第二步:数据清洗

# 重命名字段
data = data.rename(columns={"shortname":"证券简称", "industrycode":"行业代码"})
# 选择需要分析的字段
data = data[["股票代码","会计年度","经营讨论与分析内容","行业代码"]]
# 剔除金融行业
data = data[~data["行业代码"].str.contains("J")]
# 仅处理2019年的文本
data = data[data["会计年度"] == 2019]
# 重置索引
data = data.reset_index(drop=True)

得到以下数据

data.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3568 entries, 0 to 3567
Data columns (total 4 columns):
股票代码         3568 non-null int64
会计年度         3568 non-null int64
经营讨论与分析内容    3568 non-null object
行业代码         3568 non-null object
dtypes: int64(2), object(2)
memory usage: 111.6+ KB

第三步:分词处理

import jieba
import re
def get_cut_words(content):
    # 读入停用词表
    stop_words = []
    with open('停用词.txt', encoding = 'utf-8') as f:
        lines = f.readlines()
        for line in lines:
            stop_words.append(line.strip())
    # 分词
    cutword = [w for w in jieba.cut(content) if w not in stop_words and len(w) > 1 and not re.match('^[a-z|A-Z|0-9|.]*$',w)]
    strword = " ".join(cutword)
    return strword

data['strword'] = data['经营讨论与分析内容'].apply(get_cut_words)

第四步:生成bow矩阵

from sklearn.feature_extraction.text import CountVectorizer
countvec = CountVectorizer(min_df = 50, max_df = 1000) # 在5个以上年度报告出现的词才保留,在1000个以上年报出现的词剔除

res = countvec.fit_transform(data.strword) # 稀疏bow矩阵

第五步:词频向量标准化

# 利用公司总词数进行标准化
import numpy as np
def normalizer(vec):
    denom = np.sum(vec)
    return [(el / denom) for el in vec]
doc_term_matrix_normalizer = []
for vec in doc_term_matrix:
    doc_term_matrix_normalizer.append(normalizer(vec))
print(np.matrix(doc_term_matrix_normalizer))

第六步:行业标准化向量和市场标准化向量

for i in range(0,3568):
    df_firm = data1.iloc[i:i+1]
    df_firm = df_firm.melt(id_vars=['code','year','ind'],    # 要保留的字段
                            var_name="wordid",   # 拉长的分类变量
                            value_name="freq")   # 拉长的度量值名称 
    
    ind_matrix = data1[(data1["ind"] == data1.iloc[i]["ind"]) & (data1["code"] != data1.iloc[i]["code"])]
    ind_matrix = ind_matrix.drop(["code","year","ind"],axis=1)
    normind = ind_matrix.mean(axis = 0)
    
    market_matrix = data1[data1["ind"] != data1.iloc[i]["ind"]]
    market_matrix = market_matrix.drop(["code","year","ind"],axis=1)
    normmarket = market_matrix.mean(axis = 0)

    df_firm["freq_ind"] = normind.tolist()
    df_firm["freq_market_ind"] = normmarket.tolist()
    
    df_firm.to_excel("{}词条.xls".format(data1.iloc[i]["code"]),index=None)

Stata代码实现

在python代码实现过程中,我们得到了每家公司的词条信息,包括其对应的标准化词频向量、行业标准化向量、市场标准化向量,如下图所示

文本分析 | 管理层讨论信息含量原理与代码实现_第1张图片

我们只需要导入每家公司的词条信息到stata进行回归计算,即可得到其各维度的残差绝对值之和,在此基础上汇总所有公司的回归结果即可,最终回归结果的描述性统计结果如下

文本分析 | 管理层讨论信息含量原理与代码实现_第2张图片

从描述性统计结果可以明显看出,行业标准化向量和市场标准化向量都得到了1%统计显著的正回归系数,且对应的T值分别高达18.260和3.971,说明上市公司的 M D & A MD\&A MD&A的文本信息大多数与市场的,特别是行业的文本信息高度重合。我们计算的信息含量和标准信息与孟庆斌等(中国工业经济,2017)结果基本相近,可能在样本期间和数据细节处理上略有差异

计量拓展

本文讨论的管理层信息含量度量存在的不足以及改进建议

  1. 显然,仅以行业和市场两个维度对个股标准化向量进行分离是不全面的。上市公司的 M D & A MD\&A MD&A文本内容往往与自身前几年年报的 M D & A MD\&A MD&A的文本内容也是有高度重合的,投资者也不会从这部分高度重合的文本内容中得到增量信息。因此,我们建议增加公司过去三年的 M D & A MD\&A MD&A标准化向量作为解释变量,更为准确地分离出投资者没有预期到的文本信息
  2. 在大规模的文本中,我们构建的BOW矩阵将会存在大量的0值,带来的所谓的“维度灾难”,对估计效果产生不利的影响。因此,我们建议在计算过程中使用降维技术,如word2vec神经网络模型,对BOW矩阵进行降维,然后利用降维后的向量进行响应的回归

需要指出的是,由于我们的认知和能力有限,上述的操作方法和相关看法可能存在问题,希望读者指正~

你可能感兴趣的:(python,爬虫,自然语言处理)