由于最近一直在为大家更新机器学习算法的帖子,未避免我们对 Pandas 一些用法的生疏,小鱼精心准备了两篇 Pandas 系列的文章,将分别使用 Pandas 来分析上市公司的资产负债表、利润表和现金流量表。
本篇是 Pandas 分析财报系列文章的第一篇,通过本篇文章的学习,相信不仅可以加深大家对 Pandas 的熟练程度,还会收获财报金融方面的一些技巧~
首先,下载上市公司的资产负债表。同花顺 http://stockpage.10jqka.com.cn/ 个股搜索个股名称,找到【财务分析】,点击【财务指标】:
找到【财务指标】部分,依次点击【资产负债表】、【按年度】、【导出数据】。
同样的方式,可以把下方的利润表和现金流量表也下载下来,供后续分析使用。下面是小鱼下载好的三大报表:
读取资产负债表
注:下载好之后,用 Excel 打开浏览一下再用 Pandas 读,否则可能会遇到读取失败。
首先,导入工具包,并进行 pandas 和 matplotlib 的一些基本设置,定义常量 DEBT
表示资产负债表:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
pd.set_option('display.float_format', lambda x: '%.4f' % x)
pd.set_option('mode.chained_assignment', None)
# 解决绘图中显示中文的问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
DEBT = '600xxx_debt_year.xls'
BENEFIT = '600xxx_benefit_year.xls'
CASH = '600xxx_cash_year.xls'
将重复用到的代码,定义成辅助函数:
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 plot_show(t, title='', ylabel='单位:亿', kind='line', 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')
读取资产债表
debt_df = pd.read_excel(DEBT, sheet_name='Worksheet', header=1, index_col=0)
debt_df.dropna(inplace=True)
debt_df.replace({'--': 0}, inplace=True)
debt_df = debt_df.T[:6][::-1]
debt_df.columns.name = '年份'
debt_df.apply(format_thousandth)
debt_df.T
小鱼只读取了最近 6 年的资产负债表数据:
分析资产负债表
1、分析总资产,了解公司实力及扩张能力
总资产规模代表了一家公司掌控的资源规模,也就是这家公司的实力。一般情况下,总资产规模在行业中的排名可以反映出一家公司在行业中的地位。总资产排名第一的公司一般就是这个行业中的老大。
截止 2022 年 2 月 18 日,A 股 4140 家上市公司中,总资产规模大于 500 亿有 354 家,总资产规模大于 100 亿有 1188 家,总资产规模大于 50 亿有 1937 家。
选取总资产,并计算总资产的增长率:
t1 = pd.DataFrame(index=debt_df.index, columns=['资产合计(元)', '总资产增长率'])
t1['资产合计(元)'] = debt_df['资产合计(元)']
t1['总资产增长率'] = debt_df['资产合计(元)'].pct_change()
pd.merge(
t1.loc[:,['资产合计(元)']].apply(format_thousandth),
t1.loc[:,['总资产增长率']].apply(format_percentage),
left_index=True,
right_index=True
).T
总资产规模及增长率:
以柱形图展示总资产规模:
plot_show(t1['资产合计(元)'], title='资产合计', kind='bar')
该公司的总资产近 6 年一直处于增长之中,2020 年总资产规模达到了 2000 多亿。通过折线图进一步观察总资产增长率:
plot_show(t1['总资产增长率'],title='总资产增长率', ylabel='', y_format='p')
总资产增长率:大于 10% 则公司处于扩张之中,成长性较好;小于 0% 公司很可能处于收缩或衰退之中。
2、看资产负债率,了解公司的偿债风险
通过资产规模和增长率的分析,我们发现一家公司资产实力雄厚,并且总资产的增长率在 10% 以上,公司处于快速扩张之中。但是,总资产在增加,可能是负债在增加,也可能是所有者权益在增加。
注:总资产 = 总负债(和别人借的) + 所有者权益(股东的钱),负债和所有者权益解释了钱从哪里来,总资产解释了钱都流向了哪儿。
接下来,我们分析资产负债率,来排除偿债风险。
t2 = debt_df[['负债合计(元)','资产合计(元)']]
t2['资产负债率'] = debt_df['负债合计(元)'] / debt_df['资产合计(元)']
pd.merge(
t2[['负债合计(元)','资产合计(元)']].apply(format_thousandth),
t2[['资产负债率']].apply(format_percentage),
left_index=True,
right_index=True
).T
近 6 年的资产负债率:
以柱形图展示资产负债率:
plot_show(t2['资产负债率'], title='资产负债率', ylabel='', kind='bar', y_format='p')
资产负债率:非金融、行地产等行业,资产负债率大于 70%,发生债务危机的可能性较大;小于 40%,基本没有偿债风险;40%~60% 之间偿债风险较小,但在特殊情况下依然可能发生偿债危机。
3、看有息负债和准货币资金,排除偿债风险
某公司的资产负债率很高,并且在逐年增加,并不一定代表偿债风险在增加。如果负债中大多数都是经营性负债(无息),反而说明该公司在上下游的话语权很强。
下面,我们看看兜里的钱够不够还有息负债。计算近 6 年的准货币资金和有息负债:
t3 = pd.DataFrame(index=debt_df.index, columns=[
'货币资金(元)', '交易性金融资产(元)', '其他流动资产里的理财产品', '其他流动资产里的结构性存款', '准货币资金', '短期借款(元)',
'一年内到期的非流动负债(元)', '长期借款(元)', '应付债券(元)', '长期应付款', '有息负债总额',
'总货币资金与有息负债之差'])
t3[['货币资金(元)', '交易性金融资产(元)','短期借款(元)', '一年内到期的非流动负债(元)', '长期借款(元)', '应付债券(元)']] = \
debt_df[['货币资金(元)', '交易性金融资产(元)','短期借款(元)', '一年内到期的非流动负债(元)', '长期借款(元)', '应付债券(元)']]
# 下方数据需要在年报中确认
t3['其他流动资产里的理财产品'] = [0,0,0,0,0,0]
t3['其他流动资产里的结构性存款'] = [0,0,0,0,0,0]
t3['长期应付款'] = [0,0,0,0,0,0]
t3['准货币资金']=t3[['货币资金(元)','交易性金融资产(元)','其他流动资产里的理财产品','其他流动资产里的结构性存款']].sum(axis=1)
t3['有息负债总额']=t3[['短期借款(元)','一年内到期的非流动负债(元)','长期借款(元)','应付债券(元)','长期应付款']].sum(axis=1)
t3['总货币资金与有息负债之差'] = t3['准货币资金'] - t3['有息负债总额']
t3.apply(format_thousandth).T
其他流动资产里的理财产品:在其他流动资产的注释中查找理财产品,如果没有理财产品,此项的金额为 0。
其他流动资产里的结构性存款:在其他流动资产的注释中查找结构性存款,如果没有结构性存款,此项的金额为 0。
长期应付款:结合长期应付款的注释,判断是有息负债还是无息负债。
某公司连续 6 年的准货币资金和有息负债情况:
以柱形图展示有息负债和准货币资金:
plot_show(t3[['准货币资金','有息负债总额']], title='准货币资金与有息负债', kind='bar')
准货币资金与有息负债之差:大于0,无偿债风险;小于0,有偿债风险。
异常情况:准货币资金和短期借款或长期借款的金额都很大,很可能企业实际没有钱,后期风险很大。
4、看“应付预收”减“应收预付”的差额,了解公司的竞争优势
应付预收属于公司的经营性负债,是无偿占用别人的资金。金额越大,代表公司的竞争力越强,行业地位越高。应收预付属于公司的资产,即资金被别人占用了。金额越小,代表公司的竞争力越强,行业地位越高。
计算公式:(应付账款+应付票据+预收款项+合同负债)-(应收账款+应收票据+预付款项+合同资产+应收款项融资)。有的公司导不出来应收款项融资这行数据,需要手动填入。
t4 = pd.DataFrame(index=debt_df.index, columns=[
'其中:应付票据(元)', '应付账款(元)', '预收款项(元)', '合同负债(元)',
'应付与预收合计', '其中:应收票据(元)', '合同资产', '应收款项融资', '应收账款(元)',
'预付款项(元)', '应收与预付合计', '应付预收减应收预付的差额'])
t4[['其中:应付票据(元)', '应付账款(元)', '预收款项(元)', '合同负债(元)','其中:应收票据(元)',
'应收账款(元)', '预付款项(元)']] = \
debt_df[['其中:应付票据(元)', '应付账款(元)', '预收款项(元)', '合同负债(元)','其中:应收票据(元)',
'应收账款(元)', '预付款项(元)']]
t4['合同资产'] = [0,0,0,0,0,0]
t4['应收款项融资'] = [0,0,0,4066653238,3350585849,3085315740]
t4['应付与预收合计'] = t4[['其中:应付票据(元)','应付账款(元)','预收款项(元)','合同负债(元)']].sum(axis=1)
t4['应收与预付合计'] = t4[['其中:应收票据(元)', '合同资产', '应收款项融资', '应收账款(元)', '预付款项(元)',]].sum(axis=1)
t4['应付预收减应收预付的差额'] = t4['应付与预收合计'] - t4['应收与预付合计']
t4.apply(format_thousandth).T
某公司近 6 年应付预收与应收预付的情况:
应付预收 - 应收预付的差额:大于0,公司的竞争力较强,具有两头吃的能力;小于0,被其他公司无偿占用资金,公司竞争力相对较弱。
通过柱形图对比应付预收与应收预付的大小:
plot_show(t4[['应付与预收合计','应收与预付合计']], title='应付预收与应收预付', kind='bar')
进一步分析应收预付的详情:
plot_show(t4[['其中:应收票据(元)','应收款项融资','应收账款(元)','预付款项(元)']], title='应收预付明细', kind='bar')
5、看应收账款、合同资产,了解公司的产品竞争力
通过应付预收与应收预付的差额,我们知道了公司的竞争力强不强,下面分析应收账款占总资产的比例,了解公司产品的竞争力如何。
t5 = pd.DataFrame(index=debt_df.index, columns=[
'合同资产', '应收账款(元)', '应收账款加合同资产', '资产合计(元)', '(应收账款+合同资产)占总资产的比率'
])
t5[['应收账款(元)','资产合计(元)']] = debt_df[['应收账款(元)','资产合计(元)']]
t5['合同资产'] = [0,0,0,0,0,0]
t5['应收账款加合同资产'] = t5['合同资产'] + t5['应收账款(元)']
t5['(应收账款+合同资产)占总资产的比率'] = t5['应收账款加合同资产'] / t5['资产合计(元)']
pd.merge(
t5[[ '合同资产', '应收账款(元)', '应收账款加合同资产', '资产合计(元)']].apply(format_thousandth),
t5[['(应收账款+合同资产)占总资产的比率']].apply(format_percentage),
left_index=True,
right_index=True
).T
近 6 年的应收账款占总资产的比率:
以柱状图进行展示:
plot_show(t5['(应收账款+合同资产)占总资产的比率'], title='应收账款和合同资产占总资产的比率', ylabel='', kind='bar', y_format='p')
一般,我们会把(应收账款+合同资产)占资产总计的比率大于 15% 的公司淘汰掉。在特别情况下,对于行业第一名并且同时具有 3 个或以上核心竞争力的公司可以放宽到 20%,大于 20% 的一律淘汰掉。
(应收账款+合同资产)占总资产的比率:小于 1%,最好的公司,公司产品很畅销;小于 3%,优秀的公司,公司产品畅销;比率大于 10%,公司的产品比较难销售;比率大于 20%,公司的产品很难销售。
6、看固定资产,了解公司维持竞争力的成本
经过前面的分析,我们了解了公司的实力地位、成长性、债务风险、公司和产品竞争力情况,下面我们来看看公司维持竞争力的成本,也就是(固定资产+在建工程+工程物资)占总资产的比例:
- 比率大于 40%,重资产型公司,维持竞争力的成本比较高,风险相对较大。
- 比率小于 40%,轻资产型公司,保持持续的竞争力成本相对要低一些。
t6 = pd.DataFrame(index=debt_df.index, columns=[
'固定资产合计(元)', '在建工程合计(元)', '工程物资(元)', '固定资产+在建工程+工程物资',
'资产合计(元)', '固定资产工程占总资产的比率'])
t6[['固定资产合计(元)', '在建工程合计(元)', '资产合计(元)']] = \
debt_df[['固定资产合计(元)', '在建工程合计(元)', '资产合计(元)']]
t6['工程物资(元)'] = [0,0,0,0,0,0]
t6['固定资产+在建工程+工程物资'] = t6[['固定资产合计(元)', '在建工程合计(元)', '工程物资(元)']].sum(axis=1)
t6['固定资产工程占总资产的比率'] = t6['固定资产+在建工程+工程物资'] / t6['资产合计(元)']
t6_show = pd.merge(t6[['固定资产合计(元)', '在建工程合计(元)', '工程物资(元)',
'固定资产+在建工程+工程物资', '资产合计(元)']].apply(format_thousandth),
t6[['固定资产工程占总资产的比率']].apply(format_percentage),
right_index=True,left_index=True).T
t6_show
公司固定资产占总资产的比例:
注:在建工程正常情况下后期会转为固定资产,此行数据取数包括了工程物资。 工程物资正常情况下后期转为在建工程。
以柱形图进行可视化展示:
plot_show(t6['固定资产工程占总资产的比率'], title='固定资产工程占总资产的比率', ylabel='', kind='bar', y_format='p')
一般选择(固定资产+在建工程)与总资产比率小于 20% 的轻资产型公司。对于行业第一名并且同时具有 3 个或以上核心竞争力的公司可以放宽到 50%,大于 50% 的一律淘汰掉。
7、看投资类资产,判断公司的专注程度
君子务本,本立而道生。对于一家公司也一样,只有专注于主业,才能卓越。看投资类资产占总资产的比例可以帮助我们了解一家公司的主业专注度。
投资类资产计算公式:投资类资产合计 = 其他权益工具投资 + 债权投资 + 其他债权投资 + 其他非流动金融资产 + 可供出售金融资产 + 持有至到期投资 + 长期股权投资 + 投资性房地产 + 以公允价值计量且其变动计入当期损益的金融资产。
t7 = pd.DataFrame(index=debt_df.index, columns=[
'以公允价值计量且其变动计入当期损益的金融资产', '债权投资', '可供出售金融资产(元)',
'其他权益工具投资(元)', '持有至到期投资', '其他非流动金融资产(元)',
'长期股权投资(元)','投资性房地产(元)', '投资类资产合计',
'资产合计(元)', '投资类资产占总资产的比率'])
t7[['可供出售金融资产(元)','其他权益工具投资(元)','其他非流动金融资产(元)',
'长期股权投资(元)', '投资性房地产(元)', '资产合计(元)']] = \
debt_df[['可供出售金融资产(元)','其他权益工具投资(元)','其他非流动金融资产(元)',
'长期股权投资(元)', '投资性房地产(元)', '资产合计(元)']]
t7['以公允价值计量且其变动计入当期损益的金融资产'] = np.zeros(6)
t7['债权投资'] = np.zeros(6)
t7['持有至到期投资'] = np.zeros(6)
t7['投资类资产合计'] = t7.T[:8].sum()
t7['投资类资产占总资产的比率'] = t7['投资类资产合计'] / t7['资产合计(元)']
pd.concat([t7.T[:10].apply(format_thousandth),t7.T[10:].apply(format_percentage)])
近 6 年的投资类资产情况:
以柱形图可视化展示:
plot_show(t7['投资类资产占总资产的比率'], title='投资类资产占总资产的比率', ylabel='', kind='bar', y_format='p')
投资类资产占总资产的比值小于 10%,专注于主业,属于优秀的公司;比值大于 10%,不够专注于主业。一家非常专注于主业的公司,在未来持续保持竞争优势的概率较大。
8、看存货,了解公司未来业绩爆雷的风险
注:暴雷的风险主要是两个方面:经营和投资,其中经营看存货,投资看商誉。
对于应收账款占总资产的比率大于 5% 并且存货占总资产的比率大于 15% 的公司存在未来爆雷风险。应收账款占比比较大,说明货比较难卖,必须靠延长账期来把货卖出去,同时大量存货积压,这时候存货暴雷的风险往往比较大。
t8 = pd.DataFrame(index=debt_df.index, columns=['存货(元)', '资产合计(元)', '存货占总资产的比率'])
t8[['存货(元)', '资产合计(元)']] = debt_df[['存货(元)', '资产合计(元)']]
t8['存货占总资产的比率'] = debt_df['存货(元)'] / debt_df[ '资产合计(元)']
pd.concat([t8.T[:2].apply(format_thousandth), t8.T[2:].apply(format_percentage)])
近 6 年某公司存货占总资产的比例:
柱形图可视化展示存货、应收账款情况:
tmp_df = pd.merge(t8['存货占总资产的比率'], t5['(应收账款+合同资产)占总资产的比率'],right_index=True,left_index=True)
plot_show(tmp_df, title='存货和应收账款情况', kind='bar', ylabel='', y_format='p')
注:应付预收-应收预付 > 0 且应收账款/资产总计 < 1%,存货基本没有爆雷的风险;应收账款/资产总计 > 5% 且存货/资产总计 > 15%,爆雷的风险比较大。
9、看商誉,了解公司未来业绩爆雷的风险
注:溢价收购其他公司,多花的那部分钱就是商誉。商誉越大,当冤大头的可能就越大。
t9 = pd.DataFrame(index=debt_df.index, columns=['商誉(元)', '资产合计(元)', '商誉占总资产的比率'])
t9[['商誉(元)', '资产合计(元)']] = debt_df[['商誉(元)', '资产合计(元)']]
t9['商誉占总资产的比率'] = debt_df['商誉(元)'] / debt_df[ '资产合计(元)']
pd.concat([t9.T[:2].apply(format_thousandth), t9.T[2:].apply(format_percentage)])
某公司近 10 年的商誉情况:
使用柱形图可视化展示商誉:
plot_show(t9['商誉占总资产的比率'], title='商誉占总资产的比率', kind='bar', ylabel='', y_format='p')
大于 10%,商誉有爆雷的风险;小于 10%,商誉没有爆雷的风险,具体可以根据收购的资产质量权衡。
总结
以上就是基于 Pandas 分析的资产负债表了,通过资产负债表我们可以了解到一家公司的实力和成长性(资产规模和资产增长率)、债务风险(资产负债率、准货币资金与有息负债的差额)、公司竞争力(应付预收减应收预付)、产品竞争力(应收账款占总资产的比例)、维持竞争力的成本(固定资产占总资产的比例)、是否专注于主业(投资类资产占总资产的比例)以及暴雷的风险(存货和商誉)。