40年城市年鉴面板数据整理:清洗、编码与校验实战
2026/9/9 0:08:28 网站建设 项目流程

做城市经济、区域发展或者历史地理研究的人,几乎人手一册《中国城市统计年鉴》。但真要把1985—2025这40年的年鉴整理成一份能直接跑回归的面板数据,工作量比大多数论文“数据来源”段落里写的那两行字要残酷得多。不同版本的表格结构变了、指标名称换了、单位飘忽不定,连“同一座城市叫什么”这种问题都能让新手抓狂。

这篇内容就是围绕这样一份面板数据展开的。我把自己在整理、清洗、校验这份数据时的完整思路写出来,包括怎么处理版本差异、怎么定城市编码、怎么统一口径、怎么做交叉验证,以及最后拿去做分析之前必须排掉的雷。适合正在建库的科研人员、数据工作者,也适合想搞清楚“面板数据到底是怎么被造出来”的读者。

1. 为什么要把40年城市数据折腾成面板

1.1 从“每年一张表”到“一张长表”

《中国城市统计年鉴》的原始形态,本质上是一年一本的截面数据——每一卷告诉你某一年各个城市是什么情况。想研究一个城市的演变,你得自己动手把分散在各卷里的同一行数据挑出来拼在一起;想比较城市之间的差异随时间怎么变化,又要来回翻不同年份的表格。数据量小还好,一旦跨度到40年、城市数量到几百个,手工操作就是灾难。

面板数据的核心价值,在于把“时间”和“个体”两个维度同时放进一张表里。每一行不再是一座城市某年的孤立记录,而是“城市-年份”的唯一组合。这样一来,城市固定效应、时间趋势、滞后项、差分项这些分析手段才能真正落地。没有面板结构,很多计量模型根本没法定系数。

1.2 一张面板能支撑什么研究

我见过用这套数据做的课题,类型非常广。比较常见的是城市经济增长核算,把GDP、固定资产投资、从业人员、财政收支这些变量拼成平衡面板,估计生产函数或全要素生产率;也有做城市基础设施与民生关系的,用市辖区人口、用水用电量、道路面积这些指标做面板回归。还有人做城市层级演变研究,靠的就是地级市数量在不同年份的增减变化。

这几类研究的共同特点是:既要有时间上的连续性,也要有城市间的可比性。单独一年的截面数据给不了这种“跨时空可比”的基础。所以,把年鉴整理成面板,不是单纯做数据库,而是在给后续一堆研究打地基。

1.3 建立城市级面板的三个底层难点

理论上讲,整理工作好像很机械——读进来、堆起来就行。实际操作时,三个问题会一直缠着你:

  • 城市身份会变。40年里,有县变成县级市,有县级市升成地级市,有地级市被合并,有市辖区重新划界。如果不提前弄一套稳定的城市编码,后面所有合并都会对不上号。
  • 指标定义会变。人口指标用过“非农业人口”也用过“城镇人口”,GDP统计范围在“全市”和“市辖区”两个口径之间反复切换,稍不留神就把不同尺度的东西加到了一起。
  • 数据单位会变。GDP有按万元、亿元两种习惯,人口有按万人、千人两种精度,如果只靠肉眼判断,几百个城市几十列变量下来必然出错。

这三个难点就是后面所有技术设计的出发点。能把这三点处理明白,这份面板就算成功了大半。

2. 不同年代的原始版本,坑点完全不一样

整理第一步是摸清手上到底有哪些“原料”。我按出版形态和表格风格,把40年大致分成三个阶段,每个阶段的坑都不一样。

2.1 早期纸本扫描件:OCR再准也有极限

1985年到2000年前后,年鉴还是典型的印刷品时代。我接触到的多数是扫描PDF,分辨率参差不齐,有些页面发黄、倾斜、甚至装订边压住了数字。这一阶段的表格有几个通病:

  • 横表头跨页,第一页的指标名和第二页的数据列经常对不上。
  • 数字是铅字打印,识别时6和8、0和8容易混淆。
  • 小数点经常被扫描噪点吃掉,2000.5会变成20005。
  • 部分表使用“—”表示无数据,但有的表用“...”,有的表干脆留空白。

处理这个阶段,纯靠OCR远远不够。我的做法是先跑一遍OCR,再用列和列之间的数字规律去做后校验。比如某列是“年末总人口”,那它的量级应当和相邻年份在同一水平,突然翻10倍就是典型的识别错误。

2.2 中期电子表格:结构自由但表头混乱

