A Python library for using machine learning to predict the timing of phenological events in plants
Project description
Description
pysephone is a Python package for developing and benchmarking crop phenology models — models that predict the timing of key developmental events in plants, such as flowering, leaf-out, or harvest maturity. Accurate phenology predictions are essential for agricultural planning, yield forecasting, and understanding how ecosystems respond to climate variability and long-term change. As growing seasons shift under climate change, the ability to reliably model phenological timing across species and regions becomes increasingly important for both science and policy.
pysephone provides a standardised pipeline that connects observational phenology databases with meteorological drivers, and a suite of models ranging from classical process-based approaches to deep learning, all sharing a common interface.
The package is designed to make it straightforward to:
- load and preprocess phenological observation data from multiple sources,
- pair observations with season-windowed meteorological time series (ERA5 reanalysis supported out of the box; other drivers can be integrated),
- define phenology datasets for standardised intercomparison of models,
- fit and evaluate a variety of models, and
- systematically compare model behaviour across species, regions, and climate conditions.
Installation
git clone https://github.com/ronvree/pysephone.git
cd pysephone
pip install -e .
Requires Python ≥ 3.14.
The base install is intentionally lightweight (process-based + scikit-learn models, datasets, evaluation). Heavier and source-specific dependencies are opt-in via extras:
| Extra | Adds | Needed for |
|---|---|---|
deep |
PyTorch | LSTM / GRU / CNN / Transformer / hybrid / Beta-GDD / BSpline-GDD models |
boost |
XGBoost | XGBoostModel |
agera5 |
cdsapi, xarray, netCDF4 | Downloading AgERA5 from Copernicus CDS |
openmeteo |
openmeteo-requests | Downloading Open-Meteo ERA5 |
geo |
geopandas, shapely, rasterio | Map visualizations, WorldClim rasters |
earthengine |
earthengine-api | Fetching AlphaEarth embeddings |
stats |
scikit-posthocs, autorank | Friedman/Nemenyi comparison + critical-difference plots |
all |
everything above | Convenience meta-extra (every model + data source) |
Install one or more with e.g. pip install "pysephone[deep]" or pip install "pysephone[deep,agera5]". Accessing a model whose extra isn't installed raises a clear error telling you which extra to add.
Reproducing BloomBench specifically needs pip install "pysephone[deep,boost,agera5,stats]" — its models are CNN/LSTM/Transformer (deep), XGBoost (boost), RandomForest/Mean/Linear (base); climate features come from AgERA5 (agera5); and the compare step's Nemenyi/critical-difference plots need stats. It does not use AlphaEarth/Earth Engine or Open-Meteo.
Data Sources
| Source | Description |
|---|---|
| PEP725 | Pan-European Phenology Database — multi-species observations across Europe |
| GMU Cherry Blossom | Cherry blossom bloom dates from Japan, Switzerland, and South Korea |
| USA-NPN | USA National Phenology Network — deciduous fruit-tree observations |
| AgERA5 | Daily agrometeorological indicators from Copernicus CDS (downscaled temperature/radiation, Penman–Monteith inputs, etc.) |
| Open-Meteo ERA5 | ERA5 reanalysis via the Open-Meteo archive |
Meteorological data is cached locally in HDF5 for fast repeated access. Additional providers can be integrated by implementing the FeatureProvider interface.
Some reference datasets (e.g. the cherry-blossom bloom records) are bundled with the package. These third-party datasets retain their original licenses and attribution requirements — see DATA_SOURCES.md. Note that liestal.csv is non-commercial-use only and kyoto.csv is provided for academic use with required citations.
Authentication & configuration
Some data sources reach external APIs that require your own account/project — pysephone ships no credentials and no default project. Set these up once before running the download steps:
Copernicus CDS (AgERA5). Authentication uses the cdsapi convention — pysephone handles no keys itself. Provide your credentials via either:
- a
~/.cdsapircfile, or - the
CDSAPI_URLandCDSAPI_KEYenvironment variables.
Google Earth Engine (AlphaEarth embeddings). Requires a Google Cloud project with the Earth Engine API enabled:
- Authenticate once:
python -c "import ee; ee.Authenticate()". - Tell pysephone which project to use, in priority order:
- pass
ee_project="your-gcp-project"tofetch_alphaearth_embeddings_batched(...), or - set the
PYSEPHONE_EE_PROJECTenvironment variable (Earth Engine's nativeEARTHENGINE_PROJECTis also honored), or - leave it unset to let Earth Engine resolve its own default project.
- pass
Data location. All caches, downloaded data, and outputs are written under a single data root. By default this is an OS-native per-user directory (%LOCALAPPDATA%\pysephone on Windows, ~/.local/share/pysephone on Linux/macOS). Override it with the PYSEPHONE_DATA_ROOT environment variable — e.g. set PYSEPHONE_DATA_ROOT=<repo> to keep data inside a source checkout during development.
Models
| Category | Models |
|---|---|
| Baseline | Mean |
| Process-based | GDD, Utah+GDD, ChillingDays+GDD, Dynamic+GDD |
| Machine learning | Random Forest |
| Deep learning | LSTM, Hybrid (TTCNN chilling + GDD forcing) |
All models share a common fit / predict interface, making it easy to add new models or swap them in evaluation pipelines.
Pipeline Overview
Data source (PEP725 / GMU Cherry)
↓ preprocessing (outlier removal, grid aggregation)
Observations (indexed by source, location, year, species, obs type)
↓ paired with Calendar + meteorological feature provider
Dataset (yields season-windowed feature arrays per sample)
↓
Model.fit(target_fn, dataset) → Model.predict(sample)
↓
SingleTargetRegression.run(...) → metrics, error DataFrames, plots
The Calendar defines the season window (start date + length) for each entry. Feature providers retrieve the corresponding meteorological time series for each sample.
Reproducing BloomBench
BloomBench is a multi-species benchmark for evaluating ML phenology models on fruit-tree flowering. The benchmark is shipped as a first-class library module: pysephone.benchmarks.bloombench.
It exposes both a Python API and a thin CLI. Reproducing the benchmark needs the deep-learning, boosting, AgERA5, and stats extras (it does not use Earth Engine or Open-Meteo):
pip install "pysephone[deep,boost,agera5,stats]"
# 1. Populate the AgERA5 cache once (Copernicus CDS credentials required).
jupyter nbconvert --execute notebooks/download_agera5.ipynb
# 2. Tune hyperparameters per (dataset, model) — overnight run.
python -m pysephone.benchmarks.bloombench hpo
# 3. Fit & evaluate every (seed, dataset, model) triple.
python -m pysephone.benchmarks.bloombench run --seeds 0 1 2
# 4. Friedman + Nemenyi + critical-difference plots.
python -m pysephone.benchmarks.bloombench compare --seeds 0 1 2
The same flow as Python:
from pysephone.benchmarks.bloombench import (
load_bloombench_datasets, run_benchmark, run_comparison, run_hpo,
)
datasets, _ = load_bloombench_datasets()
run_hpo(datasets) # one-time HPO
results = run_benchmark(seeds=[0, 1, 2], datasets_dict=datasets)
report = run_comparison(seeds=[0, 1, 2])
For the interactive flow with tables / heatmaps / critical-difference plots, see notebooks/bloombench_extended_hpo.ipynb (one-time HPO) and notebooks/bloombench_extended.ipynb (replication).
Project Structure
.
├── src/pysephone/
│ ├── benchmarks/ # End-to-end benchmark suites (BloomBench, …)
│ ├── data/ # Data ingestion and sources (PEP725, GMU Cherry, USA-NPN, AgERA5)
│ ├── dataset/ # Observations, Dataset, Calendar, feature providers, registry
│ ├── evaluation/ # Evaluation logic and regression metrics
│ ├── models/ # Model implementations (CF, RF, LSTM, Hybrid, …)
│ ├── utils/ # Shared utilities
│ └── visualize/ # Visualisation helpers
├── notebooks/ # Jupyter notebooks for exploration and analysis
├── scripts/ # Standalone scripts
├── tests/ # Test suite
└── data/ # Raw and processed data (git-ignored)
Notebooks
| Notebook | Description |
|---|---|
cherry_blossom_cf_models.ipynb |
Process-based model evaluation on GMU Cherry datasets |
cf_models_pep725_fruit_trees.ipynb |
CF model evaluation across PEP725 fruit tree species |
unusual_year_model_eval.ipynb |
Model comparison on climatologically unusual vs normal years |
unusual_seasons_*.ipynb |
Exploration of unusual seasons in GMU / PEP725 data |
dataset_adequacy_*.ipynb |
Sample sufficiency analysis per dataset |
lstm_cherry_exploration.ipynb |
LSTM model exploration on cherry blossom data |
model_exploration.ipynb |
General model exploration notebook |
pvtt_winter_wheat.ipynb |
PVTT model for winter wheat phenology |
Dependencies
Base install: pandas, numpy, scipy, scikit-learn, matplotlib, nlopt, tables, h5py, requests, requests-cache, retry-requests, tqdm, unidecode, platformdirs.
Heavier and source-specific dependencies (torch, xgboost, cdsapi, openmeteo-requests, geopandas/shapely/rasterio, earthengine-api, …) are opt-in via the extras above.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pysephone-0.1.0.tar.gz.
File metadata
- Download URL: pysephone-0.1.0.tar.gz
- Upload date:
- Size: 2.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
edc696a370b3e6746e7c4102243aef133a67bf382e12001611fbe2e51c815b1e
|
|
| MD5 |
a407751d5264db3af519007bcfef8fd7
|
|
| BLAKE2b-256 |
6d65a10c3143e2dd50ce9e763faee3b2985b8cd0ce9df3d4bf75e6adc7097b9f
|
Provenance
The following attestation bundles were made for pysephone-0.1.0.tar.gz:
Publisher:
publish.yml on ronvree/pysephone
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pysephone-0.1.0.tar.gz -
Subject digest:
edc696a370b3e6746e7c4102243aef133a67bf382e12001611fbe2e51c815b1e - Sigstore transparency entry: 1694130466
- Sigstore integration time:
-
Permalink:
ronvree/pysephone@f03a8b4838aa04ba4b5007c1498b3e7764177f6e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/ronvree
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f03a8b4838aa04ba4b5007c1498b3e7764177f6e -
Trigger Event:
release
-
Statement type:
File details
Details for the file pysephone-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pysephone-0.1.0-py3-none-any.whl
- Upload date:
- Size: 2.5 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ce863c1502548cf3b6882047b1c690eac8fb93c81c66dd8903a6004fa24743b
|
|
| MD5 |
110768c998d5ff19e633abc04fb78cbe
|
|
| BLAKE2b-256 |
3f154076a3f30075f42999bab5a962ba4466ff50f2c0285dba94a825a924f97e
|
Provenance
The following attestation bundles were made for pysephone-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on ronvree/pysephone
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pysephone-0.1.0-py3-none-any.whl -
Subject digest:
0ce863c1502548cf3b6882047b1c690eac8fb93c81c66dd8903a6004fa24743b - Sigstore transparency entry: 1694130576
- Sigstore integration time:
-
Permalink:
ronvree/pysephone@f03a8b4838aa04ba4b5007c1498b3e7764177f6e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/ronvree
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f03a8b4838aa04ba4b5007c1498b3e7764177f6e -
Trigger Event:
release
-
Statement type: