Jupyter Notebook和Python到底有什么区别?城乡规划GIS实战对比(附:数据处理脚本)
Jupyter Notebook和Python到底有什么区别?城乡规划GIS实战对比(附:数据处理脚本)这个问题,很多刚开始做规划数据分析、GIS制图或空间统计的同学都会遇到:明明都是写 Python,为什么有人打开的是 Jupyter Notebook,有人运行的是 .py 脚本?在城乡规划 GIS 实战里,二者不是“谁替代谁”的关系,而是适合不同阶段、不同工作方式的工具组合。
引言:先把结论说清楚
简单说,Python是一门编程语言,也可以理解为完成 GIS 自动化、空间数据处理、统计分析的“能力本身”;Jupyter Notebook是一个交互式写 Python 的工作环境,更适合边看数据、边画图、边调试。
在城乡规划 GIS 工作中,你可以这样理解:
- Jupyter Notebook适合探索数据、检查字段、快速画图、写分析过程说明。
- Python 脚本适合批量处理、重复运行、定时任务、交给同事或服务器执行。
- 二者都可以调用 GeoPandas、Pandas、Shapely、Rasterio、ArcPy、PyQGIS 等 GIS 常用库。
如果你正在处理居住用地、道路、人口、POI、行政区边界等规划数据,建议先用 Jupyter Notebook 探索流程,再把稳定步骤整理成 Python 脚本。