2000年以后,年鉴开始以电子文档形式流传,有些是Word转换的PDF,有些直接是Excel表格。数据精度提高了,但新的问题出现了:编辑排版不再像铅字时代那样几十年不变,表头经常重新设计。

举例来说,“地区生产总值”这个指标,在不同年份的卷册里就有好几种写法:

  • “地区生产总值(当年价格)”
  • “GDP”
  • “全市生产总值”
  • “国内生产总值(当年价)(市辖区)”

如果不做映射,光靠关键词匹配,这些都会被当成不同变量。这个阶段最考验表头标准化能力,我后面专门讲映射表怎么做。

2.3 近年发布形态:规范了但路径复杂

近几年,年鉴发布渠道相对稳定,很多研究机构直接从统计数据库下载分年数据。这个阶段的表格结构规范很多,指标编码相对统一,但也不是完全省心。常见的问题是:

  • 部分指标只发布了市级汇总,没有分市辖区口径。
  • 有些表格附加了注释行、脚注行,读入时会污染数据。
  • 下载工具默认导出是宽表格式,需要先变成长表才能统一入库。

下面是三个阶段特征的快速对照:

阶段主要载体最头疼的问题相对可靠的字段
1985—2000扫描PDF/复印本OCR识别错误、“—”与“...”混用行政区划代码、城市名称
2001—2015电子表格/文档转PDF表头名称不统一、口径切换城市代码、多数总量指标
2016—2025统计数据库导出注释行干扰、部分口径缺失机器可读、指标编码规范

这个阶段划分不绝对,但能帮你快速判断某卷数据最需要重点核查的位置。拿到一份新年份的数据,先想清楚它属于哪个阶段,再决定清洗策略,效率会高很多。

3. 面板结构设计必须定死的四个技术决策

刚开始做面板的人,容易一上来就急着写代码把表格堆在一起。我强烈建议先停下,把下面四个决定做了再动手。

3.1 城市识别编码:永远不要用城市名称做关联键

城市名称是最不靠谱的字段。“北京”在早期表格里可能写作“北京市”,“宣武区”后来并入了“西城区”,有些年份用全称、有些年份用简称,甚至同一个城市在不同表格里有不同的排序逻辑。

我的做法是自定义一套稳定的city_id编码规则,不依赖任何版本里现有的城市代码。具体规则可以这样设计:

  • 前4位:省份代码(与通用行政区划代码前4位保持一致)
  • 中4位:城市代码(按2010年前后的行政区划框架确定基准)
  • 后2位:市区/全县扩展位,全市口径统一为00,市辖区口径统一为01

这样在合并时只认city_id,城市名称只是辅助展示字段。遇到行政区划调整,比如某县撤县设区,我不会改历史数据的city_id,而是单独维护一张“行政变更表”,记录该县的变更类型、生效年份、原来归属、新归属。数据分析时,可以根据需要把city_id对应到最新行政区划框架做聚合。

3.2 指标标准化:建一张“同义指标映射表”

前面提到过,“地区生产总值”可能叫法有五种。我在建库时维护了一张indicator_mapping表,把原始表头统一映射到标准指标名。标准指标名的设计要有规律,通常是:

{指标类别}_{细化对象}_{口径}

例如:

  • gdp_total_city:全市GDP
  • gdp_total_district:市辖区GDP
  • pop_total_city:全市年末总人口
  • pop_nonagri_city:全市非农业人口
  • finance_revenue_city:全市地方财政收入

映射表至少包含四列:原始表头原文、原始表头所在年份、标准指标名、备注。有了这张表,每年的数据读进来后,先用它做自动翻译,翻译不了的人工确认,确认结果再回填到映射表里。第一次建表很痛苦,但维护几轮之后,新数据源的适配速度会越来越快。

3.3 单位统一:所有金额类指标统一为“万元”,人口统一为“万人”

单位问题是新手最容易忽略的。早期年鉴里GDP可能以“万元”为单位,中后期以“亿元”为单位。人口也是,有“万人”也有“千人”的精度。我建议从源头就统一:

  • 金额类变量一律换算成“万元”,保留两位小数。
  • 人口类变量一律换算成“万人”,保留两位小数。
  • 人均类变量,比如人均GDP、人均可支配收入,如果折算后数值特别大或特别小,要回查原始单位是否看反了。

统一单位不只是为了好看,更重要的是防止后续回归时出现“系数大得离谱”的尴尬局面。我自己就经历过:某核心解释变量单位没换,跑完回归系数8.7×10^6,一开始还以为发现了什么惊人效应,最后发现是“亿元”和“万元”差了四个数量级。

