scikit-learn 物种分布数据集(Species Distribution Dataset)完全指南:从 fetch 加载到分布密度建模
【免费下载链接】scikit-learnscikit-learn: machine learning in Python项目地址: https://gitcode.com/gh_mirrors/sc/scikit-learn
本文基于 scikit-learn 内置的真实世界数据集之一——物种分布数据集(Species distribution dataset),系统讲解它的数据结构、加载方式、网格重建原理,并结合仓库源码与官方示例,演示如何基于该数据集完成物种地理分布密度建模(Species Distribution Modeling,SDM)。读完本文,你将掌握fetch_species_distributions的完整用法、返回 Bunch 中每个字段的含义,以及如何用 One-Class SVM 与核密度估计(KDE)对只有正样本的生态观测数据构建分布预测模型。
数据集概述:两种南美哺乳动物的地理分布
物种分布数据集(见文档 species_distributions.rst)记录的是中美洲与南美洲两种哺乳动物的地理分布信息。该数据最初由 Phillips et al. (2006) 在最大熵(Maximum Entropy)物种分布建模研究中整理提供,现被收录进 scikit-learn 的sklearn.datasets模块,作为真实世界数据集之一在 real_world.rst 用户指南中被介绍。
数据集包含的两种物种分别是:
- Bradypus variegatus:即褐喉树懒(Brown-throated Sloth),分布于中美洲与南美洲的热带雨林;
- Microryzomys minutus:又称森林小稻鼠(Forest Small Rice Rat),是一种栖息于秘鲁、哥伦比亚、厄瓜多尔与委内瑞拉的啮齿类动物。
这两种物种的观测记录(出现点坐标)共同构成了train与test两个记录数组,同时数据集中还包含用于描述环境背景的 14 个环境变量栅格(coverage),这是后续构建"物种分布密度图"的核心材料。
非典型数据结构:为什么没有 data 与 target
绝大多数 scikit-learn 数据集加载函数会返回一个包含data和target两个属性的Bunch对象,用于监督学习。但物种分布数据集不是一个典型数据集:fetch_species_distributions返回的Bunch中并不包含data/target属性,而是提供了一系列地理信息,允许使用者自行构建一张物种"密度图"(density map)。这一点在 species_distributions.rst 中有明确说明。
这种设计的根本原因在于:生态观测数据通常只有正样本(物种被观测到的位置),没有明确的"负样本"(未观测到并不代表不存在)。因此该数据集不适合直接喂给分类器做X, y式的监督学习,而更适合密度估计类任务。官方示例正是利用这一特性,将问题转化为单类密度估计来处理。
加载数据:fetch_species_distributions 的完整用法
加载函数定义在 sklearn/datasets/_species_distributions.py 中,并在 sklearn/datasets/init.py 中作为公开 API 导出:
from sklearn.datasets import fetch_species_distributions species = fetch_species_distributions()函数签名与参数说明
fetch_species_distributions的全部参数均为关键字参数(keyword-only),通过validate_params做了类型校验:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
data_home | str / path-like / None | None | 指定数据下载与缓存的目录。默认情况下所有 scikit-learn 数据存放在~/scikit_learn_data子目录下(由get_data_home决定) |
download_if_missing | bool | True | 若为False且本地不存在缓存数据,则直接抛出OSError,而不是从源站点下载 |
n_retries | int(≥1) | 3 | 遇到 HTTP 错误时的重试次数(1.5 版本新增) |
delay | float(>0) | 1.0 | 每次重试之间的等待秒数(1.5 版本新增) |
例如,离线环境下显式禁用下载:
species = fetch_species_distributions(data_home="/tmp/skdata", download_if_missing=False)底层下载与缓存机制
从源码实现看(_species_distributions.py),数据加载流程如下:
- 两份远程数据源:
SAMPLES(物种出现点样本,figshare 5976075 文件)与COVERAGES(14 个环境变量栅格,figshare 5976078 文件),均带 SHA-256 校验和,由_fetch_remote负责下载与完整性校验; - 解析:样本文件按 CSV 解析为 record array(
_load_csv),栅格文件按带 6 行头部的网格格式解析(_load_coverage),缺失值以-9999标记; - 缓存:解析完成后组装成
Bunch,以species_coverage.pkz为名通过joblib.dump(..., compress=9)落盘;下次调用若缓存已存在,则直接joblib.load,不再重复下载。
因此首次调用会触发网络下载,之后全部走本地缓存,速度很快。
返回的 Bunch 结构与字段详解
fetch_species_distributions返回的Bunch包含以下属性:
| 属性 | 形状 / 类型 | 含义 |
|---|---|---|
coverages | [14, 1592, 1212]的int16数组 | 14 个环境特征在网格每个点上的取值;缺失数据用-9999表示(海洋区域) |
train | record array,(1624,) | 训练点,每条记录含三个字段:species(物种名,如b'microryzomys_minutus')、dd long(经度,度)、dd lat(纬度,度) |
test | record array,(620,) | 测试点,格式与train相同 |
Nx | int,1212 | 网格沿 x 轴(经度方向)的点数 |
Ny | int,1592 | 网格沿 y 轴(纬度方向)的点数 |
x_left_lower_corner | float,-94.8 | 网格左下角 x 坐标(经度),单位:度 |
y_left_lower_corner | float,-56.05 | 网格左下角 y 坐标(纬度),单位:度 |
grid_size | float,0.05 | 网格相邻点之间的步长,单位:度 |
这里需要特别强调:Nx是 1212、Ny是 1592,而coverages的形状是[14, 1592, 1212](注意顺序是 y 在前、x 在后),每个网格点对应一份 14 维环境特征向量。train/test中的经纬度坐标并不直接落在整数网格索引上,因此使用前需要借助下文介绍的construct_grids将坐标映射到网格。
快速查看数据结构
import numpy as np species = fetch_species_distributions() print(species.coverages.shape) # (14, 1592, 1212) print(species.train.shape) # (1624,) print(species.train[:3]) # array([(b'microryzomys_minutus', -64.7 , -17.85 ), # (b'microryzomys_minutus', -67.8333, -16.3333), # (b'microryzomys_minutus', -67.8833, -16.3 )], # dtype=[('species', 'S22'), ('dd long', '<f4'), ('dd lat', '<f4')]) print(species.Nx, species.Ny, species.grid_size) # 1212 1592 0.05重建地图网格:construct_grids 的原理
文档指出,地图网格可以通过x_left_lower_corner、y_left_lower_corner、Nx、Ny、grid_size五个属性重建。仓库在 sklearn/datasets/_species_distributions.py 中提供了construct_grids辅助函数完成这一换算,其实现逻辑为:
def construct_grids(batch): # x,y coordinates for corner cells xmin = batch.x_left_lower_corner + batch.grid_size xmax = xmin + (batch.Nx * batch.grid_size) ymin = batch.y_left_lower_corner + batch.grid_size ymax = ymin + (batch.Ny * batch.grid_size) # x coordinates of the grid cells xgrid = np.arange(xmin, xmax, batch.grid_size) # y coordinates of the grid cells ygrid = np.arange(ymin, ymax, batch.grid_size) return (xgrid, ygrid)关键点解读:
- 网格单元左下角偏移一个
grid_size,即坐标点取的是每个栅格单元的中心位置,而不是左下角本身; xgrid/ygrid是 1 维数组,长度分别为Nx与Ny,代表所有网格点在经纬度坐标系下的坐标序列;- 组合出经纬度范围后,可用
np.meshgrid生成覆盖南美洲的二维平面,用于等值线(contour)绘图或作为预测评估的背景点。
实战一:用 One-Class SVM 建模物种分布密度
生态观测只有正样本这一特性,决定了官方示例 plot_species_distribution_modeling.py 采用OneClassSVM将问题转化为密度估计:已知物种在某地被观测到,建模"哪些环境条件下该物种可能出现"。
完整建模流程如下:
from time import time import numpy as np import matplotlib.pyplot as plt from sklearn import metrics, svm from sklearn.datasets import fetch_species_distributions data = fetch_species_distributions() xgrid, ygrid = construct_grids(data) X, Y = np.meshgrid(xgrid, ygrid[::-1]) # 提取某物种的观测点与其对应的 14 维环境特征 def create_species_bunch(species_name, train, test, coverages, xgrid, ygrid): bunch = Bunch(name=" ".join(species_name.split("_")[:2])) species_name = species_name.encode("ascii") points = dict(test=test, train=train) for label, pts in points.items(): pts = pts[pts["species"] == species_name] bunch["pts_%s" % label] = pts ix = np.searchsorted(xgrid, pts["dd long"]) iy = np.searchsorted(ygrid, pts["dd lat"]) bunch["cov_%s" % label] = coverages[:, -iy, ix].T return bunch BV_bunch = create_species_bunch( "bradypus_variegatus_0", data.train, data.test, data.coverages, xgrid, ygrid ) # 标准化特征 mean = BV_bunch.cov_train.mean(axis=0) std = BV_bunch.cov_train.std(axis=0) train_cover_std = (BV_bunch.cov_train - mean) / std # 拟合 OneClassSVM clf = svm.OneClassSVM(nu=0.1, kernel="rbf", gamma=0.5) clf.fit(train_cover_std)步骤要点:
- 提取物种点:用
record array的species字段做等值过滤,再用np.searchsorted把经纬度映射到网格索引,从而取出每个观测点的 14 维环境特征(coverages[:, -iy, ix],注意 y 轴方向取反); - 特征标准化:对训练用环境特征做 z-score 标准化,消除 14 个变量量纲差异;
- 训练单类模型:
OneClassSVM(nu=0.1, kernel="rbf", gamma=0.5)拟合物种"可能出现"的环境超球面。
预测与评估:利用土地掩膜与背景点计算 AUC
预测时不需要对全部网格点打分——coverages[6]这一层记录的是所有陆地点的测量值(见 plot_species_distribution_modeling.py),> -9999即表示陆地,海洋直接置为无效值-9999:
land_reference = data.coverages[6] Z = np.ones((data.Ny, data.Nx), dtype=np.float64) idx = (land_reference > -9999).nonzero() coverages_land = data.coverages[:, idx[0], idx[1]].T pred = clf.decision_function((coverages_land - mean) / std) Z[idx[0], idx[1]] = pred Z[land_reference == -9999] = -9999由于没有真实的负样本,官方示例采用"背景点 + 测试点"策略评估模型:在南美洲范围内随机采样 10000 个背景网格点作为伪负样本,与物种测试点合并后计算 ROC 曲线与 AUC(示例中np.random.seed(13)保证可复现)。这是物种分布建模中常用的评估范式。
实战二:用核密度估计(KDE)可视化物种分布
第二个官方示例 plot_species_kde.py 展示了纯可视化用法——不训练任何分类模型,而是直接在经纬度球面坐标上对观测点做核密度估计,观察物种的分布形态:
import numpy as np from sklearn.neighbors import KernelDensity from sklearn.datasets import fetch_species_distributions data = fetch_species_distributions() Xtrain = np.vstack([data["train"]["dd lat"], data["train"]["dd long"]]).T ytrain = np.array( [d.decode("ascii").startswith("micro") for d in data["train"]["species"]], dtype="int", ) Xtrain *= np.pi / 180.0 # 经纬度转弧度 kde = KernelDensity( bandwidth=0.04, metric="haversine", kernel="gaussian", algorithm="ball_tree" ) kde.fit(Xtrain[ytrain == 0])技术要点:
- Haversine 度量:经纬度是球面坐标,直接用欧氏距离会失真,示例使用
metric="haversine"计算球面大圆距离,配合algorithm="ball_tree"构建球面 Ball Tree,是 scikit-learn 在 neighbors 模块中提供的经典地理空间方案; - 坐标转换:输入前须把经纬度从度转换为弧度(乘以
π/180); - 掩膜评估:与 One-Class SVM 示例相同,只在陆地掩膜(
coverages[6] > -9999)覆盖的网格点上计算score_samples,海洋区域填-9999,避免在无意义的洋面上绘制密度; - 该示例"不做任何学习",其定位与分类式示例互补,二者引用的是同一份数据。
参考资料与进一步阅读
- 数据来源文献:Phillips, Anderson & Schapire, "Maximum entropy modeling of species geographic distributions",Ecological Modelling, 190:231-259, 2006。该数据集即为文献中 Maxent 方法所用的样本与覆盖栅格(
_species_distributions.py中注明了原始数据出处); - 用户指南:真实世界数据集列表 中将
fetch_species_distributions与其他大规模数据集加载函数并列介绍; - 官方示例:
- plot_species_distribution_modeling.py——One-Class SVM 密度建模 + AUC 评估;
- plot_species_kde.py——Haversine 球面核密度估计可视化;
- 源码实现:sklearn/datasets/_species_distributions.py 中
fetch_species_distributions的完整 docstring 自带 doctest 示例;测试侧通过 sklearn/conftest.py 中的fetch_species_distributions_fxtfixture 复用加载逻辑,并在 test_public_functions.py 中保证该函数属于公开 API。
小结
物种分布数据集是 scikit-learn 中形态非常特殊的一个真实世界数据集:它不提供现成的data/target监督信号,而是给出物种出现点坐标、环境栅格与网格几何参数,让使用者自行组装"密度图"。理解coverages、train/test与Nx、Ny、x_left_lower_corner、y_left_lower_corner、grid_size这几个字段的含义,以及construct_grids的网格中心换算规则,是正确使用该数据集的前提。无论是用 One-Class SVM 做单类密度建模,还是用 Haversine KDE 做球面分布可视化,它都是练习"仅正样本生态建模"这一经典问题的理想数据载体。
【免费下载链接】scikit-learnscikit-learn: machine learning in Python项目地址: https://gitcode.com/gh_mirrors/sc/scikit-learn
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考