首页 > 编程开发 > python数据分析 >
-
财务数据可视化:杜邦分析图、营收利润趋势图
第27章 基本面量化实战(续)
27.4 财务数据可视化:杜邦分析图、营收利润趋势图
27.4.1 先讲个真实需求:老板要我把枯燥的财务数据做成能看懂的图
之前做基本面分析,给老板发几十页的财务数据表格,老板看一眼就扔回来,说“你就告诉我这家公司好不好,好在哪,别给我看数字”。后来我把财务数据做成两张图:一张杜邦分析图,把ROE拆解成盈利能力、运营效率、杠杆水平三个维度,一眼就能看出公司赚的是什么钱;一张营收利润趋势图,把近5年的营收、净利润、现金流走势放在一起,一眼就能看出公司的增长稳不稳。老板看了直接说以后所有分析报告都要配这两张图,省得他猜。今天我就把这两张最vb.net教程C#教程python教程SQL教程access 2010教程实用的财务可视化图表的制作方法讲清楚,帮你把财务分析做的直观易懂。
27.4.2 核心概念:两张图的分析逻辑
-
杜邦分析图
把核心指标ROE层层拆解,找到公司盈利的核心驱动因素:
ROE = 销售净利率 × 总资产周转率 × 权益乘数
销售净利率高:说明公司产品赚钱,是“茅台型”公司,靠高毛利盈利
总资产周转率高:说明公司运营效率高,是“沃尔玛型”公司,靠薄利多销盈利
权益乘数高:说明公司用杠杆赚钱,是“银行型”公司,靠借别人的钱赚钱 -
营收利润趋势图
把近3-5年的营收、净利润、经营现金流的同比增速放在一张图里,看增长的一致性和质量:
三者同步增长:说明增长质量高,真金白银赚到钱
净利润增速远高于营收增速:可能是靠压缩费用、卖资产,增长不可持续
现金流增速远低于净利润增速:说明盈利有水分,可能是大量赊销,钱没收回来
27.4.3 实战:用Python制作财务可视化图表
-
实战代码:财务可视化工具
python
# 1. 导入需要的库
import tushare as ts
import pandas as pd
import numpy as np
from datetime import datetime
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import os
# 2. 初始化Tushare
pro = ts.pro_api()
# 3. 定义财务可视化类
class FinancialVisualizer:
def __init__(self):
self.save_dir = '财务可视化图表'
os.makedirs(self.save_dir, exist_ok=True)
# 3.1 获取单只股票的财务数据(近5年年报)
def get_financial_data_for_vis(self, ts_code, year_range=5):
"""
获取最近N年的年报财务数据,用于可视化
"""
print(f'正在获取{ts_code}近{year_range}年的财务数据...')
end_year = datetime.now().year - 1 # 取到上一年的年报
start_year = end_year - year_range + 1
years = list(range(start_year, end_year + 1))
all_data = []
for year in years:
end_date = f'{year}1231'
# 获取财务指标
fina = pro.fina_indicator(
ts_code=ts_code, end_date=end_date,
fields='end_date,roe,roic,gross_margin,net_profit_margin,turnover_rate,current_ratio,assets_liab_ratio'
)
# 获取利润表
income = pro.income(
ts_code=ts_code, end_date=end_date,
fields='end_date,total_revenue,net_profit'
)
# 获取现金流量表
cashflow = pro.cashflow(
ts_code=ts_code, end_date=end_date,
fields='end_date,net_operate_cash_flow'
)
# 合并数据
df = fina.merge(income, on='end_date', how='left')
df = df.merge(cashflow, on='end_date', how='left')
all_data.append(df)
all_data = pd.concat(all_data, ignore_index=True)
all_data['end_date'] = pd.to_datetime(all_data['end_date'], format='%Y%m%d')
all_data['year'] = all_data['end_date'].dt.year
all_data.sort_values('year', inplace=True)
# 单位转换:营收、净利润、现金流从元转成亿元
all_data['total_revenue'] = all_data['total_revenue'] / 1e8
all_data['net_profit'] = all_data['net_profit'] / 1e8
all_data['net_operate_cash_flow'] = all_data['net_operate_cash_flow'] / 1e8
# 计算同比增速
all_data['revenue_yoy'] = all_data['total_revenue'].pct_change() * 100
all_data['profit_yoy'] = all_data['net_profit'].pct_change() * 100
all_data['cashflow_yoy'] = all_data['net_operate_cash_flow'].pct_change() * 100
# 计算杜邦三因子
all_data['权益乘数'] = 1 / (1 - all_data['assets_liab_ratio'] / 100)
all_data['总资产周转率'] = all_data['turnover_rate'] / 100 # 接口返回的是百分比
all_data['销售净利率'] = all_data['net_profit_margin'] / 100
# 获取股票名称
self.stock_name = pro.stock_basic(ts_code=ts_code, fields='name').iloc[0]['name']
self.ts_code = ts_code
print(f'{self.stock_name}财务数据获取完成,共{len(all_data)}年数据')
return all_data
# 3.2 绘制杜邦分析图(瀑布图+拆解条形图)
def plot_dupont_analysis(self, df, save_html=True, show_plot=True):
"""
杜邦分析图:展示最近一年ROE的拆解逻辑
"""
latest_year = df['year'].iloc[-1]
latest_data = df[df['year'] == latest_year].iloc[0]
# 杜邦分解计算
roe = latest_data['roe']
net_profit_margin = latest_data['net_profit_margin']
asset_turnover = latest_data['turnover_rate']
equity_multiplier = (1 / (1 - latest_data['assets_liab_ratio'] / 100)) * 10 # 放大10倍方便展示
# 构建瀑布图数据
waterfall_measure = ['absolute', 'relative', 'relative', 'relative', 'total']
waterfall_x = ['净资产收益率(ROE)', '销售净利率', '总资产周转率', '权益乘数', 'ROE(验证)']
waterfall_y = [
0,
net_profit_margin,
asset_turnover - net_profit_margin,
equity_multiplier - asset_turnover,
roe
]
waterfall_text = [
f'{roe:.2f}%',
f'{net_profit_margin:.2f}%',
f'{asset_turnover:.2f}次/年',
f'{equity_multiplier/10:.2f}倍',
f'{roe:.2f}%'
]
# 构建杜邦三因子对比条形图(近3年)
recent_3y = df.tail(3)
bar_x = recent_3y['year'].astype(str).tolist()
bar_y1 = recent_3y['net_profit_margin'].tolist()
bar_y2 = recent_3y['turnover_rate'].tolist()
bar_y3 = (recent_3y['权益乘数'] * 10).tolist() # 同比例放大
# 组合子图
fig = make_subplots(
rows=2, cols=1,
subplot_titles=(
f'{latest_year}年杜邦分析拆解(ROE驱动因素)',
'近3年杜邦三因子变化趋势'
),
vertical_spacing=0.2,
row_heights=[0.6, 0.4]
)
# 第一层:瀑布图
fig.add_trace(
go.Waterfall(
name='杜邦拆解',
orientation='v',
measure=waterfall_measure,
x=waterfall_x,
y=waterfall_y,
text=waterfall_text,
textposition='outside',
connector={'line': {'color': 'rgb(63, 63, 63)'}},
increasing={'marker': {'color': '#FF4B4B'}},
decreasing={'marker': {'color': '#2ECC71'}},
totals={'marker': {'color': '#3498DB'}}
),
row=1, col=1
)
# 第二层:条形图
fig.add_trace(
go.Bar(name='销售净利率(%)', x=bar_x, y=bar_y1, marker_color='#FF4B4B'),
row=2, col=1
)
fig.add_trace(
go.Bar(name='总资产周转率(次)', x=bar_x, y=bar_y2, marker_color='#3498DB'),
row=2, col=1
)
fig.add_trace(
go.Bar(name='权益乘数(×10)', x=bar_x, y=bar_y3, marker_color='#9B59B6'),
row=2, col=1
)
# 布局调整
fig.update_layout(
title_text=f'{self.stock_name}({self.ts_code}) 杜邦分析报告',
title_font_size=18,
height=800,
width=1000,
showlegend=True,
barmode='group'
)
fig.update_yaxes(title_text='数值', row=1, col=1)
fig.update_yaxes(title_text='数值(同比放大)', row=2, col=1)
# 保存与展示
if save_html:
filename = f'{self.save_dir}/{self.stock_name}_{latest_year}年杜邦分析.html'
fig.write_html(filename)
print(f'杜邦分析图已保存到{filename}')
if show_plot:
fig.show()
return fig
# 3.3 绘制营收利润趋势图(双轴折线+柱状组合图)
def plot_revenue_profit_trend(self, df, save_html=True, show_plot=True):
"""
营收利润趋势图:展示近N年营收、净利润、现金流的绝对值和增速
"""
years = df['year'].astype(str).tolist()
# 绝对值数据(亿元)
revenue = df['total_revenue'].tolist()
profit = df['net_profit'].tolist()
cashflow = df['net_operate_cash_flow'].tolist()
# 增速数据(%)
revenue_yoy = df['revenue_yoy'].fillna(0).tolist()
profit_yoy = df['profit_yoy'].fillna(0).tolist()
cashflow_yoy = df['cashflow_yoy'].fillna(0).tolist()
# 构建双轴子图
fig = make_subplots(
rows=2, cols=1,
subplot_titles=(
'营收、净利润、经营现金流绝对值(亿元)',
'同比增速(%)'
),
vertical_spacing=0.15,
specs=[[{"secondary_y": False}], [{"secondary_y": False}]]
)
# 第一层:绝对值柱状图
fig.add_trace(
go.Bar(name='总营收', x=years, y=revenue, marker_color='#3498DB', opacity=0.8),
row=1, col=1
)
fig.add_trace(
go.Bar(name='净利润', x=years, y=profit, marker_color='#FF4B4B', opacity=0.8),
row=1, col=1
)
fig.add_trace(
go.Bar(name='经营现金流', x=years, y=cashflow, marker_color='#2ECC71', opacity=0.8),
row=1, col=1
)
# 第二层:增速折线图
fig.add_trace(
go.Scatter(name='营收增速', x=years, y=revenue_yoy, mode='lines+markers', line=dict(color='#3498DB', width=3)),
row=2, col=1
)
fig.add_trace(
go.Scatter(name='净利润增速', x=years, y=profit_yoy, mode='lines+markers', line=dict(color='#FF4B4B', width=3)),
row=2, col=1
)
fig.add_trace(
go.Scatter(name='现金流增速', x=years, y=cashflow_yoy, mode='lines+markers', line=dict(color='#2ECC71', width=3)),
row=2, col=1
)
# 添加增速为0的参考线
fig.add_hline(y=0, line_dash="dash", line_color="gray", row=2, col=1)
# 布局调整
fig.update_layout(
title_text=f'{self.stock_name}({self.ts_code}) {min(years)}-{max(years)}年营收利润趋势',
title_font_size=18,
height=700,
width=1000,
barmode='group',
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1)
)
fig.update_yaxes(title_text='金额(亿元)', row=1, col=1)
fig.update_yaxes(title_text='增速(%)', row=2, col=1)
# 保存与展示
if save_html:
filename = f'{self.save_dir}/{self.stock_name}_{min(years)}-{max(years)}年营收利润趋势.html'
fig.write_html(filename)
print(f'营收利润趋势图已保存到{filename}')
if show_plot:
fig.show()
return fig
# 3.4 一键生成完整财务分析报告
def generate_full_report(self, ts_code, year_range=5):
"""
一键生成杜邦分析+营收利润趋势的完整报告
"""
df = self.get_financial_data_for_vis(ts_code, year_range)
self.plot_dupont_analysis(df)
self.plot_revenue_profit_trend(df)
print(f'{self.stock_name}完整财务分析报告生成完成!')
return df
# 4. 执行可视化
if __name__ == '__main__':
visualizer = FinancialVisualizer()
# 生成贵州茅台的财务分析报告
moutai_data = visualizer.generate_full_report(ts_code='600519.SH', year_range=5)
# 也可以单独生成某一张图
# visualizer.plot_dupont_analysis(moutai_data)
# visualizer.plot_revenue_profit_trend(moutai_data)
逐行讲解:
get_financial_data_for_vis():获取指定股票最近N年的年报数据,自动计算同比增速和杜邦三因子,统一单位为亿元,避免数值过大不好展示。
plot_dupont_analysis():绘制杜邦分析图,分上下两层:
上层是瀑布图,直观展示ROE是怎么从三个因子乘出来的,一眼就能看出哪个因子是主要驱动
下层是近3年的三因子对比条形图,看驱动因素有没有变化,比如是不是靠加杠杆拉高了ROE
注意权益乘数放大了10倍,不然数值太小在图上看不到,标注里会说明放大倍数,不影响理解
plot_revenue_profit_trend():绘制营收利润趋势图,也是上下两层:
上层是柱状图,看每年的营收、净利润、现金流绝对值规模,直观看到公司的体量变化
下层是折线图,看三者的增速,重点看是不是同步增长,有没有背离的情况
加了增速为0的参考虚线,低于虚线就是负增长,一眼就能看到哪年增长下滑了
generate_full_report():一键生成两张图,不用单独调用,适合批量生成多个公司的分析报告。
运行结果:
生成两个交互式HTML文件,打开后可以放大缩小,鼠标悬停能看到具体数值,汇报的时候投影出来非常直观,不用再解释一堆数字。
27.4.4 基础知识拓展:财务可视化的分析技巧
- 杜邦分析图的三种典型模式
| 模式 | 特征 | 代表行业/公司 | 风险点 |
|---|---|---|---|
| 高利润型 | 销售净利率>20%,周转率和乘数低 | 白酒、医药(茅台、恒瑞) | 毛利率下滑风险,一旦竞争加剧利润会快速下跌 |
| 高周转型 | 总资产周转率>1次/年,净利率低 | 零售、物流(沃尔玛、顺丰) | 运营效率下滑风险,一旦成本上升利润会被挤压 |
| 高杠杆型 | 权益乘数>3倍,净利率和周转率低 | 银行、地产、基建 | 债务风险,一旦利率上升或者行业下行,会出现资不抵债 |
重点警惕:三个因子都不突出,ROE却很高的公司,大概率是财务造假,比如早年的康美药业,杜邦三因子都很普通,ROE却常年保持20%+,后来证明是造假。
2. 营收利润趋势图的危险信号
连续两年净利润增速低于营收增速:说明公司的盈利能力在下降,可能是行业竞争加剧,降价促销,或者成本上升。
现金流连续两年低于净利润:说明公司的钱都压在应收账款和存货上,赚的是纸面利润,一旦下游客户倒闭,会出现大额坏账。
营收增速突然跳升:比往年增速高2-3倍,除非有重大业务突破,否则大概率是关联交易或者虚增收入,要小心。
净利润增速突然为负:第一次出现亏损,尤其是周期股,很可能是行业下行的开始,不要轻易抄底。
3. 可视化的注意事项
不要过度美化:重点是清晰传递信息,不要加太多花里胡哨的特效,反而干扰判断。
单位要统一:所有金额单位要么都是亿元,要么都是万元,不要混着来,容易误导。
异常值要标注:如果某一年增速特别高或者特别低,要在图上标注原因,比如是并购、资产出售还是行业周期。
27.4.5 实战优化:添加财务健康度评分
-
实战代码:添加财务评分功能
python
# 在FinancialVisualizer类中添加财务评分方法
def calculate_financial_score(self, df):
"""
从盈利能力、成长能力、现金流质量、偿债能力四个维度打分,满分100分
"""
latest = df.iloc[-1]
score = 0
# 1. 盈利能力(30分):ROE>15%得满分,每低1%扣2分
roe_score = max(0, min(30, 30 - (15 - latest['roe']) * 2))
score += roe_score
# 2. 成长能力(30分):近3年净利润复合增速>20%得满分,每低2%扣3分
cagr = (df['net_profit'].iloc[-1] / df['net_profit'].iloc[-4]) ** (1/3) - 1
cagr_score = max(0, min(30, 30 - (20 - cagr*100)/2 *3))
score += cagr_score
# 3. 现金流质量(20分):近3年平均现金流/净利润>1得满分,每低0.1扣2分
cash_ratio = (df['net_operate_cash_flow'].tail(3) / df['net_profit'].tail(3)).mean()
cash_score = max(0, min(20, 20 - (1 - cash_ratio)/0.1 *2))
score += cash_score
# 4. 偿债能力(20分):资产负债率<50%得满分,每高5%扣2分
debt_score = max(0, min(20, 20 - (latest['assets_liab_ratio'] - 50)/5 *2))
score += debt_score
# 生成评级
if score >= 80:
rating = '优秀'
elif score >= 60:
rating = '一般'
else:
rating = '较差'
print(f'{self.stock_name}财务健康度评分:{score:.1f}分,评级:{rating}')
# 保存评分结果
pd.DataFrame({
'维度': ['盈利能力', '成长能力', '现金流质量', '偿债能力', '总分', '评级'],
'得分': [roe_score, cagr_score, cash_score, debt_score, round(score,1), rating]
}).to_csv(f'{self.save_dir}/{self.stock_name}_财务评分.csv', index=False, encoding='utf-8-sig')
return score, rating
# 修改generate_full_report方法,加入评分
def generate_full_report(self, ts_code, year_range=5):
df = self.get_financial_data_for_vis(ts_code, year_range)
self.calculate_financial_score(df) # 新增评分
self.plot_dupont_analysis(df)
self.plot_revenue_profit_trend(df)
print(f'{self.stock_name}完整财务分析报告生成完成!')
return df
优化效果:
生成报告的时候会自动给公司打个分,80分以上的是优秀公司,60分以下的要小心,不用自己再去对比各个指标,快速筛选优质公司。
27.4.6 总结:财务可视化的应用场景
1.个股研究:研究一家公司的时候,先看这两张图,5分钟就能搞懂公司的盈利模式和增长情况,比看几十页财报快很多。
2.行业对比:把同一个行业的几家公司的图放在一起对比,一眼就能看出哪个是行业龙头,哪个竞争力弱。
3.投资汇报:给客户或者老板汇报的时候,用这两张图代替枯燥的表格,对方一眼就能看懂,不用你费半天劲解释。
4.持仓跟踪:每个季度给持仓股生成一次报告,看基本面有没有变化,一旦出现危险信号及时卖出。
通过这个实战,你应该已经掌握了最实用的财务可视化方法,以后做财务分析再也不用发一堆没人看的表格了。接下来我们就讲如何把这些基本面分析工具整合起来,做一个自动化的基本面选股系统。
下一节咱们就讲自动化基本面研究平台的搭建。
转载请注明出处:https://www.xin3721.com/ArticlePrograme/csharp49726.html










