Cost frontiers (#6): metafrontier(frontier = "cost") estimates group
cost frontiers with composed error v + u and a cost metafrontier that
lies on or below every group cost frontier. Cost efficiency, TGR (the
ratio of minimum costs) and TE* lie in (0, 1]. Supported for
cross-sectional, heteroscedastic and panel (BC92/BC95) group models,
deterministic (LP and QP) and stochastic metafrontiers, the sfaR
(S = -1) and frontier (ineffDecrease = FALSE) engines,
models = imports (the frontier type is read from the models, and
mixing production and cost models is an error), boot_tgr(),
Murphy-Topel standard errors and poolability_test().
simulate_metafrontier(frontier = "cost") generates cost data. Cost
DEA and cost latent class models are not supported and stop with an
error. Production results are unchanged.
Fare-Primont and Lowe TFP indexes (#10): tfp_index() computes
multiplicatively complete, transitive TFP levels and indexes (O'Donnell
2012), decomposes TFP efficiency (TFPE = OTE x OME x ROSE = OTE x OSE x
RME, and the input-oriented analogue) against each group's technology
and against the metatechnology, and reports the TFP metatechnology
ratio (TFPE_meta = TFPE_group x MTR). Output or input orientation, CRS
or VRS, contemporaneous or sequential technologies; print, summary
and autoplot methods. Results against the metatechnology match
productivity::fareprim() and productivity::lowe().
Sample-selection metafrontier (#7): metafrontier(selection = d ~ z)
estimates each group frontier as Greene's (2010) sample-selection
stochastic frontier (probit first step; second step by Gauss-Hermite
quadrature over u, half-normal or exponential), with the noise
correlated with the selection error (rho). Unselected rows enter only
the probit and may have a missing outcome; TE, TGR and TE* are
reported for the selected rows. Works with frontier = "cost" and both
metafrontier types; summary() shows the selection equations and
check_convergence() the probit. simulate_metafrontier(selection = )
generates selection data. The bootstrap, the Murphy-Topel correction
and the poolability test are not available for selection fits and stop
with an error. Group estimates match sfaR::sfaselectioncross();
efficiencies are checked against direct numerical integration because
sfaR 1.0.1 misaligns the probit index in
efficiencies.sfaselectioncross().
Spatial SFA metafrontier (#8): metafrontier(spatial = list(W = ))
estimates each group frontier as a spatial-autoregressive stochastic
frontier, y = rho W y + x'beta + v - u (Glass, Kenjegalieva and
Sickles, 2016), with W restricted to the group's own units (optionally
re-row-standardised), so there are no spillovers between technologies.
rho is estimated by profiling the full likelihood (log-Jacobian from
the eigenvalues of W); each group model reports rho, a Hessian that
includes rho, and direct, indirect and total input effects with
delta-method standard errors (LeSage and Pace, 2009). The metafrontier
envelops the long-run group frontiers x'beta / (1 - rho) (for
row-standardised W; in general the mean row sum of (I - rho W)^-1),
because with row-standardised W the intercept and rho are nearly
collinear and the direct technology x'beta is poorly determined; on
simulated data the seed-to-seed spread of the TGR relative to the true
gap halves (regression-tested). W may be a matrix, a Matrix or an
spdep listw; rows dropped for missing values are dropped from W.
Works with frontier = "cost" and both metafrontier types.
simulate_metafrontier(spatial = list(rho, k)) generates SAR data with
a block-diagonal kNN W. The bootstrap, the Murphy-Topel correction and
the poolability test are not available for spatial fits and stop with
an error.
boot_tgr(type = "nonparametric") no longer returns
observation-level intervals: ci is NULL and confint() signals
an error. Each replicate refits the model on rows resampled within
groups, so column i of tgr_boot is a random draw from a group,
not DMU i, and the previous ci[i, ] was not specific to DMU i
whatever the row order. Evaluating the original DMUs against each
bootstrap frontier instead would, for DEA, be the naive bootstrap,
which is inconsistent (Kneip, Simar and Wilson, 2008). The
parametric bootstrap keeps the design fixed and still returns
observation-level intervals.SFA with dist = "exponential" could diverge silently. The
normal-exponential log-density adds sigma_v^2 / (2 sigma_u^2) to
log Phi(-a), which is close to -a^2 / 2 for large a. When the
optimiser tried large values of sigma_v / sigma_u, the two terms
cancelled, and their rounding error, which can be positive and
astronomically large, replaced the true value. BFGS climbed this
error: on the package's test data one group frontier ended at
sigma_v of about 4e91 and sigma_u of about 5e34 with a
log-likelihood of 2e100, reported as converged, and the
second-stage stochastic metafrontier diverged in the same way even
given correct group frontiers. The failure was common: with the
default settings of simulate_metafrontier(), 75 of 80 group fits
(40 seeds, 150 observations per group) diverged. Exponential fits
made with earlier versions should be re-estimated;
check_convergence() flags the affected stages of stored objects
(see below). For a > 40 the log-density is now evaluated in an
algebraically equivalent form based on the asymptotic expansion of
the Mills ratio, which avoids the cancellation; the affected group
fit on the test data now agrees with
sfaR::sfacross() (regression-tested). Below a = 40 the
evaluation is unchanged, so fits that never reached that region
give identical results. The fix covers group frontiers with and
without inefficiency determinants, the stochastic metafrontier,
latent class models, and Murphy-Topel standard errors.
An SFA stage whose log-likelihood at the optimiser's solution is not
finite, or exceeds n * (-log(sigma_v) - log(2 * pi) / 2) (the
largest value any model with N(0, sigma_v^2) noise can attain), is
now reported with convergence code 99 and a warning, rather than
with the optimiser's code 0. check_convergence(), summary() and
print() show such stages as not converged, with the reason in the
note column. check_convergence() applies the check to the stored
fit, so objects created with earlier versions are flagged too.
Stochastic metafrontier (meta_type = "stochastic"): the TGR was
computed as exp(group_frontier - meta_frontier), which equals
exp(v* - u*) and so included the Stage 2 noise; values above 1
were common. The TGR is now E[exp(-u*) | eps*], the conditional
expectation of Huang, Huang and Liu (2014), and lies in (0, 1].
te_meta changes accordingly. Stochastic fits from earlier versions
should be re-estimated (regression-tested).
malmquist_meta(method = "sfa"): technical change was inverted. The
SFA path works with frontier(x) / y, the reciprocal of the output
distance function, but used the DEA ratios, so a frontier shift up
was reported as technical regress (TC_group, TC_meta, MPI,
MPI_group and MPI_meta were all affected). EC_meta also used
the metafrontier level where the TGR belongs; it now equals
TEC x TGC. A simulated 22% frontier shift is now recovered
(regression-tested). The DEA path was correct and is unchanged.
Rows with a missing model variable or group label are now dropped
once, before estimation, in metafrontier(), malmquist_meta()
(also for a missing period or id) and latent_class_metafrontier().
Previously the group fits dropped them but the pooled stage and the
stored group_vec and nobs did not: SFA fits had fewer TGRs than
rows, so summary() failed and print() misreported group means;
DEA fits silently recycled group efficiencies onto the wrong rows;
rows with a missing group got te_group = 0 and entered the
metafrontier; and latent class fits failed. A fit on data with
missing values now equals the fit on the complete rows
(regression-tested).
boot_tgr(type = "nonparametric"): group-level intervals mixed
bootstrap draws across groups whenever the data were not sorted by
group. Each replicate stacks the resampled rows group by group, so
column j of tgr_boot is a draw from the j-th position block,
but ci_group, and the distribution panels of plot() and
autoplot(), selected columns by the groups of the original rows.
With unsorted groups of different TGR levels, the intervals were
pulled towards the pooled mean and could exclude the group estimate.
Group intervals now use the position blocks, stored in the new
element boot_group, and do not depend on row order
(regression-tested). Results for data already sorted by group are
unchanged. Nonparametric boot_tgr objects created with earlier
versions from unsorted data should be recomputed.
boot_tgr() now bootstraps only the rows used in the fit. For an
SFA fit with rows dropped for missing values, the nonparametric
bootstrap previously stopped with a replacement-length error, and the
parametric bootstrap, with only recycling warnings, simulated each
group's response from fitted frontier values shifted out of line with
the inputs from the group's first dropped row onwards, giving wrong
intervals. Both now match the bootstrap on the complete cases
(regression-tested).
boot_tgr() refitted each replicate by looking up a column named
"group" in the data whenever the fit was given group as a vector.
Without such a column every replicate failed; with an unrelated
column of that name the replicates were silently regrouped by it. The
refits now use the fitted group labels directly, and results match
those of the same fit made with a column name (regression-tested).
boot_tgr() now stops with an informative error for metafrontiers
built from pre-fitted models, whose group models cannot be
re-estimated on bootstrap samples. Every replicate previously failed,
leaving only a warning. Fit from formula, data and group
instead.
boot_tgr(type = "parametric") failed in every replicate for fits
with engine = "sfaR" or engine = "frontier": their group models
stored no fitted frontier values, and the error scales sigma_v and
sigma_u of sfaR fits were NA because every sfaR release
reports them as log variances (Zv_(Intercept), Zu_(Intercept)),
not under the names the extractor looked for. External-engine group
models now store fitted, and sfaR fits yield sigma_v, sigma_u
and, for dist = "tnormal", mu, matching the internal estimates
(regression-tested for "hnormal", "tnormal" and "exponential").
The corrected scales also apply to sfacross objects passed to
metafrontier(models = ) or as_metafrontier_model().
boot_tgr() returns percentile intervals for the group median TGR
(ci_group_median) alongside those for the group mean
(ci_group), and print() shows both.pnorm(log.p = TRUE)), with an asymptotic closed form in the far
left tail of the conditional distribution. The previous
natural-scale ratio Phi(mu*/sigma* - sigma*) / Phi(mu*/sigma*)
could return 0, Inf, or values above one on platforms without
extended-precision long doubles (the CRAN noLD additional check)
when both Phi terms were subnormal, which occurred in degenerate
fits with sigma_u near zero. On standard platforms results are
unchanged to near machine precision; in the affected tail region the
new evaluation is also more accurate than the previous fallback to
the JLMS value.E[exp(-u)|eps]
(estimator = "bc88"), which provides consistent efficiency
estimates. The previous JLMS estimator remains available via
estimator = "jlms", and both are stored on every fit;
efficiencies(fit, estimator = ) switches without refitting.
Deterministic and stochastic TGRs are unaffected (they do not depend
on the estimator), but TE and TE* values change slightly relative to
0.2.x.malmquist_meta() output id column now carries the user-supplied
firm identifier (previously a within-group loop index).as_metafrontier_model() method for frontier::sfa() fits is
now registered for the correct class "frontier"; the previous
registration ("sfa") never dispatched.te_group/te_meta whenever the input rows were not already in
string-sorted order. Coefficients, TGRs, and group means were
unaffected. Results are now row-order invariant (regression-tested).T
(previously the firm-specific last period), matching Battese and
Coelli's (1992) unbalanced formulation and the package's own
simulator. Balanced panels are numerically unchanged.na.action, and uses the same BFGS-to-Nelder-Mead fallback cascade
as the cross-sectional path.phi >= 1) in the
output-oriented LPs that made cross-period evaluation of
super-efficient DMUs infeasible even under CRS, silently biasing
Malmquist TC and MPI means. Cross-period scores with phi < 1 now
solve correctly.as_metafrontier_model() is now idempotent (converting an already
converted object is a no-op), so the previously documented
pre-conversion workflow works.poolability_test() now derives data.name from the passed
expression instead of deparsing the stored call.autoplot(boot) (and the base plot() method) now actually draw
the dashed CI bound lines promised by the documentation.autoplot(malm, which = "mpi_trend") now plots each transition at
its end period (axis 2..T), consistent with the caption convention
"change relative to the previous period".estimator = c("bc88", "jlms") on metafrontier() and
malmquist_meta() (see Breaking changes).objective = c("lp", "qp") on metafrontier(): both identification
criteria of Battese, Rao and O'Donnell (2004) for the deterministic
metafrontier. The default LP minimises the sum of absolute
deviations (O'Donnell, Rao and Battese, 2008, Eqs. 23-25); the QP
minimises the sum of squared deviations, solved exactly via
quadprog (new in Suggests) with a constrOptim() barrier
fallback. The bootstrap respects the choice.engine = c("internal", "sfaR", "frontier", "Benchmarking") on
metafrontier(): delegate group-frontier estimation to external
packages; engine = "Benchmarking" also delegates the pooled DEA
metafrontier via XREF/YREF.check_convergence(): new exported diagnostic reporting one row per
estimation stage (group frontiers and metafrontier). print() and
summary() methods now include convergence status; every stage
warns on non-zero optimiser codes.malmquist_meta(id = ): explicit firm matching across periods, with
errors on duplicated (id, period) pairs, warnings counting dropped
observations on unbalanced panels, and a message when falling back
to positional matching. Cross-period infeasible programmes (possible
under vrs/drs/irs/fdh) are now counted and reported in a
consolidated warning, stored as n_infeasible, and shown by
print()/summary(); the SFA path announces its pointwise-maximum
approximation in a message and in the documentation.simulate_panel_metafrontier() gains an attrition
argument for generating unbalanced test panels.rts = "fdh" (free disposable hull; exact enumeration for
radial measures, binary MIP for DDF), type = "hyperbolic" (graph
efficiency; closed form under CRS, bisection otherwise; always
feasible cross-period), user-supplied direction vectors or
firm-specific direction matrices for DDF (reported via the additive
ddf_gap), and slack = TRUE two-stage slack maximisation.poolability_test() now dispatches a permutation test for DEA fits
(group labels exchangeable under the pooled-technology null), with
B and seed arguments.coef(), vcov(), and summary() expose all estimated parameters:
extraPar = TRUE returns variance parameters (and eta for BC92)
with back-transformed values; vcov(which = "group") returns full
per-group covariance matrices; group summary tables now include the
variance parameters and eta with standard errors.simulate_metafrontier() gains beta_groups (group-specific slope
coefficients; the true TGR is then computed against the pointwise
maximum over group frontiers and varies within groups),
input_means (group-specific input distributions), and
input_corr (correlated log inputs).em_converged) and warns when it did not.boot_tgr(): Fixed orientation/rts not propagating to bootstrap
replicates (always defaulted to output/CRS).boot_tgr(): Fixed hardcoded group column name; now respects
the user's original group variable..loglik_to_u_hat() now respects the dist argument
with correct JLMS formulas for half-normal, truncated-normal, and
exponential distributions.autoplot methods now use proper conditional S3 registration
(@exportS3Method ggplot2::autoplot) instead of direct export()..extract_benchmarking() now attempts to retrieve XREF/YREF from
Farrell objects and .estimate_from_models() gives a clear error
when DEA models lack the required X/y/beta.technology_gap_ratio() documentation now correctly notes that TGR
can exceed 1 under the stochastic metafrontier.simulate_panel_metafrontier(): Simulate balanced panel data with
time-varying inefficiency and technical change for Monte Carlo studies.eta parameter.boot_tgr(): Bootstrap confidence intervals for technology gap ratios.
Supports parametric (residual resampling) and nonparametric (case
resampling) approaches, with percentile and BCa interval types.ncores argument using the parallel package.print, confint, and plot for boot_tgr objects.latent_class_metafrontier(): EM algorithm for estimating latent class
stochastic frontier models within the metafrontier framework.select_n_classes(): Automatic selection of the optimal number of
latent classes via BIC.print, summary, coef, and efficiencies for
lc_metafrontier objects.autoplot.metafrontier(): Visualise TGR distributions and efficiency
decompositions using ggplot2.autoplot.malmquist_meta(): Plot Malmquist index components over time.autoplot.boot_tgr(): Visualise bootstrap distributions and
confidence intervals for TGR.as_metafrontier_model(): Convert pre-fitted model objects from
sfaR (sfacross), frontier (sfa), and Benchmarking (Farrell)
packages into metafrontier-compatible format..safe_mills()) prevents NaN in extreme tail regions for both
panel SFA and latent class models.lm.wfit() for
weighted regression instead of explicit diagonal weight matrices.dea_batch_fast() for
improved performance on large datasets.boot_tgr() now validates R >= 1 with an
informative error message.summary() methods now return invisible S3 objects for
programmatic access, with print() methods for display.@examples on all exported S3 methods and autoplot functions.@param documentation for control argument in
metafrontier() with optim options.Initial CRAN release.
metafrontier(): Main estimation function supporting SFA- and DEA-based
metafrontiers.y ~ x1 + x2 | z1 + z2).models= argument (sfaR, frontier).malmquist_meta(): Metafrontier Malmquist TFP index with three-way
decomposition (TEC x TGC x TC*) following O'Donnell, Rao, and Battese (2008).technology_gap_ratio() / tgr_summary(): Extract and summarise TGR by group.efficiencies(): Extract group, metafrontier, and TGR efficiency scores.poolability_test(): Likelihood ratio test for technology heterogeneity.simulate_metafrontier(): Data-generating process for Monte Carlo studies.print, summary, coef, vcov, logLik,
fitted, residuals, nobs, plot, confint, predict.