如何整理 2022 年县域统计年鉴:caj 文件转 pdf、文本识别与数据清洗

最近知网上更新了 2022 年中国县域统计年鉴,不过并没有提供 excel 格式的数据。所以我只下载了 caj 文件。今天的课程中我们将一起学习如何把 caj 文件转换成 pdf 文件、对 pdf 文件进行文本识别以及处理识别后的结果。

后面如果知网提供了 excel 文件就直接整理 excel 文件即可。不过本文讲解的方法依然适合很多没有提供 excel 文件的统计年鉴数据提取整理。

caj 文件转换成 pdf 文件

caj 文件需要使用知网提供的 caj 阅读器打开,我们没办法直接对 caj 文件进行文本识别和提取,因此我们需要先把 caj 文件转换成 pdf 或者其他可处理的格式。

对于页数较少的 caj 文件,可以通过一页页的截图,不过页数多的话这种方法就非常麻烦了。为此我找到了一个把 caj 转换成 pdf 文件的工具:

附件中我为大家下载好了三个文件:

  1. caj2pdf-qt-macos-x86_64-v0.1.5.zip:Mac OS 安装
  2. caj2pdf-qt-windows-x86-v0.1.5.zip:32 位的 Windows OS 安装
  3. caj2pdf-qt-windows-x86_64-v0.1.5.zip:64 位 Windows OS 安装

Windows 上的安装使用我尚未测试,大家可以遇到问题的时候再来研究,根据原作者的介绍,Windows 的安装需要注意:

解压缩后点 caj2pdf.exe 启动。

注意,Windows Defender 或其它一些杀毒软件可能会拦截转换进程,导致转换失败。

这是由于在构建程序的时候使用了 PyInstaller 将 caj2pdf 命令行工具打包成了单个可执行文件,而一些杀毒软件会将 PyInstaller 打包的程序当成病毒处理,参考:https://github.com/pyinstaller/pyinstaller/issues/5492

如果你信任编译好的压缩包,可以将这个文件夹添加到排除项:将排除项添加到 Windows 安全中心。

我使用的是 Mac OS,使用过程也遇到了问题:

双击打开:

不过我们可以这样打开。首先右键选择“显示包内容”:

找到这个文件:

双击打开即可:

然后使用这个工具转换 caj 文件生成 pdf 文件即可。

这样我们就得到了 pdf 文件:中国县域统计年鉴_王贵荣_主编_2021年县(市)社会经济主要指标.pdf。

pdf 文件识别文本、提取表格数据

图像的文本识别工具很多,例如 tesseract,不过尝试之后发现都不是特别好用,最好用的工具还是这个在线的应用。

如果要识别的文件页数较少,选择免费转换即可。付费转换的价格也不贵,例如这份 pdf 文件有 419 页,收费 17.67 元(如果要识别很多文件,最好先把文件合并成一个 pdf 文件上传识别,因为这个网站是阶梯收费,页数越多,单价越低)。

上传之后根据自己的需要设定解析参数即可:

解析过程可能需要数十分钟,耐心等待即可。

这次大家就不用再付费转换了,附件中有我转换后的数据,大家可以使用附件中的结果继续学习后面的内容。也就是 中国县域统计年鉴_王贵荣_主编_2021年县(市)社会经济主要指标_output.xlsx 文件。

手动检查

解析的结果并不一定能直接使用 Stata 处理(有很多细节不好编程进行),我们需要先手动检查下做一些准备工作。

这里主要是手动从每个表格中提取省份信息生成一个新列,见文件“整理结果.xlsx”。

在 Stata 中处理

下面我们使用 Stata 读取处理这个文件:

clear all
cd "~/Desktop/"
import excel using "整理结果.xlsx", clear

*- 填补省份
carryforward A, replace
drop if missing(D) & missing(E) & missing(F) ///
& missing(G) & missing(H) & missing(I) ///
& missing(J) & missing(K) & missing(L)

*- 去除 B 中的空格
replace B = subinstr(B, " ", "", .)
save data1, replace

