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

前言

受读者建议,再次详细论述我们写的第一篇推文,讲讲管理层讨论信息含量这个指标如何构建。本文的主要内容分为管理层讨论信息含量的定义、计算原理、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 M D &A的文本内容生成词条库

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

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

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

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

市场标准化向量:将公司i i i 所在行业之外其他行业所有公司的标准化向量进行算术平均,得到市场标准化向量N o r m M , t Norm_{M,t}N o r m M ,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}N o r m i ,t ​=α0 ​+α1 ​N o r m I ,t ​+α2 ​N o r m M ,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

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

&#x7B2C;&#x4E8C;&#x6B65;:数据清洗


data = data.rename(columns={"shortname":"证券简称", "industrycode":"行业代码"})

data = data[["股票代码","会计年度","经营讨论与分析内容","行业代码"]]

data = data[~data["行业代码"].str.contains("J")]

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

&#x7B2C;&#x4E09;&#x6B65;:分词处理

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)

&#x7B2C;&#x56DB;&#x6B65;:生成bow矩阵

from sklearn.feature_extraction.text import CountVectorizer
countvec = CountVectorizer(min_df = 50, max_df = 1000)

res = countvec.fit_transform(data.strword)

&#x7B2C;&#x4E94;&#x6B65;:词频向量标准化


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))

&#x7B2C;&#x516D;&#x6B65;:行业标准化向量和市场标准化向量

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

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

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

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

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

; 计量拓展

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

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

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

Original: https://blog.csdn.net/weixin_47172744/article/details/116245076
Author: AccountingCoder
Title: 文本分析 | 管理层讨论信息含量原理与代码实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/548562/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球