技术路线

[工作量]
  1. 代码130+行
  2. 调试时间 3 小时, 运行时间 20 小时
  
  
[内容]
  1. 设计正则表达式, 识别企业融资约束
  2. 构建企业管理层讨论与分析文本向量(标准化) Vec_it
  3. 构建板块(沪、深)文本向量(标准化)BoardVec_bt
  4. 构建行业文本向量(标准化) IndustryVec_it
  5. 构建融资约束样本集的文本均值向量(标准化) ConstrainedVec_it
  6. 基于前面几个变量,计算得到
     - BoardScore_bt 、 InstryScore_it
     - 得到5w多个csv文件(中间运算结果), 存储在 fin_constrain_output/{year}/{code}.csv
     
  7. [融资约束FC指标计量建模]
    - ConstrainedScore_it =β0 + β1 * BoardScore_bt + β2 * IndustryScore_it + E_it
    - BoardScore_bt  交易所引发的融资约束相似度
    - IndustryScore_it  行业特征引发的融资约束相似度
    - E_it  残差就是本文要计算的[融资约束指标FC]



一、识别融资约束样本

在获取 MD&A 的基础上,采用正则表达式(Regular Expression) 检索出隐含融资约束信息的文本,并把相应的 MD&A 进行标记,纳入对应年度的融资约束文本集中。 其中,在检索并标记融资约束文本的过程中,本文参考 Hoberg 和 Maksimovic (2015)、Buehlmaier 和 Whited(2016)的研究方法。

Hoberg 和 Maksimovic(2015)认为,融资约束体现为投资计划、项目的推迟、搁置乃至放弃,因此,他们构造了两组“推迟投资”词语列表,一组是有推迟、延期、搁置含义的动词词表; 另一组是与投资、 项目、计划等意思相近的名词词表。 若在待识别文本中,动词词表和名词词表中的词语、词组同时出现,且相隔不超过 12 词,则将其判定为有推迟投资含义的融资约束文本。

Buehlmaier 和 Whited(2016) 在构建股权融资约束文本集的过程中,直接引用了前者的“推迟投资”词表,同时,为了确定投资的推迟确实是由股权融资方面的问题引起的,还计算了距“推迟投资”语句 12 词以内股权融资相关词语出现的频率,最终只把频率排行前 250 的观测加入股权融资约束文本集。


1.1 前人不足

需要说明的是,尽管本文采用的方法借鉴了 Hoberg 和 Maksimovic(2015)和 Buehlmaier 和 Whited (2016)的做法,但与其存在着两个方面的差异。

  • 第一,本文没有通过“推迟投资”界定融资约束,而是通过公司对资金状况的描述去识别,相较而言这一做法更为直接。 例如,若公司明确表明融资能力有限,资金紧张,则被视为融资约束样本。
  • 第二,我们认为,即便“推迟投资”词表中的动词和名词在相隔 12 词以内出现,两个词之间也未必有关联,12词的窗口长度容易引起大量误判。 尤其考虑到汉语使用较为灵活,不同公司在表述上也存在着较大的差异,因此,本文使用了可覆盖更多表述形式、更加灵活的正则表达式进行检索,并根据数次检索结果排除了很多容易导致误判的情形,查准率较高。

1.2 本文完善

具体地,为了在 MD&A 文本集中检索出融资约束文本,我们在设计正则表达式时将能显示公司有融资约束的各种文字表达,以词语组合的形式进行提炼。

regex1 = "[^。]*?(融资|资金|筹资)[^。]{0, 6}?(难以|不能|无法|不足以)[^。]*"
#能在 MD&A 文本中匹配出以下形式的句子:(除句 号以外的任意长度字符串)+融资/资金/筹资+(六个 字符长度以内的任意字符串)+难以/不能/无法满足/不足以+(除句号以外的任意长度字符串);

