专精特新小巨人财务数据清洗实战:从原始压缩包到分析面板

📅 发布时间:2026/10/9 22:42:53
专精特新小巨人财务数据清洗实战:从原始压缩包到分析面板
简介2013—2024年国家级专精特新‘小巨人’企业财务数据连同第一批至第六批企业名单被打包为一份zip资料免去研究者多渠道搜集、口径对照的麻烦。面向经济管理研究人员、政策评估者及关注中小企业投资价值的分析师可用于分析企业营收、利润、资产负债等财务指标评估政策扶持效果与成长路径。包内共十个文件包含可导入Stata的数据文件、便于Excel透视的表格文件、一份数据来源说明、一份使用说明以及六份分批次企业名单文件方便核对批次与行业归属。数据跨度十年整个压缩包约三十六兆字节结构紧凑。目前已有五十五人学习或下载使用者可基于这些数据构建平衡面板开展行业分布、区域差异、企业生命周期和‘专精特新’政策效应等研究也可为金融机构筛选优质中小企业标的提供参考是一份可直接上手的数据基础。1. 专精特新“小巨人”财务数据从名单到可分析面板中间隔着一次清洗做企业研究的人拿到“专精特新‘小巨人’-财务数据第1-5批2013-2024年.zip”第一反应多半是“终于有底表了”。这份数据把主管部门分批公示的小巨人名单对应到2013—2024年度的财务记录能支撑企业筛选、信贷尽调、政策效果评估三类用途。适合产业研究的分析师、中小企业服务机构、想评估同行业标的的从业者。但压缩包不等于可分析的面板字段口径不统一、单位混乱、批次重叠任何一个都能让统计结果翻车。下面按我处理这类数据集的完整路径来拆先把结构讲清楚再给可复现的清洗流程和参数。2. 数据包结构与字段语义先搞懂哪些列能直接算哪些列是坑拿到这类压缩包第一步不是跑代码而是确认里面到底装了哪几张表。常见做法是包含名单表、财务明细表、字段说明文档有时还附带工商照面信息。我不建议上来就pd.read_csv一把梭先把结构摸透后边能省掉一半返工。2.1 批次与年份的对齐方式第1-5批和2013-2024年怎么对应这里的“批次”指认定批次是主管部门分批公示的名单顺序。第1-5批的公布时间点不同企业被认定之前就已经有多年经营历史所以财务数据才会覆盖2013-2024年这么长的区间。也就是说批次标注的是“认定身份”年份标注的是“数据所属年度”两者不是一回事。我在处理时会把这两类信息拆到不同字段认定批次、数据年份。分析时只按数据年份做时间序列批次字段仅用于分组和筛选。这种设计看似简单却能把大量后续问题挡在门外——比如你想统计“第3批企业在2022年的营收中位数”如果混用批次和年份会得到一个没有经济意义的数。批次映射关系建议单独做成一张小表字段内容说明示例认定批次第几批公示名单第4批公示年份该批次公布的年度2023假设值以数据包内说明为准数据年份财务记录的会计年度2013-2024企业身份标识用于去重的唯一ID统一社会信用代码如果你拿到的数据包没有单列公示年份可以用批次号配合名单表里的认定年度信息重建。注意不要把公示年份和数据年份放在同一列里参与计算否则做逐年对比时会出现一整年数据漂移。2.2 财务字段的常见口径资产负债表、利润表、现金流的核心字段这类数据包的字段通常按三大报表的结构组织。资产负债表里有资产总额、负债总额、应收账款、存货、所有者权益利润表里有营业收入、营业成本、税金及附加、销售费用、管理费用、研发费用、净利润现金流量表里有经营活动现金流净额、投资活动现金流净额、筹资活动现金流净额。每一类都有口径陷阱。资产总额要确认是“期末余额”还是“期初余额”营业收入要确认是“合并报表数”还是“母公司数”研发费用要确认是“费用化金额”还是“含资本化的总投入”。不同来源整理的包对同一科目的命名可能差很远比如“营业总收入”和“营业收入万元”、“净利润/亏损”和“归属于母公司股东的净利润”。所以拿到包先读字段说明文档没有文档就抽几行数据比对单位量级。我一般会先检查数据年份的取值分布和认定批次的取值分布确认区间的真实边界。这比看字段名更直观。用一句data.groupby(认定批次)[数据年份].min()就能看到每个批次的时间跨度发现异常再回头查口径。2.3 关键字段选型分析小巨人企业最常用的15个字段清单面对几十个字段真正高频参与计算的核心字段其实有限。基于我做企业评价和投贷分析的经验长期围绕下面这些列转字段常见命名含义常用单位注意点统一社会信用代码企业唯一标识文本必须按字符串读入企业名称认定时名称文本可能发生更名认定批次第几批公示文本去重时优先用代码数据年份会计年度整数与批次年份分开资产总额年末资产合计万元/元确认期末还是期初负债总额年末负债合计万元/元资产负债率必备所有者权益净资产万元/元资产减负债可校验营业收入主营业务收入万元/元确认是否含其他业务营业成本主营成本万元/元毛利率计算净利润税后利润万元/元亏损为负数研发费用当年研发投入万元/元口径差异最大应收账款期末余额万元/元周转指标分母存货期末余额万元/元制造业敏感经营活动现金流净额经营现金净流入万元/元与净利润对不上要警惕从业人员数年末人数人人均指标用选字段的原则是“少而稳”优先选财务报表里标准化程度高的科目比如营业收入和资产总额。研发费用这类字段在不同企业的确认口径差异很大作为辅助指标可以单独拿来排名容易误导。3. 把压缩包变成企业×年份宽表Python清洗流程与参数说明明确了结构这一章解决核心问题怎么把一堆杂乱的原始表清洗成能直接做回归、做排名的宽表。我用 Python 加 pandas不推荐用 Excel原因是涉及多批次合并、单位探测和大量缺失值处理脚本可以重复跑Excel 点选操作无法沉淀成流程。3.1 解压与目录检查先看文件结构再写代码拿到 zip 先解压后列目录确认里面有没有说明文档和独立的名单表。这步能避免读错文件或忽略关键映射表。import zipfile import os zip_path 专精特新“小巨人”-财务数据第1-5批2013-2024年.zip extract_dir ./data with zipfile.ZipFile(zip_path, r) as z: z.extractall(extract_dir) for root, _, files in os.walk(extract_dir): for f in files: print(os.path.join(root, f))这段代码先把压缩包完整解压到data目录再遍历所有文件目的是快速确认表数量、文件格式和命名规律。如果发现多张表命名不规律建议先建一个names_mapping字典把实际文件名映射成统一的逻辑表名后续读取就不会混乱。参数上extractall会把包内路径原样展开避免手动建目录。如果你只想要其中的财务表可以用z.read(具体文件名)直接读单个文件不必全量解压。我习惯全量解压的原因是要保留说明文档后续核对字段口径时随时能翻。3.2 字段类型修正与缺失值处理读出来先做三件事数据包里的企业标识和年份字段经常被 Excel 转为科学计数法或奇怪的格式读入时不做类型修正后面全乱。读表后第一件事就是修正类型、强制数值、统计缺失三件事按顺序做。import pandas as pd df pd.read_csv( ./data/财务明细表.csv, dtype{统一社会信用代码: str}, encodingutf-8 ) num_cols [ 资产总额, 负债总额, 所有者权益, 营业收入, 营业成本, 净利润, 研发费用, 应收账款, 存货, 经营活动现金流净额, 从业人员数 ] for col in num_cols: df[col] pd.to_numeric(df[col], errorscoerce) df[数据年份] pd.to_numeric(df[数据年份], errorscoerce) df[认定批次] df[认定批次].astype(str).str.strip() print(df.dtypes) print(df.isnull().sum())这里的关键设计是把统一社会信用代码按str读入。很多信用代码有字母有数字直接按默认类型读会被当成数值前导零丢失且末位变成e17这类科学计数法。errorscoerce的作用是把无法转数值的脏数据变成NaN这样后边统计缺失时能看到到底有多少脏值而不是让计算直接报错。年份也要转数值。有些包里的年份写成“2022年”“2022/12/31”之类的格式必须清洗成纯整数。astype(str).str.strip()用于去掉认定批次字段里的空格和多余字符防止“第4批”和“第4批 ”被当成两个类别。3.3 生成企业×年份宽表合并名单表再透视原始明细表大概率是长表一行是一个企业在某年的财务记录。为了后续做面板回归或横向对比需要把它转成“企业×年份”的宽表。先关联名单表拿到批次信息再按营收、净利润等核心指标做透视。name_list pd.read_csv(./data/名单表.csv, dtype{统一社会信用代码: str}) detail df.merge( name_list[[统一社会信用代码, 企业名称, 认定批次]], on统一社会信用代码, howleft ) detail detail.dropna(subset[统一社会信用代码, 数据年份]) for col in [营业收入, 净利润, 资产总额]: detail[col] detail[col].fillna(0) wide detail.pivot_table( index[统一社会信用代码, 企业名称, 认定批次], columns数据年份, values营业收入, aggfuncsum ) wide.to_csv(./output/营收宽表.csv, encodingutf-8-sig) print(wide.shape)合并时用howleft保住财务记录名单表里没有的代码会显示为空方便后边排查“有财务无名单”的异常数据。fillna(0)要慎用它适合“该企业当年确实没有这条收入记录”的场景但在判断企业是否停业时会掩盖真实缺失。所以我只对核心金额列做填充并且在输出表里额外保留一个记录数列标记该年份是否真的有原始记录。pivot_table的aggfuncsum用于同一企业同年出现多行入库记录时合并正常情况下一般是一行出现多条就得多长个心眼——后面避坑章节会具体讲。4. 财务指标计算与批量落库从原始科目到可用指标宽表只是“材料”真正能指导分析的是衍生指标。财务指标计算的本质是让不同规模、不同行业的企业可以在同一尺度上比较。第3批和第5批的营收规模可能差几十倍光看绝对值没有意义必须转成比率和增速。4.1 五类核心指标的计算逻辑成长、盈利、营运、偿债、创新小巨人企业的分析重点集中在五个维度成长性、盈利质量、营运效率、偿债能力和创新投入。每个维度对应一组指标计算逻辑不复杂难在分母处理和数据质量。维度指标计算公式参数说明成长性营收同比增速(本年营收 - 上年营收) / 上年营收上年营收为0或缺失时置空成长性三年净利润复合增速(末年净利润 / 基年净利润)^(1/3) - 1基年净利润必须大于0盈利质量毛利率(营业收入 - 营业成本) / 营业收入营业收入为0时置空盈利质量净利率净利润 / 营业收入亏损企业为负盈利质量ROA净利润 / 资产总额资产总额为0时置空营运效率应收账款周转率营业收入 / 应收账款应收账款为0时置空营运效率存货周转率营业成本 / 存货适用于制造业为主偿债能力资产负债率负债总额 / 资产总额保留两位小数偿债能力流动比率流动资产 / 流动负债需数据支持流动科目创新投入研发费用率研发费用 / 营业收入口径要与企业披露一致我的经验是优先做成长性和盈利质量的指标这两类最常被用来给企业划档。营运效率指标在小巨人群体里要分组看机械制造和软件企业的存货逻辑完全不同混在一起算周转率几乎没有参考价值。def calc_indicators(d): d d.copy() d[营收同比增速] d.groupby(统一社会信用代码)[营业收入].pct_change() d[毛利率] (d[营业收入] - d[营业成本]) / d[营业收入] d[净利率] d[净利润] / d[营业收入] d[ROA] d[净利润] / d[资产总额] d[资产负债率] d[负债总额] / d[资产总额] d[研发费用率] d[研发费用] / d[营业收入] return d.replace([float(inf), -float(inf)], 0)代码里有几个关键选择。pct_change()默认按行顺序计算所以必须先对数据按统一社会信用代码 数据年份排序否则算出的同比增速是乱的。groupby(统一社会信用代码)保证增速在企业内部计算不会跨企业做减法。最后一行把inf统一替换成0避免除零导致后续分析报错。计算时有个容易被忽略的参数同比增速需要完整的两年数据。如果企业某年缺失pct_change()返回NaN这是正确行为不要用fillna(0)掩盖否则会凭空造出“增长率0%”的错误信息。缺失在分组统计时自然体现为更小的样本量比编造值更诚实。4.2 批量写入SQLite指标表与主表的关联设计清洗和指标计算完成后建议落到SQLite里长期使用。用关系型数据库管理的好处是后续增量更新第6批、第7批数据时可以只插入新记录不用重算整份宽表。我一般建三张表enterprise企业主表、financial_detail财务明细表、indicator指标表。import sqlite3 conn sqlite3.connect(./output/小巨人财务库.db) detail.to_sql(financial_detail, conn, if_existsreplace, indexFalse) indicators.to_sql(indicator, conn, if_existsreplace, indexFalse) conn.execute(CREATE INDEX IF NOT EXISTS idx_code_year ON financial_detail(统一社会信用代码, 数据年份)) conn.execute(CREATE INDEX IF NOT EXISTS idx_code ON indicator(统一社会信用代码)) conn.commit() conn.close()表结构设计上financial_detail存原始科目indicator存衍生指标两表以统一社会信用代码 数据年份为关联键。索引不是随便建的idx_code_year要覆盖两表联查最常用的过滤条件否则几万条记录全表扫描会让查询明显变慢。写入后要做一个完整性校验分别查询两张表的记录数再核对企业数 × 年份数是否对齐。明细表和指标表行数不一致往往是合并时how参数选错或者透视时dropna吞掉了样本。这种问题在入库前发现容易入库后排查麻烦所以我会把校验脚本直接放在入库流程的最后每次跑完自动打印对账结果。这才算一次完整入库。5. 小巨人财务数据清洗避坑5个让人翻车的细节处理这类数据时真正让我吃过亏的往往不是技术问题而是口径和业务逻辑问题。下面五条按“现象→原因→解决”来写每一条都是多次遇到过的。5.1 现象同一家企业在结果表中出现多行统计企业数量时被重复计数原因小巨人名单分批次公示一家企业可能在多个批次中被收录也可能在第一批、第二批均出现过类似但被更名的记录此外同一集团下多个子公司分别认定名称相似人眼能分辨代码却分不开。解决以统一社会信用代码作为去重唯一键。如果数据包没有信用代码只能用企业名称加注册地联合去重。去重时明确保留规则我一般保留最新批次因为代表当前有效认定身份如果做的是历史回测就保留最早批次。去重后输出一行说明标注“按信用代码去重后企业总数为N”让使用者知道统计口径。5.2 现象某企业资产总额只有10另一家却有三千万量级相差离谱原因单位不统一。有的企业原始报表以“万元”计有的以“元”计数据包整理时未做换算导致同一列混用两种单位。这个问题隐蔽性极强做均值排序时单个单位错误就能污染整批结果。解决读入后先做单位探测。计算资产总额和从业人数的量级比值正常企业人均资产在万元级别如果某企业比值比中位数高千倍以上大概率是单位问题。统一换算为“万元”median_ratio df[资产总额] / df[从业人员数].replace(0, pd.NA) df[单位标记] 万元 df.loc[median_ratio median_ratio.median() * 1000, 单位标记] 元 df.loc[df[单位标记] 元, 资产总额] df[资产总额] / 10000这里用中位数而不是均值做基准是因为均值容易被极端值带偏。标记列务必保留不要处理完就删掉。后续人工抽样验证时还要靠它反查误判的记录。5.3 现象同一指标在前后年份的变化异常营收翻100倍后又跌回原值原因企业从新三板转板到北交所或科创板披露口径从“母公司报表”切到“合并报表”或从“境内报表”切到“全集团报表”导致报表主体变化金额不连续。解决在明细表里为每行记录增加报表类型字段取值至少区分“合并报表”和“母公司报表”。计算时间序列指标时先按报表类型分组严禁混用。如果你无法判断某条记录的报表类型宁可直接从时间序列分析中剔除该企业也不要让口径断层毁掉整个面板。5.4 现象年份字段出现“2023年第4批”“2024年认定”这类混合值原因整理者把认定公示年份和数据年份写在了同一个字段里或者用户下载时把名单表的批次发布日期误认为是财务数据年度。解决建一个批次-公示年份映射表把认定信息与财务年度彻底分离。数据年份必须是会计年度数字只要是非整数值就做非法值标记。处理后将两列分别输出方便使用者理解“第4批企业在2013-2024年间的财务表现”和“2023年公示的第4批企业”是两个不同维度的查询条件。5.5 现象大量企业净利润为0分析时以为是亏损预警原因数据包对缺失值直接填了0尤其非上市中小企业的财务记录不全整理者用0表示“无记录”导致真实亏损、持平、缺失三类情况混在一起。解决清洗时严格区分0和NaN。0代表企业真实填报为零NaN代表没有记录。统计损益时先展示缺失率缺失率超过30%的指标不要进入建模流程。对净利润缺失但营业收入完整的企业用同批次同行业的中位值做敏感性分析而不是直接填充0。这个细节决定了分析结论能不能站住脚。6. 让数据真正可用抽样验证方法与批次更新跟踪清洗完成不代表数据可信。每次入库后我都会做一遍抽样验证方法很简单从五批企业里随机抽10家每家取最近一个完整年度的营业收入和净利润去企业公开披露的渠道核对量级是否一致。核对时不要求精确到千元只比对数量级和正负号方向这已经能暴露大部分单位错误和字段错位。除了横向核对还要做时间序列连续性抽查。任选一家企业打印它连续5年的营收走势正常情况下应该平缓变化。出现一年暴涨、一年归零的锯齿曲线优先怀疑报表类型切换或缺失值填0而不是企业经营真的剧烈波动。我踩过最深的坑就是单位不统一当时算出的区域营收均值失真幸好发布前做了抽样核对才没让错误结论出门。现在任何面板输出前都会强制跑一遍极值检查找出营收或资产变动超过10倍的记录逐条确认原因。后续第6批、第7批名单公示后增量更新的做法是新版名单先和现有enterprise表按信用代码比对已存在的企业只更新批次状态不重插财务记录新出现的企业才追加。历史年份的财务数据要保留不动因为财务指标被修改会直接影响面板回归的稳定性。这套流程跑通之后这份数据就能从“一次性分析素材”变成“可持续跟踪的数据库”。希望帮到你。本文还有配套的精品资源点击获取