简介这份面向非遗研究者、地理信息分析人员与文化遗产规划者的空间数据集收录2006—2021年间我国五批次共3610个国家级非物质文化遗产项目的空间分布信息。通过项目名称、分类、地理位置、申报时间、批次、传承人及保护状况等属性可支撑地域文化差异分析、时空演变研究、保护成效评估与文旅资源规划等场景。压缩包共8个文件包含shp矢量几何、dbf属性数据、prj坐标参考、shx空间索引及xml元数据等完整Shapefile组件整体约565KB可直接在ArcGIS、QGIS等平台加载使用目前已有341人浏览学习。借助该数据用户可快速绘制非遗分布地图并关联属性字段进行统计为论文写作、政策研究与遗产保护工作提供量化基础。1. 从压缩包到专题地图:IhChina 数据集到底能做什么拿到一个名叫中国五批3610个国家级非物质文化遗产空间分布数据集IhChina_2006-2021.zip的压缩包时,大多数人的第一反应是双击解压,然后拖进 GIS 里看看里面到底是什么。这个数据集的核心价值在于,它把 2006 年第一批到 2021 年第五批公布的国家级非遗项目收进同一个空间框架,每个项目对应一个可落图的坐标点。对做文化地理、遗产保护规划、城市品牌地图的人来说,它回答的是非遗项目到底分布在哪、是否扎堆、与河流/城市群/交通线的关系如何这类问题。本文按一条完整落地路径来讲:zip 解压与完整性检查、字段与坐标系识别、用 GeoPandas 读成空间数据框、做点图与空间统计,最后给五条实操避坑。适合 GIS 分析师、文化遗产研究方向的学生,以及刚接手这套数据但不知道从哪下手的地图产品经理。2. 先把 zip 解压变成可诊断的过程:目录结构、字段与坐标系拿到手的第一步不是双击解压,而是把解压行为变成一个能反馈错误的命令行操作。很多非遗数据集不是单一文件,空间数据文件往往以 shapefile 三件套(shp、dbf、shx)出现,也可能带 .prj 投影文件,甚至同目录下还放了一张带经纬度的 CSV 表。用命令行解压,既能看清包内层级,也能提前发现压缩包损坏,而不是等到 GIS 加载到一半才报错。2.1 用 unzip -l 看目录,用 -t 验证 CRC:EOCD 报错不用慌unzip -l IhChina_2006-2021.zip # 只列出压缩包内容,不解压 unzip -t IhChina_2006-2021.zip # 逐个文件校验 CRC 完整性 unzip -q IhChina_2006-2021.zip -d ./ihchina # 静默解压到指定目录-l的作用是列出压缩包里的所有文件名、原始大小和压缩后大小。拿到包先跑这条,能立刻知道里面是 shp 三件套还是纯 CSV,有没有顶层目录,文件数量是不是对得上五批 3610 项的量级。-t会逐个文件做 CRC 校验,输出No errors detected才算完整。如果这里报出invalid zip archive: could not find EOCD,不要急着换解压工具硬解。EOCD 是 zip 文件结尾的核心目录记录,报这个错通常意味着文件上传/下载被截断,或者把分卷压缩包误当成单文件下载了。处理办法是重新下载完整文件,或者让提供方用标准 zip 工具重压后再传。-d ./ihchina指定解压目录。我一般会建一个英文且不带空格的目录,后面用 Python 读取时可以少处理很多路径转义问题。如果压缩包里中文文件名在 Linux 下解压出来是乱码,部分 unzip 版本支持-O gbk指定文件名原始编码,但 macOS 自带的 unzip 不一定支持,这时先解压再用convmv -f gbk -t utf8批量修正文件名。注意-q是安静模式,只在出错时输出信息,适合看到完整体验。提示:不要直接双击解压后就去 GIS 里加载。双击方式遇到深层目录或坏包时只弹一个模糊错误,你分不清是文件截断还是路径问题;命令行至少能告诉你 CRC 在哪一步断的。2.2 字段映射:五批名录、十大门类与经纬度列的三种存在形式解压后先看目录清单,通常是一组同名文件。shapefile 的 .dbf 里存属性字段,常见字段有项目名称、项目编号、申报地区或保护单位、类别,以及批次或公布年份。国家级非遗按十大门类划分:民间文学、传统音乐、传统舞蹈、传统戏剧、曲艺、传统体育游艺与杂技、传统美术、传统技艺、传统医药、民俗。空间字段有两种存在方式:一是矢量几何直接写在 .shp 里,读取后自带 geometry;二是只给一个经纬度表格,需要你把 lng/lat 两列转成点。五批名录的公布年份分别落在 2006、2008、2011、2014、2021 年,所以这套数据里通常会有公布年份或批次字段。但不同机构整理的数据字段名差别很大,有的写publish_year,有的写batch,有的干脆把年份写在备注文本里,不能下意识认为一定有标准列。拿到字段后先做一次频率统计,确认每个字段的取值:import pandas as pd df pd.read_csv(./ihchina/IhChina_2006-2021.csv, encodingutf-8) print(df.columns.tolist()) for col in [批次, batch, publish_year, 类别, category]: if col in df.columns: print(f--- {col} ---) print(df[col].value_counts(dropnaFalse).head(10))这段代码先打印完整列名,再把常见的中英文别名列逐一做频数统计。dropnaFalse会把空值也计入,方便看到哪些行缺字段。列名不统一时不要硬改原文件,我的习惯是复制一份副本,把字段名统一映射成name / batch / year / category / lng / lat,后续绘图和统计都基于这份标准化副本。2.3 坐标底细:WGS84、GCJ02 与投影坐标系的判断空间数据集的坐标系决定地图会不会偏。国家级非遗点位的坐标常见来源有两类:一类是实地采集或从行政区划中心点落点,采用 WGS84 经纬度,EPSG 编号 4326;另一类是早年从在线地图上抓取的坐标,很可能被写成 GCJ02 加密坐标。GCJ02 是国内地图产品常用的偏移坐标系,如果你用 4326 底图去叠,会发现点整体错位几百米甚至更远。判断方法不能靠猜,直接打印数据边界:import geopandas as gpd gdf gpd.read_file(./ihchina/IhChina_2006-2021.shp, encodingutf-8) print(gdf.crs) print(gdf.total_bounds)crs会显示数据自带的空间参考;total_bounds返回 xmin, ymin, xmax, ymax。如果 crs 是 EPSG:4326 且边界大致落在 (73, 18, 135, 54) 区间,通常就是正常的 WGS84 经纬度;如果经度范围明显不在 73~135 之间,比如出现负坐标,大概率是经纬度两列写反了;如果边界在正常范围但底图叠加后有几百米偏移,再考虑 GCJ02 加密问题。GCJ02 没有现成的标准 EPSG 代码,常见做法是写转换函数把加密坐标还原成 WGS84,或者放弃在线底图,改用不加密的底图数据。对绝大多数空间统计而言,统一到 EPSG:4326 就够了,后续投影转换在分析时单独做。3. 用 Python 把 3610 个非遗点读进 GeoDataFrame:环境、编码与去重数据读取是整套流程里最枯燥但也最容易翻车的一步。环境没装对、编码选错、坐标系没赋值,都会让后续画图和分析建立在错误基础上。这一章给出可直接复制的命令和代码,并解释每个参数为什么这样设。3.1 一次装齐读取与空间运算环境:为什么选 conda 而不是裸 pip空间数据处理绕不开 GDAL、GEOS、PROJ 这几个底层库。GeoPandas 依赖 Fiona 读取矢量、依赖 pyproj 做坐标转换,这些库都用原生 C/C 代码编译,直接用 pip 安装时经常卡在编轮子或者缺系统依赖。社区普遍推荐用 conda 创建独立环境,由 conda-forge 提供预编译包。conda create -n ihchina python3.11 -y conda activate ihchina conda install -c conda-forge geopandas pyproj matplotlib contextily -y创建名为ihchina的 Python 3.11 环境,然后安装 GeoPandas 全家桶。matplotlib用于出图,contextily用于叠加在线底图。如果网络条件有限,可以先把 pyproj 和 shapely 装好再装 geopandas,避免一次下载太多导致中断。装完后用python -c import geopandas; print(gpd.__version__)验证,能输出版本号就说明环境正常。如果你坚持用 pip,建议先安装wheel,再执行pip install geopandas。Windows 上纯 pip 大概率会要求你先装 Microsoft C Build Tools,这也是我推荐 conda 的原因。3.2 读取 shapefile 或 CSV:encoding 参数不是玄学读 shapefile 只要一行代码,但编码参数经常决定幸福还是崩溃。dbf 属性表的历史编码非常混乱,国内数据的字段中文名常用 GBK/GB2312,但也有不少新整理的数据用 UTF-8。GeoPandas 默认按 UTF-8 解码,遇到 GBK 数据就会抛解码异常或读出乱码。import geopandas as gpd from pathlib import Path data_dir Path(./ihchina) # 方案一:读取 shapefile,乱码时换 encoding 参数 shp data_dir / IhChina_2006-2021.shp gdf gpd.read_file(shp, encodingutf-8) # 方案二:读取 CSV 并手动生成点几何 import pandas as pd df pd.read_csv(data_dir / IhChina_2006-2021.csv, encodingutf-8) gdf gpd.GeoDataFrame( df, geometrygpd.points_from_xy(df[lng], df[lat]), crsEPSG:4326, )第一种方式读取 shapefile,如果字段名或属性值乱码,把encodingutf-8改成encodinggbk再读,大多数情况能解决。第二种方式适合数据包只有 CSV 坐标表的情形,points_from_xy(df[lng], df[lat])要注意参数顺序,先经度后纬度,和通常说的经纬度顺序相反,写反了整张图会水平翻转。读取后一定做三件事:打印前五行、打印 crs、打印行数。如果crs为空,后面画图会默认按平面坐标处理,点位会完全错乱。CSV 路线里手动指定crsEPSG:4326就是在做这件事。3.3 数量核对、空几何过滤与重复点检查:输出 3610 只是开始标题写 3610,不代表能落图的点一定有 3610 个。部分项目只精确到地市名称,没有具体坐标;也有项目在录入时重复,同一地址出现多行同类记录。开始分析前先把数据洗干净。print(读取行数:, len(gdf)) print(空几何:, gdf.geometry.is_empty.sum()) print(空值几何:, gdf.geometry.isna().sum()) gdf_clean gdf[~gdf.geometry.is_empty ~gdf.geometry.isna()].copy() gdf_clean gdf_clean[gdf_clean.geometry.type.isin([Point, MultiPoint])] gdf_clean gdf_clean.drop_duplicates(subset[项目名称, 批次]) print(清洗后行数:, len(gdf_clean)) print(范围:, gdf_clean.total_bounds)先统计空几何和空值几何的条数,再过滤掉无效行。geometry.type.isin只保留点类型,防止混入意外的面要素。drop_duplicates(subset[项目名称, 批次])把同一项目同一批次下的重复坐标去掉;如果你希望保留保护单位级别的完整颗粒度,可以省去这一步,只按坐标精确去重。最后打印的范围是快速体检:经度该在 73~135 之间,纬度该在 18~54 之间,一旦出现 0 或负值,返回 2.3 检查坐标来源。4. 点图、核密度与最近邻:把分布地图变成研究证据干净的 3610 个点放进地图后,第一眼是密密麻麻的红色圆点。分布特征要用统计方法讲清楚,否则看起来集中在东部这句话说服不了评审。这一章先出基础点图,再做核密度,最后用最近邻指数检验聚集是否显著。4.1 先出点图:颜色按批次、大小按类别,用底图对齐位置点图是整套分析的第一张产出。绘图前一定要把数据投影到 Web 墨卡托(EPSG:3857),因为套路底图都是 3857 切出来的,直接用经纬度画会出现高纬度拉伸,图像是畸形的。import matplotlib.pyplot as plt import contextily as ctx fig, ax plt.subplots(figsize(12, 10)) gdf_clean.to_crs(epsg3857).plot( axax, markersize4, alpha0.7, colordarkred, ) ctx.add_basemap(ax, sourcectx.providers.CartoDB.Positron) ax.set_axis_off() plt.savefig(ihchina_points.png, dpi300, bbox_inchestight)to_crs(epsg3857)先做投影转换,markersize4控制点的大小,3610 个点密度很大,过大会糊成一片;alpha0.7控制透明度,重叠区域会看到颜色叠加效果。ctx.add_basemap会自动按当前坐标范围拉取在线瓦片,底图投影必须和地理数据一致。离线环境可以先缓存瓦片,或者直接用ax.set_extent配合省界矢量作为底图。4.2 核密度:把全国遍地都是非遗变成可比较的密度场点图只能看到点在哪里聚集,核密度估计能算出每个位置的点密度,把自然语言的东部密、西部疏变成一个网格数值。常用scipy.stats.gaussian_kde实现,核心参数是带宽bw_method。import numpy as np from scipy.stats import gaussian_kde coords np.vstack([gdf_clean.geometry.x, gdf_clean.geometry.y]) xmin, ymin, xmax, ymax gdf_clean.total_bounds grid_x, grid_y np.meshgrid( np.linspace(xmin, xmax, 500), np.linspace(ymin, ymax, 500), ) kde gaussian_kde(coords, bw_method0.03) density kde(np.vstack([grid_x.ravel(), grid_y.ravel()])).reshape(grid_x.shape)bw_method是高斯核的带宽参数,数值越小核越尖锐,适合看出局部热点;数值越大越平滑,适合展示宏观格局。经纬度单位下 0.03 大约对应 3 公里左右的可视化带宽,具体要按数据密度调试。一个重要的坑:直接在经纬度坐标上做核密度,在高纬度地区实际物理距离会被压缩,严谨的做法是先投影到 Albers 等面积投影或 UTM 分带,再做网格和核密度。这里给出的是快速草图,正式论文中不建议直接提交。4.3 最近邻指数:把看起来扎堆升级成统计量核密度图有视觉效果,但缺少一个可引用的数字。平均最近邻距离可以判断点模式是聚集、随机还是均匀。指数 R1 表示完全随机,R小于 1 表示聚集,R大于 1 表示扩散。from scipy.spatial import cKDTree coords np.column_stack([gdf_clean.geometry.x, gdf_clean.geometry.y]) tree cKDTree(coords) nearest_dist, _ tree.query(coords, k[2]) # k2 是因为最近邻是自身 observed float(nearest_dist[:, 0].mean()) area (gdf_clean.total_bounds[2] - gdf_clean.total_bounds[0]) * \ (gdf_clean.total_bounds[3] - gdf_clean.total_bounds[1]) expected 0.5 * np.sqrt(area / len(gdf_clean)) R observed / expected print(平均最近邻距离:, observed) print(随机期望距离:, expected) print(最近邻指数 R:, R)cKDTree.query的k[2]表示取第 2 近的点,也就是真最近邻,因为第 1 近是点自身。观察距离是实际点之间的相邻距离,期望距离基于矩形研究区域换算。这套数据算出来 R 通常远小于 1,因为非遗点沿传统农耕区和交通线聚集,但要注意area用的是外接矩形,不是中国真实国土面积,矩形面积偏大,期望距离会被高估,R 会更低。严谨做法是加载中国国界矢量后计算实际面积。5. 五个必踩的坑:中文编码、坐标偏移、批次解析与重复点位这套数据本身不复杂,但把它喂给 GIS 或 Python 的路上坑很多。以下五条都是实操中反复出现的真实教训,每一条按现象、原因、解决三个层面说明。5.1 字段名和属性值全部乱码现象:读取后字段名变成系统之类的内容,或者中文项目名显示成问号。原因:dbf 属性表使用 GBK 或 GB2312 编码,GeoPandas 默认按 UTF-8 解码,编码不匹配就会乱码。解决方式很简单:gdf gpd.read_file(./ihchina/IhChina_2006-2021.shp, encodinggbk)如果gbk不行,优先尝试gb18030或utf-8。在 QGIS 中加载时,也可以在图层属性里手动指定文件编码。需要注意的一点是,乱码问题只发生在 shapefile 的 dbf 属性表里,GeoJSON 或 CSV 通常自己带编码声明,问题会少一些。5.2 点位整体跑到海里或境外现象:底图显示点落在国界线之外,或者整体往东南方向偏移几百米。原因:一是经纬度颠倒,把纬度填进了经度列;二是 GCJ02 加密坐标未经转换就放进 WGS84 底图。解决:先看total_bounds,如果出现类似 (18, 73, 54, 135) 的输出,说明经度和纬度写反,需要交换两列;如果边界正常但叠加后偏移,就要写 GCJ02 转 WGS84 的修正函数。另一个排查技巧是把点数据和一份省界矢量做空间连接,统计落在各省范围内外的数量,能快速定位异常点。5.3 五批数据堆在一起,年份却是字符串现象:想按年份筛选不同批次分布,却发现下拉框里年份无法排序,或者gdf[gdf[year] 2015]报错。原因:数据源把2006 年第一批写成文本,甚至包含空格和单位,列类型是 object。解决:先做文本清理再用to_datetime解析。df[year_text] df[年份].astype(str).str.replace(年, ).str.strip() df[year] pd.to_datetime(df[year_text], format%Y).dt.year这样能保留完整日期字段和纯年份字段。做时间维度分析时,建议统一使用纯整数年份,不要混用带格式的日期列,否则分面和筛选的代码会越来越绕。5.4 清洗后只剩不到 3610 行,是因为项目数不等于坐标数现象:使用drop_duplicates()去掉重复后,行数大幅缩水,有人误以为数据缺失。原因:同一非遗项目可能包含多个保护单位或多个申报地区,数据里一个项目名对应多个经纬度;也可能不同批次里出现同名保护单位。解决:先想清楚分析单元。如果目标是研究每个项目的地理位置,以项目编号或项目名称批次为去重键;如果目标是研究保护单位分布,保留全部点位另加一步空间去重。这个决策要在论文里写清楚,不能含混。5.5 zip 解压后多了一层同名目录,导致脚本找不到文件现象:脚本里写的是./ihchina/IhChina_2006-2021.shp,实际解压后路径却是./ihchina/IhChina_2006-2021/IhChina_2006-2021.shp,批量处理时全部 FileNotFoundError。原因:压缩包内部本身有顶层目录,双击解压后又套了一层文件夹。解决:回到 2.1 用unzip -l先看包内路径,再决定解压参数。最省事的做法是解压命令里直接指定输出目录,确保脚本路径与真实路径一致。6. 按批次做一组分面地图并导出 PDF:从数据包到成果图的最后一公里分析做完后,最后的成果图不能只有一张全量点图。按批次分面,能看到非遗项目从 2006 到 2021 的空间扩散过程,比单张总图更适合放进报告或汇报 PPT。这里用一个小代码块收束整套流程。import matplotlib.pyplot as plt gdf_clean[year] gdf_clean[year].fillna(0).astype(int) years [2006, 2008, 2011, 2014, 2021] labels [第一批, 第二批, 第三批, 第四批, 第五批] fig, axes plt.subplots(1, 5, figsize(25, 5)) for i, (year, label) in enumerate(zip(years, labels)): subset gdf_clean[gdf_clean[year] year] subset.to_crs(epsg3857).plot( axaxes[i], markersize2, colornavy, alpha0.6, ) axes[i].set_title(f{label} · {year}, fontsize12) axes[i].set_axis_off() plt.tight_layout() plt.savefig(ihchina_by_batch.pdf, formatpdf, bbox_inchestight)先确保年份列是整数,避免文本干扰筛选;然后按五个公布年份依次出图。subplot(1, 5)生成一行五张子图,每张子图只画对应批次的项目点位;横向排列时,地图之间的比例尺保持一致,视觉上可以直接比较空间范围。bbox_inchestight用来裁掉空白边距,PDF 格式在矢量输出和排版嵌入上比 PNG 更稳。我第一次用这套数据时,直接把 3610 个点全画在一张图上,结果整个地图黑压压一片,什么结论都看不出来。后来养成习惯,每次出图都先按年份或类别分面,这才看出沿黄河、沿运河两条明显的集聚脉络。另一个习惯是每次读数据都打印一遍 crs,防止中间某一步把坐标系悄悄弄丢。这两点算不上高深技巧,但能省掉大量返工时间,希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?