regex2 = "[^。]*?(融资|资金|筹资)[^。]{0, 6}?(成本|压力|难度)[^。]{0, 4}?(升|增|高|大)[^。]*"
#可在句号以外的任意长度字符串)+融资/资金/筹资+(六 个字符长度以内的任意字符串)+成本/压力/难度+ (4 个字符长度以内的任意字符串)+升/高/增/大+ (除句号以外的任意长度字符串)。

仅仅考虑融资约束文本的各种可能表述是不够的,会出现大量误判,例如,机械地将“资金”之后 4 个字符以内出现“不足”的语句识别为融资约束语句,非常容易 造成误判,因为部分 MD&A 提及公司“资金管理水平不足”,而资金管理水平反映的是公司运营能力, 和融资约束无直接关系。 诸如此类的匹配应视作误判而排除,因此我们利用正则表达式灵活的语法规则,同时构造了排除性条件。 在此基础上,将这些对应着不同判断逻辑的“规则字符串”合并至同一个正则表达式中。 如果难以合并,则利用程序语言的条件判断逻辑,对正则表达式组进行组合使用。 在具体操作中,本文就使用了正则表达式组。



二、 构建中文融资约束样本识别代码

前面的样本识别都是论文原文,接下来是大邓对该论文的融资约束样本识别算法的复现

2.1 融资约束文本的场景

这是一个相对复杂的需求,需要综合考虑多种情况, 对于每种情况,都构建一个单独的正则表达式,用于匹配对应的文本。可以使用“或”运算符, 合并为一个更大的正则表达式。

import re


#融资不足情况
regex1 = r"(?:融资|资金|筹资)[^。]{0,6}?(?:难以|不能|无法|不足以)[^。]*"
#融资成本或压力过大情况
regex2 = r"(?:融资|资金|筹资)[^。]{0,6}?(?:成本|压力|难度)[^。]{0,4}?(?:升|增|高|大)[^。]*"

#可以使用“或”运算符, 合并为一个更大的正则表达式
pattern = r"(" + regex1 + r")|(" + regex2 + r")"


#实验数据
text1 = "公司在过去几年中进行了大量的投资,导致资金短缺,难以支持公司未来的发展计划。"
text2 = "公司在过去几年中进行了大量的投资计划,资金状况良好,没有融资压力。"

#实验结果
matches1 = re.findall(pattern, text1)
print(matches1)
matches2 = re.findall(pattern, text2)
print(matches2)

Run

    [('资金短缺,难以支持公司未来的发展计划', '')]
    []

在上面的例子中,pattern能识别出文本是否含有融资约束。

  • text1有融资约束,所以返回带 有内容matches1
  • text2没有融资约束,所以返回 没有内容matches2

2.2 识别中文融资约束样本的最终代码

前面的内容都是算法逐步实现的过程,现在咱们合并为一个函数代码

import re

def is_financial_constraint(text):
    #正则表达式组
    regex1 = r"(?:融资|资金|筹资)[^。]{0,6}?(?:难以|不能|无法|不足以)[^。]*"
    regex2 = r"(?:融资|资金|筹资)[^。]{0,6}?(?:成本|压力|难度)[^。]{0,4}?(?:升|增|高|大)[^。]*"
    pattern = r"(" + regex1 + r")|(" + regex2 + r")"
    
    #带内容的结果为融资约束,为True;反之,为False
    if len(re.findall(pattern, text))>=1:
        return True
    else:
        return False
    
    
#实验数据
text1 = "公司在过去几年中进行了大量的投资,导致资金短缺,难以支持公司未来的发展计划。"
text2 = "公司在过去几年中进行了大量的投资计划,资金状况良好,没有融资压力。"

#实验结果
print('text1文本是否为融资约束: ', is_financial_constraint(text1))
print('text2文本是否为融资约束: ', is_financial_constraint(text2))

Run

    text1文本是否为融资约束:  True
    text2文本是否为融资约束:  False



三、批量识别融资约束样本

接下来对对 data/mda01-23.csv.gz 数据集所有md&a进行识别。

