使用 Stata 统一海关税号编码

一、为什么要统一海关税号编码

海关数据中的商品编码(税号编码),也就是我们常说的 HS 编码(Harmonized System,商品名称及编码协调制度),是国际贸易中对商品进行分类的通用语言。它有两个特点需要特别注意:

  1. 前 6 位是国际通用码,全球统一;后 2 位是国内子目,由各国海关自行扩展。我国海关数据中的商品编码通常为 8 位。
  2. HS 编码每隔约 5 年修订一次。世界海关组织(WCO)会新增、删除、拆分或合并部分税目,因此不同年份的数据使用的是不同版本的 HS 编码。

在我们的海关数据(2000–2016 年)中,年份与 HS 版本的对应关系如下:

library(tidyverse)

year_hs <- tibble(
year = 2000:2016,
hs_version = c(rep("HS1996", 2), rep("HS2002", 5),
rep("HS2007", 5), rep("HS2012", 5))
)
year_hs %>%
group_by(hs_version) %>%
summarise(年份区间 = paste0(min(year), "–", max(year)), .groups = "drop") %>%
rename(HS版本 = hs_version) %>%
knitr::kable(align = "c")

如果直接把跨年份的数据放在一起做面板分析,同一个商品在不同年份可能对应不同的编码,会导致匹配错误。因此在做任何跨期分析之前,必须先把所有年份的编码统一到同一个版本。

本讲义演示如何用 Stata 把海关数据统一到 HS1996 / HS2002 / HS2007 / HS2012 / HS2017 五个版本,并保留原始编码。为节省篇幅,正文以 2014–2016 年(均为 HS2012 版本)的数据为例;处理全部年份只需把年份宏改为 2000 2001 ... 2016 即可。完整可运行代码见配套脚本 使用 Stata 统一海关税号编码.do 与 绘制缺失比例图.do。

二、数据准备

原始海关数据体量庞大,我们先用 Stata 把每年的数据抽取出 newhgid(观测 ID)、年份、商品编码 三个变量,另存为分年 .dta 文件,减小后续读取与转换的压力:

cd "/Users/ac/Desktop/使用 Stata 统一海关税号编码/"
cap mkdir "海关数据商品编码分年"
forval y = 2000/2016 {
use ".../海关数据分年/`y'.dta", clear
keep newhgid 年份 商品编码
save "海关数据商品编码分年/`y'", replace
}

本项目中已备好 海关数据商品编码分年/2014.dta、2015.dta、2016.dta 三个分年文件,可直接进入下一步。

三、读取 HS 转换表

版本之间的转换依赖联合国统计司提供的官方对照表。每个 Excel 文件里通常有两张表:

  • Conversion Table:只保留在新版本中仍有对应关系的旧码,覆盖不全;
  • Correlation Table:记录 1:1 / 1:n / n:1 / n:n 全部对应关系,覆盖度更高。

转换表可以从这里下载:https://unstats.un.org/unsd/classifications/Econ

因此我们统一使用 Correlation Table。所有转换表方向都是「新版本 → 旧版本」,其中第 1 列(A 列)是新版本码、第 3 列(C 列)是旧版本码。我们分别按正向(旧→新)和反向(新→旧)读取,一对多时取第一个匹配。

读取逻辑用一个小程序 read_corr 封装,统一把编码清洗为 6 位字符串(保留前导 0),并保存为实体中间文件 中间文件/corr_XXtoYY.dta:

*- 此处代码需下载讲义材料查看~

global rc_file "$convdir/HS 2002 to HS 1996 - Correlation and conversion tables.xls"
global rc_sheet "Correlation table"
global rc_out "$mid/corr_02to96.dta"
read_corr

global rc_file "$convdir/HS 2007 to HS 2002 Correlation and conversion tables.xls"
global rc_sheet "Correlation Tables"
global rc_out "$mid/corr_07to02.dta"
read_corr

global rc_file "$convdir/HS 2012 to HS 2007 Correlation and conversion tables.xls"
global rc_sheet "Correlation HS2012-HS2007"
global rc_out "$mid/corr_12to07.dta"
read_corr

