2000-2021海关数据库字段解析与清洗实战

发布时间:2026/11/6 7:02:57
2000-2021海关数据库字段解析与清洗实战

做微观实证的人,兜兜转转总会撞上这套数据。2000到2021年的海关进出口记录,企业级、月度、带全部原始变量,再配一份变量对照表和现成的清洗代码——这套组合摆在任何一个做贸易、做企业、做产业的研究者桌上,都算得上硬通货。我自己是2019年前后开始频繁用这类海关数据库的,前前后后跑过七八个版本,从最早只有Excel分表的残缺包,到后来几百G的原始文本,踩的坑比写的论文还多。这篇文章不讲虚的,就把这套海关数据库从字段结构、变量对照表的读法、清洗代码的逻辑,到实际跑数时那些没人写在说明书里的坑,一次性说透。适合三类人看:第一次接触这套数据、不知道该从哪个字段下手的新手;已经拿到数据但清理到一半发现对不上账的中级使用者;以及想把这批数据用出花来、做产品和企业层面分析的老手。全文的代码都可以直接改成你自己的路径跑起来。

1. 拆开这套2000-2021年海关数据库:字段到底长什么样

1.1 一张报关单被拆成多少个字段

理解这套数据最快的方式,是把每一条记录想成一张快递面单。寄件人、收件人、寄的什么东西、多重、值多少钱、走哪条线、什么时候发的——报关单本质上是同一个逻辑,只是把"寄件"换成了"进出口",把"快递公司"换成了报关行和口岸。所以你会看到字段天然分成几组:主体信息管"谁",商品信息管"什么",贸易信息管"怎么走的",数量金额管"多少",地域信息管"从哪到哪",时间字段管"什么时候"。

具体到实际操作里,最核心的几组字段是这样分布的:主体侧有收发货单位名称、企业海关编码、经营单位名称及编码;商品侧有HS编码、商品名称、规格型号;贸易侧有进出口标志、贸易方式、运输方式、成交方式、币制;数量金额侧有成交数量、计量单位、美元金额、人民币金额;地域侧有起运国或运抵国、境内收发货地、经营单位所在地、申报口岸;时间侧就是报关年月。这六组字段不是随便排的,它们决定了你后面能做哪一类分析。少了企业编码,你做不了企业异质性;少了HS编码,你做不了产品层面;少了贸易方式,你的样本边界就是糊的。

新手最容易犯的错,是拿到表就开始对着"金额"跑描述统计,完全不看旁边那些代码字段。结果样本里混进了保税区仓储转口、退运货物、加工贸易料件,最后算出来的"出口规模"跟统计年鉴对不上,还以为是数据有问题。其实数据没问题,是你没筛。

1.2 四个维度决定你的样本边界

我在动手清理之前,一定会先在纸上写清楚四个维度:时间窗口、主体范围、商品口径、流向口径。这四个维度一旦定死,后面所有筛选都是在执行,不会反复改主意。

时间窗口看着简单,实际有讲究。2000到2021这22年,跨越了HS编码的多次版本更替,也跨越了企业注册编码规则的若干次调整,还跨越了口岸合并与海关机构改革。如果你要做跨年比较,就必须在时间维度上先想清楚:是要连续面板,还是允许断点。连续面板意味着你得放弃那些只在部分年份出现的字段,比如某些年份才单独列出的规格型号或征免性质。

主体范围决定你按哪个主体归集。报关单上至少有三个主体身份:收发货单位、经营单位、申报单位。做生产型企业分析,通常用收发货单位;做外贸流通分析,往往用经营单位;做代理报关行为研究才用申报单位。这三个口径算出来的企业数量和集中度能差出一大截,别混着用。

商品口径是HS编码的位数问题。报关单上通常是8位本国子目,部分年份有10位。做跨年比较要退回6位国际通用子目,做本国政策研究才用8位。流向口径就是进出口标志,注意有些年份的字段里有"进出口"合并值,需要拆开处理。

