用STATA复刻经典文献:手把手教你计算企业避税程度(附完整do文件与数据)
在实证研究领域,能够准确复现经典文献的核心指标是每位研究者必备的基础能力。当我们阅读《公司避税活动与内部代理成本》这类发表在顶级期刊的论文时,常常会被其中精巧的指标设计所吸引,但真正动手实现时却面临诸多挑战——从原始数据清洗到复杂公式的代码实现,每一步都可能成为阻碍研究进程的绊脚石。
本文将扮演"实验室助手"的角色,带你完整走通企业避税程度计算的实战流程。不同于单纯的理论讲解,我们会聚焦在Wind数据预处理、STATA代码逐行解析、异常值处理技巧等实操环节,确保你不仅能理解RATE_diff、LRATE_diff、BTD、DDBTD这四个关键指标的经济含义,更能独立完成从原始数据到最终结果的完整计算链条。
1. 研究准备与数据清洗
1.1 数据来源与样本筛选
我们使用的原始数据来自Wind金融终端,时间跨度为2000-2020年。在导入STATA前,需要特别注意以下样本筛选规则:
* 导入原始数据 import excel "raw_data.xlsx", sheet("Sheet1") firstrow clear * 执行样本筛选 drop if industry == "金融业" // 剔除金融行业 drop if profit_before_tax <= 0 // 剔除税前利润非正样本 drop if effective_tax_rate <0 | effective_tax_rate >1 // 剔除异常实际税率金融行业样本通常具有特殊的税务特征,而亏损企业(税前利润≤0)的税负计算逻辑与盈利企业存在本质差异。实际税率超出[0,1]区间的样本显然不符合经济实质,这些都需要在分析前严格剔除。
1.2 关键变量识别与缺失值处理
原始数据包含数十个字段,我们需要重点关注以下核心变量:
| 变量名 | 含义 | 计算用途 |
|---|---|---|
| stkcd | 证券代码 | 面板标识 |
| year | 年度 | 时间维度 |
| profit_before_tax | 税前利润 | BTD计算 |
| tax_expense | 所得税费用 | 实际税率计算 |
| deferred_tax | 递延所得税 | 应纳税所得额计算 |
| operating_cf | 经营活动现金流 | DDBTD计算 |
| total_assets | 总资产 | 标准化分母 |
处理缺失值的实用命令:
* 检查各变量缺失情况 misstable sum * 按年份统计缺失模式 bysort year: misstable sum * 删除关键变量缺失的观测 foreach var in profit_before_tax tax_expense total_assets { drop if missing(`var') }2. 核心指标计算详解
2.1 实际税率与RATE_diff计算
名义税率减去实际税率的差值(RATE_diff)是最直观的避税衡量指标。在STATA中实现时需注意:
* 计算实际税率 gen effective_rate = tax_expense / profit_before_tax * 获取法定税率(需手动匹配不同年份政策) gen statutory_rate = . replace statutory_rate = 0.33 if year <= 2007 replace statutory_rate = 0.25 if year > 2007 * 计算税率差值 gen RATE_diff = statutory_rate - effective_rate * 处理极端值 sum RATE_diff, detail replace RATE_diff = . if RATE_diff < -1 | RATE_diff > 1 // 剔除经济意义不合理的观测注意:2008年企业所得税法修订导致法定税率变化,实际操作中需根据公司注册地和优惠政策调整具体数值。
2.2 五年移动平均LRATE_diff计算
长期避税程度通常用五年移动平均来衡量,这涉及面板数据的时间窗操作:
* 按公司排序 sort stkcd year * 计算移动平均 by stkcd: gen LRATE_diff = (RATE_diff + RATE_diff[_n-1] + RATE_diff[_n-2] + /// RATE_diff[_n-3] + RATE_diff[_n-4]) / 5 * 验证计算 list stkcd year RATE_diff LRATE_diff if stkcd == "600000" & inrange(year,2010,2015)对于非平衡面板数据,更稳健的做法是使用tssmooth ma命令:
tsset stkcd year tssmooth ma LRATE_diff_smooth = RATE_diff, window(4 0 0) // 当前年及前四年2.3 会计-税收差异BTD计算
BTD指标需要分步计算应纳税所得额:
* 计算当期所得税费用 gen current_tax = tax_expense - deferred_tax * 计算应纳税所得额 gen taxable_income = current_tax / statutory_rate * 计算BTD gen BTD = (profit_before_tax - taxable_income) / total_assets * 标准化处理 winsor2 BTD, cuts(1 99) replace常见问题处理:
- 当statutory_rate为0时会产生缺失值,需提前处理特殊样本
- 递延所得税数据缺失时,可考虑用0替代(需在文献中说明)
2.4 扣除应计利润的DDBTD计算
DDBTD的计算需要先建立总应计利润模型:
* 计算总应计利润TACC gen TACC = (net_profit - operating_cf) / total_assets * 分年度分行业回归 egen industry_year = group(industry year) xtset stkcd year * 执行回归获取残差 qui reg BTD TACC i.industry_year predict residual, residuals * 计算公司层面均值 bysort stkcd: egen u = mean(residual) * 生成DDBTD gen DDBTD = u + residual提示:对于大样本数据,上述回归可能消耗较多内存,建议使用
reghdfe命令提高效率。
3. 结果验证与稳健性检验
3.1 描述性统计对比
将计算结果与原文表格进行对比是验证正确性的关键步骤:
* 生成描述性统计 estpost tabstat RATE_diff LRATE_diff BTD DDBTD, /// stat(mean sd p25 p50 p75 N) columns(statistics) * 输出LaTeX格式 esttab using "table1.tex", replace /// cells("mean(fmt(4)) sd(fmt(4)) p25 p50 p75 count") /// noobs nomtitle nonumber典型问题排查:
- 均值差异超过0.05可能表明计算逻辑有误
- 标准差过大需检查异常值处理是否充分
- 样本量不一致需核对筛选条件
3.2 指标相关性分析
各避税指标间应存在合理相关性:
* 计算相关系数矩阵 pwcorr RATE_diff LRATE_diff BTD DDBTD, star(0.05) * 可视化检验 graph matrix RATE_diff LRATE_diff BTD DDBTD, half预期结果:
- RATE_diff与LRATE_diff相关性应最高(>0.7)
- BTD与DDBTD相关性适中(0.4-0.6)
- 横截面指标与长期指标相关性较低属正常现象
4. 高级技巧与实战建议
4.1 面板数据特殊处理
对于非平衡面板数据,移动平均计算需要特别处理:
* 创建完整时间序列框架 fillin stkcd year * 标记实际存在的观测 gen valid = !missing(RATE_diff) * 计算有效观测的移动平均 by stkcd: gen valid_count = sum(valid) by stkcd: gen moving_sum = sum(RATE_diff) if valid by stkcd: gen LRATE_diff_adv = (moving_sum - moving_sum[_n-5]) / /// (valid_count - valid_count[_n-5]) if _n >=54.2 并行计算加速处理
大数据集下可使用并行计算提高效率:
* 安装并行计算包 ss install parallel, replace * 设置并行进程 parallel setclusters 4 * 并行执行回归 parallel bs, reps(100): reg BTD TACC i.industry i.year4.3 结果可视化呈现
用图形直观展示指标特征:
* 绘制核密度图 kdensity RATE_diff, title("RATE_diff分布") normal * 按年度绘制均值趋势 collapse (mean) RATE_diff LRATE_diff, by(year) twoway (line RATE_diff year) (line LRATE_diff year), /// legend(label(1 "当期差异") label(2 "五年平均"))5. 完整流程封装与自动化
将整个分析流程封装为do文件时,建议采用模块化结构:
/* 主程序框架示例 */ version 16 clear all set more off // 参数设置 global raw_data "input.xlsx" global output "results.dta" // 执行各模块 do "1_data_prep.do" do "2_calculation.do" do "3_validation.do" do "4_visualization.do" // 保存最终结果 save "$output", replace在do文件中加入这些实用元素:
timer命令统计各步骤耗时capture log close确保日志文件正常关闭assert语句验证关键变量范围- 自定义
program封装重复操作
实际项目中,我会在do文件头部添加详细的元数据注释:
/* Title: 企业避税指标计算程序 Author: [你的姓名] Date: 2023-08-20 Version: 1.2 Input: - Wind导出的原始数据.xlsx - 年度法定税率对照表.csv Output: - 计算结果.dta - 描述性统计表.tex - 分布图.png Dependencies: - reghdfe - winsor2 - parallel */这种结构化的do文件不仅方便自己日后复查,也便于团队协作时他人快速理解代码逻辑。当需要调整某个计算步骤时,可以直接定位到相应模块进行修改,而不必通读全部代码。