global rc_file "$convdir/HS 2017 to HS 2012 Conversion And Correlation Tables.xlsx"
global rc_sheet "Correlation HS17-HS12"
global rc_out "$mid/corr_17to12.dta"
read_corr

四、构建完整查找表

有了相邻版本的对照关系后,就可以把它们链式拼接起来,为每个源版本构建一张包含全部 5 个目标版本的查找表。核心技巧是级联填充:若某一步转换缺失,则沿用上一版本的码,保证链条不中断。

先由 corr_* 派生出 8 张相邻版本的「旧→新 / 新→旧」映射表(实体保存为 中间文件/fwd_* 与 中间文件/bwd_*):

*- --- 正向(旧 -> 新)---
*- 此处代码需下载讲义材料查看~

*- --- 反向(新 -> 旧)---
use "$mid/corr_02to96.dta", clear
rename newer hs02
rename older hs96
duplicates drop hs02, force
save "$mid/bwd_0296.dta", replace

use "$mid/corr_07to02.dta", clear
rename newer hs07
rename older hs02
duplicates drop hs07, force
save "$mid/bwd_0702.dta", replace

use "$mid/corr_12to07.dta", clear
rename newer hs12
rename older hs07
duplicates drop hs12, force
save "$mid/bwd_1207.dta", replace

use "$mid/corr_17to12.dta", clear
rename newer hs17
rename older hs12
duplicates drop hs17, force
save "$mid/bwd_1712.dta", replace

以 HS2012 源(对应 2014–2016 年数据)为例——它向前只需一步到 HS2017,向后需要依次退回到 HS2007、HS2002、HS1996。下面构建这张完整查找表(实体保存为 中间文件/lookup_HS2012.dta):

*- --- HS2012 源:正向 hs12->hs17,反向 hs12->hs07->hs02->hs96 ---
*- 此处代码需下载讲义材料查看~

其余三个源版本(HS1996 / HS2002 / HS2007)的查找表构建方式完全类似,只是正向、反向链条的长度不同,完整代码见配套脚本 使用 Stata 统一海关税号编码.do。

我们来瞄一眼 HS2012 查找表长什么样(直接读取已生成的中间文件):

library(haven)
lk <- read_dta(file.path(base_dir, "中间文件", "lookup_HS2012.dta"))
head(lk, 6)

可以看到 source_code(原始 HS2012 码)与 hs12 列完全一致(恒等),而 hs96 / hs02 / hs07 / hs17 则是按版本链条推导出的对应编码。

五、执行编码转换

商品编码为 8 位:前 6 位参与版本转换,后 2 位(国内子目)保留不变。转换时按 source_code 与查找表做 merge,未匹配到的码保留原码,再把 5 个版本列拼回后 2 位子目,即完成统一:

*- 此处代码需下载讲义材料查看~

最终把 HS1996 ~ HS2017 五个新变量与原始变量一起写回 .dta,保存至 统一HS编码/。结果文件共 8 个变量:newhgid、年份、商品编码、HS1996、HS2002、HS2007、HS2012、HS2017。

六、统一质量:无法统一的比例

并非所有编码都能成功统一。若某个 6 位码不是目标版本中的合法 HS 编码(例如中国海关特有的 98/99 章特殊码、或数据录入错误),它就无法被统一,我们保留其原码。下面按观测数加权,统计 2014–2016 年各版本无法统一的比例。

「合法码集合」= 4 张 Correlation Table 双向列的并集(与 R / Python 版口径完全一致;各版本集合保存为 中间文件/valid_hsXX.dta)。统计逻辑如下:

*- 此处代码需下载讲义材料查看~

use "$proj/各版本缺失比例_2014-2016.dta", clear
export delimited using "$proj/各版本缺失比例_2014-2016.csv", replace

读入 Stata 生成的 CSV,整理成宽表(行=年份,列=目标版本):

library(tidyverse)
miss <- read_csv(file.path(base_dir, "各版本缺失比例_2014-2016.csv"))
miss %>%
select(year, target_version, unmatched_pct) %>%
pivot_wider(names_from = target_version, values_from = unmatched_pct) %>%
rename(年份 = year) %>%
knitr::kable(digits = 4, align = "c")