背景:为什么 GIS 学习者容易混淆 Jupyter Notebook 和 Python
很多人第一次接触 Python GIS,是从 Anaconda、Jupyter Notebook 或 ArcGIS Pro 的 Python 窗口开始的。于是容易产生一个误解:以为 Jupyter Notebook 就是 Python,或者以为写 .py 文件才算真正使用 Python。
实际上,它们处在不同层级:
- Python:语言本身,负责表达逻辑,例如读取数据、筛选字段、空间连接、面积统计。
- Python 解释器:执行 Python 代码的程序,例如系统里的
python.exe。 - Jupyter Notebook:浏览器里的交互式开发环境,把代码、结果、图表、说明文字放在一个
.ipynb文件中。 - Python 脚本:通常是
.py文件,适合一次性从上到下执行完整流程。
在城乡规划 GIS 项目里,常见任务包括用地现状汇总、缓冲区分析、道路可达性统计、规划范围内指标计算、地块属性清洗等。这些任务既可以在 Jupyter Notebook 中完成,也可以写成 Python 脚本完成,区别主要在于工作方式。
原理:Jupyter Notebook 和 Python 的核心区别
理解 Jupyter Notebook 和 Python 的区别,可以从“运行方式”“文件形态”“适用场景”三个角度看。
1. 运行方式不同
Jupyter Notebook 把代码拆成一个个单元格,每个单元格可以单独运行。你可以先读取规划范围数据,再查看字段,再画地图,再修改筛选条件。
Python 脚本通常从第一行运行到最后一行,更强调完整流程。比如每天自动读取最新 POI 数据、叠加街道边界、输出统计表,就更适合脚本。
2. 文件形态不同
- Jupyter Notebook 文件扩展名通常是
.ipynb。 - Python 脚本文件扩展名通常是
.py。 .ipynb可以保存代码、运行结果、图表和文字说明。.py更简洁,适合版本管理、自动运行和工程化维护。
3. 对 GIS 分析的影响不同
对于 GIS 初学者和规划分析人员,Jupyter Notebook 的优势是“看得见中间过程”。例如你做空间叠加时,可以随时查看坐标系、属性表、几何数量和地图效果。
但当流程稳定后,Notebook 里散落的单元格可能带来风险:如果单元格没有按顺序运行,结果可能和你以为的不一样。此时,把流程整理成 Python 脚本更可靠。
步骤:用同一个城乡规划 GIS 任务对比二者
下面用一个常见任务说明:统计每个街道范围内的居住用地面积。假设你有两个数据:
street_boundary.geojson:街道边界面数据。landuse.geojson:用地现状面数据,字段land_type表示用地类型。
目标是筛选出居住用地,与街道边界叠加,计算每个街道的居住用地面积,并导出 CSV 表格。
步骤 1:在 Jupyter Notebook 中探索数据
Notebook 适合先检查数据是否能正常读取、字段是否正确、坐标系是否适合计算面积。
import geopandas as gpd
streets = gpd.read_file("data/street_boundary.geojson")
landuse = gpd.read_file("data/landuse.geojson")
print(streets.head())
print(landuse.head())
print(streets.crs)
print(landuse.crs)
print(landuse["land_type"].value_counts())
这一步的重点不是马上出结果,而是确认数据质量。尤其要检查:
- 街道边界和用地数据是否有坐标系。
- 两个图层坐标系是否一致。
land_type字段里居住用地的实际写法是什么。- 是否存在空几何、无效几何或重复地块。
步骤 2:统一坐标系并筛选居住用地
面积计算不能直接依赖经纬度坐标系。经纬度单位是度,不是米。为了得到平方米或公顷,应投影到适合本地的投影坐标系。下面示例使用 EPSG:3857 只是演示,正式项目应改成本地 CGCS2000 高斯克吕格投影或项目指定投影。
target_crs = "EPSG:3857"
streets_proj = streets.to_crs(target_crs)
landuse_proj = landuse.to_crs(target_crs)
residential = landuse_proj[landuse_proj["land_type"].str.contains("居住", na=False)].copy()
print(residential.shape)
如果你的数据来自国土空间规划、控规或现状调查,字段值可能不是“居住”,而是“R”、“城镇住宅用地”、“0701”等代码。正式脚本需要根据实际字段字典调整筛选条件。
步骤 3:空间叠加并计算面积
使用 overlay 做相交叠加,让居住用地按街道边界切分,然后计算每块相交区域的面积。
intersected = gpd.overlay(residential, streets_proj, how="intersection")
intersected["area_sqm"] = intersected.geometry.area
intersected["area_ha"] = intersected["area_sqm"] / 10000
print(intersected[["area_sqm", "area_ha"]].head())
如果数据量较大,空间叠加可能较慢。可以先用边界范围裁剪、简化字段、修复几何,减少计算量。
步骤 4:按街道汇总并导出
假设街道名称字段为 street_name,可以按街道汇总居住用地面积。
summary = (
intersected
.groupby("street_name", as_index=False)
.agg(residential_area_ha=("area_ha", "sum"))
)
summary.to_csv("output/residential_area_by_street.csv", index=False, encoding="utf-8-sig")
print(summary)
到这里,Notebook 已经完成了探索、检查、计算和导出。你可以在 Notebook 中继续画图,检查结果是否符合直觉。
步骤 5:把稳定流程整理成 Python 脚本
当你确认逻辑正确,就可以把流程整理为 planning_residential_area.py。这样更适合重复运行,也方便交给同事使用。
import geopandas as gpd
from pathlib import Path
def calculate_residential_area(
street_path,
landuse_path,
output_csv,
street_name_field="street_name",
landuse_field="land_type",
residential_keyword="居住",
target_crs="EPSG:3857"
):
street_path = Path(street_path)
landuse_path = Path(landuse_path)
output_csv = Path(output_csv)
output_csv.parent.mkdir(parents=True, exist_ok=True)
streets = gpd.read_file(street_path)
landuse = gpd.read_file(landuse_path)
if streets.crs is None:
raise ValueError("街道边界数据缺少坐标系,请先定义 CRS。")
if landuse.crs is None:
raise ValueError("用地数据缺少坐标系,请先定义 CRS。")
streets_proj = streets.to_crs(target_crs)
landuse_proj = landuse.to_crs(target_crs)
if street_name_field not in streets_proj.columns:
raise ValueError(f"街道字段不存在:{street_name_field}")
if landuse_field not in landuse_proj.columns:
raise ValueError(f"用地类型字段不存在:{landuse_field}")
residential = landuse_proj[
landuse_proj[landuse_field].astype(str).str.contains(residential_keyword, na=False)
].copy()
if residential.empty:
raise ValueError("没有筛选到居住用地,请检查字段值或筛选关键词。")
intersected = gpd.overlay(residential, streets_proj, how="intersection")
intersected["area_ha"] = intersected.geometry.area / 10000
summary = (
intersected
.groupby(street_name_field, as_index=False)
.agg(residential_area_ha=("area_ha", "sum"))
)
summary.to_csv(output_csv, index=False, encoding="utf-8-sig")
return summary
if __name__ == "__main__":
result = calculate_residential_area(
street_path="data/street_boundary.geojson",
landuse_path="data/landuse.geojson",
output_csv="output/residential_area_by_street.csv",
street_name_field="street_name",
landuse_field="land_type",
residential_keyword="居住",
target_crs="EPSG:3857"
)
print(result)
这个脚本已经比 Notebook 更接近实际项目使用方式:输入路径、字段名、筛选关键词和目标坐标系都可以调整;同时加入了基本错误提示,便于排查数据问题。
常见坑:城乡规划 GIS 使用 Jupyter Notebook 和 Python 时最容易出错的地方
坑 1:以为 Notebook 运行顺序等于代码显示顺序
Notebook 的单元格可以任意运行。如果你先运行了下面的单元格,再回头修改上面的变量但没有重新运行全部流程,结果可能已经不一致。
建议在提交成果前,使用“重新启动内核并全部运行”的方式检查 Notebook 是否能从头到尾稳定执行。
坑 2:用经纬度直接算面积
很多规划数据是 EPSG:4326 经纬度坐标系,直接执行 geometry.area 得到的不是平方米,而是“度的平方”,没有实际规划统计意义。
面积、长度、缓冲区距离等计算,应优先使用适合项目区的投影坐标系。
坑 3:没有检查 CRS 是否一致
街道边界和用地图层坐标系不同,会导致叠加失败、错位或结果为空。空间分析前应检查:
gdf.crs是否存在。- 两个图层是否处于同一坐标系。
- 是否只是“定义坐标系”错误,而不是需要“投影转换”。
坑 4:字段名称和字段值不稳定
不同来源的规划用地数据字段差异很大。居住用地可能写成“居住用地”“住宅用地”“R”“0701”。脚本里不要盲目写死字段,应在 Notebook 中先查看字段和唯一值。
坑 5:Notebook 适合展示,但不等于适合生产流程
Notebook 很适合写分析说明和结果展示,但如果一个任务需要每天、每周或每个项目反复跑,建议整理成 Python 脚本,减少人工点击和运行顺序错误。
方法比较:Jupyter Notebook 和 Python 脚本在 GIS 实战中的选择
| 对比项 | Jupyter Notebook | Python 脚本 |
|---|---|---|
| 适合阶段 | 数据探索、教学演示、分析说明 | 批处理、自动化、项目交付 |
| 文件类型 | .ipynb |
.py |
| 运行方式 | 按单元格交互运行 | 从上到下完整运行 |
| GIS 优势 | 方便查看属性表、地图和中间结果 | 适合批量转换、空间处理、自动导出 |
| 主要风险 | 单元格顺序混乱,结果不易复现 | 调试不如 Notebook 直观 |
| 适合人群 | GIS 学生、规划分析师、数据探索人员 | GIS 工程师、数据处理人员、自动化运维人员 |
如果你只是在学习 Python GIS,建议优先从 Jupyter Notebook 开始,因为它能让你快速看到每一步的输出。如果你已经在做项目交付,建议逐步训练自己把 Notebook 中验证过的代码整理成 Python 脚本。
检查清单:规划 GIS 数据处理前后应该确认什么
无论你使用 Jupyter Notebook 还是 Python 脚本,城乡规划 GIS 数据处理都建议按下面清单检查。
- 输入数据:文件路径是否正确,编码是否正常,字段是否完整。
- 坐标系:是否存在 CRS,是否需要投影转换,面积单位是否为平方米。
- 几何质量:是否有空几何、无效面、自相交、多部件异常。
- 字段规则:用地类型代码是否符合项目字典,筛选条件是否遗漏类别。
- 空间关系:叠加结果是否为空,边界是否错位,是否存在跨界地块。
- 统计结果:汇总面积是否明显偏大或偏小,单位是否转换为公顷或平方公里。
- 输出文件:CSV 是否能被 Excel 正确打开,中文是否乱码,字段名是否清晰。
- 可复现性:Notebook 是否能全部重新运行,脚本是否能在新环境中执行。
实务建议:Notebook 里保留“为什么这样做”的说明,Python 脚本里保留“怎样稳定运行”的参数和错误提示。这样既方便学习复盘,也方便项目复用。
FAQ:Jupyter Notebook 和 Python 在 GIS 学习中的常见问题
Q1:学 GIS 自动化,应该先学 Jupyter Notebook 还是 Python?
应该先理解 Python 基础语法,再用 Jupyter Notebook 练习 GIS 数据读取、字段处理和空间分析。Notebook 只是写 Python 的环境,不是另一门语言。
Q2:Jupyter Notebook 能不能做正式 GIS 项目?
可以,但更适合分析报告、方法验证和过程展示。如果是需要反复运行的正式生产流程,建议整理成 Python 脚本,便于自动化和维护。
Q3:ArcGIS Pro 里的 Python 和 Jupyter Notebook 有关系吗?
ArcGIS Pro 自带 Python 环境,并支持 Notebook 工作方式。你可以在 ArcGIS Pro 的 Notebook 中调用 ArcPy,也可以把成熟代码保存为 .py 脚本执行。
Q4:QGIS 用户需要 Jupyter Notebook 吗?
不一定,但很有用。QGIS 用户可以在 Notebook 中使用 GeoPandas、Shapely、PyProj 等库处理数据,也可以在 QGIS Python 控制台或处理工具箱中使用 PyQGIS。
Q5:为什么我的 Python 脚本在 Notebook 里能跑,保存成 .py 后就报错?
常见原因包括相对路径变化、当前工作目录不同、依赖库环境不同、Notebook 中已有变量没有写入脚本。把路径、参数和导入库写完整,通常可以解决。
Q6:城乡规划 GIS 数据处理中,Notebook 最适合做什么?
最适合做字段检查、坐标系检查、用地类别探索、快速制图、统计逻辑验证和成果说明。它能把代码、文字和结果放在同一个文件里,便于教学和沟通。
Q7:什么时候必须把 Notebook 改成 Python 脚本?
当任务需要批量处理多个区县、多个年份、多个数据版本,或者需要放到服务器、计划任务、项目模板中运行时,就应该改成 Python 脚本。
结论:不要纠结谁更高级,要看 GIS 任务处在哪个阶段
Jupyter Notebook 和 Python 的区别,本质上不是“初级工具”和“高级工具”的区别,而是“交互式探索”和“脚本化自动化”的区别。对于城乡规划 GIS 实战,最稳妥的路线是:先用 Jupyter Notebook 看清数据、验证方法、记录过程,再用 Python 脚本固化流程、批量处理、稳定交付。
如果你刚开始学习 Python GIS,可以从本文的居住用地面积统计示例入手,先在 Notebook 中逐行运行,确认坐标系、字段和结果;等流程跑通后,再整理为可复用的 .py 脚本。这样既能避免“只会点软件”的局限,也能逐步建立真正可复现的 GIS 数据处理能力。