import pandas as pd

#读取md&a
df = pd.read_csv('data/mda01-23.csv.gz', compression='gzip')
df.columns = ['会计年度', '股票代码', '经营讨论与分析内容']
df['会计年度'] = df['会计年度'].astype(str)


#上市公司行业信息
ind_info_df = pd.read_excel('data/上市公司基本信息2000-2023.xlsx', usecols=['Symbol', 'EndDate', 'IndustryCodeC'])
ind_info_df = ind_info_df[ind_info_df.Symbol!='股票代码']
ind_info_df['会计年度'] = ind_info_df.EndDate.fillna('').apply(lambda date: date[:4])
ind_info_df.rename(columns={'Symbol': '股票代码', 'IndustryCodeC':'行业代码'}, inplace=True)
ind_info_df = ind_info_df[['股票代码', '会计年度', '行业代码']]

#合并数据
df = pd.merge(df, ind_info_df, on=['股票代码', '会计年度'], how='inner')
df.head()

Run

57545


新建板块字段, 上海证券交易所股票大多以 6、9开头, 而深圳证券交易所以0、3开头

def plate(code):
    if (code[:2]=='A6') or (code[:2]=='A9'):
        return '上海'
    elif (code[:2]=='A0') or (code[:2]=='A3'):
        return '深圳'
    else:
        return '其他'
    
df['板块'] = df['股票代码'].apply(plate)

df.head()  


df['融资约束'] = df['经营讨论与分析内容'].apply(is_financial_constraint)
df.head()


#融资约束样本占比
df['融资约束'].sum()/len(df)
0.10631679555130767

注意

设计的 函数is_financial_constraint 应该要检查, 检查的目的是改良正则表达式组, 这里假装我们检查完了,没什么问题。



四、构建融资约束指标

前面的融资约束样本识别,只是识别出融资约束是否存在,信息的颗粒度比较粗糙。这篇论文使用文本相似度算法,构建了每家企业的融资约束指标

本文同样参照 Hoberg 和 Maksimovic(2015)的研究方法,我们认为,融资约束程度相近的公司,其在“管理层讨论与分析”中的用词和表述也会趋于一致。 因此,通过采用余弦相似度的方法,能够在识别出全体样本的融资约束程度,并以连续变量的形式进行呈现。

具体实现算法步骤

  1. 给每个 md&a 文本转化为向量 Vec_it

  2. 当年所有属于融资约束样本的 Vec_it , 求均值得到 ConstrainedVec_t

  3. 每家企业当年融资约束水平(程度) 由 Vec_itConstrainedVec_t 之积 , 即 ConstrainedScore_it 所体现。

  4. 考虑到市场板块、行业性因素对融资约束的影响,不能直接使用 ConstrainedScore_it

    • 对历年隶属于各个板块的公司 MD&A,求标准化词频向量的均值并做标准化处理,记为 BoardVectb_bt ,该向量反映了上市板 b 在 t 年的共同性信息披露内容。
    • Vec_it 与对应板块 BoardVec_bt 之积,即为因 MD&A 共性内容导致的相似度, 记作 BoilerplateScore_i
    • 利用相同方法,计算出因行业特征引发的相似度,记作 IndustryScore_it
  5. ConstrainedScore_it = β0 + β1 * BoardScore_bt + β2 * IndustryScore_it + E_it

    • BoardScore_bt 交易所引发的融资约束相似度
    • IndustryScore_it 行业特征引发的融资约束相似度
    • E_it 残差就是本文要计算的[融资约束指标FC]



4.1 计算2023年的Vec_it

计算量太大,先以2023为例写代码。

df_per_year = df[df['会计年度']=='2023']
df_per_year.reset_index(inplace=True)
df_per_year.head()