1.3 变量对照表为什么比数据本身更值钱

很多人拿到压缩包,第一反应是先看数据有多大,把对照表扔在一边。我的经验正好反过来:先花两个小时把对照表从头翻到尾,比你先跑三天数据有用得多。原因很直接——这套数据里超过一半的字段是代码型的,你看到的是一串数字或短码,不懂代码表就等于在读天书。

对照表至少承担三项功能。第一项是翻译,把贸易方式、运输方式、成交方式、币制、国别地区、境内地区这些代码翻成人类能懂的文字。第二项是版本标注,同一个代码在不同年份可能含义不同,好的对照表会把年份区间标出来。第三项是口径提示,比如哪些贸易方式属于保税监管、哪些属于常规贸易,这类信息通常不会写在数据字典里,但对照表里会有端倪。

我见过太多人跳过这一步,直接用金额字段加总,最后发现跟公开统计差了30%,然后回头一个个字段试。那两个小时的功课,能省掉后面两周的返工。所以我在自己的项目里定了个规矩:拿到任何一份带代码表的数据,先按对照表把每个字段的可选值分布跑一遍,看看有没有"未知""其他""99"这种兜底值占了很大比例。这一步做完,你对这份数据的信任度就有了基本判断。

2. 拿到数据先做三件事:完整性自检与量纲校准

2.1 年份覆盖与月度连续性检查

这22年的数据,最怕的不是缺字段,是缺月份。有些年份的包看着完整,打开一看某几个月只有几千条记录,明显是推送中断或者文件损坏。我通常第一件事就是跑一张年月记录数表,按年月分组计数,然后看两件事:一是每年的月份数是不是12,二是相邻月份的量级有没有断崖。

具体做法很简单,先建一个年月字段,然后做交叉表。如果发现某年只有11个月,或者某个月记录数只有邻月的十分之一,那就要去核对原始文件清单,看是不是少了解压包。这一步不做,后面所有的时间序列分析都是歪的。

还有一个隐蔽问题:2000年前后的记录颗粒度跟2015年之后完全不是一个量级。早期年份数据量小,很多字段是空的,甚至有些年份只有年度汇总没有月度明细。这不是数据质量问题,是当年的信息化水平决定的。所以做长面板的人必须接受一个现实:前几年的样本量和后几年不可比,加权或者分段回归是常态。

2.2 字段缺失率与空值分布的快速摸底

第二件事是跑缺失率。我一般会输出一张表,列出每个字段的非空比例,然后按缺失率排序。缺失率超过60%的字段,基本可以考虑放弃或者只在特定年份使用。

下面这张表是我在多个年份版本上归纳出来的典型缺失格局,具体数值会随版本不同有浮动,但结构大体一致:

字段组代表字段典型缺失情况处理建议
主体信息收发货单位名称早期年份缺失较多,部分记录只有编码无名称以编码为准,名称仅作辅助校验
商品信息规格型号缺失率长期偏高不做主变量,仅用于案例佐证
贸易信息成交方式中等缺失按贸易方式分组后用众数填补或直接剔除
数量金额人民币金额早期年份缺失明显优先用美元金额,人民币字段仅作交叉验证
地域信息境内收发货地部分年份口径变化明确用哪一版口径后固定,不跨年混用
时间报关年月基本完整直接作为面板时间轴

这里有个经验:金额字段的缺失要特别小心。有些记录的美元金额为空但人民币金额有值,反过来也有。如果直接dropna,会误删大量有效样本。正确做法是先看两个金额字段的互补性,能互补的用汇率折算补齐,不能互补的再讨论剔除。

2.3 金额与数量的量纲陷阱

这是新手翻车率最高的地方,没有之一。金额字段至少有四个坑。

第一个坑是币种。美元金额和人民币金额是两套口径,直接相加是错的。要做跨年比较,建议统一到美元,用当年的平均汇率折算,汇率来源要写进代码注释,别到时候自己都忘了用的哪套。

