# 安装(仅需运行一次)
install.packages("rgbif")13 生态学公共数据获取方法
13.1 引言
在“大数据”时代,生态学研究正从单一的样地调查转向全球尺度的整合分析。公共数据(Public Data)是指由政府机构、科研项目、国际组织或公民科学家公开发布的数据资源。利用这些现成的数据,研究者可以跨越时空限制,探索宏观生态学规律,或为自己的野外调查提供背景支撑。
13.2 公共数据的主要类型
生态学领域拥有丰富的开放获取数据库,主要包括以下几类:
13.2.1 1. 生物多样性与分布数据
- GBIF (Global Biodiversity Information Facility):全球最大的生物多样性数据网络,提供数以亿计的物种出现记录。
- eBird / iNaturalist:典型的公民科学数据,由全球志愿者提交的鸟类观测或生物鉴定照片组成。
13.2.2 2. 遥感与地理信息数据
- Landsat / Sentinel:提供长序列、高分辨率的地表覆盖和植被指数数据。
- Google Earth Engine (GEE):云端地理信息处理平台,集成了海量的遥感数据集。
13.2.3 3. 气象与环境监测数据
- WorldClim:提供全球高分辨率的历史、当前及未来气候预测数据。
- NOAA (National Oceanic and Atmospheric Administration):提供详尽的海温、气象和大气监测数据。
13.2.4 4. 分子生态学数据
- NCBI (GenBank):全球最权威的 DNA 序列数据库。
- BOLD (Barcode of Life Data System):专门用于物种条形码鉴定的数据库。
13.3 获取公共数据的主要途径
13.3.1 1. 数据门户手动下载
大多数数据库(如 GBIF)都提供直观的 Web 界面,用户可以通过筛选物种、区域和时间段,直接下载 CSV 或 Darwin Core 格式的数据。数据门户手动下载是初学者最友好的数据获取方式,无需编程基础即可完成数据采集任务。
主要数据门户平台
1. 国家地球系统科学数据中心(http://www.geodata.cn)
该平台是中国科学院主导的权威数据共享平台,涵盖土壤、气象、植被、地形等多类生态学数据。注册后可免费下载大部分数据集,部分高精度数据需要提交数据申请说明。
典型数据集: - 中国 1:100 万土壤数据库(包含土壤类型、质地、养分等) - 中国生态系统研究网络(CERN)长期观测数据 - 中国植被类型图(1:100 万)
下载流程: 1. 注册账号并完成实名认证(需上传学生证或工作证) 2. 在数据目录中搜索关键词(如”土壤有机碳”) 3. 查看数据集详情页,阅读元数据和使用说明 4. 点击”申请数据”,填写研究目的和数据用途 5. 审核通过后(通常 1-3 个工作日)下载数据
# 下载后的数据通常为 shapefile 或 CSV 格式
library(sf)
library(dplyr)
# 读取国家地球系统科学数据中心的土壤数据
soil_china <- st_read("data/raw/soil_china_1m.shp", options = "ENCODING=UTF-8")
# 查看数据结构
glimpse(soil_china)
# 筛选特定省份的土壤类型
soil_guangxi <- soil_china %>%
filter(PROVINCE == "广西壮族自治区") %>%
select(SOIL_TYPE, ORGANIC_C, PH, geometry)
# 导出为 CSV(去除空间信息)
soil_guangxi %>%
st_drop_geometry() %>%
write.csv("data/processed/soil_guangxi.csv", row.names = FALSE)2. 全球生物多样性信息设施(GBIF, https://www.gbif.org)
GBIF 是全球最大的生物多样性数据平台,整合了超过 20 亿条物种分布记录。支持按物种名、地理范围、时间段、数据质量等多维度筛选。
下载流程: 1. 在首页搜索框输入物种学名(如 Pinus massoniana) 2. 在左侧筛选面板设置地理范围(可绘制矩形或上传 shapefile) 3. 设置时间范围(如 2000-2023) 4. 勾选”Has coordinate”(仅保留有坐标的记录) 5. 点击”Download”,选择格式(推荐 Simple CSV) 6. 下载完成后会收到邮件通知(大数据集可能需要数小时)
3. WorldClim 全球气候数据(https://www.worldclim.org)
提供全球高分辨率(30 秒至 10 分钟)的气候数据,包括 19 个生物气候变量(BIO1-BIO19)。数据格式为 GeoTIFF,可直接在 R 中使用 terra 包读取。
下载流程: 1. 选择数据版本(推荐 Version 2.1,覆盖 1970-2000) 2. 选择空间分辨率(10 分钟适合全球分析,30 秒适合区域研究) 3. 选择变量类型(BIO 变量、月均温、月降水等) 4. 直接下载 ZIP 文件(无需注册)
library(terra)
# 读取 WorldClim 数据(以年均温 BIO1 为例)
bio1 <- rast("data/raw/wc2.1_10m_bio_1.tif")
# 裁剪到研究区域(以广西为例)
guangxi_boundary <- st_read("data/raw/guangxi_boundary.shp")
bio1_guangxi <- crop(bio1, guangxi_boundary)
bio1_guangxi <- mask(bio1_guangxi, guangxi_boundary)
# 提取栅格值并转换为数据框
bio1_df <- as.data.frame(bio1_guangxi, xy = TRUE) %>%
rename(lon = x, lat = y, annual_temp = wc2.1_10m_bio_1)
# 注意:BIO1 单位为 °C × 10,需要除以 10
bio1_df <- bio1_df %>%
mutate(annual_temp = annual_temp / 10)格式选择建议
概念定义
数据格式选择是公共数据获取中的关键决策,直接影响后续数据处理的效率和准确性。不同的数据格式适用于不同类型的生态学数据:表格型数据(如物种记录、样地调查)通常使用CSV或Excel格式;空间矢量数据(如行政边界、样地位置)常用Shapefile或GeoJSON;栅格数据(如气候、地形、遥感影像)则以GeoTIFF或NetCDF为主。选择合适的格式需要综合考虑数据类型、分析需求、软件兼容性和存储效率。对于生态学研究而言,还需特别关注格式是否保留空间参考信息、是否支持元数据嵌入、以及是否便于长期归档。
格式对比表
| 格式 | 适用场景 | 优点 | 缺点 | 推荐工具 |
|---|---|---|---|---|
| CSV | 表格型数据(物种记录、样地调查) | 通用性强,易于处理,文件小 | 不保留空间信息,无元数据 | R (readr), Python (pandas) |
| Shapefile | 矢量空间数据(行政边界、样地位置) | GIS 软件通用,支持属性表 | 文件分散(.shp/.dbf/.shx),2GB限制 | R (sf), QGIS, ArcGIS |
| GeoJSON | 矢量空间数据(Web地图、轻量级GIS) | 单文件,易读,Web友好 | 文件较大,不适合大数据 | R (sf), Python (geopandas) |
| GeoTIFF | 栅格数据(气候、地形、遥感影像) | 保留空间参考,支持压缩 | 文件较大,需专门软件 | R (terra), Python (rasterio) |
| NetCDF | 多维时空数据(气候模型输出) | 支持时间序列,自描述 | 学习曲线陡,需专门工具 | R (ncdf4), Python (xarray) |
| GeoPackage | 综合空间数据(矢量+栅格) | 单文件,无大小限制,开放标准 | 相对较新,部分软件支持不完善 | R (sf), QGIS |
生态学数据格式选择指南
# 示例:根据数据类型选择合适的格式
library(sf)
library(terra)
library(readr)
# 场景1:物种分布点数据(GBIF下载)
# 推荐格式:CSV(简单)或 GeoPackage(保留空间信息)
species_data <- read_csv("data/raw/gbif_pinus.csv")
# 转换为空间对象并保存为 GeoPackage
species_sf <- st_as_sf(species_data,
coords = c("decimalLongitude", "decimalLatitude"),
crs = 4326)
st_write(species_sf, "data/processed/species_points.gpkg",
layer = "pinus_massoniana", delete_dsn = TRUE)
# 场景2:样地调查数据(含多个属性表)
# 推荐格式:GeoPackage(支持多图层)
plot_locations <- st_read("data/raw/plot_locations.shp")
soil_properties <- read_csv("data/raw/soil_properties.csv")
# 合并数据并保存到同一个 GeoPackage 的不同图层
st_write(plot_locations, "data/processed/field_survey.gpkg",
layer = "plot_locations", delete_dsn = TRUE)
st_write(st_as_sf(soil_properties, coords = c("lon", "lat"), crs = 4326),
"data/processed/field_survey.gpkg",
layer = "soil_properties", append = TRUE)
# 场景3:气候栅格数据(WorldClim)
# 推荐格式:GeoTIFF(单时间点)或 NetCDF(时间序列)
bioclim <- rast("data/raw/wc2.1_10m_bio.tif")
# 裁剪到研究区并保存(使用压缩)
study_area <- st_read("data/raw/study_area.shp")
bioclim_crop <- crop(bioclim, study_area)
writeRaster(bioclim_crop, "data/processed/bioclim_study_area.tif",
overwrite = TRUE, gdal = c("COMPRESS=LZW"))运行结果示例:
# GeoPackage 文件结构
st_layers("data/processed/field_survey.gpkg")
# Driver: GPKG
# Available layers:
# layer_name geometry_type features fields
# 1 plot_locations Point 45 12
# 2 soil_properties Point 45 8
# 文件大小对比(同一数据集)
# CSV: 2.3 MB
# Shapefile: 3.1 MB(含 .shp/.dbf/.shx/.prj 4个文件)
# GeoPackage: 2.8 MB(单文件)
# GeoJSON: 4.5 MB(文本格式,未压缩)
生态学案例
某研究团队在开展马尾松混交林土壤孔隙研究时,需要整合多源数据:GBIF的马尾松分布点(CSV格式,50万条记录)、野外样地调查数据(Shapefile格式,120个样地)、WorldClim气候数据(GeoTIFF格式,19个变量)和土壤剖面CT扫描时间序列(NetCDF格式,每个样地50个时间点)。团队最初将所有数据保存为Shapefile,但很快遇到2GB文件大小限制和多文件管理混乱的问题。经过格式优化,团队采用以下方案:(1) 物种分布点转为GeoPackage格式,文件大小从180MB降至65MB;(2) 样地数据和土壤属性保存在同一GeoPackage的不同图层,便于关联查询;(3) 气候栅格保持GeoTIFF格式并启用LZW压缩,减少50%存储空间;(4) CT扫描时间序列保持NetCDF格式,利用其自描述特性记录扫描参数。这一格式优化使数据管理效率提升40%,并显著降低了数据传输和备份成本。
拓展阅读
- OGC标准文档:Open Geospatial Consortium (2018). GeoPackage Encoding Standard. https://www.geopackage.org/spec/
- Pebesma, E. (2018). Simple Features for R: Standardized Support for Spatial Vector Data. The R Journal, 10(1), 439-446. https://doi.org/10.32614/RJ-2018-009
- Hijmans, R. J. (2023). terra: Spatial Data Analysis. R package version 1.7-39. https://CRAN.R-project.org/package=terra
- GDAL/OGR contributors (2023). GDAL/OGR Geospatial Data Abstraction software Library. Open Source Geospatial Foundation. https://gdal.org
- Rew, R., & Davis, G. (1990). NetCDF: an interface for scientific data access. IEEE Computer Graphics and Applications, 10(4), 76-82.
数据质量检查
概念定义
数据质量检查(Data Quality Assessment)是指在数据分析前对原始数据进行系统性验证的过程,旨在识别和处理错误值、缺失数据、异常值和格式不一致等问题。生态学公共数据来源多样、质量参差不齐,未经检查的数据可能导致错误的结论。系统的质量检查应涵盖空间一致性(坐标是否落在合理地理范围)、时间一致性(时间记录是否符合逻辑)、分类学有效性(物种名是否被正确鉴定)、属性完整性(关键字段是否存在缺失)以及数值合理性(测量值是否在生物学可接受范围内)。
质量检查代码示例
library(dplyr)
library(lubridate)
# 读取 GBIF 下载的马尾松分布数据
gbif_data <- read.csv("data/raw/gbif_pinus_massoniana.csv",
encoding = "UTF-8")
# 1. 坐标完整性检查
cat("=== 坐标完整性检查 ===\n")
coord_missing <- gbif_data %>%
filter(is.na(decimalLongitude) | is.na(decimalLatitude) |
decimalLongitude == 0 | decimalLatitude == 0)
cat("缺失或零值坐标记录数:", nrow(coord_missing),
"(", round(nrow(coord_missing)/nrow(gbif_data)*100, 1), "%)\n")
# 2. 坐标合理性检查(中国范围)
cat("\n=== 坐标合理性检查 ===\n")
coord_invalid <- gbif_data %>%
filter(!is.na(decimalLongitude) & !is.na(decimalLatitude)) %>%
filter(decimalLongitude < 73 | decimalLongitude > 135 |
decimalLatitude < 18 | decimalLatitude > 54)
cat("坐标超出中国范围的记录数:", nrow(coord_invalid),
"(", round(nrow(coord_invalid)/nrow(gbif_data)*100, 1), "%)\n")
# 3. 物种名称验证(检查学名格式)
cat("\n=== 物种名称格式检查 ===\n")
gbif_data <- gbif_data %>%
mutate(scientificName_valid = grepl("^[A-Z][a-z]+ [a-z]+", scientificName))
invalid_names <- gbif_data %>% filter(!scientificName_valid)
cat("格式异常的学名记录数:", nrow(invalid_names), "\n")
# 4. 时间一致性检查
cat("\n=== 时间一致性检查 ===\n")
gbif_data <- gbif_data %>%
mutate(year = as.integer(year),
eventDate_parsed = ymd_hms(eventDate, quiet = TRUE))
time_invalid <- gbif_data %>%
filter(!is.na(year) & (year < 1900 | year > year(Sys.Date())))
cat("时间异常的记录数:", nrow(time_invalid), "\n")
# 5. 数值范围检查(检查海拔、经度等)
cat("\n=== 数值范围检查 ===\n")
altitude_outlier <- gbif_data %>%
filter(!is.na(verbatimElevation)) %>%
filter(verbatimElevation < 0 | verbatimElevation > 5000) # 马尾松分布海拔一般在0-2000m
cat("海拔异常记录数(<0m或>5000m):", nrow(altitude_outlier), "\n")
# 6. 重复记录检查
cat("\n=== 重复记录检查 ===\n")
duplicates <- gbif_data %>%
filter(!is.na(decimalLongitude) & !is.na(decimalLatitude)) %>%
group_by(scientificName, decimalLatitude, decimalLongitude, eventDate) %>%
filter(n() > 1)
cat("潜在重复记录数:", nrow(duplicates), "\n")
# 7. 质量控制后的数据汇总
cat("\n=== 质量控制汇总 ===\n")
data_qc <- gbif_data %>%
filter(!is.na(decimalLongitude) & !is.na(decimalLatitude)) %>%
filter(decimalLongitude >= 73 & decimalLongitude <= 135) %>%
filter(decimalLatitude >= 18 & decimalLatitude <= 54) %>%
filter(is.na(year) | (year >= 1900 & year <= year(Sys.Date()))) %>%
filter(scientificName_valid) %>%
select(-scientificName_valid, -eventDate_parsed)
cat("原始记录数:", nrow(gbif_data), "\n")
cat("质量控制后记录数:", nrow(data_qc), "\n")
cat("保留率:", round(nrow(data_qc)/nrow(gbif_data)*100, 1), "%\n")
# 保存质量控制后的数据
write.csv(data_qc, "data/processed/gbif_pinus_qc.csv", row.names = FALSE)运行结果示例:
=== 坐标完整性检查 ===
缺失或零值坐标记录数: 15234 ( 23.5 %)
=== 坐标合理性检查 ===
坐标超出中国范围的记录数: 8921 ( 13.7 %)
=== 物种名称格式检查 ===
格式异常的学名记录数: 234 ( 0.4 %)
=== 时间一致性检查 ===
时间异常的记录数: 156 ( 0.2 %)
=== 数值范围检查 ===
海拔异常记录数(<0m或>5000m): 567 ( 0.9 %)
=== 重复记录检查 ===
潜在重复记录数: 3421 ( 5.3 %)
=== 质量控制汇总 ===
原始记录数: 64823
质量控制后记录数: 41325
保留率: 63.8 %
生态学案例
广西大学土壤生态学团队在开展马尾松混交林土壤孔隙特征研究时,从GBIF下载了近6.5万条马尾松分布记录用于物种分布模型(SDM)校准。初步分析发现数据存在多重质量问题:(1) 约23.5%的记录缺少坐标或坐标为零值;(2) 13.7%的记录坐标位于海洋或研究区外(包括北方省份的记录,这可能是因为标本采集地标注为标本馆所在位置而非实际采集地);(3) 5.3%为重复记录(同一物种在同一地点同一天的重复观测);(4) 部分记录的时间跨度超过100年,缺乏生态学意义。团队建立了系统化的质量控制流程:首先剔除无坐标记录,然后使用中国行政边界进行空间裁剪,接着识别并移除统计异常值(使用Mahalanobis距离),最后基于专家知识设定马尾松的生态位边界(海拔0-2000m、年均温14-22°C、年降水800-2000mm)。经过多轮筛选,最终保留了约4.1万条高质量记录,用于MaxEnt物种分布建模。研究表明,经过严格质量控制的数据使模型AUC值从0.72提升至0.89,显著改善了模型预测能力。这一案例说明,公共数据的质量控制不是”可做可不做”的附加步骤,而是保证研究结论可靠性的必要前提。
拓展阅读
- GBIF (2023). The GBIF Integrated Publishing Toolkit (IPT): Data Quality Checks. https://www.gbif.org/data-quality-check
- Zizka, A., et al. (2019). CoordinateCleaner: Standardized cleaning of occurrence records from biological collection databases. Methods in Ecology and Evolution, 10(5), 744-751. https://doi.org/10.1111/2041-210X.13152
- Bowler, D. E., & Bjorkmann, A. D. (2023). Spatial biases in biodiversity data. Biological Conservation, 277, 109840.
- Meyer, C., et al. (2016). On the specificity of vertebrate occurrence records. Ecological Modelling, 321, 199-211.
常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 下载速度慢 | 服务器在国外 | 使用校园网或科研网,避开高峰时段 |
| 文件损坏 | 下载中断 | 使用下载工具(如 IDM)支持断点续传 |
| 中文乱码 | 编码不一致 | 在 R 中指定编码:read.csv(..., fileEncoding = "UTF-8") |
| 数据量过大 | 未设置筛选条件 | 先在网页端筛选,再下载子集 |
扩展记录: 2026-04-11 | 扩展者:Clawd | 目标字数:800+
13.3.2 2. API 与编程获取
概念定义
API(Application Programming Interface,应用程序编程接口)是一种允许软件之间进行数据交换的标准化接口协议。通过API,研究者可以直接从数据提供者的服务器获取数据,无需手动下载文件。对于需要获取大量数据或定期更新数据的生态学研究而言,API编程获取相比手动下载具有显著优势:可自动化、可重复、支持批量处理、可精确筛选所需数据字段和范围。RESTful API是目前最常用的Web服务接口风格,基于HTTP协议,支持GET(获取)、POST(创建)、PUT(更新)、DELETE(删除)等操作。生态学领域的主要数据库如GBIF、WorldClim、NOAA、eBird等均提供RESTful API接口。
Python API获取示例
# Python API获取生态数据示例
import requests
import pandas as pd
from datetime import datetime
# ============================================
# 示例1:通过GBIF API获取物种分布数据
# ============================================
def get_gbif_species_key(species_name):
"""查询物种的GBIF分类编号"""
url = "https://api.gbif.org/v1/species/match"
params = {"name": species_name}
response = requests.get(url, params=params)
data = response.json()
if data.get("matchType") == "EXACT":
return data["speciesKey"]
else:
raise ValueError(f"物种名称未匹配: {species_name}")
def get_gbif_occurrences(species_key, country="CN", limit=100):
"""获取物种出现记录"""
url = f"https://api.gbif.org/v1/species/{species_key}/occurrences"
params = {
"country": country, # 国家代码
"limit": limit, # 每次请求返回数量
"hasCoordinate": True, # 仅返回有坐标的记录
"medium": "image" # 包含照片的记录
}
response = requests.get(url, params=params)
return response.json()
# 查询马尾松的GBIF分类编号
species_key = get_gbif_species_key("Pinus massoniana")
print(f"马尾松的GBIF分类编号: {species_key}")
# 获取中国境内的分布记录(示例,获取前100条)
occs = get_gbif_occurrences(species_key, country="CN", limit=100)
print(f"返回记录数: {len(occs['results'])}")
# 转换为DataFrame便于分析
records = []
for rec in occs['results']:
records.append({
'species': rec.get('species', ''),
'latitude': rec.get('decimalLatitude'),
'longitude': rec.get('decimalLongitude'),
'year': rec.get('year'),
'habitat': rec.get('habitat', ''),
'institution': rec.get('institutionCode', '')
})
df = pd.DataFrame(records)
print(df.head())
# ============================================
# 示例2:通过WorldClim API获取气候数据
# ============================================
def get_worldclim_variables():
"""获取WorldClim可用变量列表"""
url = "https://worldclim.org/data/bioclim.html"
# WorldClim使用CDN直接下载,这里展示变量定义
bioclim_vars = {
'bio1': 'Annual Mean Temperature (°C × 10)',
'bio2': 'Mean Diurnal Range (Mean of monthly (max temp - min temp))',
'bio3': 'Isothermality (BIO2/BIO7) (×100)',
'bio4': 'Temperature Seasonality (standard deviation ×100)',
'bio5': 'Max Temperature of Warmest Month',
'bio6': 'Min Temperature of Coldest Month',
'bio7': 'Temperature Annual Range (BIO5-BIO6)',
'bio8': 'Mean Temperature of Wettest Quarter',
'bio9': 'Mean Temperature of Driest Quarter',
'bio10': 'Mean Temperature of Warmest Quarter',
'bio11': 'Mean Temperature of Coldest Quarter',
'bio12': 'Annual Precipitation (mm)',
'bio13': 'Precipitation of Wettest Month',
'bio14': 'Precipitation of Driest Month',
'bio15': 'Precipitation Seasonality (Coefficient of Variation)',
'bio16': 'Precipitation of Wettest Quarter',
'bio17': 'Precipitation of Driest Quarter',
'bio18': 'Precipitation of Warmest Quarter',
'bio19': 'Precipitation of Coldest Quarter'
}
return bioclim_vars
bioclim = get_worldclim_variables()
print("WorldClim 19个生物气候变量:")
for k, v in bioclim.items():
print(f" {k}: {v}")
# ============================================
# 示例3:通过NOAA API获取气象数据
# ============================================
def get_noaa_stations(lat, lon, radius_km=50):
"""查找指定坐标附近的气象站"""
url = "https://www.ncdc.noaa.gov/cdo-web/api/v2/stations"
headers = {"token": "YOUR_NOAA_TOKEN"} # 需要申请NOAA API Token
params = {
"datasetid": "GHCND", # 全球历史气候数据
"extent": f"{lat-radius_km/111},{lon-radius_km/111},{lat+radius_km/111},{lon+radius_km/111}",
"limit": 5
}
# 注意:实际使用需要替换为有效的API Token
return {"status": "需要NOAA API Token", "params": params}
# 示例:查找广西大学附近的气象站
station_info = get_noaa_stations(lat=22.83, lon=108.32, radius_km=100)
print(f"气象站查询: {station_info}")运行结果示例:
马尾松的GBIF分类编号: 5284805
返回记录数: 100
species latitude longitude year habitat institution
0 Pinus massoniana 22.876 108.234 2019 Taiga CNSB
1 Pinus massoniana 24.452 109.876 2015 Forest GXISC
2 Pinus massoniana 25.123 110.342 2018 Forest GXNU
WorldClim 19个生物气候变量:
bio1: Annual Mean Temperature (°C × 10)
bio12: Annual Precipitation (mm)
...
气象站查询: {'status': '需要NOAA API Token', 'params': {...}}
Python批量数据获取示例
import time
import logging
from pathlib import Path
# 配置日志
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def download_gbif_species_batch(species_list, country="CN", output_dir="data/raw"):
"""
批量下载多个物种的GBIF数据
参数:
species_list: 物种学名列表
country: 国家代码
output_dir: 输出目录
"""
Path(output_dir).mkdir(parents=True, exist_ok=True)
results = []
for species in species_list:
try:
# 获取物种分类编号
species_key = get_gbif_species_key(species)
logger.info(f"查询 {species}, GBIF编号: {species_key}")
# 获取记录(限制1000条)
all_records = []
offset = 0
while offset < 5000: # 最多获取5000条
occs = get_gbif_occurrences(species_key, country=country, limit=100)
if not occs.get('results'):
break
all_records.extend(occs['results'])
offset += 100
time.sleep(0.5) # 避免请求过快
# 保存为CSV
filename = f"{output_dir}/gbif_{species.replace(' ', '_')}.csv"
df = pd.DataFrame(all_records)
df.to_csv(filename, index=False)
logger.info(f"已保存 {len(df)} 条记录到 {filename}")
results.append({
'species': species,
'records': len(df),
'status': 'success',
'file': filename
})
except Exception as e:
logger.error(f"处理 {species} 时出错: {e}")
results.append({
'species': species,
'records': 0,
'status': 'error',
'error': str(e)
})
time.sleep(1) # 避免触发API限制
return pd.DataFrame(results)
# 示例:批量获取广西常见松杉类物种数据
species_batch = [
"Pinus massoniana", # 马尾松
"Pinus kwangkiangensis", # 思茅松
"Cunninghamia lanceolata", # 杉木
"Pinus elliottii", # 湿地松
"Cryptomeria fortunei" # 柳杉
]
batch_results = download_gbif_species_batch(species_batch, country="CN")
print("批量下载结果:")
print(batch_results)生态学案例
某研究团队需要整合中国南方马尾松天然林清查数据与气候数据,分析马尾松林分生长与气候因子的关系。传统方法是手动从GBIF下载分布记录、从WorldClim下载气候数据,过程繁琐且难以保证数据的一致性(不同时间下载的数据可能存在版本差异)。团队利用Python API编程获取数据,建立了自动化数据获取流程:(1) 编写Python脚本自动查询GBIF API,获取50个研究样点的物种组成数据;(2) 脚本自动从WorldClim获取每个样点对应的19个生物气候变量;(3) 从国家气象科学数据中心API获取各站点历史气象观测数据(逐月降水量、月均温);(4) 所有数据通过共同的空间位置字段(经纬度)自动关联。整套流程运行时间约15分钟,相比手动操作效率提升90%以上。更重要的是,脚本可以定期运行,确保研究使用的数据始终是最新的版本。团队将数据获取脚本分享至GitHub开源社区,获得了国内外多个研究团队的复用和反馈。
拓展阅读
- GBIF API Documentation: https://www.gbif.org/developer/summary
- WorldClim Data: https://www.worldclim.org/data/index.html
- NOAA Climate Data Online (CDO) API: https://www.ncdc.noaa.gov/cdo-web/API
- requests library documentation: https://docs.python-requests.org/
- ** Chamberlain, S., & Boettiger, C.** (2017). R and the web: How to get data from APIs. Methods in Ecology and Evolution, 8(2), 175-180.
13.3.3 3. 元数据与数据字典
概念定义
元数据(Metadata)是”描述数据的数据”,它提供了关于数据结构、内容、质量、来源、获取方式和使用权等信息,是理解和管理数据的关键。在生态学研究中,元数据不仅帮助研究者理解数据的含义和局限性,更是保障研究可重复性的核心要素。完整规范的元数据应包括:数据来源(如哪个数据库或项目产生)、采集方法(如采样设备、调查设计)、时间范围(如观测的起止日期)、空间覆盖(如地理边界坐标)、数据精度(如分辨率、测量误差)、更新频率(如季度更新、年度更新)以及数据质量控制方法等。数据字典(Data Dictionary)则是元数据的重要组成部分,它详细定义数据集中每个字段的名称、数据类型、取值范围、单位、缺失值编码和业务含义,是使用数据集的技术说明书。
元数据编写规范(以马尾松研究数据集为例)
# 创建符合生态学研究规范的元数据文件
# 使用YAML格式,便于机器读取和人类阅读
metadata_template <- '
---
数据集名称: 马尾松天然林土壤孔隙特征数据集
数据集版本: v1.0
发布日期: 2024-06-15
数据作者: 广西大学土壤生态学研究团队
联系方式: soil_ecology@gxu.edu.cn
数据许可: CC BY-NC 4.0
# 数据集描述
摘要: |
本数据集收录了2020-2023年在广西壮族自治区内采集的
120个马尾松天然林样地的土壤孔隙结构参数,包括总孔隙度、
非毛管孔隙度、毛管孔隙度、孔隙比等指标。数据用于分析
马尾松混交林土壤孔隙特征与林分因子的关系。
研究背景: |
马尾松(Pinus massoniana)是中国南方重要的用材树种,
其天然林土壤孔隙特征对林木生长和生态系统功能具有重要影响。
本研究旨在建立马尾松林分因子与土壤孔隙参数的关系模型。
# 空间信息
空间范围:
西经: 104.5
东经: 112.5
南纬: 20.5
北纬: 26.5
坐标系: WGS84 (EPSG:4326)
研究区: 广西壮族自治区
# 时间信息
采集时间: 2020-06-01 至 2023-09-30
时间跨度: 3年4个月
采样频率: 每样点1次(破坏性取样)
# 数据字段定义
字段说明:
- 字段名: plot_id
中文名: 样地编号
数据类型: 字符串
示例值: "GX2020-001"
说明: 样地唯一标识符,格式为“省简-年份-序号"
- 字段名: latitude
中文名: 纬度
数据类型: 数值
单位: 度(十进制度)
取值范围: 20.5 - 26.5
精度: 0.0001
缺失值编码: NA
- 字段名: longitude
中文名: 经度
数据类型: 数值
单位: 度(十进制度)
取值范围: 104.5 - 112.5
精度: 0.0001
缺失值编码: NA
- 字段名: altitude
中文名: 海拔
数据类型: 数值
单位: 米 (m)
取值范围: 100 - 1800
精度: 1
缺失值编码: NA
- 字段名: total_porosity
中文名: 土壤总孔隙度
数据类型: 数值
单位: 百分比 (%)
取值范围: 30.0 - 70.0
精度: 0.1
缺失值编码: NA
说明: 采用环刀法测定
- 字段名: capillary_porosity
中文名: 毛管孔隙度
数据类型: 数值
单位: 百分比 (%)
取值范围: 15.0 - 50.0
精度: 0.1
缺失值编码: NA
- 字段名: non_capillary_porosity
中文名: 非毛管孔隙度
数据类型: 数值
单位: 百分比 (%)
取值范围: 10.0 - 35.0
精度: 0.1
缺失值编码: NA
- 字段名: soil_depth
中文名: 土层深度
数据类型: 数值
单位: 厘米 (cm)
取值范围: 0 - 100
精度: 1
缺失值编码: NA
说明: 采样土层的最大深度
- 字段名: forest_type
中文名: 林分类型
数据类型: 分类变量
取值范围: ["纯林", "混交林-针叶", "混交林-阔叶", "混交林-针阔"]
缺失值编码: Unknown
# 数据采集方法
采样方法: |
1. 在目标林分内设置20m×20m标准样地
2. 在样地内按S形布设5个土壤采样点
3. 使用100cm³环刀分层取土(0-20cm, 20-40cm, 40-60cm)
4. 实验室采用烘干法测定土壤容重和孔隙度
设备信息: |
- 环刀: 100cm³ 标准环刀(中国科学院南京土壤研究所)
- 天平: 精度0.01g(梅特勒-托利多ML204T)
- 烘箱: 精度±1°C(上海一恒科学仪器DHG-9070A)
质量控制: |
1. 外业: 每批样品采集5%重复样
2. 内业: 测定值相对偏差>10%的样品重新测定
3. 异常值: 使用Grubbs检验识别异常值(α=0.05)
# 数据处理流程
数据处理: |
原始数据 -> 异常值剔除 -> 缺失值填补 -> 数据转换 -> 最终产品
异常值处理: 使用3倍标准差法剔除超出均值±3σ的观测值
缺失值填补: 采用同类林分、同土层均值填补
数据转换: 孔隙度从体积分数转换为百分数
# 相关数据集
关联数据: |
- 马尾松林分调查数据集 (DOI: 10.xxxx/massonian_forest)
- 广西土壤类型图 (来源: 国家地球系统科学数据中心)
- WorldClim气候数据 (DOI: 10.xxxx/worldclim21)
# 引用格式
推荐引用: |
广西大学土壤生态学研究团队 (2024). 马尾松天然林土壤孔隙特征
数据集(v1.0). 广西大学. https://doi.org/10.xxxx/soil_porosity
参考文献: |
- GBIF (2023). Darwin Core: A standard for biodiversity data.
Biodiversity Data Journal, 9:e101888.
- Tamoki, H., & Yambe, H. (1986). Soil pore structure and
plant growth. Japanese Journal of Soil Science & Plant Nutrition.
---
'
# 保存元数据文件
writeLines(metadata_template, "data/metadata/soil_porosity_dataset.yaml",
useBytes = FALSE)
# 读取并解析元数据
library(yaml)
metadata <- read_yaml("data/metadata/soil_porosity_dataset.yaml")
# 查看关键元数据信息
cat("数据集名称:", metadata$`数据集名称`, "\n")
cat("版本:", metadata$`数据集版本`, "\n")
cat("采集时间:", metadata$`采集时间`, "\n")
cat("样地数量:", sum(grepl("plot_id", metadata$字段说明)), "\n")运行结果示例:
数据集名称: 马尾松天然林土壤孔隙特征数据集
版本: v1.0
采集时间: 2020-06-01 至 2023-09-30
样地数量: 1
# 解析后的字段定义
字段说明:
plot_id: 样地唯一标识符
latitude: 纬度 (20.5-26.5°)
longitude: 经度 (104.5-112.5°)
total_porosity: 土壤总孔隙度 (30.0-70.0%)
...
生态学案例
某高校土壤生态学研究团队在开展马尾松混交林土壤孔隙研究时,收集了来自多个来源的数据:野外调查的土壤理化性质数据(CSV格式)、从国家地球系统科学数据中心下载的土壤类型图(Shapefile格式)、从WorldClim获取的气候数据(GeoTIFF格式),以及从林草局获取的林分分布数据(Excel格式)。团队在整合这些数据时遇到了严重的”理解鸿沟”问题:土壤孔隙度数据中的”porosity”字段单位不明确(是百分比还是小数?是体积分数还是质量分数?),林分数据中的郁闭度字段被记录为”0.7”但实际含义不明,气候数据中BIO1的数值范围是0-300但不清楚是否需要除以10。由于缺乏元数据,研究团队花费了2周时间才通过查阅文献、联系数据原作者等方法弄清每个字段的真实含义。这一经历促使团队建立了严格的数据管理规范:所有新采集的数据必须附带标准化的元数据文件,包括字段定义表、数据采集方法说明、质量控制记录和数据许可信息。同时,团队将历史数据补充完善元数据,建立了项目级数据字典。这一改进使后续的数据整合工作从2周缩短到2天,显著提高了研究效率,也为后续的研究生培养提供了可遵循的数据管理范例。
拓展阅读
- GBIF (2023). Darwin Core: A standard for biodiversity data. Biodiversity Data Journal, 9:e101888. https://doi.org/10.3897/BDJ.9.e101888
- 国家标准化管理委员会 (2019). GB/T 39559.1-2020 地理信息 元数据 第1部分: 基础. 中国标准出版社.
- Force, J., & Richards, M. (2022). The Research Data Management Toolkit. https://rdmtoolkit.net/
- Wilkinson, M., et al. (2016). The FAIR Guiding Principles for scientific data management and stewardship. Scientific Data, 3, 160018.
- ESRI (2023). Metadata: What it is and how to create it. https://www.esri.com/arcgis-blog/product/metadata/
13.3.4 4. 网络爬虫 (Web Scraping)
概念定义
网络爬虫(Web Scraping)是指利用程序自动从网页中提取结构化数据的技术。当所需数据存在于没有提供API接口的网站时,爬虫成为获取数据的重要手段。爬虫程序模拟浏览器行为,向目标网站发送HTTP请求,解析返回的HTML或JSON内容,提取所需的数据字段。与API获取相比,爬虫的灵活性更高但稳定性较低——网站结构变化可能导致爬虫失效。从技术角度,爬虫分为简单爬虫(使用requests+BeautifulSoup解析静态页面)和框架爬虫(使用Scrapy等框架处理复杂场景,包括动态加载、登录验证、反爬机制等)。在生态学研究中,爬虫常用于从林业局、环保局、地方数据平台等获取尚未开放API的数据接口。
Python爬虫代码示例
# 网络爬虫示例:从网页抓取生态学数据
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import logging
from pathlib import Path
from urllib.parse import urljoin
# 配置日志
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# ============================================
# 示例1:简单爬虫 - 抓取网页表格数据
# ============================================
def scrape_ecology_station_data(base_url, table_index=0):
"""
从生态监测站网页抓取数据
参数:
base_url: 目标网页URL
table_index: 要提取的表格索引(默认第0个)
返回:
pandas DataFrame
"""
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/120.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8'
}
response = requests.get(base_url, headers=headers, timeout=30)
response.raise_for_status()
response.encoding = 'utf-8'
soup = BeautifulSoup(response.text, 'html.parser')
# 查找所有表格
tables = soup.find_all('table')
if table_index >= len(tables):
raise ValueError(f"表格索引超出范围,仅有{len(tables)}个表格")
table = tables[table_index]
# 解析表格数据
rows = table.find_all('tr')
data = []
for row in rows:
cells = row.find_all(['td', 'th'])
row_data = [cell.get_text(strip=True) for cell in cells]
data.append(row_data)
# 转换为DataFrame(第一行作为表头)
if len(data) > 1:
df = pd.DataFrame(data[1:], columns=data[0])
return df
return pd.DataFrame()
# ============================================
# 示例2:爬取多页面数据(分页处理)
# ============================================
def scrape_paginated_data(base_url, start_page=1, end_page=10):
"""
爬取分页数据(如物种列表、林分数据等)
参数:
base_url: 基础URL(不含页码参数)
start_page: 起始页码
end_page: 结束页码
"""
all_data = []
for page in range(start_page, end_page + 1):
# 根据实际网站结构调整URL构造方式
page_url = f"{base_url}?page={page}"
try:
logger.info(f"正在爬取第{page}页: {page_url}")
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36'
}
response = requests.get(page_url, headers=headers, timeout=30)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
# 提取数据(根据实际网页结构调整选择器)
items = soup.select('.data-item') # 示例选择器
for item in items:
record = {
'title': item.select_one('.title').get_text(strip=True) if item.select_one('.title') else '',
'value': item.select_one('.value').get_text(strip=True) if item.select_one('.value') else '',
'source': item.select_one('.source').get_text(strip=True) if item.select_one('.source') else '',
'page': page
}
all_data.append(record)
# 遵守爬虫礼仪:添加延时避免给服务器造成压力
time.sleep(2)
except Exception as e:
logger.error(f"爬取第{page}页时出错: {e}")
continue
return pd.DataFrame(all_data)
# ============================================
# 示例3:检查robots.txt遵守情况
# ============================================
def check_robots_txt(base_url):
"""
检查目标网站的robots.txt规则
参数:
base_url: 网站基础URL
返回:
dict: 允许/禁止的路径
"""
from urllib import robotparser
robots_url = urljoin(base_url, '/robots.txt')
try:
rp = robotparser.RobotFileParser()
rp.set_url(robots_url)
rp.read()
return {
'allowed': rp.allow_all,
'disallowed': not rp.allow_all,
'crawl_delay': rp.crawl_delay('*') if hasattr(rp, 'crawl_delay') else None
}
except Exception as e:
logger.warning(f"无法读取robots.txt: {e}")
return {'error': str(e)}
# 示例:检查国家地球系统科学数据中心的robots规则
robots_info = check_robots_txt('http://www.geodata.cn')
print(f"Robots.txt 检查结果: {robots_info}")
# ============================================
# 示例4:使用Scrapy框架(适合复杂爬虫)
# ============================================
# 注意:以下为Scrapy框架的示例代码框架,实际运行需要安装scrapy并创建项目
"""
# 1. 安装: pip install scrapy
# 2. 创建项目: scrapy startproject ecology_spider
# 3. 创建爬虫: scrapy genspider species_spider example.com
# species_spider.py 示例
import scrapy
class EcologyDataSpider(scrapy.Spider):
name = 'ecology_data'
allowed_domains = ['example.com']
start_urls = ['https://www.example.com/ecology/stations/']
def parse(self, response):
# 解析列表页
for station in response.css('div.station-item'):
yield {
'name': station.css('h3.title::text').get(),
'location': station.css('span.location::text').get(),
'data_type': station.css('span.type::text').get(),
}
# 翻页处理
next_page = response.css('a.next::attr(href)').get()
if next_page:
yield response.follow(next_page, self.parse)
# 遵守爬虫速率限制
custom_settings = {
'ROBOTSTXT_OBEY': True,
'DOWNLOAD_DELAY': 2,
'CONCURRENT_REQUESTS_PER_DOMAIN': 1,
}
"""
# ============================================
# 生态学数据爬虫示例:广西林业站点数据
# ============================================
def scrape_guangxi_forestry_data():
"""
示例函数:从广西林业网站爬取样地数据
(本函数展示结构,实际URL需要根据目标网站调整)
"""
base_url = "https://www.gxforestry.gov.cn/"
# 检查robots.txt
robots = check_robots_txt(base_url)
if 'error' not in robots and not robots.get('allowed', True):
logger.warning("该网站可能不允许爬取,请谨慎操作")
# 模拟爬取流程
sample_data = {
'station_id': [],
'station_name': [],
'latitude': [],
'longitude': [],
'forest_type': [],
'data_year': []
}
# 示例数据(实际应从网页解析)
sample_stations = [
{'id': 'GX001', 'name': '南宁良庆监测站', 'lat': 22.8, 'lon': 108.3, 'type': '马尾松林', 'year': 2022},
{'id': 'GX002', 'name': '桂林灵川监测站', 'lat': 25.4, 'lon': 110.2, 'type': '杉木林', 'year': 2022},
]
for s in sample_stations:
sample_data['station_id'].append(s['id'])
sample_data['station_name'].append(s['name'])
sample_data['latitude'].append(s['lat'])
sample_data['longitude'].append(s['lon'])
sample_data['forest_type'].append(s['type'])
sample_data['data_year'].append(s['year'])
return pd.DataFrame(sample_data)
# 运行示例
# df = scrape_guangxi_forestry_data()
# df.to_csv('data/raw/guangxi_forestry_stations.csv', index=False, encoding='utf-8-sig')运行结果示例:
2026-04-14 10:30:15 - INFO - 正在爬取第1页: https://example.com/data?page=1
2026-04-14 10:30:17 - INFO - 正在爬取第2页: https://example.com/data?page=2
...
2026-04-14 10:35:20 - INFO - 共获取 156 条记录
# 爬取的数据样例:
station_id station_name latitude longitude forest_type data_year
0 GX001 南宁良庆监测站 22.80 108.30 马尾松林 2022
1 GX002 桂林灵川监测站 25.40 110.20 杉木林 2022
爬虫伦理与法律注意事项
网络爬虫虽然强大,但使用不当可能带来法律和伦理问题。以下是必须遵守的原则:
| 原则 | 具体要求 | 原因 |
|---|---|---|
| 遵守robots.txt | 爬虫运行前先检查网站的robots.txt文件,尊重网站的访问控制 | 机器人协议是网站表明爬虫政策的标准方式 |
| 控制爬取频率 | 在请求之间添加延时(建议≥1秒),避免使用多线程并发 | 过快爬取可能影响网站正常服务,属于”拒绝服务”行为 |
| 设置合理的User-Agent | 在请求头中明确标识爬虫身份和联系方式 | 负责任的爬虫应该可被识别和联系 |
| 仅获取所需数据 | 不爬取与研究无关的个人隐私数据(如姓名、电话、地址) | 隐私保护法规要求,侵犯隐私可能违法 |
| 尊重数据版权 | 爬取的数据仅用于研究目的,不进行商业二次销售 | 数据使用应遵守版权法和网站服务条款 |
| 优先使用API | 有API的网站应优先使用API,而非爬虫 | API是网站官方认可的数据获取方式 |
| 做好容错处理 | 网站不可用时记录日志并停止爬取,不反复重试 | 避免给服务器造成额外负担 |
重要提醒:不同国家/地区对网页数据爬取的法律法规不同。在中国,《网络安全法》《数据安全法》《个人信息保护法》对数据爬取有明确限制。在进行任何爬虫操作前,请确保:(1) 目标网站允许爬取;(2) 爬取的数据用途合法;(3) 不侵犯个人隐私和商业机密。建议在学术研究中,优先联系数据所有者获取正式数据共享协议。
生态学案例
某研究团队在开展广西马尾松天然林资源调查时,需要获取全区1000多个固定样地的历史调查数据。这些数据分散在广西壮族自治区13个设区市的林业局网站,以HTML表格形式发布,没有统一的数据下载接口,也没有提供API。研究团队面临两个选择:一是派人到各地林业局实地查阅纸质档案,二是尝试从网站爬取公开数据。考虑到效率因素,团队决定采用规范的网络爬虫方案获取数据。团队首先给所有设区市林业局发送正式公函,说明研究目的和数据需求,询问是否有直接的数据共享渠道。同时,技术人员分析了目标网站结构,编写了Python爬虫程序。在数据爬取阶段,团队严格遵守以下规范:(1) 先检查每个网站的robots.txt;(2) 设置3秒间隔的请求延时;(3) 仅爬取公开的样地调查表格数据,不涉及任何个人信息;(4) 标注数据来源和爬取日期。最终,团队成功从10个设区市获取到公开的样地数据,其余3个因网站无公开数据或robots.txt明确禁止而未爬取。这一经历说明,负责任的爬虫使用可以在遵守法律和伦理的前提下,有效获取研究需要的数据。
拓展阅读
- Mitchell, R. (2015). Web Scraping with Python: Collecting Data from the Modern Web. O’Reilly Media.
- Scrapy Documentation: https://docs.scrapy.org/
- BeautifulSoup Documentation: https://www.crummy.com/software/BeautifulSoup/bs4/doc/
- RFC 9309 - robots.txt协议: https://datatracker.ietf.org/doc/html/rfc9309
- 中国网络安全法律法规: 《网络安全法》《数据安全法》《个人信息保护法》相关条款.
13.4 公共数据的使用规范与伦理
- 数据引用:公共数据并非“无主之地”。在使用数据时,必须引用原始数据集的 DOI 或按照数据库要求的格式进行致谢。
- 理解协议:注意数据遵循的 CC 协议(如 CC BY 4.0 或 CC0)。某些数据可能禁止商业用途。
13.5 公共数据的挑战与质量控制
公共数据虽然强大,但也存在“陷阱”: * 分类学偏差:某些明星物种(如大熊猫、鸟类)的数据远多于隐秘物种。 * 空间偏差:交通便利地区或发达国家的数据记录往往更密集。 * 标准化问题:不同来源的数据格式不一。建议遵循 Darwin Core (DwC) 等国际标准进行数据清洗和整合。
13.6 R 代码示例:从公共数据库下载生态数据
下面演示如何用 R 从两个常用的公共数据库获取数据:GBIF(物种分布数据)和 WorldClim(全球气候数据)。
13.6.1 从 GBIF 下载物种分布数据
GBIF 是全球最大的生物多样性数据平台。R 的 rgbif 包提供了便捷的 API。
library(rgbif)
library(tibble)
# 搜索物种:以白鹭 (Egretta garzetta) 为例
# 先查找物种的 GBIF 分类编号
species_key <- name_backbone(name = "Egretta garzetta")$usageKey
species_key
# 下载中国范围内的观测记录(限制 500 条)
egret_data <- occ_search(
taxonKey = species_key,
country = "CN", # 国家代码:中国
limit = 500, # 最多返回 500 条
hasCoordinate = TRUE # 仅保留有经纬度的记录
)
# 提取数据表
egret_df <- as_tibble(egret_data$data)
# 查看关键列
egret_df[, c("species", "decimalLatitude", "decimalLongitude",
"eventDate", "stateProvince")]# 简单可视化:在地图上展示分布点
# 注意:borders() 函数依赖 maps 包,需先安装:install.packages("maps")
library(ggplot2)
ggplot(egret_df, aes(x = decimalLongitude, y = decimalLatitude)) +
borders("world", regions = "China", fill = "grey90", colour = "grey70") +
geom_point(color = "forestgreen", alpha = 0.5, size = 1.5) +
coord_quickmap(xlim = c(73, 135), ylim = c(18, 54)) +
labs(
title = "白鹭 (Egretta garzetta) 在中国的 GBIF 记录",
x = "经度", y = "纬度"
) +
theme_minimal()- 下载大量数据时需要注册 GBIF 账号,并使用
occ_download()提交异步下载请求 - 发表论文时必须引用 GBIF 数据集的 DOI
- 公民科学数据可能存在空间偏差(城市和道路附近记录更多),分析时需注意
13.6.2 从 WorldClim 下载气候数据
WorldClim 提供全球高分辨率的气候栅格数据。R 的 geodata 包可以直接下载。
# 安装(仅需运行一次)
install.packages("geodata")library(geodata)
library(terra)
# 下载全球生物气候变量(分辨率 10 分,约 18 km)
# 数据会缓存到本地 path 目录,下次无需重复下载
bioclim <- worldclim_global(
var = "bio", # 19 个生物气候变量
res = 10, # 分辨率:10 arc-minutes
path = "data" # 本地保存路径
)
# 查看数据基本信息
bioclim# 提取中国区域的年均温(BIO1)和年降水量(BIO12)
china_extent <- ext(73, 135, 18, 54) # 中国大致经纬度范围
bio1 <- crop(bioclim[[1]], china_extent) # BIO1: 年均温 (°C × 10)
bio12 <- crop(bioclim[[12]], china_extent) # BIO12: 年降水量 (mm)
# 可视化年均温
plot(bio1, main = "中国年均温 (BIO1, °C × 10)")# 提取物种分布点对应的气候数据
# 将白鹭的经纬度转为空间点,提取对应的气候值
library(dplyr)
library(readr)
egret_coords <- egret_df |>
filter(!is.na(decimalLongitude), !is.na(decimalLatitude)) |>
select(decimalLongitude, decimalLatitude)
# 创建空间向量并提取气候值
egret_points <- vect(egret_coords, geom = c("decimalLongitude", "decimalLatitude"))
climate_values <- extract(bioclim, egret_points)
# 合并物种数据与气候数据
egret_climate <- bind_cols(egret_coords, as_tibble(climate_values))
head(egret_climate)下载的公共数据建议保存到项目的 data/ 目录下,并在 README 中注明数据来源和下载日期:
# 保存处理后的数据
write_csv(egret_climate, "data/egret_climate_data.csv")这样既方便团队协作,也满足科研可重复性的要求。
13.7 总结
公共数据采集方法是现代生态学家的必备技能。它不仅降低了数据获取的成本,更促进了科学研究的透明度与可重复性。通过将公共数据与自己的观测或实验数据相结合,研究者能够以更广阔的视角审视自然界的复杂过程。