Theory
The two plateau models cut the CV curve into pieces. The Modified Maximum Curvature method takes a different route: it fits a single smooth curve and then asks where that curve bends the most.
The relationship between CV and plot size is described by a power function,
with and , so the CV decreases as plots grow. This curve never becomes exactly flat, which is why the method cannot simply read off a plateau. Instead it uses the curvature of the line at each point,
and takes the optimal plot size as the point of maximum curvature, the “elbow” where the steep initial drop turns into the slow tail. Solving for gives a closed form:
Because it locates an elbow rather than a plateau, the MCM systematically returns a smaller optimum than the LRP and QRP, and correspondingly a higher CV at the optimum. This is expected, not an error.
A note on the word “modified”: in the original paper it refers to
correcting
by the cost factors
and
(labour per plot and per unit area). Modern Brazilian plot-size studies
drop that cost step, and fit_mcm() follows them:
is returned in the units of x.
Two ways to estimate a and b
This is the one place in the package where the two reference articles genuinely disagree, so both are available.
method = "loglinear" is the classic
route. Taking logs linearizes the model,
so a and b come from a straight-line
regression. Meier & Lessman weight that regression by the degrees of
freedom of each point, following Federer (1955): CVs from large plots
are computed from fewer plots and are less reliable, so they should
count less. Pass df to enable that weighting.
method = "nls" (the default) fits
directly on the original scale by nonlinear least squares, seeded from
the log-log fit. This is what the modern articles do, and it is
consistent with fit_lrp() and fit_qrp(). If
nls() fails to converge, the function falls back to the
log-linear estimate with a warning rather than erroring out.
The two give visibly different answers, so choose deliberately: match whichever article you are comparing against.
References
Original method: Meier, V. D. & Lessman, K. J. (1971). Estimation of optimum field plot shape and size for testing yield in Crambe abyssinica Hochst. Crop Science, 11(5), 648-650. (After Lessman & Atkins, 1963.)
Weighting: Federer, W. T. (1955). Experimental Design. Macmillan, New York.
The implementation is validated against published results in the package tests; see Cargnelutti Filho, A., Loro, M. V., Ortiz, V. M. & Andretta, J. A. (2025). Determinação do tamanho de parcela para avaliar a massa de parte aérea de grão-de-bico. Revista Vivências, 21(43), 499-513.
Data
The bundled simulated uniformity trial
(?uniformity_trial) again. n is the number of
plots of each size, returned by calc_cv_shapes(), and
supplies the degrees of freedom for the optional weighting.
grid_mat <- function(t)
as.matrix(uniformity_trial[uniformity_trial$trial == t,
grep("^col", names(uniformity_trial))])
tab1 <- calc_cv_shapes(grid_mat("T1"))
X <- tab1$x
n <- tab1$n
CV1 <- tab1$cv
CV2 <- calc_cv_shapes(grid_mat("T2"))$cv
CV3 <- calc_cv_shapes(grid_mat("T3"))$cvBoth x and cv must be strictly positive:
the log-log fit and the starting values are undefined otherwise.
Basic use
fit <- fit_mcm(x = X, cv = CV1)
fit
#> Modified Maximum Curvature (MCM) fit
#> Method: nls
#> Weighted by df: FALSE
#> Breakpoint (Xo): 4.495
#> CV at breakpoint: 12.742
#> R2: 0.975 RMSE: 0.725m² with for trial 1 – as expected, well below the LRP’s ~9.2 m². The MCM is the most conservative of the three methods.
summary(fit)
#> Model coefficients (CV = a * X^(-b)):
#> a b
#> 23.4700048 0.4064403
#>
#> Goodness of fit:
#> Breakpoint Breakpoint_Response R2 RMSE
#> 4.4948651 12.7415655 0.9753420 0.7248675The closed form can be verified straight from the coefficients:
a <- unname(fit$coefficients["a"]); b <- unname(fit$coefficients["b"])
c(formula = ((a^2 * b^2 * (2 * b + 1)) / (b + 2))^(1 / (2 * b + 2)),
reported = unname(fit$parameters["Breakpoint"]))
#> formula reported
#> 4.494865 4.494865Note there is no AIC or BIC here. The MCM is not nested with the
plateau models and, under "loglinear", is not even fitted
on the same scale, so an information criterion would not be comparable.
Compare methods on R2 and RMSE, both reported
on the original scale.
Choosing the estimator
rbind(
nls = fit_mcm(X, CV1, method = "nls")$parameters[c("Breakpoint", "R2")],
loglinear = fit_mcm(X, CV1, method = "loglinear")$parameters[c("Breakpoint", "R2")],
loglin_df = fit_mcm(X, CV1, method = "loglinear",
df = n - 1)$parameters[c("Breakpoint", "R2")]
)
#> Breakpoint R2
#> nls 4.494865 0.9753420
#> loglinear 4.389611 0.9744473
#> loglin_df 4.525946 0.9751706The differences are modest here but real, and they grow when the CVs
of the large plots are noisy, which is exactly the case the weighting is
meant to handle. The df weighting only applies with
method = "loglinear"; df = NULL (the default)
is the unweighted fit.
Plot
The figure has no flat segment: the power curve keeps descending, and the dotted guides mark the maximum-curvature point.
plot(fit, title = "Trial 1")
plot(fit, title = "Ensaio 1", decimal_mark = ",")
There is no cond_word argument, since the annotation has
no conditional clause: a single power equation and
,
plus
and
by the breakpoint.
plot(fit, title = "Trial 1",
save = TRUE, file = "trial1_mcm.tiff", format = "tiff", dpi = 300)Several trials at once
trials <- rbind(
data.frame(x = X, cv = CV1, trial = "Trial 1"),
data.frame(x = X, cv = CV2, trial = "Trial 2"),
data.frame(x = X, cv = CV3, trial = "Trial 3")
)
res <- fit_mcm(trials, x = "x", cv = "cv", trial = "trial")
#> Using x = 'x', cv = 'cv', trial = 'trial' -> 3 trials.
res
#> MCM fits for 3 trials
#>
#> trial a b breakpoint plateau R2 RMSE method weighted
#> Trial 1 23.4700 0.4064 4.4949 12.7416 0.9753 0.7249 nls FALSE
#> Trial 2 24.9856 0.4829 4.9563 11.5337 0.8821 1.9801 nls FALSE
#> Trial 3 25.1900 0.4554 4.9050 12.2104 0.9068 1.6560 nls FALSEThe three MCM breakpoints cluster below 5 m²; their mean is the overall recommendation, and all three sit well under the LRP and QRP optima.
The degrees of freedom can be supplied as a column, applied to every trial:
trials$df <- rep(n - 1, 3)
fit_mcm(trials, x = "x", cv = "cv", df = "df", trial = "trial",
method = "loglinear")
plot(res, label_size = 3)Warnings worth heeding
-
b <= 0: the CV is not decreasing with plot size, so a maximum-curvature point is meaningless for these data. -
nls fallback: if the message says the fit fell back
to the log-linear estimate, the reported values come from
"loglinear", not"nls". Themethodfield of the object records what was actually used.
Where to go next
vignette("lrp") and vignette("qrp") cover
the plateau models, which use the same inputs and give larger optima.
vignette("paranaiba") estimates the plot size straight from
the raw grid instead of from CV values, and
vignette("replicates") turns
into a number of replications.