第二个坑是单位。有的年份美元金额单位是"美元",有的是"千美元",字段名里不一定写清楚。判断方法很朴素:随机抽一批记录,用数量乘一个合理的单价区间,看哪个量级对得上。我一般会抽某类大宗商品做样本,因为它的国际价格公开可查,反推单位最可靠。

第三个坑是成交方式。出口统计通常按离岸价,进口统计通常按到岸价,这是国际惯例。这意味着进口金额里含运费和保险,出口金额里不含。你做贸易差额、做单位价值比较、做进口品到岸成本分析的时候,如果不把这个差异说出来,结论就会被质疑。做企业层面的成本分析更是如此,进口到岸价和出口离岸价之间隔着一段物流成本,这段成本恰恰是很多贸易研究的核心变量。

第四个坑是数量字段的不可加总性。不同HS编码的计量单位完全不同,千克、台、米、立方米、升混在一起,你直接把"数量"列求和,得到的是一个没有任何经济含义的数字。这一点我在第一次跑数据时吃了大亏,算出来的"平均单价"离谱到没法看。正确做法是分组聚合,按HS编码分别求数量和金额,再算单价;或者先把计量单位统一到千克,只对可换算的商品做单价分析。

3. 变量对照表的正确打开方式:代码字段到分析变量的翻译

3.1 贸易方式代码:决定你样本边界的第一道筛子

如果只能保留一个筛选维度,我会选贸易方式。原因很实际:这套数据里混着一般贸易、来料加工、进料加工、补偿贸易、保税区仓储转口、保税监管场所进出境货物、退运货物、修理物品等等,它们的金额含义完全不同。一般贸易反映的是正常的跨境买卖,加工贸易反映的是全球价值链分工,保税区仓储转口很多时候只是货物在境内保税区转了一圈,根本没有进入本国经济循环。

我见过的典型错误是把保税区仓储转口货物算进出口总额,导致某些口岸城市的贸易数据虚高。另一个常见错误是忘了剔除退运货物和修理物品,这两类会产生负值金额,直接把你的均值拉到沟里。

正确做法是先按对照表把贸易方式分成几大类:常规贸易、加工贸易、保税物流、其他特殊监管。然后根据研究问题选一类或几类。做企业出口决策研究,一般只用常规贸易加加工贸易;做口岸功能研究,才需要把保税物流单独拎出来。

3.2 企业属性字段:注册码、名称与所有制

企业海关编码通常是10位,前几位与注册地行政区划相关,中间几位与企业类型或经济区划相关,后几位是顺序号。这个编码是企业的唯一识别,但它不是永远不变的。企业迁址、改制、合并,编码可能变;企业注销后再注册,名称相近但编码不同。

所以我处理企业维度时,一定做两件事。第一,同时保留企业名称和编码,用编码做主键,用名称做校验,发现同名不同码或者同码不同名的情况单独标记出来。第二,构建一个企业年度对照表,记录每个编码在哪些年份出现过,避免把"退出市场"和"数据缺失"混为一谈。

所有制字段需要额外小心。报关单上的企业类型代码反映的是注册类型,不等于实际控制人。做所有制异质性分析,如果只有这套数据,结论只能表述为"按注册类型分组",不能过度解读。这一点在写作时要老实交代,审稿人很在意。

3.3 商品HS编码与版本迁移

2000到2021年,HS编码经历了多次国际版本更新,本国子目也调整过多轮。做跨年产品层面分析,绕不开版本迁移。我的做法分三步。

第一步,所有年份统一截取6位国际通用子目。6位在多数版本间是稳定的,虽然也有小范围调整,但可控。第二步,准备一张跨版本转换对照表,把旧版本的6位码映射到新版。这张表不是官方随数据附赠的,需要自己找或者用国际组织的对应表整理。第三步,对无法映射的编码做归并,归到上一级4位或者标记为"其他",并在论文附录里说明归并比例。如果归并比例超过5%,结论的稳健性就要打问号。