处理2023年的 「经营讨论与分析内容」字段内容,使其:

  1. 只保留中文内容
  2. 剔除停用词
  3. 整理为用空格间隔的字符串(类西方语言文本格式)
  4. 将本文转为向量后,标准化。
  5. 合并一些需要的字段,如 ['股票代码', '会计年度', '板块', '行业代码', '融资约束']
%%time
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
import cntext as ct
import jieba
import re


#cntext1.x
#stopwords = ct.load_pkl_dict('STOPWORDS.pkl')['STOPWORDS']['chinese']

#cntext2.x
stopwords= ct.read_yaml_dict('enzh_common_StopWords.yaml')['Dictionary']['chinese']


def transform(text):
    #只保留md&a中的中文内容
    text = ''.join(re.findall('[\u4e00-\u9fa5]+', text))
    #剔除停用词
    words = [w for w in jieba.cut(text) if w not in stopwords]
    #整理为用空格间隔的字符串(类西方语言文本格式)
    return ' '.join(words)


df_per_year['clean_text'] = df_per_year['经营讨论与分析内容'].apply(transform)
cv = CountVectorizer(min_df=0.05, max_df=0.5) 
# 生成稀疏bow矩阵
#dtm 文档-词频-矩阵
dtm_per_year = cv.fit_transform(df_per_year['clean_text']) 
dtm_per_year = pd.DataFrame(dtm_per_year.toarray(), index=dtm_per_year.index)

#向量标准化normalize
dtm_per_year = dtm_per_year.apply(lambda row: row/np.sum(row), axis=1)

#合并多个字段为新的df
dtm_per_year = pd.concat([df_per_year[['股票代码', '会计年度', '板块', '行业代码', '融资约束']], dtm_per_year], axis=1)
dtm_per_year.head()

Run

    CPU times: user 5.88 s, sys: 901 ms, total: 6.78 s
    Wall time: 49.7 s


4.2 2023年的板块评分、行业评分

计算2023年所有公司的 板块评分BoardScore行业评分IndustrySocre。该部分代码运行较慢,运行下来大约2小时。

%%time

import os
import pandas as pd


year = 2023

if not os.path.exists('fin_constrain_output'):
    os.mkdir('fin_constrain_output')
        

for idx in range(len(dtm_per_year)):
    code = dtm_per_year.loc[idx, '股票代码']
    ind = dtm_per_year.loc[idx, '行业代码']
    year = dtm_per_year.loc[idx, '会计年度']
    board = dtm_per_year.loc[idx, '板块']
    
    
    Vec = dtm_per_year.iloc[idx, 5:]
    Ind_Vec = dtm_per_year[dtm_per_year['行业代码']==ind][dtm_per_year['股票代码']!=code].iloc[:, 5:].mean(axis=0)
    Ind_Score = Vec * (Ind_Vec/np.sum(Ind_Vec))
    FinConstrain_Vec = dtm_per_year[dtm_per_year['融资约束']==True].iloc[:, 5:].mean(axis=0)
    FinConstrain_Score = Vec * (FinConstrain_Vec/np.sum(FinConstrain_Vec))
    Board_Vec = dtm_per_year[dtm_per_year['板块']==board][dtm_per_year['股票代码']!=code].iloc[:, 5:].mean(axis=0)
    Board_Score = Vec * (Board_Vec/np.sum(Board_Vec))
    

    dtm_per_year_melted = dtm_per_year.melt(id_vars=['股票代码', '会计年度', '行业代码', '板块', '融资约束'],
                                            var_name='word_id', 
                                            value_name='word_freq')
    

    corporate_df = pd.DataFrame({'word_id': dtm_per_year_melted[dtm_per_year_melted['股票代码']==code]['word_id'].values,
                                 'word_freq': dtm_per_year_melted[dtm_per_year_melted['股票代码']==code]['word_freq'].values,
                                 'ind_freq': Ind_Score,
                                 'board_freq': Board_Score,
                                 'fin_constrain_freq': FinConstrain_Score})
    corporate_df['股票代码'] = code
    corporate_df['行业代码'] = ind
    corporate_df['板块'] = board
    corporate_df['会计年度'] = year
    
    corporate_df.reset_index(inplace=True)
    corporate_df = corporate_df[['股票代码', '行业代码', '会计年度', '板块', 'word_id', 'word_freq', 'ind_freq', 'board_freq', 'fin_constrain_freq']]
    
    if not os.path.exists('fin_constrain_output/{year}'.format(year=year)):
        os.mkdir('fin_constrain_output/{year}'.format(year=year))
    
    corporate_df.to_csv('fin_constrain_output/{year}/{code}.csv'.format(year=year, code=code), index=False, mode='w')
  

