【财报分析】财务数据分析和图表绘制

2022-03-15  本文已影响0人  山药鱼儿

连载的上一篇文章 【财报分析】读取和处理财报数据 通过对同花顺个股下载的财报数据进行了清洗以及整合,得到了我们本次分析需要的数据集 data.csv

data.csv 一共包含了 161 列,全部都是财报中的会计统计科目~像小鱼这样准备好了数据集,就可以开始接下来的分析工作啦!

说明:如果你准备好了和小鱼一样的数据集,对于本篇文章中的代码,你可以直接运行~

准备工作

读取配置文件,从配置文件中获取图片保存的名称 images 、分析的标题 titles 以及 18 个子表的字段信息 tables_fields

import json

with open('./ini.json', encoding='utf-8') as f:
    config = json.load(f)

images = config['images']
titles = config['titles']
tables_fields = config['tables_fields']

读取 data.csv 获取分析数据集:

import pandas as pd

df = pd.read_csv('data.csv', index_col=0)

定义子表的初始化函数 init_table ,参数为配置文件中定义的 tables_fields 中的键 t1 t2

def init_table(table_index):
    t = pd.DataFrame(
        index=df.index, 
        columns=tables_fields[table_index]['columns']
    )
    t[tables_fields[table_index]['auto']] = df[tables_fields[table_index]['auto']]
    return t

init_table 函数除了完成 DataFrame 索引和列的初始化之外,对于配置中 tables_fields 定义的 auto 字段,将从数据集 df 找到对应的字段完成赋值操作。

接下来是关于表格展示的相关配置和函数:

pd.set_option('display.float_format', lambda x: '%.2f' % x)
pd.set_option('mode.chained_assignment', None)

def format_thousandth(arr):
    """将 arr 或 Series 中的数字使用千分位表示"""
    return arr.apply(lambda x:format(x, ',.0f'))
    
def format_percentage(arr):
    """将 arr 或 Series 中的数字使用百分制表示,小数点后保留 2 位"""
    return arr.apply(lambda x:format(x, '.2%'))

def format_show_table(table,ignore=None):
    table = table.copy()
    for column in table.columns:
        if ignore and column in ignore:
            continue
        if column.endswith('率'):
            table[column] = format_percentage(table[column])
        else:
            table[column] = format_thousandth(table[column])
    return table.T

对于子表中列名以 结尾的,比如资产负债率、毛利率等我们使用百分制展示;对于其他数值类型的列,比如资产总计、负债总计等为了增强可读性,使用千分位显示金额。

最后是绘制柱形图、折线图的函数 plot_show 的定义:

import numpy as np
import matplotlib.pyplot as plt

# 解决绘图中显示中文的问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

def plot_show(t, title='', ylabel='单位:亿', kind='bar', y_format='hundred million'):
    """绘图"""
    t.plot(kind=kind)
    
    y_max = t.max()
    y_min = t.min()
    while type(y_max) == pd.Series:
        y_max = y_max.max()
        y_min = y_min.min()
    
    if y_min > 0:
        y_min = 0
    
    num_yticks = np.linspace(y_min, y_max, 8)
    if y_format == 'hundred million':
        plt.yticks(num_yticks, map(lambda x:round(x/100000000,2), num_yticks))
    else:
        plt.yticks(num_yticks, map(lambda x:format(x,'.2%'), num_yticks))
        
    if kind == 'line':
        plt.xticks(t.index, t.index)
    
    plt.ylabel(ylabel)
    plt.title(title)
    plt.savefig(title+'.png')

绘图中主要是关于横纵坐标轴刻度的设置以及柱形图、折线图的选择,绘图完成后需要将绘制结果保存到 .png 格式的图片,以便最后将分析成果保存到 Word 文档。

财报分析

财报分析分 18 个步骤展开,顺序就按照我们 ini.json 配置文件中 title 定义的列表来进行。

1. 看总资产,判断公司实力及扩张能力

分析总资产规模及增长率:

t1 = init_table('t1')
t1['总资产增长率'] = t1['资产合计(元)'].pct_change()