3.4 缺失值策略:先保留原始缺失,别急着插补

很多做分析的人喜欢直接对缺失数据插补,我建议库本身保持纯正——缺失就是缺失,在数据文件里用NA表示,不做任何插补。原因很简单:插补方法的选择本质上是一种研究决策,应该由最终的分析者根据研究假设去决定,而不是在基建阶段就替他做了。

当然,缺失情况要在数据说明文档里记录清楚。我一般会在字段说明里标注某指标在哪些年份缺失率高、缺失可能是完全随机还是受统计口径变化影响。这样分析者拿到数据后,可以自己决定用插补、删除还是多重填补。

4. 从纸面到电脑:一条可复制的流水线

数据设计定了之后,真正繁琐的录入清洗阶段就开始了。下面是我实际使用的四条操作主轴。

4.1 扫描件的OCR与双人复核

对早期扫描PDF,我用的是一套“OCR+抽样复核”的思路。先批量转成图片,再通过OCR工具识别成结构化表格。光学识别之后必须跑核验脚本,我会随机抽取每卷5%的页面做双人独立复核,不一致的位置返工。

OCR过程中最容易被忽视的是表格中数字之间的分隔线。如果分隔线被识别成字符,数字列就会被顶开,产生错位。我的经验是:识别前先用图像处理工具把横竖线检测出来并去除,只留文字和数字,准确率能提升不少。

4.2 表格结构统一:从宽表到长表

不同年份Excel的表格布局不一样。有的年份城市在行、指标在列,有的年份反过来,还有的年份一张表里塞了多个指标的分块。我的标准做法是:先把所有原始数据读取成“城市代码-指标名-数值”的长表,再通过透视表转换成最终的面板宽表形式。

读入时最怕的是表头有多层合并单元格。处理这种表,我不会直接让程序去猜第几行是列名,而是先人工指定表头行号,再配合程序校验指标名是否在映射表里存在。如果某个表头翻译不了,程序会单独输出到一个待处理列表,而不是静默丢弃。

4.3 数据校验脚本

整理过程离不开自动化校验。我常用的一段Python逻辑大致是这样的:

import pandas as pd def validate_year_panel(df, city_col='city_id', year_col='year', value_col='value'): # 检查是否有重复的城市-年份组合 dup = df.duplicated(subset=[city_col, year_col]).sum() # 检查数值是否超出合理区间 absurd = df[(df[value_col] < 0)].shape[0] # 检查同一城市相邻年份变化率是否超过阈值 df = df.sort_values([city_col, year_col]) df['pct_change'] = df.groupby(city_col)[value_col].pct_change() spikes = df[df['pct_change'].abs() > 5].shape[0] return {'重复记录': dup, '负值数量': absurd, '异常跳变数量': spikes}

这段代码不复杂,但很实用。pct_change的阈值我设成5,也就是说某城市某指标一年内翻了超过5倍,就要回原始表检查是真实变化还是录入错误。现实里经济指标年际变化超过50%都少见,5倍几乎必然是数据问题。

4.4 人工校对环节不能省

不管自动化脚本多完善,人工校对依然是兜底。我习惯让一个人在原始PDF界面和标准化数据之间切换着抽查,另一个人只负责看“异常跳变名单”。正常情况下一卷数据几百行,人工核对的重点不是逐格读,而是对着异常列表定向复查。

另外,早期卷册里偶尔会发现整列数据错位——比如“工业总产值”那列实际装的是“固定资产投资”。这种错误脚本很难发现,靠的是人工浏览时对量级的敏感。比如工业产值和固定资产投资,这两个指标在某市的量级通常不会相同,一旦发现十几年的变化趋势完全一致,就要回头怀疑是否错位。

5. 数据质量的交叉验证方法

数据整理完,不能直接说“可以用了”。我会做三轮验证,确认面板本身没有结构性硬伤。

5.1 纵向加总检验

利用年鉴本身的多级汇总关系做校验。多个地级市组成一个省份,所以每年全省地级市GDP加总应当接近省统计年鉴里的数据。偏差超过一定范围(我一般容忍1%-2%),说明有城市数据录入或者口径不对。

这种加总校验对部分指标尤其好用:GDP、财政总收入、固定资产投资额,都是上下级关系非常清晰的指标。人口指标受“市辖区”与“全市”口径干扰大一些,加总校验的阈值会放宽。

5.2 横向关联指标检验

有些指标之间存在近似恒等关系。比如“年末总人口”应该大致等于“非农业人口”加“农业人口”;“建成区面积”一般不会小于“城区面积”。利用这些内在逻辑做横向比对,能发现单个指标看不出的问题。

