简介这份资源是CSMAR中国上市公司财务报表数据库的说明文档面向会计、金融、经济管理领域的研究者与数据分析人员用于了解该数据库的结构设计与字段规范。文档以2006年企业会计准则为框架涵盖财务年报、中报、季报、金融财务及B股财务等数据并按资产负债表、利润表、现金流量表直接法与间接法重新分类适用于一般行业及银行、保险、证券等特殊行业。资源包共1个doc文件大小约856KB内容包含数据库简介、字段提取方式、报表类型说明及财务附注库的29张表、430个字段等细节便于读者快速掌握会计期间区分、历史数据兼容及新增科目列示等要点。目前已有211人学习下载适合需要理解财务报表数据结构、开展实证研究或搭建分析模型的人员参考。1. 从一份“CSMAR中国上市公司财务报表数据库.doc”说起它到底是什么能解决什么问题如果你手里拿到一份名为“CSMAR中国上市公司财务报表数据库.doc”的文件第一反应大概率是这是一份数据字典还是一份导出的数据表实际上它更可能是一份数据库说明文档里面记录了表结构、字段含义、单位、报告期格式、缺失值约定以及若干张核心财务报表的字段清单。它解决的不是“数据从哪来”而是“数据怎么读、怎么对齐、怎么避免把单位搞错”。这类文档最常见的落地场景是某高校课题组或某公司研究团队需要把CSMAR的财务数据导入本地数据库做实证分析、因子回测或行业对比。适合谁适合已经拿到数据、但被字段名、合并键、单位、报告期格式卡住的人。如果你正准备从零搭建一个财务数据仓库这份文档就是你的“黑匣子说明书”——不看它后面全是玄学。2. 把.doc读成可查询的数据字典字段解析与表结构还原2.1 先别急着写代码把文档里的表结构抽出来拿到一份.doc格式的数据库说明第一步不是打开Python而是先把它转成可检索的文本。常见做法是用python-docx读取段落和表格把每个字段的“字段名、中文含义、类型、单位、备注”抽成结构化数据。很多文档里字段说明是以表格形式存在的直接复制粘贴会丢格式所以用脚本抽更稳。# 用 python-docx 抽取 .doc/.docx 中的表格与段落 # 注意老式 .doc 需要先另存为 .docxpython-docx 不直接支持 .doc from docx import Document import pandas as pd doc Document(CSMAR中国上市公司财务报表数据库.docx) rows [] # 遍历文档中的所有表格假设字段说明在表格里 for table in doc.tables: for row in table.rows: cells [c.text.strip() for c in row.cells] # 只保留至少有4列的行避免把标题行也收进来 if len(cells) 4: rows.append(cells[:5]) df pd.DataFrame(rows, columns[字段名, 中文含义, 类型, 单位, 备注]) df df.drop_duplicates(subset[字段名]) df.to_csv(csmar_finance_dict.csv, indexFalse, encodingutf-8-sig) print(df.head(10))这段代码的逻辑很直接把文档里所有表格行拉平按列切分去重后落成CSV。参数上唯一需要调的是cells[:5]——如果你的文档表格列数更多比如还有“是否主键”“取值范围”就把切片改成cells[:7]同时把columns列表补全。跑完之后你会得到一份可以grep、可以merge的字段字典后面写SQL或Pandas时直接查。注意老式.doc文件不能直接被python-docx读取先用Word或LibreOffice另存为.docx这一步没有捷径。2.2 识别三张核心表资产负债表、利润表、现金流量表CSMAR的财务报表数据库通常按报表类型分表常见的是资产负债表、利润表、现金流量表外加一个公司基本信息表。文档里会给出每张表的字段清单但不会告诉你“哪几个字段是合并键”。我一般会从文档里先找这几类字段字段类型常见字段名示例作用公司标识Stkcd、Symbol跨表合并的主键报告期Accper、EndDate区分年报、季报报表类型Typrep区分合并报表与母公司报表金额字段A001000000、B001100000具体科目单位通常是元币种Currency多数为CNY少数为其他文档里如果写了“A001000000 代表货币资金”你就得把这个字段名和中文含义对应起来。很多新手直接拿字段名当列名用结果写SQL时完全不知道A001000000是什么这就是没读文档的代价。2.3 用Pandas把字段字典和实际数据对齐拿到字段字典后下一步是把它和你实际下载的数据文件对齐。常见做法是读入一个样本CSV检查列名是否和字典里的“字段名”一致不一致的做映射。import pandas as pd # 读入字段字典和实际数据样本 dict_df pd.read_csv(csmar_finance_dict.csv) sample_df pd.read_csv(sample_balance_sheet.csv, nrows100) # 找出实际数据里有、但字典里没有的列 actual_cols set(sample_df.columns) dict_cols set(dict_df[字段名].dropna()) missing_in_dict actual_cols - dict_cols print(数据里有但字典没记录的列, missing_in_dict) # 找出字典里有、但实际数据没出现的列可能是该表不包含 missing_in_data dict_cols - actual_cols print(字典有但数据没出现的列数量, len(missing_in_data))这段代码的价值在于它能帮你快速发现“文档和实际数据不一致”的情况。比如文档里写的是Accper实际数据里可能是AccPer大小写差异会导致合并失败。参数上nrows100只是抽样确认无误后可以去掉。如果发现大量字段对不上优先怀疑是报表类型或报告期筛选错了而不是数据本身有问题。3. 从文档到本地数据库建表、导入与报告期对齐3.1 建表时把单位统一成“元”别在分析阶段再换算文档里通常会注明“金额单位元”但有些导出文件可能是“万元”或“千元”。我一般会在建表阶段就统一成元避免后面做比率分析时反复乘除。建表SQL可以这样写-- 以资产负债表为例建一张本地表 CREATE TABLE balance_sheet ( stkcd VARCHAR(10) NOT NULL, -- 公司代码 accper DATE NOT NULL, -- 报告期 typrep VARCHAR(2), -- 报表类型A合并B母公司 currency VARCHAR(10) DEFAULT CNY, a001000000 DECIMAL(20,2), -- 货币资金单位元 a002000000 DECIMAL(20,2), -- 应收账款 a003000000 DECIMAL(20,2), -- 存货 PRIMARY KEY (stkcd, accper, typrep) );主键设计是关键stkcd accper typrep能唯一确定一行。如果你只按stkcd accper建主键合并报表和母公司报表会冲突。参数上DECIMAL(20,2)足够容纳大多数上市公司的金额但如果你要存银行或保险公司的数据建议改成DECIMAL(24,2)。typrep字段一定要保留否则后面做合并报表分析时还得回去翻原始文件。3.2 用Python批量导入CSV处理编码和日期格式CSMAR导出的CSV常见编码是gbk或utf-8-sig日期格式可能是YYYY-MM-DD也可能是YYYYMMDD。导入时如果不处理后面按报告期筛选就会翻车。import pandas as pd from sqlalchemy import create_engine engine create_engine(mysqlpymysql://user:passlocalhost:3306/finance_db) # 读入时指定编码避免中文列名乱码 df pd.read_csv(balance_sheet_2023.csv, encodinggbk) # 统一报告期格式 df[accper] pd.to_datetime(df[accper].astype(str), format%Y%m%d, errorscoerce) # 金额字段转数值去掉千分位逗号 for col in [a001000000, a002000000, a003000000]: df[col] pd.to_numeric(df[col].astype(str).str.replace(,, ), errorscoerce) # 写入数据库按主键冲突则替换 df.to_sql(balance_sheet, engine, if_existsappend, indexFalse, chunksize5000)逻辑说明pd.to_datetime里的format%Y%m%d要按实际格式改如果数据是2023-12-31就改成format%Y-%m-%d。errorscoerce会把无法解析的日期变成NaT方便后面排查。chunksize5000是为了避免一次性写入太大导致内存溢出。如果主键冲突报错说明数据里有重复行需要先按主键去重再写入。3.3 报告期对齐年报、季报别混在一起算文档里会说明Accper字段的含义但不会告诉你“季报的累计值和单季值怎么区分”。常见坑是把一季报的营业收入直接和年报比结果同比增速完全不对。我一般会在ETL阶段加一个report_type字段标记“Q1、Q2、Q3、Annual”并且只在对齐后再做计算。# 根据报告期月份标记报表类型 df[report_type] df[accper].dt.month.map({ 3: Q1, 6: Q2, 9: Q3, 12: Annual }) # 只保留年报做年度分析 annual_df df[df[report_type] Annual].copy() # 如果要算单季值用累计值做差分 df df.sort_values([stkcd, accper]) df[single_quarter_revenue] df.groupby(stkcd)[revenue].diff()这段代码的关键是groupby(stkcd).diff()——它把同一公司的累计值转成单季值。参数上sort_values必须先按公司和报告期排序否则差分结果会错位。如果你不做这一步直接拿累计值做环比出来的数字连自己都不敢信。4. 避坑与排查字段、单位、报告期最容易翻车的5个地方4.1 现象合并报表和母公司报表混在一起资产总额翻倍原因Typrep字段没做过滤直接把两种报表类型都导入了同一张表。解决在ETL阶段加WHERE typrep A或者在Pandas里df df[df[typrep] A]。如果你确实需要两种报表就建两张表别混在一起。4.2 现象金额字段出现负数但文档里写的是“正数表示借方”原因CSMAR的某些科目用正负号区分借贷方向文档里会说明但新手容易忽略。解决先读文档里的“备注”列确认符号含义。如果确实需要统一方向在ETL阶段对特定科目做abs()或乘-1但一定要在代码里写注释否则半年后自己都忘了为什么。4.3 现象报告期是“2023-12-31”但导入数据库后变成“2024-01-01”原因时区或日期解析格式不对。解决用pd.to_datetime时显式指定format不要依赖自动推断。如果数据里只有日期没有时间导入MySQL时用DATE类型不要用DATETIME避免时区偏移。4.4 现象字段名大小写不一致合并时对不上原因文档里写Accper实际数据里是AccPer或者反过来。解决在读取CSV后统一转成小写或大写再做映射。我一般会在ETL脚本开头加一行df.columns [c.lower() for c in df.columns]然后字典里的字段名也统一小写。4.5 现象缺失值被当成0算出来的比率完全失真原因CSMAR里缺失值可能是空字符串、NULL、-9999或0文档里会说明。解决读文档里的“缺失值约定”在Pandas里用na_values参数指定。比如pd.read_csv(..., na_values[, NULL, -9999])然后再做填充或剔除。千万别直接fillna(0)否则资产负债率可能算出负数。5. 进阶用法用字段字典自动生成建表语句与校验规则5.1 从字段字典生成DDL减少手写错误当你把文档里的字段字典抽成CSV后可以进一步用它生成建表语句。这样每次新增一张表不用手动敲字段名和类型。import pandas as pd dict_df pd.read_csv(csmar_finance_dict.csv) # 只保留需要的字段并做类型映射 type_map {数值: DECIMAL(20,2), 文本: VARCHAR(50), 日期: DATE} ddl_lines [CREATE TABLE auto_generated (, id INT AUTO_INCREMENT PRIMARY KEY,] for _, row in dict_df.iterrows(): field row[字段名] col_type type_map.get(row[类型], VARCHAR(100)) comment row[中文含义] ddl_lines.append(f {field} {col_type} COMMENT {comment},) ddl_lines.append( UNIQUE KEY uk_stkcd_accper (stkcd, accper)) ddl_lines.append();) print(\n.join(ddl_lines))这段代码的价值在于字段字典一变DDL跟着变不用手动同步。参数上type_map要根据文档里的实际类型描述调整比如“字符”对应VARCHAR“金额”对应DECIMAL。UNIQUE KEY那行需要你根据实际主键改别直接抄。5.2 用校验规则做数据质量检查建完表之后我一般会跑几条校验SQL确认数据没有明显异常。比如-- 检查同一公司同一报告期是否有重复行 SELECT stkcd, accper, COUNT(*) AS cnt FROM balance_sheet GROUP BY stkcd, accper, typrep HAVING cnt 1; -- 检查资产总计是否等于负债加所有者权益允许一定误差 SELECT stkcd, accper, a001000000, a002000000 FROM balance_sheet WHERE ABS(a001000000 - (a002000000 a003000000)) 1;第一条查重复第二条查勾稽关系。参数上ABS(...) 1里的1是容忍度因为四舍五入可能差几分钱。如果差得离谱说明字段映射错了回去翻文档。5.3 一个我踩过的坑别把文档里的“示例值”当成真实数据有一次我在文档里看到某个字段的示例值是1000000就以为所有公司的货币资金都在百万级别结果建表时把类型设成DECIMAL(10,2)导入时直接溢出。后来才发现文档里的示例只是随便写的真实数据里有的公司货币资金是几十亿。所以建表类型一定要按业务上限来设别按文档示例来。我现在的习惯是金额字段一律DECIMAL(20,2)文本字段一律VARCHAR(100)起步不够再改。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?