t1_show = format_show_table(t1)
t1_show

将表格结果通过柱形图和折线图展示:

plot_show(t1['资产合计(元)'], title=images[0][0])

2015-2016年 X 公司的总资产一直处在增长之中,到 2020 年总资产规模达到了 124 个亿。

下面是总资产规模的增长率:

plot_show(t1['总资产增长率'][1:], title=images[0][1], ylabel='', y_format='p', kind='line')

总资产规模的增长放缓,从 2016 年到 2019 年增速从 27% 下降到 12%,2020 年增速有所上升。2016 - 2020年总资产的增长率维持在 10% 以上,虽然所有下降,但仍处于快速扩张之中。

2. 看资产负债率,了解公司的偿债风险

t2 = init_table('t2')
t2['资产负债率'] = t2['负债合计(元)'] / t2['资产合计(元)']

t2_show = format_show_table(t2)
t2_show

绘图展示资产负债率:

plot_show(t2['资产负债率'], title=images[1], ylabel='', y_format='p')

X 公司的资产负债率维持在 34% 左右,小于 40%,偿债风险比较小。

3. 看有息负债和准货币资金,排除偿债风险

除了资产负债率,还可以通过准货币资金减有息负债的差额,看看兜里的钱够不够还债。

t3 = init_table('t3')
t3['准货币资金'] = t3.T[:4].sum()
t3['有息负债总额'] = t3.T[5:10].sum()
t3['总货币资金与有息负债之差'] = t3['准货币资金'] - t3['有息负债总额']

t3_show = format_show_table(t3)
t3_show

X 公司准货币资金与有息负债的差额大于 0 ,无偿债风险。

plot_show(t3[['准货币资金','有息负债总额']], title=images[2])

从柱形图来看,准货币资金是远大于有息负债的,准货币资金处于不断增长之中。

plot_show(t3['总货币资金与有息负债之差'], title=images[2][1], kind='line')

将差额绘制成折线图,进一步观察趋势:

4. 看“应付预收”减“应收预付”的差额,了解公司的竞争优势

了解了实力、偿债风险,我们来分析公司在上下游的话语权如何?也就是公司的竞争优势。

t4 = init_table('t4')
t4['应付与预收合计'] = t4.T[:4].sum()
t4['应收与预付合计'] = t4.T[5:10].sum()
t4['应付预收减应收预付的差额'] = t4['应付与预收合计'] - t4['应收与预付合计']

t4_show = format_show_table(t4)
t4_show

X 公司应付预收减应收预付大于 0 ,说明公司在上下游中是有一定话语权的,具有两头吃的能力。

plot_show(t4[['应付与预收合计','应收与预付合计']], title=images[3][0])

但是 X 公司的应收与预付近 6 年在不断增加,甚至导致近 3 年应付预收与应收预付的差额呈现下降趋势。X 公司在行业中的话语权可能在减弱,行业竞争激烈。

好在应收与预付中,大部分都是应收票据:

plot_show(t4[['其中:应收票据(元)','应收款项融资','应收账款(元)','预付款项(元)']], 
          title=images[3][1])

可以通过年报进一步了解应收票据是银行承兑票据还是商业承兑票据,以及应收账款的账龄如何?应收账款坏账计提标准是否足够严格?

plot_show(t4['应付预收减应收预付的差额'], title=images[3][2], kind='line')

将应付预收与应收预付的差额绘制成折线图,进一步观察趋势:

5. 看应收账款、合同资产,了解公司的产品竞争力

了解了公司的竞争力,我们来进一步看看公司产品的竞争力如何?也就是看应收账款。如果一家公司的应收账款很多,很可能公司产品不够畅销,产品竞争力不够,必须靠让渡账期来把货卖出去。

t5 = init_table('t5')
t5['应收账款+合同资产'] = t5.T[:2].sum()
t5['(应收账款+合同资产)占总资产的比率'] = t5['应收账款+合同资产'] / t5['资产合计(元)']

t5_show = format_show_table(t5)
t5_show

X 公司应收账款占总资产的比例维持在 10% 以内,产品不够畅销,但也不算难销售。