8位本国子目只在做特定政策研究时用,比如某些商品的出口退税调整、某些品类的监管条件变化。用8位就必须接受一个现实:跨年数据里会有大量编码对不上,只能做短窗口分析。

3.4 地区与运输方式:容易被当成配角的两个维度

地域字段有两套口径,境内收发货地和经营单位所在地,很多人以为差不多,实际差得远。收发货地反映的是货物的实际起运或运抵地,经营单位所在地反映的是贸易主体的注册地。一家贸易公司注册在某省会城市,货从另一个港口出去,这两套口径给出的答案完全不同。

做区域产业分析,我用收发货地;做贸易主体集聚分析,我用经营单位所在地。两个都用的时候,会把差值拿出来看,差值大的地区往往就是"注册地与货源地分离"的典型,这本身就是个有意思的现象。

国别字段也有两套:起运国或运抵国,原产国或最终目的国。做转口贸易研究时,这两套口径的差异是关键信息。一票货物从某地起运,经保税区中转,最终目的地是第三方,起运国和原产国就对不上。只看一套字段,你会漏掉整个中转环节。

运输方式字段看起来简单,实际是判断贸易形态的好帮手。海运占比高的年份和区域,说明是大宗货物;空运占比高,说明是高价值轻货;陆运占比高,说明是邻接地区的边境贸易。我在做口岸效率分析时,经常用运输方式分布作为辅助变量,效果不错。

4. 一套能复现的清洗流水线:从原始表到企业-产品-月度面板

4.1 环境与数据读取策略

原始数据量级不小,2010年之后单年记录数在千万级以上,直接全量读进内存会爆。我的习惯是用分块读取,先把数据结构摸清楚,再决定是分年处理还是分块聚合。

import pandas as pd DTYPE_MAP = { "企业海关编码": "string", "经营单位编码": "string", "hs编码": "string", "贸易方式": "string", "运输方式": "string", "成交方式": "string", "起运国代码": "string", "收发货地代码": "string", "报关年月": "string", "美元金额": "float64", "人民币金额": "float64", "数量": "float64", } def read_year(path): chunks = [] for chunk in pd.read_csv( path, dtype=DTYPE_MAP, encoding="utf-8-sig", chunksize=2_000_000, low_memory=False, ): chunks.append(chunk) return pd.concat(chunks, ignore_index=True)

这里把代码类字段一律设成字符串,是个必须坚持的习惯。企业编码、HS编码如果被识别成整型,前导零会丢,长度会变,后面合并全乱。我吃过一次亏,某个口岸的编码带前导零,转成整型后和其他年份对不上,排查了整整一天。

Stata用户可以用对应的读入方式,注意指定编码和字符串类型:

import delimited using "data/raw/2020.csv", encoding("utf-8") clear tostring 企业海关编码 hs编码 贸易方式, replace format(%20s) destring 美元金额 数量, replace force

4.2 字段标准化与类型转换

读进来之后先做三步标准化:时间、金额、编码。

时间字段可能长成"202003""2020-03""2020/3"等各种样子,统一成六位字符串年月,再派生出一个日期型变量用于排序。金额字段要核对单位,美元金额统一到美元,人民币金额统一到元,同时保留一个折算后的统一口径字段。编码字段去空格、转大写、补齐位数。

def standardize(df): df["报关年月"] = ( df["报关年月"].astype("string").str.replace(r"[^0-9]", "", regex=True).str.zfill(6) ) df["年月"] = pd.to_datetime(df["报关年月"], format="%Y%m", errors="coerce") df["hs8"] = df["hs编码"].astype("string").str.strip().str.zfill(8) df["hs6"] = df["hs8"].str[:6] df["企业海关编码"] = df["企业海关编码"].astype("string").str.strip().str.upper() return df

注意 errors="coerce",遇到异常月份直接置空,后面统一处理,不要让一行脏数据把整个日期列变成字符串。

4.3 去重与异常值处理

