gpfield gpfield logo

gpfield is the spatial member of an agricultural-analytics R-package orchestra. It does change-of-support Gaussian-process regression for field and multi-environment-trial (MET) data: it smooths a noisy field surface, predicts at a support different from the one observed (a point process predicted as paddock-scale block averages, with the predictive variance propagated through the support map), and -- the part that matters in practice -- abstains when the data support or the estimated spatial range cannot bear the requested prediction resolution, rather than returning a confident wrong surface.

The Gaussian process is a self-contained exact solver in base R, so the package takes no heavy external GP dependency and installs and checks anywhere.

Related packages

gpfield fits an exact Gaussian process, whose cost grows with the cube of the number of observations, so it suits field trials rather than very large datasets. It adds a typed refusal when the estimated range or the data cannot support the requested prediction, a one-call smoother for row-column trial layouts (gp_field_smooth()), and output in the orchestra manifest that the other orchestra packages read.

Installation

# install.packages("remotes")
# remotes::install_github("max578/gpfield")
install.packages("gpfield", repos = c('https://max578.r-universe.dev', 'https://cloud.r-project.org'))

What it does

A minimal example

library(gpfield)

# A noisy field on a 10 x 10 plot grid.
set.seed(1L)
field <- expand.grid(row = seq_len(10L), col = seq_len(10L))
field$yield <- 3 + 0.3 * field$row + sin(field$col / 2) +
  rnorm(nrow(field), 0, 0.2)

# Fit the GP.
fit <- gp_fit(gpfield_spec(c("row", "col"), "yield"), field, seed = 1L)
fit
#> <gpfield_fit>
#>   kernel:      matern
#>   n training:  100
#>   length-scale (standardised): 4.34
#>   range (raw units):           34.31
#>   marginal var / noise var:    3.317 / 0.02171
#>   log marginal likelihood:     10.6

# Predict two paddock-scale block averages (change of support).
blocks <- block_support(
  lower = rbind(c(1, 1), c(6, 6)),
  upper = rbind(c(5, 5), c(10, 10))
)
gp_predict(fit, support = "block", blocks = blocks, seed = 1L)
#> <gpfield_prediction>
#>   support:   block
#>   locations: 2
#>   mean range: [4.725, 4.778]
#>   sd range:   [0.03812, 0.03812]

# Ask for a resolution the field cannot support: gpfield abstains.
gp_predict(fit, data.frame(row = c(1, 60), col = c(1, 60)))
#> <gpfield_abstention>
#>   scope:  gp_predict
#>   reason: range_too_short
#>   detail: query spacing 83.44 exceeds 1 x the estimated range 34.31; the field cannot be resolved this finely
#>     spacing = 83.44
#>     range = 34.31
#>     spacing_tol = 1

Contributing

Bug reports and suggestions are welcome through the GitHub issues.

Citation

citation("gpfield")

Licence

MIT (c) 2026 Max Moldovan. The orchestra is open-source across the board.