plot_show(t5['(应收账款+合同资产)占总资产的比率'], title=images[4], ylabel='', y_format='p')

从柱形图来看,应收账款占比最近 3 年不断增加,也一定程度上说明产品销售越来越不容易了,行业竞争激烈。

6. 看固定资产,了解公司维持竞争力的成本

固定资产占比最好不要过高,一方面固定资产会折旧摊销,另一方面固定资产需要保养维护,固定资产占比过高,维持竞争力的成本也会相对比较高。

t6 = init_table('t6')
t6['固定资产+在建工程+工程物资'] = t6.T[:3].sum()
t6['固定资产工程占总资产的比率'] = t6['固定资产+在建工程+工程物资'] / t6['资产合计(元)']

t6_show = format_show_table(t6)
t6_show

X 公司固定资产占总资产的比例近 4 年均为 10% ,远小于 40%,X 公司属于轻资产型企业,维持竞争力的成本低。

plot_show(t6['固定资产工程占总资产的比率'], title=images[5], ylabel='', y_format='p')

固定资产占总资产的比率整体呈下降趋势,近几年维持在 10% 的位置,非常优秀。

7. 看投资类资产,判断公司的专注程度

只有专注,才可能卓越。下面来看投资类资产占总资产的比例:

t7 = init_table('t7')
t7['投资类资产合计'] = t7.T[:8].sum()
t7['投资类资产占总资产的比率'] = t7['投资类资产合计'] / t7['资产合计(元)']

t7_show = format_show_table(t7)
t7_show

X 公司投资类资产占总资产的比例维持在 1% 左右,远小于 10% ,非常专注于主业。

plot_show(t7['投资类资产占总资产的比率'], title=images[6], ylabel='', y_format='p')

从 2017 年开始投资类资产占总资产的比例不断下降,在激烈的竞争环境下,公司在变得越来越专注。

8. 看存货,了解公司未来业绩爆雷的风险

存货和商誉是上市公司非常容易暴雷的科目,首先来看存货。

t8 = init_table('t8')
t8['存货占总资产的比率'] = t8['存货(元)'] / t8[ '资产合计(元)']

t8_show = format_show_table(t8)
t8_show

X 公司的存货 2015-2018 年维持在 14% ,存货占比较高,2019 和 2020 年有所下降,2020 年为 11.13%。

tmp_df = pd.merge(
    t8['存货占总资产的比率'], 
    t5['(应收账款+合同资产)占总资产的比率'],
    right_index=True,
    left_index=True
)
plot_show(tmp_df, title=images[7], ylabel='', y_format='p')

结合应收账款来看,从 2018 年开始应收账款在不断增加,应收账款占比基本在 5% 以上,可见产品销售难度不断增加,公司也在合理控制存货。

9. 看商誉,了解公司未来爆雷的风险

商誉来源于溢价收购其他公司,如果收购的资产确实很优质,那另当别论。一般情况下,商誉越大当冤大头的可能性也就越大,后期很可能发生商誉的计提损失。

t9 = init_table('t9')
t9['商誉占总资产的比率'] = t9['商誉(元)'] / t9[ '资产合计(元)']

t9_show = format_show_table(t9)
t9_show

X 公司商誉占总资产的比例在 1% 以下,无商誉暴雷风险。

plot_show(t9['商誉占总资产的比率'], title=images[8], ylabel='', y_format='p')

10. 看营业收入,了解公司的行业地位及成长性

一般而言,营业收入高的公司,公司实力和地位也比较高。和总资产一样,营业收入也关注规模和增长率两个方面。

t10 = init_table('t10')
t10['营业收入增长率'] = t10['其中:营业收入(元)'].pct_change()

t10_show = format_show_table(t10)
t10_show

2016 年和 2017 年 X 公司的营业收入增速在 20% 以上,保持了比较高速的增长。2018 年之后营业收入增速在 5% 左右,小于 10%,增速放缓。X 公司很可能从成长期进入了成熟期。

plot_show(t10['其中:营业收入(元)'], title=images[9][0])

营业收入在不断增加。