举一个真实例子:某城市2005年“市辖区年末总人口”是1200万人,而同一年“市辖区建设用地面积”只有98平方公里。人口1200万无论如何也不是98平方公里能容纳的,回去一查,发现原始表数据列错位,人口列引的是全市总人口。这种错误靠单列检查几乎查不出来。

5.3 跨年连续性检验

连续年份差值检验用来抓“跳变”,但不能机械理解。城市数据里的确存在一些合理的剧烈变化,比如行政区划调整后,某市辖区面积突然增加,或者某县划入导致人口翻倍。这些属于真实变化,不应被当作错误处理。

所以我的做法是标记,不自动修改。所有被标记的“剧烈变化”都要在变更表里找依据;找不到依据的,再回原表人工核对。通过这种“标记-解释-复核”三步,既不会漏掉错误,又不会误删真实的结构性变动。

校验层用什么逻辑容错范围主要风险
纵向加总城市加总≈省份偏差1%-2%口径不一致导致锚点失效
横向关联子指标加总≈父指标视指标而定指标概念本身有交叉定义
跨年连续相邻年份变化率阈值5倍真实行政调整被误判

6. 面板数据拿去分析之前的最后排雷

数据基本成型后,还有几个几乎所有人都会踩的坑,我单独整理出来。

6.1 先看平衡性再跑模型

面板数据不要求完全平衡,但你先要知道自己有多少非平衡。如果某核心变量在2000年以前只有50个城市有数据、之后才扩展到全部城市,而你没有做任何处理就直接跑固定效应,结果可能被样本构成变化带偏。

我的习惯是:建表后立刻输出一张“城市×年份覆盖表”,行是城市,列是年份,有数据的地方填1,没有的填0。这张矩阵能让你一眼看出数据缺失是否有规律。分析时要么明确样本区间,比如只用1995—2025年的子集,要么用非平衡面板允许的估计方法,而不是假装数据平衡。

6.2 区分口径再做分析

这是最重要的提醒。年鉴里“全市”和“市辖区”是两个完全不同的经济空间概念。市辖区口径通常更偏向城市核心区的经济发展水平,全市口径包含了广大农村县域,两者在研究问题上的含义差别很大。

使用面板数据时,务必看清楚每个变量后缀是_city还是_district,并且在同一回归中使用相同口径的变量。最怕的是把A变量的全市口径和B变量的市辖区口径混在一个方程里,结果系数的解释就成了灾难。我给每个表格文件名都加了口径后缀,就是怕自己三个月后忘记当时用的是哪套口径。

6.3 维护一份“数据变更日志”

几十年的项目做下来,修改无处不在。今天发现1988年某市GDP录错了,明天确认2003年某指标单位不统一,后天又调整了城市编码规则。如果不记录,半年后回看,你根本不知道现在这份数据跟最初那份差别在哪。

我的做法是在库里放一个CHANGELOG.md,每做一次修改就追加一行:修改时间、涉及文件、修改原因、原始值、新值、操作人。这看起来繁琐,但对于需要长期维护的数据集来说,这比任何“自动化清洗脚本”都更值钱。

6.4 分析前跑一遍描述性统计

最后建议,拿到面板之后先跑描述性统计,而且要看两个维度的分布:按年份看所有变量的均值、标准差;按城市看核心变量的时间均值。这两张表能帮你快速发现“异常城市”——比如某个城市GDP30年基本没变,那大概率是数据复制粘贴错了,而不是这座城市真的停滞了。

我自己踩过这样一个坑:某城市1988年的“地方财政预算内收入”和1989年的值一字不差,一开始以为是巧合,后来发现是录入时整行重复粘贴,把下一年的真实数据覆盖掉了。如果当时没有先看描述性统计,后面所有用到这个变量的模型都会被这条脏数据污染。

7. 这些年整理数据攒下的一点经验

从一张张扫描页变成一行行规范记录,这个过程没有太多技术奇迹,拼的就是耐性和规则意识。我最大的体会是:面板数据不是“收集”出来的,而是“设计”出来的。编码规则、指标映射、单位基准、口径标注,这些在设计环节做扎实了,后面所有步骤都会顺;反过来,设计偷懒,后面就会用无数个小时的重复劳动来还债。

整理这套1985—2025年城市面板的过程中,我最常提醒自己的话就是:数据里的每一个数字都对应一座城市某一年的真实状态,清洗它们时不较真,后面的分析结果就不值得相信。希望这篇内容能帮你把地基打得比我当初更稳、更少踩坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询