去重是这套数据里最需要经验的一步。同一条报关单可能因为征免性质拆分、分批进出口、数据分月推送等原因出现多条记录。判断重复不能只看企业加商品,要组合多个维度。

我用的是组合键加金额校验:企业编码、HS编码、年月、贸易方式、国别、数量、金额七项全同才算重复。只同前四项但金额不同,通常是真的分批报关,不能删。

KEY = ["企业海关编码", "hs8", "年月", "贸易方式", "起运国代码", "数量", "美元金额"] before = len(df) df = df.drop_duplicates(subset=KEY, keep="first") print(f"去重前后: {before} -> {len(df)}")

异常值处理分两类。金额为零或负的记录,先看贸易方式,属于退运、修理、无偿援助的保留并单独标记,其他情况的零值直接剔除。金额极大但数量极小的记录,多半是单位换算错误或者高价值精密设备,不要一刀切删掉,先抽出来人工核对。

4.4 企业-产品-月度面板的构建

清洗完就可以聚合了。聚合的关键是明确分析单元。做企业出口行为研究,单元是企业-产品-月度;做企业整体研究,单元是企业-月度;做产品市场研究,单元是产品-国别-月度。

panel = ( df.query("进出口标志 == '出口'") .groupby(["企业海关编码", "hs6", "年月"], as_index=False) .agg( 出口额=("美元金额", "sum"), 出口量=("数量", "sum"), 记录数=("美元金额", "size"), 国别数=("起运国代码", "nunique"), ) )

聚合之后要回头检查两件事。一是总量守恒,聚合后的出口额总和应该等于清洗后明细的出口额总和,差一点点就是分组键里有空值。二是企业数量守恒,聚合后的企业数应该等于明细里去重后的企业数,对不上说明编码有空值或格式不一致。

4.5 HS版本对齐与跨年可比性

跨年面板的最后一步是HS版本对齐。我的做法是准备一张映射表,字段是旧6位码、新6位码、生效年份,然后用它逐段更新。对于映射不上的编码,归并到4位,并在结果里加一个标记列,方便后面做稳健性检验时剔除。

def align_hs(df, mapping): merged = df.merge(mapping, on=["hs6", "year_bucket"], how="left") merged["hs6_aligned"] = merged["hs6_new"].fillna(merged["hs6"].str[:4] + "00") merged["hs_mapped"] = merged["hs6_new"].notna() return merged

对齐完成后,先算一下映射成功率。低于95%就要重新检查映射表,别急着往下跑。

5. 踩过的坑:常见异常与排查实录

5.1 典型问题速查表

下面这张表是我这些年攒下来的高频问题清单,基本上每次帮人看数据都能对上几条:

现象最可能的原因排查与处理
某年出口总额比公开统计低很多单位是千美元,被当美元抽大宗商品反推单位量级
企业数量每年大幅波动编码格式不统一或含空值统一字符串、补齐位数、剔除空码
平均单价离谱偏高数量单位不可加总按HS分组算单价,不跨组求和
进口金额普遍高于预期成交方式为到岸价,含运保明确口径,必要时扣除运保估算
某地区贸易额异常高保税区仓储转口混入按贸易方式筛除保税物流
面板里企业大量进出把数据缺失当成退出建企业年度对照表区分两种情形
跨年产品分类对不上HS版本迁移未处理统一6位并做映射归并
双边贸易数据对不上起运国与目的国口径差异转口贸易需两套口径同时看

5.2 几个只能靠经验判断的细节

第一件事是早期年份的数据颗粒度。2000年前后的记录,很多字段是空的,尤其是企业名称、规格型号、境内收发货地。拿这些年份做企业层面分析,样本会大幅缩水。我的处理是分段建模,早期年份做宏观和产品层面,中后期才做企业层面,绝不强行拼成一条长面板然后宣称"22年企业面板"。

第二件事是同一家企业名称的写法差异。全角半角括号、有限公司与有限责任公司、集团公司与集团股份有限公司,同一家企业能有五六个写法。只靠名称匹配会漏掉大量样本,必须用编码做主键。名称只在编码缺失的时候作为兜底,而且要用模糊匹配加人工抽检。