plot_show(t10['营业收入增长率'][1:], title=images[9][1], ylabel='', y_format='p', kind='line')

营业收入增速明显放缓。

11. 看毛利率,了解公司的产品竞争力及风险

毛利率高的公司,经营风险是比较小的。公司通常具有定价权,产品比其他公司卖得贵,消费者还愿意买单。毛利率高的企业可能具有品牌优势护城河和高转换成本护城河。

t11 = init_table('t11')
t11['毛利率'] = (t11['其中:营业收入(元)']-t11['其中:营业成本(元)']) / t11['其中:营业收入(元)']
t11['毛利率波动率'] = t11['毛利率'].pct_change()

t11_show = format_show_table(t11)
t11_show

X 公司的毛利率在 53% 以上,大于 40%,属于高毛利率的企业。

plot_show(t11['毛利率'], title=images[10][0], ylabel='', y_format='p' )

毛利率从 2015-2017 年出现下降趋势,近 3 年开始上升,2020 年毛利率为 56.16%。

plot_show(t11['毛利率波动率'][1:], title=images[10][1], ylabel='', y_format='p', kind='line')

毛利率的波动幅度上下在 10% 以内,还是比较稳定的,财务造假调节利润的可能性较小。

12. 看期间费用率,了解公司的成本管控能力

一家公司的产品毛利润不错,但如果成本管控能力差,那么留下的利润就会很少。

t12 = init_table('t12')
t12.loc[t12['财务费用(元)']<0, '财务费用(元)'] = 0
t12['四费合计'] = t12.T[:4].sum()
t12['期间费用率'] = t12['四费合计'] / t12['其中:营业收入(元)']
t12['毛利率'] = t11['毛利率']
t12['期间费用率占毛利率的比率'] = t12['期间费用率'] / t12['毛利率']

t12_show = format_show_table(t12)
t12_show

X 公司期间费用率占毛利率的比率维持在 60% 左右,远大于 40%。成本管控能力不足,大部分的毛利润没有被保留下来。

plot_show(t12['期间费用率'], title=images[11][0], ylabel='', y_format='p')

2015-2017 年期间费用率呈下降趋势,2018-2020年期间费用率在 33% 左右。

plot_show(t12['期间费用率占毛利率的比率'], title=images[11][1], ylabel='', y_format='p')

2016-2020 年期间费用率占毛利率在 60% 附近波动,成本管控能力有待加强。

13. 看销售费用率,了解公司产品的销售难易度

期间费用从金额来看,X 公司主要还是销售费用比较高。前面我们分析应收账款占比比较高,产品不够畅销,此处也进一步说明了这一点,为了卖出去货,增加销售费用来做广告、营销。

t13 = init_table('t13')
t13['销售费用率'] = t13['销售费用(元)'] / t13['其中:营业收入(元)']

t13_show = format_show_table(t13)
t13_show

X 公司销售费用率维持在 23% 以上,大于 15%,产品不够畅销,存在一定的销售风险。

plot_show(t13['销售费用率'], ylabel='', title=images[12], y_format='p')

2016-2020年销售费用率在 25% 左右波动,小于 30%,需要付出较高的销售费用才能把货卖出去。

14. 看主营利润,了解公司主业的盈利能力及利润质量

主营利润观察两点,主营利润率和主营利润占营业利润的比率:

t14 = init_table('t14')
t14['四费合计'] = t12['四费合计']
t14['主营利润'] = t14['其中:营业收入(元)'] - t14.T[1:4].sum()
t14['主营利润率'] = t14['主营利润'] / t14['其中:营业收入(元)']
t14['主营利润占营业利润的比率'] = t14['主营利润'] / t14['三、营业利润(元)']

t14_show = format_show_table(t14)
t14_show

2015-2020 年 X 公司的主营利润率在 19% 以上,大于 15%,可见主业盈利能力强。

plot_show(t14['主营利润率'], title=images[13][0], ylabel='', y_format='p')

主营利润率整体维持在 20% 左右。

plot_show(t14['主营利润占营业利润的比率'], title=images[13][1], ylabel='', y_format='p')

