简介:BestiaPop 是一个面向作物建模与气候数据预处理场景的 Python 工具包,适合需要为 APSIM、DSSAT 等模型准备输入数据的研究者与进阶学习者。它可自动获取 SILO 或 NASA POWER 的站点或栅格气候数据,并转换为 MET、WTH 等作物模型可读取的格式,填补原始数据与建模套件之间的处理缺口。压缩包共 46 个文件,约 8.2MB,包含 15 个 py 源码、4 个 ipynb 示例笔记本,以及 rst 文档、csv 示例数据、met/wth 格式样例和少量图片、样式与配置文件,便于按模块阅读和运行示例。已有 585 人学习,可帮助读者理解气候数据下载、转换流程与包结构,也可作为自动化数据准备脚本的参考。
1. 装 bestiapop 前先看版本:一个 GDAL 冲突把 Notebook 卡了三天
装 bestiapop 这个 Python 包,最容易翻车的不是包本身,而是它背后那套 GDAL/rasterio 栅格栈。很多人第一次在 Jupyter Notebook 里import bestiapop,看到DLL load failed或者gdal-config not found,以为是包坏了,重装三遍才发现是 conda 与 pip 混着装 GDAL。这个包面向的是作物模型输入:把气候数据按模板栅格自动栅格化,输出 GeoTIFF 或 NetCDF,省掉手工裁剪、重采样和投影对齐。它适合做农业气象、作物模型、区域试验数据分析的人,尤其适合在 Jupyter Notebook 里做批量生成。环境装不对,后面所有气候插值、掩膜、重采样都会变成黑匣子,所以我先把版本和依赖顺序讲清,再往下走安装步骤。
2. 准备 Python 与 GDAL:用 conda 把栅格栈一次装齐
bestiapop 的安装难点不在纯 Python 依赖,而在 GDAL、rasterio、pyproj、netCDF4 这一串带 C 扩展的库。我的血泪经验是:不要先pip install bestiapop,再回头补 GDAL。pip 在 Windows 和 macOS 上经常拿不到预编译的 GDAL wheel,最后变成源码编译,报错一路追到gdal-config。更稳的顺序是先建独立环境,用 conda-forge 把栅格栈装齐,再装 bestiapop,让依赖解析尽量少打架。
2.1 选 Python 3.9/3.10 而不是系统自带的 3.8
我一般用 Miniconda 或 conda 建一个专用环境,Python 选 3.9 或 3.10。系统自带的 Python 3.8 不是不能用,但很多新版本栅格库对 3.8 的轮子支持在减少,遇到问题时容易卡在“找不到匹配版本”。独立环境还有一个好处:bestiapop 跑崩了,直接删环境重来,不影响你机器上其他项目。下面三行是建环境和确认解释器路径:
conda create -n bestiapop-env python=3.9 -y conda activate bestiapop-env python -V-n bestiapop-env是环境名,后面所有命令都要先激活它。python -V不是摆设,它确认当前 shell 用的是 conda 环境里的解释器,而不是/usr/bin/python。如果你在 Windows 的 PowerShell 里,激活命令通常是conda activate bestiapop-env;如果提示conda不是命令,先用 Anaconda Prompt 或把 conda 加入 PATH。
2.2 用 conda-forge 安装 gdal、rasterio、netCDF4
这一步是整个安装的地基。下面命令把 GDAL、rasterio、netCDF4、pyproj 和 geopandas 一次装进同一个环境,渠道统一用 conda-forge,避免 defaults 与 conda-forge 混用导致 ABI 不一致:
conda install -c conda-forge gdal rasterio netCDF4 pyproj geopandas -y-c conda-forge指定渠道,-y跳过确认。GDAL 提供栅格读写和投影转换的底层能力,rasterio 是 Python 侧的栅格接口,netCDF4 负责气候数据常见的 NetCDF 格式,pyproj 处理坐标系转换。把它们放在同一个 conda 环境里,动态库路径和版本会由 conda 统一求解,比 pip 单独装 GDAL 再装 rasterio 稳得多。装完先验底层:
gdalinfo --version python -c "from osgeo import gdal; print(gdal.__version__)" python -c "import rasterio; print(rasterio.__version__)"gdalinfo --version查的是命令行工具,后面两句查的是 Python 绑定。两条路径都要能走通,否则 bestiapop 在 Notebook 里可能导入成功、一读栅格就崩。不同平台的做法我列在下面,命令不用全跑,按你的系统选一行:
| 平台 | 推荐安装方式 | 注意点 |
|---|---|---|
| Windows | conda-forge 安装 gdal/rasterio | 不要用 pip 单独装 GDAL |
| Linux | conda-forge 或系统包管理器装 libgdal-dev | 源码编译时才需要 dev 包 |
| macOS | conda-forge 安装 | 避免混用 Homebrew 的 GDAL |
提示:如果你已经在环境里用 pip 装过 GDAL,先
pip uninstall gdal rasterio,再走 conda 安装。混装后的报错通常不是缺包,而是两个 GDAL 动态库互相覆盖。
2.3 检查 Jupyter Notebook 内核与虚拟环境是否对齐
bestiapop 的很多使用场景是在 Jupyter Notebook 里做气候数据探索,所以内核必须指向bestiapop-env。常见翻车是终端里import bestiapop成功,Notebook 里却失败,因为 Notebook 内核还挂在 base 环境。下面命令安装 Jupyter 和 ipykernel,并把当前环境注册成内核:
conda install -c conda-forge jupyterlab ipykernel -y python -m ipykernel install --user --name bestiapop-env --display-name "Python (bestiapop)" jupyter kernelspec list--name是内核内部标识,--display-name是 Notebook 菜单里看到的名字。jupyter kernelspec list能列出所有内核及其路径,确认新内核指向bestiapop-env的python。在 Notebook 里再跑一句import sys; print(sys.executable),如果输出的不是.../envs/bestiapop-env/bin/python,说明你选错内核了,回到 Kernel 菜单切换,不要继续装包。
3. 安装 bestiapop:pip 与源码两种路径的取舍
栅格栈就位后,bestiapop 本身反而简单。它主要是 Python 包,安装方式无非 pip 和源码两种。我的建议是:生产环境用 pip 装稳定版,需要改代码或跟进修复时再用源码 editable 安装。无论哪种,都先确认当前python和pip来自同一个 conda 环境,否则包会装到别的解释器里,Notebook 永远找不到。
3.1 用 pip 安装稳定版并锁住依赖
先升级 pip,再安装 bestiapop。下面命令里的--no-deps不是必选项,只有在你确认 conda 已经提供 GDAL/rasterio 等依赖、且 pip 试图重装它们时才加:
python -m pip install --upgrade pip python -m pip install bestiapop python -c "import bestiapop; print(bestiapop.__file__)"第一行升级 pip,避免老版本解析 wheel 时出问题。第二行安装 bestiapop,pip 会拉取它声明的纯 Python 依赖。第三行打印包的安装路径,用来确认它落在bestiapop-env的site-packages下。如果安装过程中看到 pip 开始重装GDAL或rasterio,立刻停掉,改用python -m pip install bestiapop --no-deps,然后按报错补缺失的纯 Python 包。依赖分工可以按下面这张表记:
| 依赖 | 建议来源 | 作用 |
|---|---|---|
| GDAL | conda-forge | 栅格与投影底层库 |
| rasterio | conda-forge | Python 栅格读写 |
| netCDF4 | conda-forge | NetCDF 气候数据 |
| numpy/pandas | conda 或 pip | 表格与数组处理 |
| bestiapop | pip | 气候数据栅格化主包 |
注意:
--no-deps只跳过依赖安装,不会跳过包本身的安装。用它之前先跑一遍第 3.3 节的自检脚本,确认缺的是哪个包,不要凭感觉关依赖解析。
3.2 从源码安装开发版:什么时候值得
如果你需要看 bestiapop 内部怎么处理投影、怎么定义模板栅格,或者项目仓库里有尚未发版的修复,可以用源码 editable 安装。下面命令假设你已经把源码放到本地目录,<bestiapop-src>换成实际路径:
cd <bestiapop-src> python -m pip install -e . --no-deps python -c "import bestiapop; print(bestiapop.__file__)"-e是 editable 模式,源码改动会直接反映到环境里,适合调试。--no-deps仍然是为了避免 pip 重装 GDAL/rasterio。安装后bestiapop.__file__应该指向源码目录,而不是site-packages。如果你只是正常使用,不需要改代码,老老实实走 3.1 的 pip 安装,少一层路径问题。
3.3 装完先跑依赖自检,不要急着写业务代码
安装完成后,我习惯先跑一个依赖自检脚本,把“包能不能导入”和“底层库能不能用”分开看。把下面代码存成check_env.py,在激活的环境里执行:
import importlib mods = ["bestiapop", "rasterio", "osgeo", "netCDF4", "pyproj", "numpy", "pandas"] for name in mods: try: mod = importlib.import_module(name) version = getattr(mod, "__version__", "ok") print(f"{name}: {version}") except Exception as exc: print(f"{name}: FAILED -> {exc}")这段脚本用importlib.import_module逐个导入,避免一个失败就中断后面检查。getattr(mod, "__version__", "ok")是为了兼容没有版本号的模块。输出里如果bestiapop成功、osgeo失败,问题在 GDAL 环境;如果rasterio成功、bestiapop失败,问题在包安装路径。分开看,排错才不会抓瞎。
4. 配置 bestiapop 工作目录:输入栅格、输出路径和 GDAL_DATA
bestiapop 的配置不复杂,但路径和坐标系最容易出玄学问题。它要读气候数据、读模板栅格、写输出栅格,三个路径只要有一个带中文、空格或相对路径,Notebook 里的报错就可能指向一个完全不相干的地方。我的做法是固定一个工作目录,把输入、模板、输出分开放,环境变量只在确实需要时手工设。
4.1 目录结构:把气候数据、模板栅格和输出分开
先建目录,不要在 Notebook 里临时mkdir。下面命令建一个最小工作区:
mkdir -p ~/bestiapop-work/{climate,template,output,notebooks}climate放 NetCDF 或 CSV 气候数据,template放决定输出范围、分辨率和投影的模板栅格,output放生成结果,notebooks放 Jupyter Notebook。分开的好处是清理输出时不会误删输入,换模板时也不用改代码里的相对路径。目录对应关系如下:
| 目录 | 放什么 | 常见格式 |
|---|---|---|
| climate | 温度、降水等气候序列 | NetCDF、CSV |
| template | 目标栅格的范围与分辨率 | GeoTIFF |
| output | 栅格化后的气候数据 | GeoTIFF、NetCDF |
| notebooks | 调用脚本与检查记录 | .ipynb |
提示:模板栅格的 CRS、transform、nodata 会直接影响输出。先用
gdalinfo template.tif或 rasterio 看一眼,不要拿一个经纬度模板去套投影坐标的气候数据。
4.2 环境变量:GDAL_DATA 和 PROJ_LIB 最容易写错
conda 安装的 GDAL 通常已经配好数据路径,大多数情况下不需要手动设GDAL_DATA和PROJ_LIB。只有在你混装了系统 GDAL、或者把环境搬过目录时,才需要手工指定。先查当前值:
python -c "from osgeo import gdal; print(gdal.GetConfigOption('GDAL_DATA'))" python -c "from osgeo import gdal; print(gdal.GetConfigOption('PROJ_LIB'))"如果输出为空或指向不存在的目录,再在启动 Notebook 前设置。Linux/macOS 的写法:
export GDAL_DATA=$(python -c "import osgeo, pathlib; print(pathlib.Path(osgeo.__file__).parent / 'gdal_data')") export PROJ_LIB=$(python -c "import pyproj; print(pyproj.datadir.get_data_dir())")这两行用 Python 自己算路径,比手写/usr/share/gdal可靠。GDAL_DATA指向 GDAL 的坐标轴、单位等数据文件,PROJ_LIB指向 PROJ 的投影数据库。写错的典型现象是坐标转换报Unable to open PROJ database,或者输出栅格坐标系变成unknown。Windows 下在“系统属性”里加环境变量,或者直接在 Notebook 里用os.environ设置,但要注意设置时机必须在导入 GDAL 之前。
4.3 在 Jupyter Notebook 里固定内核与工作目录
Notebook 的当前目录经常是启动目录,不是项目目录。我习惯在第一个 cell 里固定工作目录和环境变量,后面所有相对路径都基于它:
import os from pathlib import Path project = Path.home() / "bestiapop-work" os.chdir(project) os.environ["GDAL_DATA"] = str(Path.home() / "miniconda3/envs/bestiapop-env/share/gdal") os.environ["PROJ_LIB"] = str(Path.home() / "miniconda3/envs/bestiapop-env/share/proj") print("cwd:", Path.cwd()) print("python:", os.sys.executable)os.chdir把工作目录切到项目根目录,os.environ只在当前进程生效,适合 Notebook 这种一次性会话。路径里的miniconda3要换成你的实际安装位置,不确定就用conda run -n bestiapop-env python -c "import osgeo; print(osgeo.__file__)"反推。最后打印os.sys.executable,确认 Notebook 内核和环境一致。这个 cell 我一般放在最上面,跑一次,后面不再改。
5. 避坑与排查:bestiapop 安装后最常见的五个报错
这一章按“现象 → 原因 → 解决”记,都是我装栅格栈时反复遇到的。报错信息不要只看最后一行,Python 的 traceback 经常把真正的动态库错误藏在中间。
5.1 现象:ImportError: DLL load failed while importing _gdal
现象是终端或 Notebook 里from osgeo import gdal直接抛DLL load failed,有时前面还有一行ImportError: numpy.core.multiarray failed to import。原因通常是 conda 和 pip 混装了 GDAL,或者环境里存在两个gdal.dll,Python 加载到了错误的那一个。解决不是反复重装 bestiapop,而是重建一个干净环境:先conda deactivate,删掉旧环境,按第 2 章的顺序用 conda-forge 装 gdal/rasterio,再pip install bestiapop --no-deps。如果不想重建,至少先pip uninstall gdal rasterio,再conda install -c conda-forge gdal rasterio --force-reinstall。Windows 上还要检查 PATH 里有没有别的 GDAL 目录排在 conda 环境前面。
5.2 现象:pip 报错 gdal-config not found
现象是pip install bestiapop过程中某个依赖开始编译,报gdal-config not found或Could not find gdal-config。原因是 pip 找不到 GDAL 开发头文件,试图从源码编译。解决分平台:Linux 上装libgdal-dev和gdal-bin,再设CPLUS_INCLUDE_PATH;Windows 和 macOS 上不要走源码编译,直接用 conda-forge 装 GDAL,然后pip install bestiapop --no-deps。如果你确实需要编译,先确认gdal-config --version能输出,再装对应版本的 Python GDAL 绑定。我的建议是能不编译就不编译,栅格栈的编译依赖太多,省下来的时间不值得。
5.3 现象:Notebook 里 import bestiapop 成功但内核不对
现象是终端里一切正常,Notebook 里import bestiapop报ModuleNotFoundError,或者导入成功但读栅格时报 GDAL 版本不对。原因是 Notebook 内核指向 base 环境或另一个 Python。解决是在 Notebook 里跑import sys; print(sys.executable),确认路径包含envs/bestiapop-env。不对就重新注册内核:python -m ipykernel install --user --name bestiapop-env --display-name "Python (bestiapop)",然后在 Kernel 菜单里切换。切换后重启内核,不要只重启 cell。这个坑很隐蔽,因为浏览器里看不出解释器路径,只有打印出来才死心。
5.4 现象:栅格输出全为 NoData 或坐标偏移
现象是 bestiapop 跑完不报错,但输出栅格打开全是 NoData,或者位置整体偏移半个像元。原因通常是模板栅格的 CRS 与气候数据坐标不一致,或者 nodata 值写错。先用 rasterio 检查模板:
import rasterio with rasterio.open("template/template.tif") as src: print("crs:", src.crs) print("transform:", src.transform) print("nodata:", src.nodata) print("shape:", src.shape)crs看投影,transform看左上角原点和像元大小,nodata看无效值。如果气候数据是经纬度、模板是投影坐标,先统一重投影再生成;如果nodata是-9999,检查调用时有没有把它当成有效值参与插值。坐标偏移半个像元一般是transform的原点定义问题,from_origin和from_bounds别混用。
5.5 现象:处理大区域时进程被系统杀掉
现象是 Notebook 内核突然重启,终端只留下Killed,没有任何 Python traceback。原因是整块区域一次性读进内存,栅格太大,系统 OOM。解决不是加内存条,而是切分任务:按行政区、按瓦片或按纬度带把模板裁成小块,循环生成后再用 rasterio 合并。下面命令可以边跑边看内存:
python -c "import psutil; print(psutil.Process().memory_info().rss / 1024**3, 'GB')"psutil需要先装。更简单的办法是在 Notebook 里用%%memit,或者每处理完一个瓦片就gc.collect()。如果 bestiapop 内部一次读整个 NetCDF,先把气候数据按时间或空间切片,再喂给它。大区域任务不要放在 Notebook 里一次跑完,写成脚本分批执行,出问题还能续跑。
6. 验证跑通:用一个小栅格和一份气候表走完生成流程
装完不要拿真实区域直接跑。先造一个 10×10 的模板栅格,再准备一份很小的气候表,把“读模板、读气候、写输出”这条链路走通。模板可以用 rasterio 直接生成,不依赖外部数据:
import numpy as np import rasterio from rasterio.transform import from_origin transform = from_origin(100, 40, 0.1, 0.1) with rasterio.open( "template/template.tif", "w", driver="GTiff", height=10, width=10, count=1, dtype="float32", crs="EPSG:4326", transform=transform, nodata=-9999 ) as dst: dst.write(np.ones((10, 10), dtype="float32"), 1)from_origin(100, 40, 0.1, 0.1)表示左上角经纬度和像元大小,height/width是行列数,crs用 WGS84,nodata设-9999。这段代码只造一个占位模板,真实项目里换成你的研究区模板。然后在 Notebook 里按 bestiapop 的示例调用方式,把气候数据路径、模板路径、输出路径换成climate/、template/、output/下的文件。函数名和参数以你安装版本的help(bestiapop)为准,不要照抄网上不同版本的示例。
生成后必须检查输出,不要只看文件大小。下面代码读输出栅格,检查形状、CRS、nodata 和数值范围:
import numpy as np import rasterio with rasterio.open("output/climate_output.tif") as src: arr = src.read(1) print("shape:", src.shape) print("crs:", src.crs) print("nodata:", src.nodata) print("min/max:", np.nanmin(arr), np.nanmax(arr)) print("nan count:", np.isnan(arr).sum())shape应该和模板一致,crs不应该为空,min/max不应该全是-9999。如果nan count等于总像元数,说明气候数据没有正确对齐到模板,回到第 5.4 节查 CRS 和 nodata。最后在 Jupyter Notebook 里把内核切到Python (bestiapop),重启后从头跑一遍,确认不是缓存掩盖了问题。从那以后我每次新建环境都先跑这个 10×10 的最小闭环,再开真实区域;这粒后悔药不贵,但能省掉半天排查。希望帮到你。
本文还有配套的精品资源,点击获取