*- 统一 B 中的指标名称
use data1, clear
replace B = subinstr(B, ",", "", .)
replace B = subinstr(B, "-", "", .)
replace B = subinstr(B, ".", "", .)
replace B = subinstr(B, "~", "", .)
replace B = subinstr(B, "・", "", .)
replace B = subinstr(B, ",", "", .)
replace B = subinstr(B, "、", "", .)
duplicates drop B, force
keep B
gsort B
drop if B == "" | B == "一、基本情况行政区域面积" | B == "一基本情况行政区域面积"
replace B = "提供住宿的民政服务机构床位数" if ///
inlist(B, "提供住宿的民政0艮务机构床位数", ///
"提供住宿的民政服务机构床位数", ///
"提供住宿的民谢艮务机构床位数")

replace B = "提供住宿的民政服务机构" if ///
inlist(B, "提供住宿的民呦艮务机构", ///
"提供住宿的民政^务机构", ///
"提供住宿的民斑艮务机构", ///
"提供住宿的民班艮务机构")

使用该代码处理原始数据:

use data1, clear
replace B = subinstr(B, ",", "", .)
replace B = subinstr(B, "-", "", .)
replace B = subinstr(B, ".", "", .)
replace B = subinstr(B, "~", "", .)
replace B = subinstr(B, "・", "", .)
replace B = subinstr(B, ",", "", .)
replace B = subinstr(B, "、", "", .)
drop if B == "" | B == "一、基本情况行政区域面积" | B == "一基本情况行政区域面积"
replace B = "提供住宿的民政服务机构床位数" if ///
inlist(B, "提供住宿的民政0艮务机构床位数", ///
"提供住宿的民政服务机构床位数", ///
"提供住宿的民谢艮务机构床位数")

replace B = "提供住宿的民政服务机构" if ///
inlist(B, "提供住宿的民呦艮务机构", ///
"提供住宿的民政^务机构", ///
"提供住宿的民斑艮务机构", ///
"提供住宿的民班艮务机构")
drop C

根据 B == “指标” 拆分每个表:

gen z = _n if B == "指标"
order z
carryforward z, replace
save data2, replace

检查有没有识别错误的:

*- 检查有没有识别错误的
gen id = _n
collapse (count) id, by(z)

sum id

*> Variable | Obs Mean Std. dev. Min Max
*> -------------+---------------------------------------------------------
*> id | 418 23.19378 1.228463 20 43

*- 可以看到 z 的最大数量是 43,说明有识别错误的,修正下
list z if id > 25

*- 检查下
use data2, clear
keep if z == 6162

可以看到有部分海南省的数据少了“指标”这一行,我们再回到原始数据修正这一行数据得到“整理结果2.xlsx”

重新处理 “整理结果2.xlsx”:

import excel using "整理结果2.xlsx", clear
carryforward A, replace
drop if missing(D) & missing(E) & missing(F) ///
& missing(G) & missing(H) & missing(I) ///
& missing(J) & missing(K) & missing(L)
replace B = subinstr(B, " ", "", .)
replace B = subinstr(B, ",", "", .)
replace B = subinstr(B, "-", "", .)
replace B = subinstr(B, ".", "", .)
replace B = subinstr(B, "~", "", .)
replace B = subinstr(B, "・", "", .)
replace B = subinstr(B, ",", "", .)
replace B = subinstr(B, "、", "", .)
drop if B == "" | B == "一、基本情况行政区域面积" | B == "一基本情况行政区域面积"
replace B = "提供住宿的民政服务机构床位数" if ///
inlist(B, "提供住宿的民政0艮务机构床位数", ///
"提供住宿的民政服务机构床位数", ///
"提供住宿的民谢艮务机构床位数")

replace B = "提供住宿的民政服务机构" if ///
inlist(B, "提供住宿的民呦艮务机构", ///
"提供住宿的民政^务机构", ///
"提供住宿的民斑艮务机构", ///
"提供住宿的民班艮务机构")
drop C
gen z = _n if B == "指标"
order z
carryforward z, replace
save data2, replace

*- 再次检查有没有识别错误的
gen id = _n
collapse (count) id, by(z)

sum id
*- 这个时候 z 变量最多的数量是 24,没什么问题了

继续整理 data2.dta:

use data2, clear

*- 转换成长数据
*- gather 和 spread 的安装:ssc install tidy
gather D - L
spread B value
order z A 指标