第三件事是月度数据的季节性。12月和1月的记录数往往异常,因为年末集中报关和年初积压。做月度时间序列要加季节虚拟变量,做企业进入退出分析要避免用单月数据判定退出,至少看连续三个月。

第四件事是金额字段的精度。有些版本保留两位小数,有些只到整数。保留两位的做单位价值分析没问题,只到整数的在小额交易上误差很大,这时候要改用数量或者记录数作为辅助指标。

第五件事是合并多个年份时的字段名不一致。同一个概念在不同年份可能叫"收发货单位"和"境内收发货人",叫"运抵国"和"目的国"。我一般先建一张字段名对照表,统一命名后再concat,不然合并完会出现两列各有一半空值的尴尬局面。

6. 数据到手之后能做什么:三类高频分析场景的落地思路

6.1 企业出口行为:从是否出口到出口多少

这套数据最经典的用法是企业层面的出口决策分析。核心逻辑是把企业分成出口企业和非出口企业,看哪些特征决定了出口倾向和出口强度。听起来简单,实际有两个门槛。

第一个门槛是分母问题。这套数据只包含有进出口记录的企业,没有记录的企业不在里面。所以严格来说,你算的是"在有贸易记录的企业中,谁出口更多",而不是"在所有企业中谁更可能出口"。要解决这个问题,需要把数据跟企业注册库或者工业库做匹配,匹配率通常不高,这是这类研究的通病,写文章时要说清楚。

第二个门槛是出口的持续性。单月有出口记录和全年持续出口是两回事。我一般会构造三个指标:是否出口、出口月份数、出口目的地数量。三个指标分别对应不同的理论机制,比只用一个出口额丰富得多。

6.2 产品-市场匹配:出口增长的边际分解

第二个高频场景是做增长的边际分解,把出口增长拆成产品维度、市场维度和企业维度。这套数据在这方面的优势是维度齐全,产品、国别、企业三个维度同时具备,能做比较细的分解。

实操上,先构造企业-产品-市场-年度四维数据,然后分别计算每个维度的进入、退出和存续增长。存续部分还能再拆成量和价。这个分解框架在贸易研究里很成熟,用这套数据做出来说服力强,因为颗粒度足够细。

要注意的是价格的计算。前面说过数量单位的问题,做价格分解必须先把单位统一,只对同一计量单位的商品算单价,跨单位的价格加总要出大问题。我的做法是只保留千克计量的商品做价格分析,其他单位的产品单独处理。

6.3 区域与口岸:货物流向的空间格局

第三个场景是做区域分析。这套数据的地域字段虽然有两套口径,但配合使用能挖出不少东西。比如把收发货地和经营单位所在地做交叉,能识别出哪些地区是"贸易主体集聚地",哪些是"货物实际集散地"。两者高度重合的地区是综合型贸易中心,分离度高的地区是通道型口岸。

口岸字段也能单独做文章。不同口岸的货类结构、贸易方式结构、运输方式结构差异很大,这些结构差异背后是基础设施、腹地产业和监管政策的差异。做口岸竞争力分析,这套数据能提供非常扎实的描述性证据。

我个人在实际操作中的体会是,这套数据真正的价值不在单一年份的横截面,而在纵向的连续性。22年的跨度足够观察好几轮结构性变化,比如加工贸易占比的持续下降、民营企业出口份额的上升、内陆口岸的崛起。这些变化单看一年是看不出来的,拉长时间轴才清晰。也正因为如此,前面那些清洗和口径对齐的功夫,一分都省不了。

最后再分享一个小技巧:每次清洗完一个年份,把关键统计量存进一张单独的汇总表,包括记录数、企业数、HS编码数、金额合计、贸易方式分布。等22年全部跑完,把这张表拉出来一看,哪一年出了问题一目了然。这个习惯让我躲过了好几次数据包损坏和字段错位的事故,比事后追溯省事太多。