主营利润占营业利润的比率在 83% 以上,营业利润大部分是由主业创造的,利润质量还是很不错的。

15. 看净利润,了解公司的经营成果及含金量,净利润主要看净利润含金量

利润表为全责发生制,现金流量表为收付实现制,通过净利润现金含量可以检验企业是否既赚到了利润,又赚到了真金白银。

t15 = init_table('t15')
t15['净利润现金比率'] = t15['经营活动产生的现金流量净额(元)'] / t15['归属于母公司所有者的净利润(元)']

t15_show = format_show_table(t15)
t15_show

公司 X 连续 5 年的净利润现金比率在 87% 以上,连续 5 年的平均净利润现金含量大于 100%,经营成果含金量不错。

>> print(f"连续 5 年的平均净利润现金含量:{t15['净利润现金比率'].mean():.2%}")
连续 5 年的平均净利润现金含量:107.21%

16. 看归母净利润,了解公司的整体盈利能力及持续性

归母净利润也主要看两点,ROE 和归母净利润的增长率。

t16 = init_table('t16')
t16['ROE 净资产收益率'] = t16['归属于母公司所有者的净利润(元)'] / t16['归属于母公司所有者权益合计(元)']
t16['归属于母公司所有者的净利润增长率'] = t16['归属于母公司所有者的净利润(元)'].pct_change()

t16_show = format_show_table(t16)
t16_show

公司 X 的 ROE 连续 5 年均在 20% 以上,非常优秀,自有资本获利能力强。归母净利润的增长率在 2018-2020年小于 10%,成长性看上去不足,可以查找年报或者询问董秘了解异常原因。

plot_show(t16['ROE 净资产收益率'], title=images[15][0], ylabel='', y_format='p')

ROE 从 2016 年开始一直在下降之中,赚钱能力在下降。

plot_show(t16['归属于母公司所有者的净利润增长率'][1:], 
          title=images[15][1], ylabel='', y_format='p', kind='line')

归母净利润的增长率也出现下降趋势,2018年不足 1% ,2020 年为 4.46%,远小于 10%。

17. 看购买固定资产、无形资产和其他长期资产支付的现金,了解公司的增长潜力

明日的增长来自于今日的投资,扩张投入的资金占经营活动产生的现金流量净额的比例在 3%~60% 之间是比较合理的,小于 3% 回报较低,大于 60% 可能太过激进。

t17 = init_table('t17')

t17['购建支付的现金与经营活动产生的现金流量净额的比率'] = \
t17['购建固定资产、无形资产和其他长期资产支付的现金(元)'] / t17['经营活动产生的现金流量净额(元)']

t17_show = format_show_table(t17)
t17_show

2016-2020年 X 公司购建支付的现金与经营活动产生的现金流量净额的比率在 11%~19% ,位于合理区间。

plot_show(t17['购建支付的现金与经营活动产生的现金流量净额的比率'], 
          title=images[16], ylabel='', y_format='p')

2019 年和 2020 年构建支付的现金明显增加,可以查阅年报找到 2019 年和 2020 年是否有大的投资扩张。

18. 看分配股利、利润或偿付利息支付的现金,了解公司的现金分红情况

分红对于投资者来说非常重要,一家公司的分红比例低于 20%,很可能不是能力问题就是品质问题(高科技互联网公司除外)。合理区间为 20%~70%,分红会比较慷慨,并且可持续性强。

t18 = init_table('t18')

t18['分配股利、利润或偿付利息支付的现金占经营活动产生的现金流量净额的比率'] = \
t18['分配股利、利润或偿付利息支付的现金(元)'] / t18['经营活动产生的现金流量净额(元)']

t18_show = format_show_table(t18)
t18_show

公司 X 购建支付的现金与经营活动产生的现金流量净额的比率 2016-2020 年 18%~48% 之间。前面我们分析发现公司 X 的有息负债非常少,可见这里支付的现金基本上都是在分红。

plot_show(t18['分配股利、利润或偿付利息支付的现金占经营活动产生的现金流量净额的比率'], 
          title=images[17], ylabel='', y_format='p')

