| Title: | On Farm Experimentation Mean Test |
|---|---|
| Description: | Provides tools for comparing treatments in unreplicated on-farm experiments (OFE) using georeferenced data. The package includes functions for constructing spatial grids, estimating the effective sample size from spatial autocorrelation, performing repeated permutation tests, adjusting pairwise comparisons, and visualizing the results. It implements the OFE-mean test described in Córdoba, Paccioretti and Balzarini (2025) <doi:10.1007/s11119-024-10206-0>. |
| Authors: | Mariano Córdoba [aut, cph], Pablo Paccioretti [aut, cre, cph], Monica Balzarini [aut] |
| Maintainer: | Pablo Paccioretti <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.0.0.9000 |
| Built: | 2026-09-20 09:27:59 UTC |
| Source: | https://github.com/ppaccioretti/ofemeantest |
Convenience wrapper that builds a grid with [all_cells_grid()] and filters cells using [select_grid()]. It is the main entry point for generating grids for on-farm experiment (OFE) analysis.
make_ofe_grid( data, x, cellsize, min_per_cell = 1L, angle_deg = 0, buffer = 0, shift = c(0, 0), return_points = FALSE )make_ofe_grid( data, x, cellsize, min_per_cell = 1L, angle_deg = 0, buffer = 0, shift = c(0, 0), return_points = FALSE )
data |
An 'sf' object of points. See [all_cells_grid()]. |
x |
Character scalar with the treatment column name in 'data'. See [select_grid()]. |
cellsize |
Grid cell size. See [all_cells_grid()]. |
min_per_cell |
Minimum observations per cell. See [select_grid()]. |
angle_deg |
Grid rotation in degrees. See [all_cells_grid()]. |
buffer |
Buffer applied to the data bounding box before gridding. See [all_cells_grid()]. |
shift |
Offset applied to the grid origin. See [all_cells_grid()]. |
return_points |
Logical. See [select_grid()]. |
An 'ofe_grid' object returned by [select_grid()], containing the full grid, the selected cells, and associated metadata.
[all_cells_grid()], [select_grid()]
grid_ofe <- make_ofe_grid( ofe_f2, x = "Treatment", cellsize = 9, min_per_cell = 1L, angle_deg = 0, buffer = 0, shift = c(0, 0), return_points = TRUE ) plot_grid_selection(grid_ofe)grid_ofe <- make_ofe_grid( ofe_f2, x = "Treatment", cellsize = 9, min_per_cell = 1L, angle_deg = 0, buffer = 0, shift = c(0, 0), return_points = TRUE ) plot_grid_selection(grid_ofe)
The impact of broadcast phosphorus (P) fertilization on corn was evaluated. The Fertilized strip received superphosphate at rates ranging from 250 to 500 kg ha-1 \[0-21-0\], while the Control strip received no P fertilizer (0 kg ha-1). Each strip was 2.2 ha in area, totaling 100 ha.
ofe_f2ofe_f2
A sf object with 3070 rows and 3 variables:
character, identifying Fertilized or Control strip
numeric, corn grain yield in tn/ha
sf geometry column
Coordinate reference system is "WGS 84 / UTM zone 20S", EPSG:32720.
Companion on-farm experiment used to demonstrate the package on a second field. Same layout as [ofe_f2]: a single fertilized strip compared against an adjacent control strip on dense yield-monitor data, with yields cleaned following Vega et al. (2019).
ofe_pofe_p
A sf object with 3070 rows and 3 variables:
character, identifying Fertilized or Control strip
numeric, grain yield in tn/ha
sf geometry column
Coordinate reference system is "WGS 84 / UTM zone 20S", EPSG:32720.
Analyzes unreplicated on-farm experiments to support field-specific inference about treatment effects. Spatial statistical methods are combined with permutation tests to compare the means of two or more treatments.
ofemt( data, y, x, cellsize = 10, min_per_cell = 4, n_p = 1000, n_s = 200, alpha = 0.05, shift = c(0, 0), p_adjust_method = c("none", "bonferroni", "holm", "BH"), crs = NULL, keep_components = c("none", "light", "full"), grid = NULL, angle_deg = 0, buffer = 0, seed = 7L )ofemt( data, y, x, cellsize = 10, min_per_cell = 4, n_p = 1000, n_s = 200, alpha = 0.05, shift = c(0, 0), p_adjust_method = c("none", "bonferroni", "holm", "BH"), crs = NULL, keep_components = c("none", "light", "full"), grid = NULL, angle_deg = 0, buffer = 0, seed = 7L )
data |
An 'sf' object containing point geometries and the response and treatment columns specified in 'y' and 'x'. |
y |
response column (numeric). |
x |
treatment column (factor/character). |
cellsize, shift, angle_deg, buffer, min_per_cell
|
grid settings (used when 'grid' is NULL). |
n_p |
number of permutations per ANOVA run. |
n_s |
number of sampling runs. |
alpha |
significance threshold for letters. |
p_adjust_method |
p-value adjustment method across pairwise comparisons. The adjustment is applied *within each sampling run*, across the 'choose(k, 2)' comparisons of that run; the reported 'p_adj' is the median of those per-run adjusted values (see Details). |
crs |
optional target projected CRS if 'data' is in lon/lat. |
keep_components |
What spatial components to embed in the result. One of:
Roughly, '"light"' costs one polygon per grid cell and '"full"' adds one row per observation. |
grid |
optional: an 'ofe_grid' (e.g., from [make_ofe_grid()]). If 'NULL', the grid is generated internally using 'cellsize', 'min_per_cell', 'shift', 'angle_deg' and 'buffer', and an informative message is emitted. |
seed |
integer; controls reproducibility of the permutation sampling (the grid itself is deterministic from its construction arguments). Set 'seed = NULL' to let results vary across runs. |
The OFE-mean test accounts for spatial dependence when comparing treatments in unreplicated on-farm experiments. The procedure involves:
Aggregating the georeferenced observations within grid cells.
Estimating the spatial autocorrelation of the treatment-adjusted residuals.
Calculating the effective sample size (ESS) from the estimated spatial dependence.
Drawing repeated balanced subsamples whose size is determined by the ESS.
Performing pairwise permutation analysis of variance tests for each subsample.
Generating an empirical distribution of p-values for each treatment comparison.
The median of each empirical p-value distribution is reported as the p-value associated with the null hypothesis of no treatment effect.
For experiments with more than two treatments, all pairwise treatment comparisons are performed.
Multiplicity adjustment
When 'p_adjust_method != "none"', multiplicity adjustment is performed separately within each sampling run. [stats::p.adjust()] is applied to the 'choose(k, 2)' pairwise p-values obtained in that run, where 'k' is the number of treatments. The reported adjusted p-value is the median of the resulting empirical distribution of adjusted p-values.
The 'p_adj' column in 'perm_runs' contains the adjusted p-value from each sampling run. Therefore, [plot_pvalue_hist()] displays the empirical distribution used to calculate the reported median.
Compact letter display
Treatments are ordered by decreasing response before the compact letter display is generated. Treatments that do not share a letter are considered significantly different at the significance level specified by 'alpha'. Treatment labels are internally recoded before calling [multcompView::multcompLetters()] and subsequently restored. Consequently, labels containing spaces or special characters are preserved in the results.
An object of class 'ofemt_result': a list with
One-row data frame with the cell size, the number of cells in the full grid and in the selection, the min/median/max number of observations per selected cell, the number of cells entering the analysis ('n'), the effective sample size ('ESS'), the spatial autocorrelation estimate (‘Rho') and Moran’s *I*.
'table' of selected cells per treatment.
One row per pairwise comparison, with the median p-value across runs ('p_value') and its multiplicity-adjusted counterpart ('p_adj').
One row per treatment, sorted by decreasing median response, with the compact letter display.
The raw per-run output: 'n_s * choose(k, 2)' rows with columns 'Trt_1', 'Trt_2', 'Comparison', 'p_value' (the permutation p-value of that comparison in that run), 'p_adj' (the same value after adjusting within the run) and 'run' (run index, '1:n_s'). This is the empirical p-value distribution the method is built on; it is what [plot_pvalue_hist()] draws and what you would use to inspect the run-to-run variability behind the reported medians.
The settings actually used (grid geometry, 'n_p', 'n_s', 'alpha', 'p_adjust_method', 'seed', and 'grid_source', which records whether the grid was supplied or built internally).
Optional spatial components; see 'keep_components'.
Córdoba, M., Paccioretti, P. and Balzarini, M. (2025). A new method to compare treatments in unreplicated on-farm experimentation. Precision Agriculture, 26, Article 4. doi:10.1007/s11119-024-10206-0
[make_ofe_grid()], [plot_grid_selection()], [plot_pvalue_hist()]
res <- ofemt(ofe_f2, y = "Yield_tn", x = "Treatment", cellsize = 10, min_per_cell = 4, alpha = 0.05) # Keep the geometries to inspect how the grid lands on the points res <- ofemt(ofe_f2, y = "Yield_tn", x = "Treatment", cellsize = 10, min_per_cell = 4, keep_components = "full") plot(res)res <- ofemt(ofe_f2, y = "Yield_tn", x = "Treatment", cellsize = 10, min_per_cell = 4, alpha = 0.05) # Keep the geometries to inspect how the grid lands on the points res <- ofemt(ofe_f2, y = "Yield_tn", x = "Treatment", cellsize = 10, min_per_cell = 4, keep_components = "full") plot(res)
Draws the three layers that matter when tuning a grid, on one set of axes: the **full grid** in light grey, the **selected cells** (those that passed the single-treatment and 'min_per_cell' filters) shaded and outlined in black, and the **observations** as points. Seeing the points against the cell boundaries is the quickest way to judge the effect of 'cellsize', 'shift', 'angle_deg' and 'buffer'.
plot_grid_selection( x, data = NULL, points = TRUE, main = NULL, legend = TRUE, legend_pos = NULL, point_size = NULL, engine = c("ggplot2", "base"), ... ) ## S3 method for class 'ofe_grid' plot(x, ...) ## S3 method for class 'ofemt_result' plot(x, ...)plot_grid_selection( x, data = NULL, points = TRUE, main = NULL, legend = TRUE, legend_pos = NULL, point_size = NULL, engine = c("ggplot2", "base"), ... ) ## S3 method for class 'ofe_grid' plot(x, ...) ## S3 method for class 'ofemt_result' plot(x, ...)
x |
Either an 'ofe_grid' (from [make_ofe_grid()]) or an 'ofemt_result' (from [ofemt()]) that was run with 'keep_components = "light"' or '"full"'. With an 'ofemt_result' every layer is taken from the object itself, so no extra arguments are needed; the points are only available under 'keep_components = "full"'. |
data |
Optional 'sf' points to overlay. Rarely needed: the observations are taken from the object itself — 'points_sel' for an 'ofe_grid' built with 'make_ofe_grid(return_points = TRUE)', 'points_joined' for an 'ofemt_result' run with 'keep_components = "full"'. Pass 'data' when the object carries no points, or to override the stored ones. |
points |
Logical; set to 'FALSE' to skip the point layer. When 'TRUE' (the default) and no observations are available, a message explains how to obtain them rather than silently drawing a grid without points. |
main |
Plot title. Defaults to a one-line summary of the grid parameters, which is what makes successive calls comparable. |
legend |
Logical; draw the legend. Default 'TRUE'. |
legend_pos |
Where to place the legend. Defaults to the engine's own sensible choice: '"right"' (outside the panel) for '"ggplot2"', and '"topleft"' for '"base"'. Base-style keywords are translated for the ggplot2 engine, so '"bottomright"' works with either; '"none"' hides it. |
point_size |
Size of the observation dots. Defaults to '0.15' for the ggplot2 engine and '0.35' (as 'cex') for the base engine. Yield-monitor data runs to tens of thousands of points, where the default can still read as a solid mass — lower it to see the cell boundaries underneath. |
engine |
Which graphics system to draw with. '"ggplot2"' (the default) places the legend outside the plotting panel, so it can never sit on top of the data and the result does not depend on the device size. '"base"' uses base graphics and draws the legend inside the panel. If **ggplot2** is not installed the function falls back to '"base"' with a message. |
... |
Further arguments passed to the underlying [plot()] call for the full-grid layer. Base engine only; ignored by the ggplot2 engine. |
With 'engine = "ggplot2"', a 'ggplot' object. In non-interactive contexts (e.g. scripts or inside 'pdf()'), call 'print()' on the returned object to render it. With 'engine = "base"', invisibly 'NULL' — the function is called for the plot it draws.
[make_ofe_grid()], [ofemt()]
g <- make_ofe_grid(ofe_f2, x = "Treatment", cellsize = 9, min_per_cell = 4) plot_grid_selection(g, data = ofe_f2) plot(g, data = ofe_f2) # same thing res <- ofemt(ofe_f2, y = "Yield_tn", x = "Treatment", cellsize = 9, keep_components = "full") plot(res) # grid + selection + points, no extra args # Base graphics instead, or a ggplot you keep customising plot(res, engine = "base") plot(res) + ggplot2::labs(subtitle = "Lote 2")g <- make_ofe_grid(ofe_f2, x = "Treatment", cellsize = 9, min_per_cell = 4) plot_grid_selection(g, data = ofe_f2) plot(g, data = ofe_f2) # same thing res <- ofemt(ofe_f2, y = "Yield_tn", x = "Treatment", cellsize = 9, keep_components = "full") plot(res) # grid + selection + points, no extra args # Base graphics instead, or a ggplot you keep customising plot(res, engine = "base") plot(res) + ggplot2::labs(subtitle = "Lote 2")
Produces one histogram per pairwise comparison showing the empirical distribution of permutation *p*-values across the 'n_s' sampling runs of an ['ofemt_result']. Two reference lines are drawn on each panel: a solid line at the **median** of the distribution — the value [ofemt()] reports for that comparison — and a dashed line at the significance threshold **alpha**.
plot_pvalue_hist( results, which = c("auto", "adjusted", "raw"), bins = 30, engine = c("ggplot2", "base") )plot_pvalue_hist( results, which = c("auto", "adjusted", "raw"), bins = 30, engine = c("ggplot2", "base") )
results |
An object of class 'ofemt_result', typically obtained from [ofemt()]. Must contain 'perm_runs' and 'params$alpha'. |
which |
Which p-values to plot. '"auto"' (the default) uses the multiplicity-adjusted values whenever the analysis was run with 'p_adjust_method != "none"', and the raw values otherwise. '"adjusted"' and '"raw"' force one or the other. The adjustment is applied within each run before the histogram is built, so the median line coincides with the 'p_adj' reported in the 'ANOVA permutation test' table. |
bins |
Number of histogram bins. Default 30. |
engine |
Which graphics system to draw with, '"ggplot2"' (the default) or '"base"'. If **ggplot2** is not installed the function falls back to '"base"' with a message. |
With 'engine = "ggplot2"', a 'ggplot' object. With 'engine = "base"', invisibly 'NULL'.
res <- ofemt(ofe_f2, y = "Yield_tn", x = "Treatment", cellsize = 9, p_adjust_method = "bonferroni") plot_pvalue_hist(res) # adjusted p-values plot_pvalue_hist(res, which = "raw") plot_pvalue_hist(res, engine = "base")res <- ofemt(ofe_f2, y = "Yield_tn", x = "Treatment", cellsize = 9, p_adjust_method = "bonferroni") plot_pvalue_hist(res) # adjusted p-values plot_pvalue_hist(res, which = "raw") plot_pvalue_hist(res, engine = "base")
Displays a concise summary of an ['ofemt_result'] object, including general information about the experimental grid, descriptive statistics, and permutation-based test results.
## S3 method for class 'ofemt_result' print(x, ...)## S3 method for class 'ofemt_result' print(x, ...)
x |
An object of class ['ofemt_result'], typically returned by [ofemt()]. The object must contain, at minimum, a '"General information"' data frame. Optionally, it may include components such as '"Means comparison"' and '"ANOVA permutation test"'. |
... |
Additional arguments passed to or from other methods (ignored). |
This print method provides a human-readable summary in the console:
**General information:** Cell size, number of selected cells, and per-cell observation counts (minimum, median, maximum).
**Spatial statistics:** Sample size ('n'), effective sample size ('ESS'), spatial correlation ('Rho'), and Moran’s *I*.
**Means comparison:** Shows the first 10 rows of the table of comparisons between treatment means.
**ANOVA permutation test:** Displays pairwise tests summarizing median *p*-values and corrected *p*-values across permutation runs.
If any of these components are missing, they are simply skipped in the printed output.
Invisibly returns the input object 'x', unchanged.
res <- ofemt(ofe_f2, y = "Yield_tn", x = "Treatment") print(res)res <- ofemt(ofe_f2, y = "Yield_tn", x = "Treatment") print(res)