4.3 计算所有年份板块评分、行业评分

这部分代码,全部运行下来,耗时 20 小时。

%%time
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
import pandas as pd
import re
import os
from tqdm import tqdm
import cntext as ct
import jieba



if not os.path.exists('fin_constrain_output'):
    os.mkdir('fin_constrain_output')
    
    
    
#cntext1.x
#stopwords = ct.load_pkl_dict('STOPWORDS.pkl')['STOPWORDS']['chinese']
#cntext2.x
stopwords= ct.read_yaml_dict('enzh_common_StopWords.yaml')['Dictionary']['chinese']



def is_financial_constraint(text):
    #正则表达式组
    regex1 = r"(?:融资|资金|筹资)[^。]{0,6}?(?:难以|不能|无法|不足以)[^。]*"
    regex2 = r"(?:融资|资金|筹资)[^。]{0,6}?(?:成本|压力|难度)[^。]{0,4}?(?:升|增|高|大)[^。]*"
    pattern = r"(" + regex1 + r")|(" + regex2 + r")"
    
    #带内容的结果为融资约束,为True;反之,为False
    if len(re.findall(pattern, text))>=1:
        return True
    else:
        return False
    


def transform(text):
    #只保留md&a中的中文内容
    text = ''.join(re.findall('[\u4e00-\u9fa5]+', text))
    #剔除停用词
    words = [w for w in jieba.cut(text) if w not in stopwords]
    #整理为用空格间隔的字符串(类西方语言文本格式)
    return ' '.join(words)

    
def plate(code):
    #判断股票是在上海证券交易所还是深圳证券交易所
    if (code[:2]=='A6') or (code[:2]=='A9'):
        return '上海'
    elif (code[:2]=='A0') or (code[:2]=='A3'):
        return '深圳'
    else:
        return '其他'

    
    

#读取md&a
df = pd.read_csv('data/mda01-23.csv.gz', compression='gzip')
df.columns = ['会计年度', '股票代码', '经营讨论与分析内容']
df['会计年度'] = df['会计年度'].astype(str)


#上市公司行业信息
ind_info_df = pd.read_excel('data/上市公司基本信息2000-2023.xlsx', usecols=['Symbol', 'EndDate', 'IndustryCodeC'])
ind_info_df = ind_info_df[ind_info_df.Symbol!='股票代码']
ind_info_df['会计年度'] = ind_info_df.EndDate.fillna('').apply(lambda date: date[:4])
ind_info_df.rename(columns={'Symbol': '股票代码', 'IndustryCodeC':'行业代码'}, inplace=True)
ind_info_df = ind_info_df[['股票代码', '会计年度', '行业代码']]

#合并数据
df = pd.merge(df, ind_info_df, on=['股票代码', '会计年度'], how='inner')
df['板块'] = df['股票代码'].apply(plate)
df = df[df['板块'].isin(['上海', '深圳'])]


    
#识别融资约束
df['融资约束'] = df['经营讨论与分析内容'].apply(is_financial_constraint)