*- 删除不是数据的
drop if missing(指标)
drop var

*- 使用 destring 把能转换成数值变量的都转换成数值变量
destring, replace

*- 把数据变紧凑些:

foreach i of varlist _all {
cap format `i' %10s
}

save data3, replace

把“整理结果2.xlsx”复制一份命名为“整理结果3.xlsx”,然后逐个检查为什么有些变量无法 destring:

use data3, clear
destring 乡, force gen(乡2)
keep if !missing(乡) & missing(乡2)
list A 指标 乡 乡2

*> +-----------------------------------------+
*> | A 指标 乡 乡2 |
*> |-----------------------------------------|
*> 1. | 新疆维吾尔自治区 英吉沙县 ,10 . |
*> +-----------------------------------------+

*- 在“整理结果3.xlsx”中改正这个错误。

use data3, clear
destring 住户存款余额, force gen(住户存款余额2)
keep if !missing(住户存款余额) & missing(住户存款余额2)
list A 指标 住户存款余额 住户存款余额2

*> +---------------------------------------+
*> | A 指标 住户~额 住户存~2 |
*> |---------------------------------------|
*> 1. | 河北省 滦州市 386力 82 . |
*> +---------------------------------------+

*- 在“整理结果3.xlsx”中改正这个错误。

use data3, clear
destring 固定电话用户, force gen(固定电话用户2)
keep if !missing(固定电话用户) & missing(固定电话用户2)
list A 指标 固定电话用户 固定电话用户2

*> +----------------------------------------------------+
*> | A 指标 固定电话~户 固定电~2 |
*> |----------------------------------------------------|
*> 1. | 新疆维吾尔自治区 莎车县 , 21511 . |
*> +----------------------------------------------------+

*- 在“整理结果3.xlsx”中改正这个错误。

use data3, clear
local p = "地区生产总值"
destring `p', force gen(`p'2)
keep if !missing(`p') & missing(`p'2)
list A 指标 `p' `p'2

*> +---------------------------------------+
*> | A 指标 地区~值 地区生~2 |
*> |---------------------------------------|
*> 1. | 四川省 荥经县 82 乃 33 . |
*> +---------------------------------------+

use data3, clear
local p = "年末金融机构各项贷款余额"
destring `p', force gen(`p'2)
keep if !missing(`p') & missing(`p'2)
list A 指标 `p' `p'2

*> +---------------------------------------+
*> | A 指标 年末~额 年末金~2 |
*> |---------------------------------------|
*> 1. | 湖南省 衡东县 15277万 . |
*> 2. | 陕西省 洛川县 65 乃 29 . |
*> +---------------------------------------+

*- 在“整理结果3.xlsx”中改正这个错误。

use data3, clear
local p = "户籍人口"
destring `p', force gen(`p'2)
keep if !missing(`p') & missing(`p'2)
list A 指标 `p' `p'2

*> +-----------------------------------------------+
*> | A 指标 户籍人口 户籍人~2 |
*> |-----------------------------------------------|
*> 1. | 内蒙古自治区 翁牛特旗 46,8 . |
*> 2. | 内蒙古自治区 扎兰屯市 39,7 . |
*> +-----------------------------------------------+

*- 在“整理结果3.xlsx”中改正这个错误。

use data3, clear
local p = "提供住宿的民政服务机构"
destring `p', force gen(`p'2)
keep if !missing(`p') & missing(`p'2)
list A 指标 `p' `p'2

*> +--------------------------------------+
*> | A 指标 提供~构 提供住~2 |
*> |--------------------------------------|
*> 1. | 四川省 金川县 I . |
*> +--------------------------------------+

*- 在“整理结果3.xlsx”中改正这个错误。

use data3, clear
local p = "街道办事处"
destring `p', force gen(`p'2)
keep if !missing(`p') & missing(`p'2)
list A 指标 `p' `p'2

*> +--------------------------------------+
*> | A 指标 街道~处 街道办~2 |
*> |--------------------------------------|
*> 1. | 福建省 松溪县 I . |
*> +--------------------------------------+

*- 在“整理结果3.xlsx”中改正这个错误。

use data3, clear
local p = "设施农业种植占地面积"
destring `p', force gen(`p'2)
keep if !missing(`p') & missing(`p'2)
list A 指标 `p' `p'2

*> +--------------------------------------+
*> | A 指标 设施~积 设施农~2 |
*> |--------------------------------------|
*> 1. | 陕西省 麟游县 ,76 . |
*> +--------------------------------------+

*- 在“整理结果3.xlsx”中改正这个错误。

这样我们就纠正了这些错误。

重新处理 “整理结果3.xlsx”:

import excel using "整理结果3.xlsx", clear
carryforward A, replace
drop if missing(D) & missing(E) & missing(F) ///
& missing(G) & missing(H) & missing(I) ///
& missing(J) & missing(K) & missing(L)
replace B = subinstr(B, " ", "", .)
replace B = subinstr(B, ",", "", .)
replace B = subinstr(B, "-", "", .)
replace B = subinstr(B, ".", "", .)
replace B = subinstr(B, "~", "", .)
replace B = subinstr(B, "・", "", .)
replace B = subinstr(B, ",", "", .)
replace B = subinstr(B, "、", "", .)
drop if B == "" | B == "一、基本情况行政区域面积" | B == "一基本情况行政区域面积"
replace B = "提供住宿的民政服务机构床位数" if ///
inlist(B, "提供住宿的民政0艮务机构床位数", ///
"提供住宿的民政服务机构床位数", ///
"提供住宿的民谢艮务机构床位数")

replace B = "提供住宿的民政服务机构" if ///
inlist(B, "提供住宿的民呦艮务机构", ///
"提供住宿的民政^务机构", ///
"提供住宿的民斑艮务机构", ///
"提供住宿的民班艮务机构")
drop C
gen z = _n if B == "指标"
order z
carryforward z, replace
gather D - L
spread B value
order z A 指标
drop if missing(指标)
drop var
destring, replace

*- 删除辅助变量 z
drop z

ren A 省
ren 指标 县
order 省 县 行政区域面积 乡 镇 街道办事处 户籍人口 ///
地区生产总值 第一产业增加值 第二产业增加值 第三产业增加值 ///
地方一般公共预算收入 地方一般公共预算支出 住户存款余额 ///
年末金融机构各项贷款余额 设施农业种植占地面积 油料产量 ///
棉花产量 规模以上工业企业 固定电话用户 普通中学在校学生 ///
小学在校学生 医疗卫生机构床位 提供住宿的民政服务机构 ///
提供住宿的民政服务机构床位数

*- 紧凑数据
foreach i of varlist _all {
cap format `i' %10s
}
save data4, replace

最后我们再检查变量的数值中是否有识别错误的,通常我会检查数据的分布:

use data4, clear
foreach i of varlist _all {
di "hist `i'"
}

也可以把所有的变量分布绘制到一幅图上:

use data4, clear
gather 行政区域面积 乡 镇 街道办事处 户籍人口 ///
地区生产总值 第一产业增加值 第二产业增加值 ///
第三产业增加值 地方一般公共预算收入 ///
地方一般公共预算支出 住户存款余额 ///
年末金融机构各项贷款余额 设施农业种植占地面积 ///
油料产量 棉花产量 规模以上工业企业 固定电话用户 ///
普通中学在校学生 小学在校学生 医疗卫生机构床位 ///
提供住宿的民政服务机构 提供住宿的民政服务机构床位数
tw hist value || ///
kdensity value, by(var, rescale note("") leg(off))

gr export picn.png, width(2400) replace

看起来基本没什么明显的问题,大家如果时间充裕的话,也可以逐个检查那些分布右偏的变量

检查区县名称的识别错误

虽然在人工检查过程中我们也纠正了一些识别错误问题,不过应该还有很多我们没有发现,在下次课中我们会讲解如何为数据添加行政区划代码,在这个过程中我们就能发现和纠正区县名称识别错误的问题了。另外如果把多年的数据匹配起来,就可以通过检查数据的趋势来识别出数值错误的问题。

点击这里跳转到 RStata 短书平台获取附件:如何整理 2022 年县域统计年鉴:caj 文件转 pdf、文本识别与数据清洗

评论