2017-2020 年分红比例在 28% 以上,位于合理区间。2015-2019 年分红比率一直在增加,2020 年下降较多,可以阅读年报找到原因。

附:三大活动产生的现金流量净额

t19 = init_table('t19')
t19_tmp = init_table('t19')

t19_tmp['经营活动产生的现金流量净额(元)'][t19['经营活动产生的现金流量净额(元)']>0] = "正"
t19_tmp['经营活动产生的现金流量净额(元)'][t19['经营活动产生的现金流量净额(元)']<0] = "负"

t19_tmp['投资活动产生的现金流量净额(元)'][t19['投资活动产生的现金流量净额(元)']>0] = "正"
t19_tmp['投资活动产生的现金流量净额(元)'][t19['投资活动产生的现金流量净额(元)']<0] = "负"

t19_tmp['筹资活动产生的现金流量净额(元)'][t19['筹资活动产生的现金流量净额(元)']>0] = "正"
t19_tmp['筹资活动产生的现金流量净额(元)'][t19['筹资活动产生的现金流量净额(元)']<0] = "负"

t19_tmp['三大活动现金流量净额类型'] = \
t19_tmp['经营活动产生的现金流量净额(元)'] + t19_tmp['投资活动产生的现金流量净额(元)'] + t19_tmp['筹资活动产生的现金流量净额(元)']

t19['三大活动现金流量净额类型'] = t19_tmp['三大活动现金流量净额类型']
t19_show = format_show_table(t19, ignore=['三大活动现金流量净额类型'])
t19_show

三大活动分别是经营活动、投资活动和筹资活动,我们期望经营活动产生的现金流量净额足够强大,可以覆盖自身的扩张(投资活动)和分红(筹资活动),也就是 正负负 类型。

另外一种就是 正正负 类型的公司也比较不错,一般处于成熟期,扩张的速度没那么快,但是分红稳定。

plot_show(df[['经营活动产生的现金流量净额(元)',
              '投资活动产生的现金流量净额(元)',
              '筹资活动产生的现金流量净额(元)']], 
          title=images[18])

公司 X 就是属于 正负负 类型的公司,其经营活动产生的现金流量净额非常强大。

保存分析成果

将表格和图片保存到 Word 文档。定义相关函数,分别保存表格、标题及图片:

import docx

def save_table(table):
    rows, cols = table.shape[0]+1, table.shape[1]
    t = doc.add_table(rows, cols) 

    # add the header rows. 
    for col in range(cols-1): 
        t.cell(0, col+1).text = str(table.columns[col+1])

    # add the index cols.
    for row in range(rows-1):
        t.cell(row+1, 0).text = str(table.index[row])

    # add the rest of the dataframe 
    for row in range(rows-1): 
        for col in range(cols-1): 
            t.cell(row+1,col+1).text = str(table.values[row, col+1]) 
            
    doc.add_paragraph()
    
def save_title(title):
    doc.add_paragraph(title)
    doc.add_paragraph()
    
def save_image(image_path):
    if isinstance(image_path, list):
        for img in image_path:
            doc.add_picture(f'{img}.png')
    elif image_path:
        doc.add_picture(f'{image_path}.png')

将分析成功保存成名为 X 公司财报分析20220316.docx 格式命名的 Word 文档:

from datetime import datetime
from itertools import zip_longest

tables = (t1_show, t2_show, t3_show, t4_show, t5_show, t6_show, 
          t7_show, t8_show, t9_show, t10_show, t11_show, t12_show,
          t13_show, t14_show, t15_show, t16_show, t17_show, t18_show)
doc = docx.Document() 

for title, table, image in zip_longest(titles, tables, images):
    save_title(title)
    if table is not None:
        save_table(table)
    save_image(image)
    
doc.save(f"X 公司财报分析{datetime.strftime(datetime.now(), '%Y%m%d')}.docx")

以上就是本节的全部内容啦~数据分析的前提一定是数据的准备,经过清晰、整理后的数据,才使得我们的分析变得流畅、准确。

上一篇下一篇

猜你喜欢

热点阅读