for year in df['会计年度'].unique():
    df_per_year = df[df['会计年度']==year]
    df_per_year.reset_index(inplace=True)

    df_per_year['clean_text'] = df_per_year['经营讨论与分析内容'].apply(transform)
    cv = CountVectorizer(min_df=0.05, max_df=0.5) 
    # 生成稀疏bow矩阵
    #dtm 文档-词频-矩阵
    dtm_per_year = cv.fit_transform(df_per_year['clean_text']) 
    dtm_per_year = pd.DataFrame(dtm_per_year.toarray(), index=dtm_per_year.index)
    #向量标准化normalize
    dtm_per_year = dtm_per_year.apply(lambda row: row/np.sum(row), axis=1)
    #合并多个字段为新的df
    dtm_per_year = pd.concat([df_per_year[['股票代码', '会计年度', '板块', '行业代码', '融资约束']], dtm_per_year], axis=1)
    
    
    for idx in tqdm(range(len(dtm_per_year)), desc=f'{year}进度'):
        code = dtm_per_year.loc[idx, '股票代码']
        ind = dtm_per_year.loc[idx, '行业代码']
        year = dtm_per_year.loc[idx, '会计年度']
        board = dtm_per_year.loc[idx, '板块']


        Vec = dtm_per_year.iloc[idx, 5:]
        Ind_Vec = dtm_per_year[dtm_per_year['行业代码']==ind][dtm_per_year['股票代码']!=code].iloc[:, 5:].mean(axis=0)
        Ind_Score = Vec * (Ind_Vec/np.sum(Ind_Vec))
        FinConstrain_Vec = dtm_per_year[dtm_per_year['融资约束']==True].iloc[:, 5:].mean(axis=0)
        FinConstrain_Score = Vec * (FinConstrain_Vec/np.sum(FinConstrain_Vec))
        Board_Vec = dtm_per_year[dtm_per_year['板块']==board][dtm_per_year['股票代码']!=code].iloc[:, 5:].mean(axis=0)
        Board_Score = Vec * (Board_Vec/np.sum(Board_Vec))


        dtm_per_year_melted = dtm_per_year.melt(id_vars=['股票代码', '会计年度', '行业代码', '板块', '融资约束'],
                                                var_name='word_id', 
                                                value_name='word_freq')


        corporate_df = pd.DataFrame({'word_id': dtm_per_year_melted[dtm_per_year_melted['股票代码']==code]['word_id'].values,
                                     'word_freq': dtm_per_year_melted[dtm_per_year_melted['股票代码']==code]['word_freq'].values,
                                     'ind_freq': Ind_Score,
                                     'board_freq': Board_Score,
                                     'fin_constrain_freq': FinConstrain_Score})
        corporate_df['股票代码'] = code
        corporate_df['行业代码'] = ind
        corporate_df['板块'] = board
        corporate_df['会计年度'] = year

        corporate_df.reset_index(inplace=True)
        corporate_df = corporate_df[['股票代码', '行业代码', '会计年度', '板块', 'word_id', 'word_freq', 'ind_freq', 'board_freq', 'fin_constrain_freq']]
        
        if not os.path.exists('fin_constrain_output/{year}'.format(year=year)):
            os.mkdir('fin_constrain_output/{year}'.format(year=year))
        
        corporate_df.to_csv('fin_constrain_output/{year}/{code}.csv'.format(year=year, code=code), index=False, mode='w')
             



4.4 融资约束2023

    - ConstrainedScore_it =β0 + β1 * BoardScore_bt + β2 * IndustryScore_it + E_it
    - BoardScore_bt  交易所引发的融资约束相似度
    - IndustryScore_it  行业特征引发的融资约束相似度
    - E_it  残差就是本文要计算的[融资约束指标FC]

import pandas as pd

csv_df = pd.read_csv('fin_constrain_output/2023/A000001.csv',  converters={'股票代码': str})
csv_df.head()


#更改字段名。
csv_df.columns = ['股票代码', '行业代码', '会计年度', '板块', 'word_id', 'Vec', 'IndustryScore', 'BoardScore', 'ConstrainedScore']
csv_df.head()


import statsmodels.formula.api as smf

