核心避坑指南关键代码拆解为了不让电脑在处理这 140 万数据时原地升天我在代码里埋了几个关键的“保命”操作1. 化整为零分块读取Chunking别指望能一口气把几 GB 的 CSV 全吞进内存。脚本里最核心的思路就是设置chunksize每次只读 10 万行像吃奥利奥一样一口一口啃完。# 开启分块迭代140万数据拆成14次吃完 chunk_iter pd.read_csv( csv_path, chunksizeCHUNK_SIZE, # CHUNK_SIZE 设为了 100,000 low_memoryFalse ) for chunk_id, chunk in enumerate(chunk_iter): # 局部战场一次只处理这10万条2. 暴力清洗别让脏数据报了错从各种系统里导出的文本坐标经常夹杂着空值NaN或者奇怪的空格、特殊字符。如果不清洗就直接去构建点代码一秒钟报错给你看。这里我用了两道防火墙先删空值再用pd.to_numeric强转数值转失败的变空值最后再删一次。# 保证经纬度列干干净净全是纯数字 chunk chunk.dropna(subset[X_FIELD, Y_FIELD]) chunk[X_FIELD] pd.to_numeric(chunk[X_FIELD], errorscoerce) chunk[Y_FIELD] pd.to_numeric(chunk[Y_FIELD], errorscoerce) chunk chunk.dropna(subset[X_FIELD, Y_FIELD])3. 内存流裁剪不等写入直接在内存里 Clip利用 GeoPandas 的points_from_xy配合底层的 C 语言加速把坐标秒变几何对象Shapely Point。接着不需要存盘直接在内存里把这个分块和裁剪边界进行空间相交计算# 内存里快速捏出点要素并执行空间裁剪 geometry gpd.points_from_xy(chunk[X_FIELD], chunk[Y_FIELD]) gdf gpd.GeoDataFrame(chunk, geometrygeometry, crsCRS) gdf_clip gpd.clip(gdf, clip_gdf)4. 卸磨杀驴主动召唤垃圾回收Python 的自动内存管理有时候比较“迟钝”分块循环一多内存还是会像滚雪球一样涨上来。所以处理完一块就得立刻用del把变量扬了再用gc.collect()强行把内存抠出来。# 这一块搞定了立马给内存减负 del chunk, gdf, gdf_clip gc.collect()⚠️ 避坑铁律别再抱着用 Shapefile 的幻想了老一辈 GIS 人习惯开口闭口就是.shp但在百万级数据面前Shapefile 就是个弟弟单个.shp文件大小卡死在2GB140万数据带点属性分分钟写爆。属性表字段名最多10 个字符长一点的字段直接给你截成乱码。所以听哥一句劝脚本里果断采用现代化的GeoPackage (.gpkg)格式。单文件存储、不限大小、支持空间索引读写性能甩 shp 几条街。 拿去即用的完整脱敏脚本路径和敏感字段我已经全部做好了脱敏处理替换成了通用的./data、longitude/latitude。大家copy过去之后只需要在参数配置中心改成你自己的路径就能直接跑# -*- coding: utf-8 -*- 功能 1. 批量读取海量 CSV 坐标表 2. 根据指定的 X、Y 字段批量生成点矢量 3. 自动匹配地理坐标系如 WGS 1984 4. 基于空间边界如 File Geodatabase 中的面图层进行精准裁剪 5. 针对 140万 超大数据集进行了内存深度优化 依赖安装 pip install pandas geopandas shapely pyogrio fiona import os import gc import glob import pandas as pd import geopandas as gpd # # ⚙️ 参数配置中心 # # 1. 输入数据配置 CSV_FOLDER r./data/csv_inputs # 存放待处理 CSV 文件的文件夹 X_FIELD longitude # CSV中代表经度(X)的字段名 Y_FIELD latitude # CSV中代表纬度(Y)的字段名 # 2. 裁剪边界配置 (支持 .gdb, .shp, .gpkg 等) CLIP_VECTOR r./data/boundary.gdb # 空间裁剪面所在的矢量文件路径 CLIP_LAYER study_area # 如果是 GDB填写对应的面图层名 # 3. 缓存与输出配置 TEMP_DIR r./data/temp_cache # 局部处理时的临时缓存目录 OUTPUT_FILE r./output/result.gpkg # 最终汇总的 GeoPackage 文件路径 OUTPUT_LAYER clipped_points # 输出到 GPKG 内的图层名称 # 4. GIS标准配置 CRS EPSG:4326 # 目标坐标系GCS_WGS_1984 CHUNK_SIZE 100000 # 单次分块读取的行数视内存大小可调 # # 自动化核心流程 # def main(): # 创建临时缓存与输出目录 os.makedirs(TEMP_DIR, exist_okTrue) os.makedirs(os.path.dirname(OUTPUT_FILE), exist_okTrue) # 扫描目标文件夹下的所有 CSV 文件 csv_files glob.glob(os.path.join(CSV_FOLDER, *.csv)) if len(csv_files) 0: raise FileNotFoundError(f在路径 [{CSV_FOLDER}] 下未找到任何 CSV 文件) print(f[INFO] 共发现 {len(csv_files)} 个待处理的 CSV 文件) # 载入空间裁剪边界 print(\n[GIS] 正在读取裁剪面边界...) clip_gdf gpd.read_file(CLIP_VECTOR, layerCLIP_LAYER) # 确保裁剪边界坐标系与点数据一致 clip_gdf clip_gdf.to_crs(CRS) temp_files [] # 循环遍历每个 CSV 文件 for file_index, csv_path in enumerate(csv_files): print(f\n正在处理文件 ({file_index 1}/{len(csv_files)}): {os.path.basename(csv_path)}) # 开启分块迭代读取 chunk_iter pd.read_csv( csv_path, chunksizeCHUNK_SIZE, low_memoryFalse ) for chunk_id, chunk in enumerate(chunk_iter): print(f - 正在处理第 {chunk_id 1} 块数据...) # 第一次过滤移除坐标存在空值的行 chunk chunk.dropna(subset[X_FIELD, Y_FIELD]) # 数据类型强转防止非数值型异常文本堵塞几何构建 chunk[X_FIELD] pd.to_numeric(chunk[X_FIELD], errorscoerce) chunk[Y_FIELD] pd.to_numeric(chunk[Y_FIELD], errorscoerce) # 第二次过滤移除强转后产生的空值行 chunk chunk.dropna(subset[X_FIELD, Y_FIELD]) if chunk.empty: continue # 构建矢量几何点 geometry gpd.points_from_xy(chunk[X_FIELD], chunk[Y_FIELD]) gdf gpd.GeoDataFrame(chunk, geometrygeometry, crsCRS) # 执行空间裁剪 gdf_clip gpd.clip(gdf, clip_gdf) # 如果裁剪后仍有残余点则写入临时文件 if not gdf_clip.empty: temp_output os.path.join( TEMP_DIR, ftemp_{file_index}_{chunk_id}.gpkg ) gdf_clip.to_file( temp_output, layerpoints, driverGPKG ) temp_files.append(temp_output) # 实时、显式释放内存 del chunk, gdf, gdf_clip gc.collect() # # 成果合并与落盘 # if len(temp_files) 0: print(\n[⚠️警告] 没有任何点落在裁剪边界内未生成任何结果。) return print(\n[GIS] 开始汇总合并所有中间分块结果...) merged_list [] for temp_file in temp_files: gdf gpd.read_file(temp_file) merged_list.append(gdf) # 读完即删减小物理空间占用 os.remove(temp_file) # 拼接所有的 Dataframe merged pd.concat(merged_list, ignore_indexTrue) merged gpd.GeoDataFrame(merged, geometrygeometry, crsCRS) print([GIS] 正在向本地写入最终的 GeoPackage 文件...) merged.to_file( OUTPUT_FILE, layerOUTPUT_LAYER, driverGPKG ) print(\n 处理成功完成) print(f 最终成果保存至{OUTPUT_FILE}) if __name__ __main__: main()