再用 Stata 的 heatplot(配合 palettes、colrspace)把结果画成热力图,直观展示各年各版本的无法统一比例(中文字体使用已安装的霞鹜文楷 LXGW WenKai):

use "$proj/各版本缺失比例_2014-2016.dta", clear
gen ylab = string(year) + " (" + source_version + ")"
rename unmatched_pct 无法统一比例

heatplot 无法统一比例 ylab target_version, ///
values(format(%4.3f)) ///
color(Oranges) ///
ylabel(, labsize(2.3) angle(0)) ///
xlabel(, labsize(2.8)) ///
ytitle("") ///
xtitle("统一目标版本", size(2.8)) ///
title("{bf:2014–2016 各版本 HS 编码无法统一的观测值比例}", size(3.4) color(black)) ///
subtitle("数值为占当年观测总数的百分比(%);括号内为该年数据的源 HS 版本", ///
size(2.4) color(gs7)) ///
caption("数据:中国海关商品编码 | HS 代码转换表来源:unstats.un.org/unsd/classifications/Econ", ///
size(2.0) color(gs9)) ///
graphregion(color(white) margin(l=6)) plotregion(color(white))

graph export "$proj/各版本缺失比例热力图_2014-2016.png", replace width(4800)

下面即为 Stata 实际生成的热力图:

从表中可以看出一个关键点(这也是「统一口径」与早期旧口径差异的来源):

  • 源版本自身恒等:2014–2016 均为 HS2012 源,故对照 HS2012 的比例很低(2014 年为 0%,2015 年 0.04%,2016 年 1.33%);
  • 对照越旧的版本缺失率越高:因为这些年份的数据本是 HS2012 时代的新码(含 98/99 章海关特殊码、980400 跨境电商监管码等),在 HS1996 / HS2002 的合法码集合里根本不存在,所以 2014 年对照 HS1996 高达 9.03%、对照 HS2002 为 6.82%;
  • 2016 年偏高:当年 980400 系列跨境电商监管码大量出现(单一码即占约 23 万条观测),这类码不属于国际 HS 体系,在任何版本下都无法统一,使 2016 年各版本缺失比例普遍高于 2014–2015 年。

注意:这里的「无法统一」= 原始 6 位码不在目标版本的合法 HS 编码集合中(与 R / Python 版口径一致)。这与早期一张旧口径「全部年份」热力图(仅当 5 个版本列全空才算缺失)含义不同——旧口径下 2014 年几乎为 0%,新口径下则为 9.03%。比较时务必使用同一口径。

七、小结

用 Stata 统一海关税号编码的完整流程可以概括为四步:

  1. 抽取变量:用 Stata 把大数据瘦身为 newhgid + 年份 + 商品编码;
  2. 读取 Correlation Table:正、反两个方向分别构建相邻版本的对照关系(封装为 read_corr 程序);
  3. 链式拼接 + 级联填充:为每个源版本构建到 5 个目标版本的查找表(保存于 中间文件/lookup_HS*.dta);
  4. merge 转换:前 6 位查表转换、后 2 位保留,缺失则保留原码,输出 8 变量 .dta。

除个别中国海关特殊码与数据录入错误外,绝大多数年份——尤其是对照自身源版本时——的统一成功率都在 99% 以上,可以放心用于跨期面板分析。所有中间结果均以实体 .dta 文件保存在 中间文件/ 目录,便于核查与复用。

如何运行本项目:在 Stata 中先 cd 到项目目录,再 do "使用 Stata 统一海关税号编码.do"(生成统一编码与缺失比例);接着 do "绘制缺失比例图.do" 生成热力图。注意 Stata 批处理模式(stata-mp -b do)对带空格的中文文件名支持不佳,若用命令行批量运行,建议通过一个 ASCII 路径的启动器 do 间接调用,或直接在原文件名上加引号。处理 2000–2016 全年份,只需把主脚本里的 local years 2014 2015 2016 改为完整年份列表即可。

点击这里跳转到 RStata 短书平台获取附件:使用 Stata 统一海关税号编码

评论