#因变量ConstrainedScore
#解释变量IndustryScore、 BoardScore
formula = 'ConstrainedScore ~ IndustryScore + BoardScore'

model = smf.ols(formula, data=csv_df)
result = model.fit()
print(result.summary())

Run

OLS Regression Results                            
==============================================================================
Dep. Variable:       ConstrainedScore   R-squared:                       0.986
Model:                            OLS   Adj. R-squared:                  0.986
Method:                 Least Squares   F-statistic:                 1.612e+05
Date:                Sat, 27 Jul 2024   Prob (F-statistic):               0.00
Time:                        14:12:31   Log-Likelihood:                 64496.
No. Observations:                4703   AIC:                        -1.290e+05
Df Residuals:                    4700   BIC:                        -1.290e+05
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
=================================================================================
                    coef    std err          t      P>|t|      [0.025      0.975]
---------------------------------------------------------------------------------
Intercept     -1.534e-08   3.92e-09     -3.914      0.000    -2.3e-08   -7.65e-09
IndustryScore     0.1173      0.002     60.638      0.000       0.114       0.121
BoardScore        1.0034      0.007    139.246      0.000       0.989       1.018
==============================================================================
Omnibus:                     9389.385   Durbin-Watson:                   1.795
Prob(Omnibus):                  0.000   Jarque-Bera (JB):         35835031.254
Skew:                         -15.930   Prob(JB):                         0.00
Kurtosis:                     429.445   Cond. No.                     1.90e+06
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.9e+06. This might indicate that there are
strong multicollinearity or other numerical problems.


#融资约束FC
FC = sum(abs(result.resid))

print('2023年 A000001融资约束指标 FC: {}'.format(FC))

Run

2023年 A000001融资约束指标 FC: 0.00020066158329792454

4.5 融资约束2001-2023

根据步骤4.4我们成功计算出了2023的融资约束FC指标,现在推广到2001-2023, 并将计算结果存储到 fin_constrain2001-2023.csv, csv 含 codeyearFC 三个字段。

%%time

import glob
import csv
import statsmodels.formula.api as smf
import pandas as pd

with open('fin_constrain2001-2023.csv', 'w', encoding='utf-8', newline='') as csvf:
    fieldnames = ['code', 'year', 'FC']
    writer = csv.DictWriter(csvf, fieldnames=fieldnames)
    writer.writeheader()
    for file in glob.glob('fin_constrain_output/*/*.csv'):
        try:
            df_ = pd.read_csv(file)
            df_.columns = ['股票代码', '行业代码', '会计年度', '板块', 'word_id', 'Vec', 'IndustryScore', 'BoardScore', 'ConstrainedScore']
            formula = 'ConstrainedScore ~ IndustryScore + BoardScore'
            model = smf.ols(formula, data=df_)
            result = model.fit()
            FC = sum(result.resid)
            FC = sum(abs(result.resid))
            data = {
                    'code': df_['股票代码'].unique()[0],
                    'year': df_['会计年度'].unique()[0],
                    'FC': FC
                }
            writer.writerow(data)
        except:
            pass



最后查看(欣赏)这个融资约束数据 fin_constrain2001-2023.csv

fc_df = pd.read_csv('fin_constrain2001-2023.csv')
fc_df



五、获取资料

数据&代码创作不易,如果需要源代码和数据, 加微信372335839, 备注「姓名-学校-专业」

打包200元, 含
   - 管理层讨论与分析(mda01-23.csv.gz)、上市公司基本信息2000-2023.xlsx
   - cntext2安装文件(cntext-2.1.3-py3-none-any.whl)
   - 计算结果(fin_constrain2001-2023.csv)


零卖价
  - 100元  管理层讨论与分析(mda01-23.csv.gz)、上市公司基本信息2000-2023.xlsx
  - 100元  cntext2安装文件(cntext-2.1.3-py3-none-any.whl)
  - 50元   计算结果(fin_constrain2001-2023.csv)



相关内容



广而告之