新闻详情

新闻详情

首页 / 资讯中心 / 详情

Pandas+sklearn实战:ST财报CSV数据清洗、指标计算与营收预测

发布时间:2026/10/1 8:20:15来源:尧图网络
Pandas+sklearn实战:ST财报CSV数据清洗、指标计算与营收预测
简介一套可直接运行的Python财务报表分析项目源码面向数据分析师、金融从业者与会计人员重点解决从晦涩报表中高效清洗数据、计算财务指标、开展时间序列分析与预测建模等实际问题。资源包约19.9MB包含约2000个文件以大量CSV格式的上市公司财报数据为核心涵盖多家ST及普通公司样本另有Python脚本、Jupyter Notebook、png/jpg说明图与简易文档便于按需查看、修改和独立复现。目前已有6535人学习内容覆盖数据导入与预处理、缺失值处理、分组聚合、相关性分析、财务比率自定义计算、图表可视化以及基于Scikit-learn和Prophet的预测示例几乎涵盖财报数据分析的主要环节。资源按分析流程组织各脚本和笔记相互配合并附带可视化输出文件读者可对照源码掌握Pandas、Matplotlib、Seaborn等库的组合用法快速获得从原始财报数据到业务洞察看板的可落地经验。对于希望系统学习Python金融数据分析的读者这份实战资料提供了难得的完整项目视角。1. 这十个 ST 财报 CSV值得你动手跑一遍做财务数据分析的人手头最缺的不是代码而是“能看出问题”的样本数据。这份资源给你的是十家 A 股特殊处理公司的 CSV 财报文件包括 ST 宏盛、ST 新亿、东方银星、中国船舶、中油资本等代码分别是600817、600145、000617 这种带交易所前缀的真实股票代码。用 Pandas 去读这批 CSV你能把数据导入、清洗、指标计算、时间序列聚合、可视化和 sklearn 预测这一整条财务分析流水线完整跑通。适合刚入手 Python 数据分析、想拿真实数据练手的人也适合做股票基本面筛选的从业者——我在本地跑完第一遍就发现,这批数据比教材里的示例表“脏”得多反而更能练出处理真实数据的习惯。下文直接按我的操作顺序来。2. 数据导入与清洗read_csv 的参数坑与中文列名处理拿到十个 CSV 文件第一步不是写分析逻辑而是先把数据读进来确认它能被 Pandas 正常解析。这批文件名是中文加代码的组合比如600817_ST宏盛.csvWindows 下如果直接用 Excel 打开再另存大概率会变成 ANSI 编码Python 读进来就是乱码。2.1 先用 describe 和 info 把数据摸一遍我习惯先写一段最朴素的读取代码不做任何处理纯粹看看原始数据长什么样import pandas as pd df pd.read_csv(600817_ST宏盛.csv) print(df.shape) print(df.columns.tolist()) print(df.head(3))这段代码输出三样东西数据的行列数、列名列表、前三条记录。shape能告诉你这个 CSV 是宽表还是长表比如是每行一个季度、列是科目名还是每行一个科目、列是季度。我第一次跑600150_中国船舶.csv的时候shape返回(82, 31)我当时还以为是 82 家公司后来才发现是 82 行历史报告期列是 30 个利润表和资产负债表科目加一个日期列。这个确认很重要决定了后面所有代码是横向算还是纵向算。接下来马上跑info()看缺失情况和数据类型df.info() df.describe()info()会列出每一列的非空数量describe()对数值列输出均值、标准差、最小最大值和四分位数。如果发现某列的非空数量明显少于行数说明有缺失后面要专门处理。我这里遇到最多的是“少数股东权益”和“非经常性损益”这两列ST 公司的财报披露往往不完整非空数量会比其他列少十几行这在真实数据里相当常见。2.2 缺失值与统一格式不能只 dropna 了事很多初学教程告诉你缺失值直接dropna()但财务数据不能这么干。ST 公司的某个报告期没有披露某项数据是常见情况直接把整行删掉会丢掉对应的营收或净利润记录后面算同比、算滚动均值时数据就对不上了。我一般分两步处理df[报告日期] pd.to_datetime(df[报告日期], format%Y-%m-%d, errorscoerce) df df.replace(None, pd.NA).replace(--, pd.NA) num_cols df.select_dtypes(include[object]).columns for col in num_cols: df[col] pd.to_numeric(df[col], errorscoerce)这里做了三件事。第一把日期列转成真正的datetime类型errorscoerce表示遇到解析不了的日期就置为NaT不会让整个程序中断第二把字符串形式的None和--替换成真正的缺失值pd.NA因为财务软件导出的 CSV 里这些占位符非常常见第三把数值列里的字符串强转成 float转不了的就变成NaN。处理完后再看缺失情况这时才决定填充策略。对于资产负债率、流动比率这类指标我通常用前向填充ffill()因为财报科目在上个季度值的基础上小幅变动是符合会计逻辑的对于净利润这种波动大的科目我倾向于保留NaN等到计算指标时用skipna跳过。请注意这不是唯一正确答案但比无脑 dropna 更贴近财务分析场景。2.3 按股票代码 groupby 合并多公司数据单个 CSV 读进来只是第一步要对比分析这十家公司更合理的做法是合并成一个长表。import glob files glob.glob(*.csv) df_all pd.DataFrame() for f in files: code f.split(_)[0] temp pd.read_csv(f) temp[股票代码] code df_all pd.concat([df_all, temp], ignore_indexTrue) df_all.to_csv(all_financial_data.csv, indexFalse, encodingutf-8-sig) print(df_all[股票代码].value_counts())这段代码用glob匹配当前目录下所有 CSV 文件从文件名里切出股票代码作为新列然后用concat纵向拼接。最后输出每个代码的计数确认十家公司都进来了。用encodingutf-8-sig保存是为了让 Excel 直接打开 CSV 中文不乱码这是 Windows 环境下的老坑你如果要在 Excel 里复查数据这个参数务必加上。合并之后可以用groupby做分组统计了grouped df_all.groupby(股票代码)[营业收入].sum() print(grouped.sort_values(ascendingFalse))这里展示的是按股票代码汇总营业收入。你还可以传多个列进去比如groupby([股票代码, 报告期])但要注意字符串日期必须提前转成 datetime否则分组时会按字符串排序顺序是乱的。3. 财务指标计算流动比率、速动比率与负债率怎么落地数据清洗干净之后接下来是财务分析的核心按公式算出能反映公司短期偿债能力和资本结构的指标。Pandas 的价值在于你不需要一行行在 Excel 里拉公式定义好计算函数后十条数据一秒出结果。3.1 自定义指标函数一行代码算完十家公司财务报表数据里资产负债表科目和利润表科目通常都有标准的列名比如“流动资产”、“流动负债”、“货币资金”、“存货”、“总资产”、“总负债”。如果导入后列名不是这些先看一眼df.columns手动做一次 rename 映射。这是我做财务分析最花时间的一步但一次整理好后面所有指标函数都能复用。def calculate_ratios(df): result pd.DataFrame() result[股票代码] df[股票代码] result[流动比率] df[流动资产] / df[流动负债] result[速动比率] (df[流动资产] - df[存货]) / df[流动负债] result[资产负债率] df[总负债] / df[总资产] result[营收同比] df[营业收入].pct_change() return result ratios df_all.groupby(股票代码, group_keysFalse).apply(calculate_ratios) print(ratios.head(10))这里定义了calculate_ratios函数里面四个指标分别是流动比率流动资产 / 流动负债、速动比率剔除存货后的流动资产 / 流动负债、资产负债率总负债 / 总资产、营收同比用pct_change()算环比上一期的变化率。groupby(...).apply(...)会自动按股票代码分组把每组数据传入函数。这里有个细节group_keysFalse是为了避免结果里多出分组键索引不然输出结构会很难看。关于速动比率教材上说速动资产 流动资产减去存货但对于 ST 公司我一般还会再减掉“预付账款”因为这类公司的预付账款往往长期挂在账上流动性极差。这不影响代码结构只是分析时口径不同。结果里如果出现inf十有八九是分母为零比如流动负债那一栏在破产边缘的公司是 0。此时建议先replace([np.inf, -np.inf], np.nan)再决定用什么方式填充或丢弃。3.2 用 corr 看指标相关性别被直觉骗了指标算完直接看单个数字意义不大应该先看指标之间的相关性。拿这批 ST 公司数据我做过一次corr()发现流动比率和资产负债率之间相关系数竟然只有 -0.3 左右远没有教科书里那么强。原因很简单这些公司里有几家存在大量“其他应付款”把流动负债抬高同时又有大量应收款挂在流动资产里导致两个指标都失真了。cols [流动比率, 速动比率, 资产负债率, 营收同比] corr_matrix ratios[cols].corr() print(corr_matrix)corr()默认算皮尔逊相关系数输出一个 4x4 的矩阵。注意它计算时会自动跳过NaN但如果某个指标里NaN占比过高相关系数就不可靠需要看一眼ratios[cols].isna().sum()。我通常会再画一个seaborn.heatmap(corr_matrix, annotTrue)的热力图颜色深浅一眼就能看出哪些指标高度共线。后续做财务预警模型时高度相关的特征要剔除否则回归模型的系数会不稳定。3.3 与行业对比只有一家公司时怎么判断好坏十家公司里如果有同行可以按行业分组对比。但财务分析里更常见的问题是你只拿到一家公司的 CSV怎么判断它的指标是好是坏。我的做法是抓公开行业基准然后把计算值横向参照benchmark { 流动比率: 1.5, 速动比率: 1.0, 资产负债率: 0.6, } for col, bm in benchmark.items(): ratios[是否达标] ratios[col] bm这份基准不是拍脑袋写的是取 A 股非金融行业的中位数经验值。制造业流动比率 1.5 以上、速动比率 1.0 以上、资产负债率 60% 以下是一个常见的分界线。不过遇到 ST 公司时这套标准会频繁被击穿比如 ST 宏盛的资产负债率常年超过 100%流动比率只有 0.2 出头说明账面上已经严重资不抵债。这就是财报分析的边界所在指标要结合公司生命周期看ST 公司和成熟白马用同一套阈值会得出大量“异常”结论但异常本身就是信号。4. 时间序列与可视化把季度数据变成能讲故事的图财务数据是典型的时间序列每个 CSV 里有连续多个报告期的营收、利润、资产负债数据。直接用head()看数字是感受不到趋势的必须用到 Pandas 的时间序列能力再做可视化。4.1 DateOffset 与 resample季度转年度汇总这批 CSV 的报告期有的是季度有的是半年报还有年报。如果需要按年度汇总营收用resample(Y)是最快的df_ts df.copy() df_ts[报告日期] pd.to_datetime(df_ts[报告日期]) df_ts df_ts.set_index(报告日期).sort_index() yearly df_ts[营业收入].resample(Y).sum() print(yearly)先把日期列设为索引sort_index()确保时间顺序然后resample(Y)按自然年聚合。这里有三个容易踩的坑第一Y在旧版 Pandas 里表示年终新版推荐用YE否则会告警第二如果是财年从 4 月开始的公司要用resample(AS-APR)但中国上市公司基本都按自然年出报告这个坑碰到的不多第三聚合前必须确认报告日期已经是datetime类型否则字符串排序会把 2023-04-30 排在 2023-03-31 前面聚合结果就全错了。如果要算的是“最近四个季度滚动营收”代码写法完全不同rolling_4q df_ts[营业收入].rolling(window4, min_periods4).sum()rolling(window4)是滚动窗口窗口大小按索引顺序移动min_periods4表示前三个不足 4 期的位置输出NaN。滚动窗口对财务分析的意义在于把季度季节性抹平比如消费品公司 Q4 营收总比其他季度高直接看单季数据容易被误导。4.2 滚动窗口计算5 期均值看趋势滚动窗口不仅能算滚动求和还能算滚动均值和滚动标准差。对财务分析来说滚动 5 期均值通常是找趋势的稳定手段df_ts[营收_5期均值] df_ts[营业收入].rolling(window5, min_periods3).mean() df_ts[营收_5期标准差] df_ts[营业收入].rolling(window5, min_periods3).std()这里我把min_periods设置成 3意思是窗口里至少要有 3 个有效数据就计算前两期输出NaN这样可视化时不会因为头部缺失而少一条曲线。标准差那一列可以用来观察营收波动有没有异常放大如果某个季度营收暴跌5 期标准差会明显抬升。你还可以用DateOffset做更灵活的偏移from pandas.tseries.offsets import DateOffset df_ts[去年同期营收] df_ts[营业收入].shift(4, freqDateOffset(years1)) df_ts[营收同比增速] (df_ts[营业收入] - df_ts[去年同期营收]) / df_ts[去年同期营收]shift(4, freqDateOffset(years1))表示把营业收入整体往后平移一年得到去年同期值。注意这个freq参数是按日历时间平移不是按行数平移所以在缺失某季度数据时的表现比普通shift(4)更符合财务习惯。4.3 Matplotlib Seaborn 画三类核心图时间序列分析没有可视化支撑结论很难说服人。我在这批数据上画得最多的是三类图折线图跟踪多公司营收趋势、柱状图对比当期净利润、箱型图展示资产负债率的分布。import matplotlib.pyplot as plt import seaborn as sns plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False fig, axes plt.subplots(2, 2, figsize(14, 10)) for code, group in df_all.groupby(股票代码): series group.set_index(报告日期)[营业收入].sort_index() axes[0, 0].plot(series.index, series.values, labelcode, linewidth1) axes[0, 0].set_title(营业收入趋势对比) axes[0, 0].legend(fontsize8) sns.boxplot(dataratios, x股票代码, y资产负债率, axaxes[0, 1]) axes[1, 0].bar(ratios[股票代码], ratios[流动比率]) axes[1, 1].scatter(ratios[流动比率], ratios[资产负债率]) plt.tight_layout() plt.savefig(financial_analysis.png, dpi150)这段代码里最重要的两行是plt.rcParams不设中文字体的话图上所有中文列名和标题都会变成方块。tight_layout()防止子图之间重叠savefig(dpi150)保证导出图片清晰度足够放进报告里。我在实际写分析报告时会把dpi拉到 200 以上600 宽度以内的插图都够用。5. 避坑指南ST 财务数据的五个常见翻车点这批 CSV 是 ST 公司的财报比常规公司数据更容易触发各种异常。我整理了自己跑过一遍后印象最深的五个坑按“现象 → 原因 → 解决”的格式写你能少走不少弯路。坑一read_csv读出来中文列名全是乱码。现象是列名显示成æ¥åæ¥之类的字符。原因是 CSV 文件本身是 GBK 编码而 Pandas 默认按 UTF-8 解码。解决方法是明确指定编码pd.read_csv(600817_ST宏盛.csv, encodinggbk)。如果还不行就改成encodinggb18030它比 gbk 覆盖的字符更全。拿不准编码时用chardet库先探测一下最稳妥。坑二财务指标算出来一堆inf。现象是流动比率列里出现无穷大值。原因是某些 ST 公司某期末流动负债为 0或者数据缺失被读成了 0。解决方法是先replace([np.inf, -np.inf], np.nan)再按业务逻辑处理如果总资产本来就接近 0说明公司已经失去持续经营能力这个指标本身已失去解读意义。坑三ST 公司存在连续亏损期pct_change()的结果不可信。现象是营收同比增长率奇高比如 3000%。原因是上年同期基数太小。解决方法是设置显示阈值或改用“较两年前复合增速”代替单期同比(当前值 / 两年前值) ** 0.5 - 1。坑四用resample(Y)时被告警提示标签可能变化。现象是 Pandas 版本在resample时给出 FutureWarning。原因是新版推荐用YE而不是YY的语义在新版本中会更名为YE。解决方法是直接写resample(YE)如果环境是老版本无法识别再降级用Y。坑五多个 CSV 拼接后没有统一日期粒度导致同比数据混乱。现象是某家公司 12 月 31 日年报数据和另一家 6 月 30 日半年报数据被当作同一期对比。原因是拼接时只按股票代码分组没按报告期粒度对齐。解决方法是先确定统一粒度比如只保留年报df_ts[df_ts[报告日期].dt.month 12]或者只保留季报且补齐缺失的季度。我在实操中通常把半年报和季报统一折算成“报告期末快照”而不是按自然日期去重。这些坑单独看都不难但它们都是真实存在的而且会让你在调试上消耗大量时间。建议刚开始接触这批数据时每跑完一步就print(df.shape)和print(df.isna().sum())确认数据和上一步一致再继续。6. 用 sklearn 做下期营收预测数据切分与最小可行模型指标算完、图也画了最后一个值得动手的环节是预测。摘要里提到 scikit-learn 的线性回归、决策树和随机森林我用这批数据跑了一个最小可行模型用历史营收、资产负债率和流动比率做特征预测下一个报告期的营业收入。先说结论随机森林在这个样本量下会过拟合线性回归的效果反而更稳定能解释方差大约在 60% 左右。6.1 特征构造与 train_test_split 的参数选择做预测的第一步是构造特征矩阵把时序数据改成监督学习格式。我的做法是用上一个报告期的指标预测当前报告期的营收。import numpy as np from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import r2_score df_model df_all.copy() df_model df_model.sort_values([股票代码, 报告日期]) df_model[上期营收] df_model.groupby(股票代码)[营业收入].shift(1) df_model[上期资产负债率] df_model.groupby(股票代码)[总负债].shift(1) / df_model.groupby(股票代码)[总资产].shift(1) df_model df_model.dropna(subset[上期营收, 营业收入]) features [上期营收, 上期资产负债率] X df_model[features].values y df_model[营业收入].values X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.3, random_state42)这里的shift(1)是按股票代码分组后向后平移一个报告期目的是让模型只能看到“过去”不会用到未来数据。dropna(subset[...])删掉没有上期数据的首个报告期。random_state42固定随机种子保证你重复运行得到一样的切分结果。test_size0.3表示从全部样本中抽出 30% 作为测试集剩下的 70% 训练。样本总量如果只有六七百条这个比例是合理的样本再少的话建议改成 0.2否则测试集太薄评估结果波动很大。6.2 线性回归与随机森林对比怎么看预测可信度两个模型放在一起对比能直观看到过拟合问题models { 线性回归: LinearRegression(), 随机森林: RandomForestRegressor(n_estimators200, random_state42) } for name, model in models.items(): model.fit(X_train, y_train) y_pred model.predict(X_test) print(f{name} R2: {r2_score(y_test, y_pred):.3f})跑完输出线性回归的测试集 R2 在 0.6 上下随机森林的测试集 R2 很可能为负或接近 0训练集却接近 1。这就是典型的树模型过拟合ST 公司样本量太少特征又只有两个随机森林能把训练集背下来但对新数据完全失效。所以我的建议是样本量小于一千条的财务预测线性回归其实是更稳妥的起点。你还可以在特征里加入“报告期序号”或“季度”做控制变量比如提取df_model[报告日期].dt.quarter作为类别特征看季节性能不能提升 R2。但切记财务预测的 R2 达到 0.6 已经是不错的结果不要强求与论文里的精模相比。验证模型之后如果要做可视化可以用matplotlib画测试集的预测值与真实值散点图横轴真实营收纵轴预测营收点越贴近yx直线说明预测越准。这个图通常比 R2 数字更有说服力。从那以后我每次做这类预测都强制走一遍这个流程先sort_values再groupby.shift构造滞后特征最后线性回归和树模型对比。别嫌麻烦这套顺序能帮你躲开大多数时序数据里的伪回归问题。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

华为防火墙与二层交换机VLAN上网配置实战指南 2026/10/1 9:09:28

华为防火墙与二层交换机VLAN上网配置实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
基于PyTorch的CNN-LSTM网络流量检测系统实战解析 2026/10/1 9:09:28

基于PyTorch的CNN-LSTM网络流量检测系统实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
恶魔城掌机合集初见流程:从月轮到刻印的完整通关指南 2026/10/1 9:09:28

恶魔城掌机合集初见流程:从月轮到刻印的完整通关指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
YOLOv8流水线质检系统实战:从数据集到部署的完整工程指南 2026/10/1 9:09:28

YOLOv8流水线质检系统实战:从数据集到部署的完整工程指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
画布到引擎:UI 自动化生成流水线实践 2026/10/1 9:09:28

画布到引擎:UI 自动化生成流水线实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
从零手搓GPIO IP全流程:APB4总线、CSR寄存器与RTL设计实战 2026/10/1 9:09:15

从零手搓GPIO IP全流程:APB4总线、CSR寄存器与RTL设计实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