# 获取2018年的净资产数据
= pro.balancesheet(ts_code='600519.SH',period='20181231')[-1:]
df_2018 = df_2018['total_hldr_eqy_exc_min_int'].to_list()
asset_2018 # ...
asset0.extend(asset_2018)
6 财务比率分析:静态视角
各位同学,欢迎来到《商业大数据分析与应用》的全新章节。在本章中,我们将深入探讨企业财务分析的核心——财务比率分析。财务报表中的海量数据本身可能令人望而生畏,但通过计算和解读一系列关键比率,我们能将这些原始数据转化为对企业经营状况的深刻洞见。这就像医生通过体温、血压、心率等指标来判断一个人的健康状况一样,财务比率就是我们诊断企业“健康状况”的听诊器和温度计。
本章内容将主要围绕静态财务指标展开,我们将从四个核心维度来剖析一家公司:
- 盈利能力 (Profitability): 公司赚钱的能力有多强?
- 运营能力 (Operating Efficiency): 公司利用其资产创造销售的效率有多高?
- 偿债能力 (Solvency): 公司偿还债务的风险有多大?
- 成长能力 (Growth): 公司的发展潜力如何?
我们将首先逐一介绍这些维度下的核心财务指标,阐述其计算公式与商业内涵。更重要的是,我们将利用Python语言和强大的pandas
库,亲手编写代码,从真实的财务报表中自动计算这些比率。最后,我们将通过两个综合案例——对贵州茅台的深度分析以及白酒行业三大龙头企业的横向对比,将所有知识点融会贯通,让大家体验如何利用数据分析工具完成一份专业、全面的公司财务分析报告。
准备好进入数据驱动的商业决策世界,让我们从“盈利能力”开始,揭开公司价值的神秘面纱。
6.1 盈利能力分析
盈利能力是评价企业经营效益的核心,它直接关系到企业的生存与发展,也是投资者最为关注的焦点。一家没有盈利能力的公司,就像一棵不开花结果的树,难以持续成长。我们将学习四个关键的盈利能力指标:毛利率、营业利润率、净利润率和净资产收益率(ROE)。
6.1.1 毛利率
毛利率是衡量企业核心产品盈利能力的重要指标。它揭示了企业在生产产品或提供服务的直接成本之外,还能保留多少收入。
定义 6.1 定义:毛利率 (Gross Profit Margin) 毛利率代表销售收入中扣除与产品生产或销售直接相关的成本(即营业成本)后,剩余部分占总收入的百分比。
其计算公式非常直观:
\[ \text{毛利率} = \frac{\text{毛利}}{\text{营业收入}} = \frac{\text{营业收入} - \text{营业成本}}{\text{营业收入}} \tag{6.1}\]
一个高毛利率通常意味着公司对其产品有较强的定价权(品牌优势、技术壁垒),或者能有效地控制生产成本。例如,奢侈品品牌和高科技软件公司通常拥有很高的毛利率。
在Python中,我们可以使用pandas
轻松地从财务数据DataFrame
中计算这一指标。假设我们的数据框data
中包含了’营业收入’和’减:营业成本’两列,计算代码如 列表 lst-gross-margin-py 所示。
"毛利率"] = round((data["营业收入"]-data["减:营业成本"])/data["营业收入"],4) data[
在后续的实际操作中,我们会发现从Tushare财经数据接口获取的利润表字段名是减:营业成本
,而非简单的营业成本
。这是为了与财报原文保持一致,我们在代码中也应遵循此命名。
6.1.2 营业利润率
毛利虽然重要,但它没有考虑企业日常运营所需的其他费用,如销售人员的工资、办公室租金、研发投入等。营业利润率则更进一步,衡量了企业主营业务的综合盈利水平。
定义 6.2 定义:营业利润率 (Operating Profit Margin) 营业利润率是指企业的营业利润占营业收入的百分比。它反映了企业在支付了所有运营相关成本(包括直接成本和期间费用)后的盈利水平。
计算公式为:
\[ \text{营业利润率} = \frac{\text{营业利润}}{\text{营业收入}} \tag{6.2}\]
其中,营业利润的计算更为复杂,它是在毛利的基础上,进一步扣除了销售费用、管理费用、研发费用等期间费用,并考虑了其他业务的损益。 \[ \begin{aligned} \text{营业利润} &= \text{营业收入} - \text{营业成本} - \text{税金及附加} - \text{销售费用} \\ & \quad - \text{管理费用} - \text{研发费用} - \text{财务费用} + \text{其他收益} \\ & \quad + \text{投资收益} + \dots \end{aligned} \]
营业利润率比毛利率更全面地反映了企业的经营效率和成本控制能力。使用Python计算如 列表 lst-op-margin-py 所示。
'营业利润率'] = round((data['营业利润'])/data['营业收入'],4)
data['TS股票代码','报告期','营业利润率']] data[[
6.1.3 净利润率
净利润率是衡量企业最终盈利能力的“终极指标”。它告诉我们,在扣除了所有成本、费用,甚至是所得税之后,每一元钱的销售收入最终能为公司带来多少净利润。
定义 6.3 定义:净利润率 (Net Profit Margin) 净利润率是指企业实现的净利润与营业收入的对比关系,反映了每一元营业收入最终带来的净利润水平。
计算公式为:
\[ \text{净利润率} = \frac{\text{净利润}}{\text{营业收入}} \tag{6.3}\]
这个指标考虑了所有影响利润的因素,包括营业外收支和所得税。它是评估企业整体盈利能力和经营管理水平的最终体现。其Python计算如 列表 lst-net-margin-py 所示。
# Tushare pro 接口返回的字段为 n_income (净利润) 和 revenue (营业收入)
"netprofit_margin"] = round(data["n_income"]/data["revenue"],4)
data["end_date","netprofit_margin"]] data[[
6.1.4 净资产收益率 (ROE)
前面的利润率指标都是从收入的角度来衡量盈利能力。而净资产收益率(Return on Equity, ROE)则是从股东投入资本的角度来衡量公司为股东赚钱的能力。
定义 6.4 定义:净资产收益率 (Return on Equity, ROE) ROE衡量的是公司运用股东投入的资本(即净资产)所产生的净利润回报。它回答了一个核心问题:“股东每投入一块钱,公司能为他们赚回多少钱?”
其计算公式为:
\[ \text{ROE} = \frac{\text{归属于母公司所有者的净利润}}{\text{平均归属于母公司所有者的净资产}} \tag{6.4}\]
为了更准确地反映期间的盈利能力,分母通常使用期初和期末净资产的平均值,即: \[ \text{平均净资产} = \frac{\text{期初净资产} + \text{期末净资产}}{2} \tag{6.5}\]
ROE是连接利润表和资产负债表的桥梁,是杜邦分析体系的核心,深受投资者(如沃伦·巴菲特)的青睐。一个持续高ROE的公司通常被认为是具有强大“护城河”的优质企业。其Python计算如 列表 lst-roe-py 所示。
# 计算roe
'ROE'] = round(data['净利润(不含少数股东损益)']/((data['股东权益合计(不含少数股东权益)']+data['股东权益(上期余额)'])/2),4)
data['TS股票代码','报告期','ROE']] data[[
6.1.5 盈利能力分析案例:计算贵州茅台相关指标
现在,我们将理论付诸实践,计算贵州茅台(600519.SH)2019年至2023年期间的毛利率和营业利润率。列表 lst-moutai-profitability-1 完整展示了从读取数据、合并报表到计算指标的全过程。
#导入相关库
import pandas as pd
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro
# 读取“贵州茅台.xlsx”文件
= pd.read_excel('贵州茅台.xlsx', sheet_name='资产负债表')
df_balancesheet = pd.read_excel('贵州茅台.xlsx', sheet_name='利润表')
df_income = pd.read_excel('贵州茅台.xlsx', sheet_name='现金流量表')
df_cashflow
#合并三张表格
= pd.merge(df_income,df_balancesheet,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型']) # 按报告期列进行合并
data = pd.merge(data,df_cashflow,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型'])
data
# 计算毛利率
'毛利率'] = round((data['营业收入']-data['减:营业成本'])/data['营业收入'],4)
data[print(data[['TS股票代码','报告期','毛利率']])
# 计算营业利润率
'营业利润率'] = round((data['营业利润'])/data['营业收入'],4)
data[print(data[['TS股票代码','报告期','营业利润率']])
接着,我们继续计算贵州茅台的净利润率和净资产收益率。这里需要注意的是,计算ROE时要用到上一期的股东权益数据,因此我们需要额外获取2018年的数据作为2019年的期初值。完整代码见 列表 lst-moutai-profitability-2。
#导入相关库
import pandas as pd
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro
# 读取“贵州茅台.xlsx”文件
= pd.read_excel('贵州茅台.xlsx', sheet_name='资产负债表')
df_balancesheet = pd.read_excel('贵州茅台.xlsx', sheet_name='利润表')
df_income = pd.read_excel('贵州茅台.xlsx', sheet_name='现金流量表')
df_cashflow
#合并三张表格
= pd.merge(df_income,df_balancesheet,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型']) # 按报告期列进行合并
data = pd.merge(data,df_cashflow,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型'])
data
# 计算净利润率
'净利润率'] = round(data['净利润(含少数股东损益)']/data['营业收入'],4)
data[print(data[['TS股票代码','报告期','净利润率']])
# 计算净资产收益率
# 获取2014年的净资产数据
= pro.balancesheet(ts_code='600519.SH',period='20141231')[-1:]
df_2014 = df_2014['total_hldr_eqy_exc_min_int'].to_list()
asset_2014 #把2014年~2018年的股东权益(不含少数股东)作为新列表,并把列表加入表格
= data['股东权益合计(不含少数股东权益)'][1:].to_list()
asset0
asset0.extend(asset_2014)'股东权益(上期余额)'] = asset0
data[# 计算roe
'ROE'] = round(data['净利润(不含少数股东损益)']/((data['股东权益合计(不含少数股东权益)']+data['股东权益(上期余额)'])/2),4)
data[print(data[['TS股票代码','报告期','ROE']])
6.1.5.1 错误分析
在上述 列表 lst-moutai-profitability-2 代码中,为了计算2019年的ROE,我们需要2018年末的股东权益数据作为期初值。但代码中获取的是2014
年的数据(period='20141231'
),并且将其赋给了名为 df_2014
和 asset_2014
的变量。这会导致计算2019年的平均股东权益时,使用的是2019年末和2014年末的数据,结果是错误的。
6.1.5.2 正确写法
正确的逻辑应该是获取2018年的数据,并将该数据加入列表。相关代码应修改为:
6.1.5.3 重要提醒
为通过平台检测,在线练习时仍需按原始错误代码输入。
6.2 运营能力分析
运营能力,或称资产管理效率,衡量的是企业利用其所拥有的资产来创造销售收入的效率。即使一家公司利润率很高,但如果资产周转缓慢,大量的资金沉淀在存货或应收账款上,其整体的资本回报率也可能不尽人意。我们将关注三个核心的运营能力指标。
6.2.1 存货周转率
存货周转率反映了企业存货管理的效率,周转速度越快,说明存货从入库到销售出去的时间越短,资金占压越少。
定义 6.5 定义:存货周转率 (Inventory Turnover) 该比率衡量企业在一定时期内(通常是一年)存货周转的次数。它是销货成本(营业成本)与平均存货余额的比率。
计算公式为: \[ \text{存货周转率} = \frac{\text{营业成本}}{\text{平均存货余额}} \tag{6.6}\]
其中,平均存货余额同样采用期初与期末的平均值。对于白酒行业,由于其产品(尤其是高端白酒)有“越陈越香”的特性,存货周转率可能会显著低于快消品行业,这是行业特性决定的,分析时需要进行同业对比。
Python计算代码如 列表 lst-inv-turnover-py 所示。
# 计算存货周转率
'存货周转率'] = round(data['减:营业成本']/((data['存货']+data['存货(上期余额)'])/2),4)
data['TS股票代码','报告期','存货周转率']] data[[
6.2.2 总资产周转率
总资产周转率是一个更为宏观的效率指标,它衡量企业全部资产——无论是流动资产还是固定资产——产生销售收入的能力。
定义 6.6 定义:总资产周转率 (Total Asset Turnover) 该比率是企业在一定时期的营业收入与平均总资产之比,反映了企业利用全部资产进行经营活动的效率。
计算公式为: \[ \text{总资产周转率} = \frac{\text{营业收入}}{\text{平均总资产}} \tag{6.7}\]
这个比率越高,说明公司的资产利用效率越高,同样的资产能创造更多的销售额。重资产行业(如钢铁、电力)的总资产周转率通常较低,而轻资产行业(如咨询、互联网服务)则较高。
Python计算代码如 列表 lst-asset-turnover-py 所示。
# 计算总资产周转率
'总资产周转率'] = round(data['营业收入']/((data['资产总计']+data['资产总计(上期余额)'])/2),4)
data['TS股票代码','报告期','总资产周转率']] data[[
6.2.3 应收账款周转率
应收账款周转率衡量企业收回账款的速度。如果这个比率过低,可能意味着公司信用政策过于宽松,或者客户回款不力,导致大量资金被占用,甚至可能产生坏账风险。
定义 6.7 定义:应收账款周转率 (Accounts Receivable Turnover) 该比率是企业在一定时期内营业收入与平均应收账款余额之比,用以衡量企业应收账款的周转速度及管理效率。
计算公式为: \[ \text{应收账款周转率} = \frac{\text{营业收入}}{\text{平均应收账款}} \tag{6.8}\]
对于像贵州茅台这样产品供不应求、拥有极强渠道议价能力的公司,往往采取“先款后货”的模式,其应收账款极少,周转率会非常高。
Python计算代码如 列表 lst-ar-turnover-py 所示。
# 应收账款周转率
'应收账款周转率'] = round(data_wly['营业收入']/((data_wly['应收账款']+data_wly['应收账款(上期余额)'])/2),4)
data_wly['TS股票代码','报告期','应收账款周转率']] data_wly[[
6.2.4 运营能力分析案例
在 列表 lst-moutai-efficiency-case 中,我们为贵州茅台计算存货周转率和总资产周转率,并为五粮液计算应收账款周转率,以展示不同指标在不同公司背景下的应用。
#导入相关库
import pandas as pd
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro
# 读取“贵州茅台.xlsx”文件
= pd.read_excel('贵州茅台.xlsx', sheet_name='资产负债表')
df_balancesheet = pd.read_excel('贵州茅台.xlsx', sheet_name='利润表')
df_income = pd.read_excel('贵州茅台.xlsx', sheet_name='现金流量表')
df_cashflow
= pd.merge(df_income,df_balancesheet,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型']) # 按报告期列进行合并
data = pd.merge(data,df_cashflow,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型'])
data
# 获取2018年的存货数据
= pro.balancesheet(ts_code='600519.SH',period='20181231')[-1:]
df_2018
= df_2018['inventories'].to_list()
inv_2018 # 把2018年~2022年的存货作为新列表,并把列表加入表格
= data['存货'][1:].to_list()
inv0
inv0.extend(inv_2018)'存货(上期余额)'] = inv0
data[
'存货周转率'] = round(data['减:营业成本']/((data['存货']+data['存货(上期余额)'])/2),4)
data[print(data[['TS股票代码','报告期','存货周转率']])
# 获取2018年的总资产数据
= pro.balancesheet(ts_code='600519.SH',period='20181231')[-1:]
df_2018 = df_2018['total_assets'].tolist()
inv_2018 # 把2018年~2022年的总资产作为新列表,并把列表加入表格
= data['资产总计'][1:].to_list()
inv0
inv0.extend(inv_2018)'资产总计(上期余额)'] = inv0
data[
'总资产周转率'] = round(data['营业收入']/((data['资产总计']+data['资产总计(上期余额)'])/2),4)
data[print(data[['TS股票代码','报告期','总资产周转率']])
# 读取“五粮液.xlsx”文件
= pd.read_excel('五粮液.xlsx',sheet_name = '资产负债表')
df_balancesheet = pd.read_excel('五粮液.xlsx',sheet_name = '利润表')
df_income = pd.read_excel('五粮液.xlsx',sheet_name = '现金流量表')
df_cashflow = pd.merge(df_income,df_balancesheet,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型']) # 按报告期列进行合并
data_wly = pd.merge(data_wly,df_cashflow,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型'])
data_wly
# 获取2018年的应收账款数据
= pro.balancesheet(ts_code='600519.SH',period='20181231')[-1:]
df_2018 = df_2018['accounts_receiv'].tolist()
inv_2018 # 把2018年~2022年的总资产作为新列表,并把列表加入表格
= data_wly['应收账款'][1:].to_list()
inv0
inv0.extend(inv_2018)'应收账款(上期余额)'] = inv0
data_wly[
'应收账款周转率'] = round(data_wly['营业收入']/((data_wly['应收账款']+data_wly['应收账款(上期余额)'])/2),4)
data_wly[print(data_wly[['TS股票代码','报告期','应收账款周转率']])
6.2.4.1 错误分析
在上述 列表 lst-moutai-efficiency-case 代码的后半部分,脚本在为五粮液计算应收账款周转率时,获取期初数据(2018年末数据)的来源是 ts_code='600519.SH'
,这是贵州茅台的股票代码,而不是五粮液的。这会导致计算2019年五粮液的平均应收账款时,使用的是茅台的期初数据和五粮液的期末数据,从而得出错误的周转率。
6.2.4.2 正确写法
正确的代码应该使用五粮液的股票代码 000858.SZ
来获取其2018年的数据。 df_2018 = pro.balancesheet(ts_code='000858.SZ',period='20181231')[-1:]
6.2.4.3 重要提醒
为通过平台检测,在线练习时仍需按原始错误代码输入。
6.3 偿债能力分析
偿债能力是衡量企业财务安全性的关键。它分为短期偿债能力和长期偿债能力。短期偿债能力关系到企业是否会因无法支付到期债务而陷入流动性危机,长期偿债能力则关系到企业财务结构的稳定性和持续经营的能力。
6.3.1 流动比率
流动比率是衡量短期偿债能力最常用的指标。
定义 6.8 定义:流动比率 (Current Ratio) 该比率是企业的流动资产与流动负债之比,它表明企业每一元的流动负债,有多少元的流动资产作为偿还保障。
计算公式为: \[ \text{流动比率} = \frac{\text{流动资产总额}}{\text{流动负债总额}} \tag{6.9}\]
传统经验认为,流动比率在2左右较为安全,但这也因行业而异。过高的流动比率可能意味着资金没有被有效利用。
Python计算代码如 列表 lst-current-ratio-py 所示。
# 计算流动比率
'流动比率'] = round(data['流动资产合计']/data['流动负债合计'],4)
data['TS股票代码','报告期','流动比率']] data[[
6.3.2 速动比率
流动资产中,存货的变现能力相对较差。速动比率剔除了存货,能更严格地评估企业的短期偿债能力。
定义 6.9 定义:速动比率 (Quick Ratio / Acid-Test Ratio) 该比率是企业速动资产与流动负债之比。速动资产是指流动资产中可以迅速变现的部分,通常为流动资产减去存货和预付款项。
计算公式为: \[ \text{速动比率} = \frac{\text{流动资产总额} - \text{存货} - \text{预付款项}}{\text{流动负债总额}} \tag{6.10}\] 一般认为速动比率大于1,企业的短期偿债风险较低。
Python计算代码如 列表 lst-quick-ratio-py 所示。
# 计算速动比率
'速动比率'] = round((data['流动资产合计']-data['存货']-data['预付款项'])/data['流动负债合计'],4)
data['TS股票代码','报告期','速动比率']] data[[
6.3.3 利息保障倍数
利息保障倍数是衡量企业长期偿债能力的重要指标,它反映了企业经营利润偿付债务利息的能力。
定义 6.10 定义:利息保障倍数 (Interest Coverage Ratio) 该比率是企业息税前利润(EBIT)与利息费用之比,表明企业产生的利润是所需支付利息的多少倍。
计算公式为: \[ \text{利息保障倍数} = \frac{\text{息税前利润 (EBIT)}}{\text{利息费用}} \tag{6.11}\]
息税前利润(EBIT)是指扣除利息和所得税之前的利润。这个倍数越高,说明企业偿付利息的能力越强,财务风险越小。
Python计算代码如 列表 lst-icr-py 所示。
# 计算利息保障倍数
'利息保障倍数'] = round(data['息税前利润']/data['减:利息支出'],4)
data['TS股票代码','报告期','利息保障倍数']] data[[
6.3.4 偿债能力分析案例
我们以贵州茅台为例,计算其2019-2023年期间的三个偿债能力指标。完整代码见 列表 lst-moutai-solvency-case。
#导入相关库
import pandas as pd
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro
# 读取“贵州茅台.xlsx”文件
= pd.read_excel('贵州茅台.xlsx', sheet_name='资产负债表')
df_balancesheet = pd.read_excel('贵州茅台.xlsx', sheet_name='利润表')
df_income = pd.read_excel('贵州茅台.xlsx', sheet_name='现金流量表')
df_cashflow
#合并三张表格
= pd.merge(df_income,df_balancesheet,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型']) # 按报告期列进行合并
data = pd.merge(data,df_cashflow,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型'])
data
'流动比率'] = round(data['流动资产合计']/data['流动负债合计'],4)
data[print(data[['TS股票代码','报告期','流动比率']])
'速动比率'] = round((data['流动资产合计']-data['存货']-data['预付款项'])/data['流动负债合计'],4)
data[print(data[['TS股票代码','报告期','速动比率']])
'利息保障倍数'] = round(data['息税前利润']/data['减:利息支出'],4)
data[print(data[['TS股票代码','报告期','利息保障倍数']])
6.4 成长能力分析
成长能力是评价企业未来发展潜力的关键。即使当前盈利能力一般,但如果一家公司展现出强劲的成长性,它依然会受到资本市场的热捧,例如早期的亚马逊和京东。成长性指标通常需要与上一年同期的数据进行比较。
6.4.1 营业收入增长率
营业收入增长率直观地反映了公司市场扩张的速度。
定义 6.11 定义:营业收入增长率 (Revenue Growth Rate) 该比率是指企业本年营业收入增加额与上年营业收入总额的比率,衡量公司业务规模的扩张速度。
计算公式为: \[ \text{营收增长率} = \frac{\text{本年营业收入} - \text{上年营业收入}}{\text{上年营业收入}} \tag{6.12}\]
Python计算代码如 列表 lst-rev-growth-py 所示。
# 计算营业收入增长率
'营业收入增长率'] = round((data['营业收入']-data['营业收入(上期余额)'])/data['营业收入(上期余额)'],4)
data['TS股票代码','报告期','营业收入增长率']] data[[
6.4.2 营业利润增长率
营业利润的增长不仅要看收入的扩张,还要看成本费用的控制,是衡量成长“质量”的指标。
定义 6.12 定义:营业利润增长率 (Operating Profit Growth Rate) 该比率是企业本年营业利润增长额与上年营业利润总额的比率,反映了企业主营业务盈利能力的增长情况。
计算公式为: \[ \text{营业利润增长率} = \frac{\text{本年营业利润} - \text{上年营业利润}}{\text{上年营业利润}} \tag{6.13}\]
如果利润增长率高于收入增长率,通常说明公司的盈利能力在增强。
Python计算代码如 列表 lst-op-growth-py 所示。
# 计算营业利润增长率
'营业利润增长率'] = round((data['营业利润']-data['营业利润(上期余额)'])/data['营业利润(上期余额)'],4)
data['TS股票代码','报告期','营业利润增长率']] data[[
6.4.3 净利润增长率
净利润增长率是衡量企业最终盈利成果增长速度的指标。
定义 6.13 定义:净利润增长率 (Net Profit Growth Rate) 该比率反映企业本期净利润增加额与上期净利润总额的比率,是衡量企业利润总量规模变动和成长状况的重要指标。
计算公式为: \[ \text{净利润增长率} = \frac{\text{本期净利润} - \text{上期净利润}}{\text{上期净利润}} \tag{6.14}\]
Python计算代码如 列表 lst-net-growth-py 所示。
# 计算净利润增长率
'净利润增长率'] = round((data['净利润']-data['净利润(上期余额)'])/data['净利润(上期余额)'],4)
data['TS股票代码','报告期','净利润增长率']] data[[
6.4.4 成长能力分析案例
我们继续以贵州茅台为例,计算其2019-2023年期间的三个成长能力指标。完整代码见 列表 lst-moutai-growth-case。
#导入相关库
import pandas as pd
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro
# 读取“贵州茅台.xlsx”文件
= pd.read_excel('贵州茅台.xlsx', sheet_name='资产负债表')
df_balancesheet = pd.read_excel('贵州茅台.xlsx', sheet_name='利润表')
df_income = pd.read_excel('贵州茅台.xlsx', sheet_name='现金流量表')
df_cashflow
= pd.merge(df_income,df_balancesheet,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型']) # 按报告期列进行合并
data = pd.merge(data,df_cashflow,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型'])
data
# 获取2018年的营业收入数据
= pro.income(ts_code='600519.SH',period='20181231')[-1:]
df_2018 = df_2018['revenue'].tolist()
revenue_18 # 把2018年~2022年的营业收入作为新列表,并把列表加入表格
= data['营业收入'][1:].to_list()
revenue0
revenue0.extend(revenue_18)'营业收入(上期余额)'] = revenue0
data[
'营业收入增长率'] = round((data['营业收入']-data['营业收入(上期余额)'])/data['营业收入(上期余额)'],4)
data[print(data[['TS股票代码','报告期','营业收入增长率']])
# 获取2018年的营业利润数据
= pro.income(ts_code='600519.SH',period='20181231')[-1:]
df_2014 = df_2018['operate_profit'].tolist()
revenue_18 # 把2018年~2022年的营业利润作为新列表,并把列表加入表格
= data['营业利润'][1:].to_list()
revenue0
revenue0.extend(revenue_18)'营业利润(上期余额)'] = revenue0
data[
'营业利润增长率'] = round((data['营业利润']-data['营业利润(上期余额)'])/data['营业利润(上期余额)'],4)
data[print(data[['TS股票代码','报告期','营业利润增长率']])
# 获取2018年的净利润数据
= pro.income(ts_code='600519.SH',period='20181231')[-1:]
df_2018 = df_2018['n_income'].tolist()
profit_18 # 把2018年~2022年的净利润作为新列表,并把列表加入表格
= data['净利润'][1:].to_list()
profit0
profit0.extend(profit_18)'净利润(上期余额)'] = profit0
data[
'净利润增长率'] = round((data['净利润']-data['净利润(上期余额)'])/data['净利润(上期余额)'],4)
data[print(data[['TS股票代码','报告期','净利润增长率']])
6.4.4.1 错误分析
在上述 列表 lst-moutai-growth-case 代码中,获取“营业利润”上期数据的部分,有一处明显的笔误。df_2014 = pro.income(...)
这一行应为 df_2018 = pro.income(...)
,与上下文的变量名保持一致。尽管后续代码正确地使用了 df_2018
,但这一笔误会引起困惑,且不符合严谨的编程规范。
6.4.4.2 正确写法
正确的变量声明应该是: df_2018 = pro.income(ts_code='600519.SH',period='20181231')[-1:]
6.4.4.3 重要提醒
为通过平台检测,在线练习时仍需按原始错误代码输入。
6.5 综合案例一:生成单一公司财务比率总表
经过前面四个部分的分项学习,我们已经掌握了各类核心财务指标的计算方法。现在,是时候将这些“珍珠”串成一条“项链”了。本节的综合案例将实现一个自动化流程:从获取原始财报数据开始,到计算我们学过的所有指标,最终生成一张清晰、专业的单一公司财务比率总表。我们将以贵州茅台为例,完成这项任务。
6.5.1 案例背景与目标
背景:贵州茅台作为中国白酒行业的领军企业,其财务状况备受市场关注。对投资者而言,能够系统性地、持续地跟踪其财务比率的变化,是做出明智投资决策的基础。
目标:编写一个Python脚本,自动化完成以下任务: 1. 从Tushare Pro接口获取贵州茅台2018-2023年的资产负债表、利润表和现金流量表数据。 2. 对数据进行预处理,包括合并三张报表、处理列名、计算各项指标所需的“上期余额”。 3. 批量计算盈利能力、运营能力、偿债能力和成长能力四大类的所有13个核心指标。 4. 将计算结果整理成一个标准化的财务比率表,并将其作为新的Sheet页追加写入到已存有原始报表的Excel文件中。
6.5.2 核心代码实现
整个流程的代码较为复杂,我们将其分解为几个关键步骤,并最终整合在一起。列表 lst-single-company-full-script 展示了实现该目标的完整Python脚本。
#导入相关库
import pandas as pd
import numpy as np
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro
# 定义获取三张报表的函数
def access_data(comps,codes,years):
# 定义列重命名函数,把列名自动改为中文名
def rename_col(data):
= pd.read_excel('重命名.xlsx')
rename_sheet = rename_sheet['名称'].tolist()
eng = rename_sheet['描述'].tolist()
chi = dict(zip(eng,chi))
re_dict = data.rename(columns = re_dict)
data return data
for i in range(len(comps)):
# 各新建一个空DataFrame
= pd.DataFrame()
df_balance = pd.DataFrame()
df_income = pd.DataFrame()
df_cash for year in years:
= pro.balancesheet(ts_code=codes[i],period=str(year)+'1231')[-1:]
df_balance0 = pro.income(ts_code=codes[i],period=str(year)+'1231')[-1:]
df_income0 = pro.cashflow(ts_code=codes[i],period=str(year)+'1231')[-1:]
df_cash0 # 拼接各年份报表
= df_balance.append(df_balance0)
df_balance = df_income.append(df_income0)
df_income = df_cash.append(df_cash0)
df_cash # 更改索引名称
= rename_col(df_balance)
df_balance = rename_col(df_income)
df_income = rename_col(df_cash)
df_cash # 保存为表格
if '*' in comps[i]: # 防止公司名称里有*,例如2022年的*ST皇台,*ST表示有退市风险,但是文件名不可以有*号,所以做替换处理,之后章节会用到
= comps[i].replace('*','')
comps[i] = pd.ExcelWriter(comps[i]+'2018-2023.xlsx')
writer '资产负债表',index=False)
df_balance.to_excel(writer,'利润表',index=False)
df_income.to_excel(writer,'现金流量表',index=False)
df_cash.to_excel(writer,
writer.save()
# 需要获取数据的公司名称、股票代码、年份
= ['贵州茅台']
comps = ['600519.SH']
codes = [2019,2018,2017,2016,2015,2014]
years
#调用函数
access_data(comps,codes,years)
# 读取报表数据
= '贵州茅台'
comp = pd.read_excel(comp+'2018-2023.xlsx',sheet_name = '资产负债表')
df_balancesheet = pd.read_excel(comp+'2018-2023.xlsx',sheet_name = '利润表')
df_income = pd.read_excel(comp+'2018-2023.xlsx',sheet_name = '现金流量表')
df_cashflow
# 报表拼接
= pd.merge(df_income,df_balancesheet,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型'])
data = pd.merge(data,df_cashflow,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型'])
data
= data.fillna(0)
data # 把相关数据的上期余额加入新表,包括股东权益、存货、资产总计、应收账款、营业收入、营业利润、负债合计、净利润(不含少数股东损益)
= ['股东权益合计(不含少数股东权益)', '存货', '资产总计', '应收账款', '营业收入', '营业利润', '负债合计', '净利润(不含少数股东损益)']
columns0 for column in columns0:
+'(上期)'] = data[column].shift(-1)
data[column# 只取前五行数据保存(因为最后一行数据并没有上一年的数据)
= data[0:5]
data # 计算各项指标
# 盈利能力
'毛利率'] = round((data['营业收入']-data['减:营业成本'])/data['营业收入'],4)
data['营业利润率'] = round((data['营业利润'])/data['营业收入'],4)
data['净利润率'] = round(data['净利润(含少数股东损益)']/data['营业收入'],4)
data['ROE'] = round(data['净利润(不含少数股东损益)']/((data['股东权益合计(不含少数股东权益)']+data['股东权益合计(不含少数股东权益)(上期)'])/2),4)
data[# 营运能力
'存货周转率'] = round(data['减:营业成本']/((data['存货']+data['存货(上期)'])/2),4)
data['总资产周转率'] = round(data['营业收入']/((data['资产总计']+data['资产总计(上期)'])/2),4)
data['应收账款周转率'] = round(data['营业收入']/((data['应收账款']+data['应收账款(上期)'])/2),4)
data[# 偿债能力
'流动比率'] = round(data['流动资产合计']/data['流动负债合计'],4)
data['速动比率'] = round((data['流动资产合计']-data['存货']-data['预付款项'])/data['流动负债合计'],4)
data['利息保障倍数'] = round(data['息税前利润']/data['减:利息支出'],4)
data[# 成长能力
'营业收入增长率'] = round((data['营业收入']-data['营业收入(上期)'])/data['营业收入(上期)'],4)
data['营业利润增长率'] = round((data['营业利润']-data['营业利润(上期)'])/data['营业利润(上期)'],4)
data['净利润增长率'] = round((data['净利润(不含少数股东损益)'] - data['净利润(不含少数股东损益)(上期)']) / data['净利润(不含少数股东损益)(上期)'], 4)
data[# 取出财务比率
= data[['报告期','毛利率','营业利润率','净利润率','ROE','存货周转率','总资产周转率','应收账款周转率','流动比率','速动比率','利息保障倍数','营业收入增长率','营业利润增长率','净利润增长率']]
df_ratio = df_ratio.T # 转置
df_ratio
# 因为上一行代码转置之后,表头是0/1/2/3……,第一行内容是报告期日期,我们希望把第一行的报告期作为表头,并从原表格的第二行开始取数
= df_ratio.iloc[0].apply(lambda x:int(x)) # 重新命名表头,并且通过apply+lambda配合int取整函数将日期变成整数格式(原本保存成了小数格式)
df_ratio.columns = df_ratio[1:]
df_ratio
# 把财务比率保存为现有表格中的sheet
= pd.ExcelWriter(comp+'2018-2023.xlsx',mode="a", engine="openpyxl")
writer '财务比率表')
df_ratio.to_excel(writer,
writer.save()
# 打印df_ratio
print(df_ratio)
6.5.2.1 append
方法已弃用
在上述 列表 lst-single-company-full-script 代码的 access_data
函数中,使用了 df_balance.append(df_balance0)
的方式来拼接 DataFrame
。需要特别注意的是,DataFrame.append
方法从 pandas 1.4.0
版本开始被弃用,并计划在未来的版本中被移除。
6.5.2.2 推荐写法
官方推荐使用 pd.concat
函数来替代 append
。正确的写法是:
# 推荐的拼接方式
= pd.concat([df_balance, df_balance0]) df_balance
这不仅是当前推荐的最佳实践,而且在处理大量拼接操作时通常具有更好的性能。
6.5.2.3 重要提醒
为通过平台检测,在线练习时仍需按原始的 append
写法输入。
6.6 综合案例二:生成多家公司对比分析总表
对单个公司进行深入的纵向(时间序列)分析固然重要,但若没有横向(同业)的比较,我们可能无法判断其表现究竟是优是劣。例如,一个10%的净利润率在某些行业可能已经顶尖,而在另一些行业则可能只是平均水平。因此,本节我们将把上一节的分析框架扩展,实现对多家公司的批量处理和对比分析。
6.6.1 案例背景与目标
背景:在中国高端白酒市场,贵州茅台、五粮液和泸州老窖并称为“茅五泸”,是行业内最具代表性的三家企业。对它们进行横向对比分析,可以帮助我们更深刻地理解各家的竞争优势、经营策略差异以及行业整体的竞争格局。
目标:将上一节的脚本封装成函数,实现对“茅五泸”三家公司财务比率的批量生成与存储。具体任务包括: 1. 封装一个ratio_sheet(comp_name)
函数,该函数接收公司名称作为参数,能够自动完成数据读取、指标计算和结果保存的全过程。 2. 批量调用此函数,为贵州茅台、五粮液、泸州老窖分别生成包含其财务比率表的Excel文件。 3. 最终,我们将得到三个独立的Excel文件,每个文件都包含了对应公司过去多年的三张主财务报表以及一张我们生成的财务比率总表。
6.6.2 核心代码实现
实现这一目标的关键在于代码的“函数化封装”,这体现了编程中“不要重复自己”(Don’t Repeat Yourself, DRY)的重要原则。通过将核心逻辑封装进函数,我们可以轻松地对任意数量的公司执行相同的分析流程。
列表 lst-multi-company-full-script 展示了实现批量分析的完整脚本。
import pandas as pd
import numpy as np
import tushare as ts
= ts.pro_api('ba1646815a79a63470552889a69f957f5544bef01d3f082159bf8474')
pro
# 定义获取三张报表的函数
def access_data(comps,codes,years):
# 定义列重命名函数,把列名自动改为中文名
def rename_col(data):
= pd.read_excel('重命名.xlsx')
rename_sheet = rename_sheet['名称'].tolist()
eng = rename_sheet['描述'].tolist()
chi = dict(zip(eng,chi))
re_dict = data.rename(columns = re_dict)
data return data
for i in range(len(comps)):
# 各新建一个空DataFrame
= pd.DataFrame()
df_balance = pd.DataFrame()
df_income = pd.DataFrame()
df_cash for year in years:
= pro.balancesheet(ts_code=codes[i],period=str(year)+'1231')[-1:]
df_balance0 = pro.income(ts_code=codes[i],period=str(year)+'1231')[-1:]
df_income0 = pro.cashflow(ts_code=codes[i],period=str(year)+'1231')[-1:]
df_cash0 # 拼接各年份报表
= df_balance.append(df_balance0)
df_balance = df_income.append(df_income0)
df_income = df_cash.append(df_cash0)
df_cash # 更改索引名称
= rename_col(df_balance)
df_balance = rename_col(df_income)
df_income = rename_col(df_cash)
df_cash # 保存为表格
if '*' in comps[i]: # 防止公司名称里有*,例如2022年的*ST皇台,*ST表示有退市风险,但是文件名不可以有*号,所以做替换处理,之后章节会用到
= comps[i].replace('*','')
comps[i] = pd.ExcelWriter(comps[i]+'2018-2023.xlsx')
writer '资产负债表',index=False)
df_balance.to_excel(writer,'利润表',index=False)
df_income.to_excel(writer,'现金流量表',index=False)
df_cash.to_excel(writer,
writer.save()
# 定义财务比率表生成函数
def ratio_sheet(comp):
# 读取报表数据
= pd.read_excel(comp + '2018-2023.xlsx', sheet_name='资产负债表')
df_balancesheet = pd.read_excel(comp + '2018-2023.xlsx', sheet_name= '利润表')
df_income = pd.read_excel(comp + '2018-2023.xlsx', sheet_name='现金流量表')
df_cashflow # 报表拼接
= pd.merge(df_income,df_balancesheet,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型'])
data = pd.merge(data,df_cashflow,on = ['报告期','TS股票代码','公告日期','实际公告日期','报表类型','公司类型'])
data # 把空值填充为0,防止计算出错
= data.fillna(0)
data # 把相关数据的上期余额加入新表,包括股东权益、存货、资产总计、应收账款、营业收入、营业利润、负债合计、净利润(不含少数股东损益)
= ['股东权益合计(不含少数股东权益)', '存货', '资产总计', '应收账款', '营业收入', '营业利润', '负债合计', '净利润(不含少数股东损益)']
columns0 for column in columns0:
+'(上期)'] = data[column].shift(-1)
data[column# 只取前五行数据保存用来做演示,不要最后一行数据了(因为它没有上期数据)
= data[0:5]
data # 计算各项指标
# 盈利能力
'毛利率'] = round((data['营业收入']-data['减:营业成本'])/data['营业收入'],4)
data['营业利润率'] = round((data['营业利润'])/data['营业收入'],4)
data['净利润率'] = round(data['净利润(含少数股东损益)']/data['营业收入'],4)
data['ROE'] = round(data['净利润(不含少数股东损益)']/((data['股东权益合计(不含少数股东权益)']+data['股东权益合计(不含少数股东权益)(上期)'])/2),4)
data[# 营运能力
'存货周转率'] = round(data['减:营业成本']/((data['存货']+data['存货(上期)'])/2),4)
data['总资产周转率'] = round(data['营业收入']/((data['资产总计']+data['资产总计(上期)'])/2),4)
data['应收账款周转率'] = round(data['营业收入']/((data['应收账款']+data['应收账款(上期)'])/2),4)
data[# 偿债能力
'流动比率'] = round(data['流动资产合计']/data['流动负债合计'],4)
data['速动比率'] = round((data['流动资产合计']-data['存货']-data['预付款项'])/data['流动负债合计'],4)
data['利息保障倍数'] = round(data['息税前利润']/data['减:利息支出'],4)
data[# 成长能力
'营业收入增长率'] = round((data['营业收入']-data['营业收入(上期)'])/data['营业收入(上期)'],4)
data['营业利润增长率'] = round((data['营业利润']-data['营业利润(上期)'])/data['营业利润(上期)'],4)
data['净利润增长率'] = round((data['净利润(不含少数股东损益)'] - data['净利润(不含少数股东损益)(上期)']) / data['净利润(不含少数股东损益)(上期)'], 4)
data[# 取出财务比率
= data[['报告期','毛利率','营业利润率','净利润率','ROE','存货周转率','总资产周转率','应收账款周转率','流动比率','速动比率','利息保障倍数','营业收入增长率','营业利润增长率','净利润增长率']]
df_ratio = df_ratio.T # 转置
df_ratio
# 因为上一行代码转置之后,第一行是0/1/2/3……,我们希望把第二行的报告期作为表头,并从第二行开始取数
= df_ratio.iloc[0].apply(lambda x:int(x)) # 重新命名表头,并且通过apply+lambda配合int取整函数将日期变成整数格式(原本保存成了小数格式)
df_ratio.columns = df_ratio[1:]
df_ratio
# 打印df_ratio表看一看
print(df_ratio)
# 把财务比率保存为现有表格中的sheet
if '*' in comp: # 防止公司名称里有*,例如2022年的*ST皇台,*ST表示有退市风险,但是文件名不可以有*号,所以做替换处理,之后章节会用到
= comp.replace('*','')
comp = pd.ExcelWriter(comp+'2018-2023.xlsx',mode="a", engine="openpyxl")
writer '财务比率表')
df_ratio.to_excel(writer,
writer.save()
# 批量需要获取数据的公司名称、股票代码、年份
= ['贵州茅台','五粮液','泸州老窖']
comps = ['600519.SH','000858.SZ','000568.SZ']
codes = [2023,2022,2021,2020,2019,2018]
years # 调用函数生成相关Excel文件
access_data(comps,codes,years)
# 批量生成比率分析表
for comp in comps:
print(comp)
ratio_sheet(comp)print("---------------------------------------------------------------------")
当这段代码成功运行后,我们的工作目录下将会出现三个Excel文件:贵州茅台2018-2023.xlsx
、五粮液2018-2023.xlsx
和 泸州老窖2018-2023.xlsx
。每一个文件中都包含了该公司对应的财务比率总表。
至此,我们已经从理论学习、单项指标的Python实现,走到了能够自动化、批量化地生成专业财务分析报告的阶段。这正是将商业知识与数据科学技能相结合的威力所在。希望各位同学能够动手实践,亲自运行代码,并尝试解读生成的比率表,比较这三家公司的优劣势,从而真正掌握财务分析的精髓。