The ndraws x nobs matrix of per-observation log-densities, each row
evaluated at one draw's own parameter vector. Every leave-one-out and
information-criterion quantity in this package is a function of this
matrix, and frmtmb::loo(), frmtmb::waic() and loo::psis() take it directly.
Usage
# S3 method for class 'frmtmb_draws'
log_lik(
object,
newdata = NULL,
re_formula = arg_unset(),
resp = NULL,
ndraws = NULL,
draw_ids = NULL,
pointwise = FALSE,
combine = TRUE,
add_point_estimate = FALSE,
cores = NULL,
point_estimate = NULL,
ndraws_point_estimate = 1,
...
)
# S3 method for class 'frmtmb_draws'
logLik(
object,
newdata = NULL,
re_formula = NULL,
resp = NULL,
ndraws = NULL,
draw_ids = NULL,
pointwise = FALSE,
combine = TRUE,
cores = NULL,
...
)Arguments
- object
A
frmtmb_drawsfromfrm_sample().- newdata, re_formula
Accepted in brms's own second and third positions and refused: see What is conditioned on. The refusal names the reason and the replacement.
- resp
For a multivariate model, the responses whose density to report: the default is every response, summed without
rescorand joint with it (see Multivariate models).- ndraws
Number of draws to use, evenly spaced through the matrix (default: all of them).
- draw_ids
The draws to use, by row index, instead of the evenly spaced subsample
ndrawstakes. Give one or the other.- pointwise, combine, add_point_estimate, cores
brms's slots, in brms's positions.
pointwise = TRUE(brms's function of one observation) andadd_point_estimate = TRUEare refused by name;combine = FALSEis accepted on a univariate model, on one response, and on aset_rescor(TRUE)model, where there is nothing to combine;coresis accepted and unused, because the matrix is built in this process.- point_estimate, ndraws_point_estimate
brms's arguments: collapse the draws to their
"mean"or"median"first and use that one parameter vector, repeatedndraws_point_estimatetimes.- ...
Refused: an argument the method does not have is an error naming it, rather than silently changing nothing.
Value
A numeric matrix with one row per draw and one column per observation (the rows the model was fitted on).
Details
logLik() of draws is log_lik(), as brms's logLik() of a
fit is: the draws by observations matrix, not stats' logLik
object, which describes one parameter vector.
What is conditioned on
The density is CONDITIONAL on the draw's own group-level values:
frm_sample() samples b alongside everything else, so each row of
the draws matrix is a complete parameter vector and no integration is
left to do. This is exactly brms's convention, where the Stan model
also samples the group-level parameters. The consequence is worth
stating plainly: loo() on such a matrix is leave-one-OBSERVATION-out
with the groups held fixed, not leave-one-group-out, and for a model
with few observations per group the two differ.
Addition terms enter exactly as they enter the fitted objective,
because they enter through the same code: cens() replaces a row's
density by the matching CDF difference, trunc() divides by the
window mass, weights() multiplies the row's contribution, and
trials() is the family's own argument. brms composes them in the
same order (log_lik_censor(), log_lik_truncate(),
log_lik_weight()).
Autocorrelation, and what a row conditions on
brms's default cov = FALSE form of ar(), ma() and arma()
(frmtmb::frmtmb-autocor) has a column per observation, and it is
the column brms's own log_lik() returns. A row's mu is the
one-step conditional mean: the linear predictor plus a regression on
the OBSERVED earlier residuals of that row's group. A group starts
with no residuals behind it, so lag i first reaches the row at
within-group position i + 1: the FIRST row of each group takes the
unshifted mean, a row at position k carries only the lags up to
k - 1, and from position max(p, q) + 1 on a row carries all of
them. Only the first row is unshifted, at every order. So the density
of row t is conditional on y at the rows before it, and the
columns multiply to the whole likelihood exactly as they do for an
independent model.
Say what that makes the leave-one-out quantity. PSIS-LOO over these
columns leaves out one CONDITIONAL density, the density of y_t
given the observed past, and not the series: the retained columns
still read y_t through their own lagged residuals, so it is not a
forecast of an unseen point and it is not leave-one-group-out
either. It is brms's number, computed the way brms computes it, and
it answers "how well does the one-step-ahead density fit this row"
rather than "how well would the model predict a held-out series".
For the latter, refit without the held-out group.
Residual covariance terms
A residual covariance term (cov = TRUE, cosy(), unstr(),
fcor(), and frmtmb.spatial's sar()) makes the residuals of a
group one joint draw, so a row has no density of its own. A column
is then brms's leave-one-out conditional density: the density of the
row given the other rows of its group at the draw's parameters,
normal with precision \(Q_{ii}\) around
\(y_i - (Qe)_i / Q_{ii}\), or for student() the conditional t,
after the loo package's vignette on non-factorized models
(frmtmb::frmtmb-autocor, "The pointwise log-density"). The values
are brms's log_lik(), and they do NOT sum to the log-likelihood:
each conditions on the rest of its group. The cost per draw is one
small inverse per pattern of time points for the ar() family, and
the nonzeros of the weights matrix for sar(); brms inverts a dense
covariance over each group per draw.
A group that skips a time point inside its range differs from brms
under ar(), ma() and arma(): frmtmb reads the lag from the time
levels and brms from the position within the group, so the two fit
different models there and their columns differ. In a measured case
with one such group (22 rows, 3 draws), the largest difference over
the matrix was 17 percent of brms's largest value, and on the rows of
the groups without a gap the two agreed to 8e-16.
cosy() has one correlation for every pair and brms's unstr()
reads the time levels too, so those agree with gaps.
Multivariate models
With set_rescor(TRUE) a column is the joint density of the row's
response VECTOR, so the matrix keeps one column per observation.
resp naming several responses gives the joint density of those
responses, and naming one gives that response's own marginal density
(the normal, or for student() the t with the shared nu), as
brms's log_lik(resp =) computes them. combine = FALSE has nothing
to combine there and returns the same matrix, as in brms.
Without rescor, the responses are independent given the predictors
and the default sums their log-densities per row; pass resp to get
one response's contribution alone.
Likelihoods with no per-observation column
A model whose smallest independent unit is a group has no
per-observation column to leave out, and this refuses rather than
inventing one: a frmtmb.latent::hmm() sequence, and a group-level
mixture (mixture(groups = )).
An frmtmb.latent::lca() subject is one row, so its column is well defined and is
not refused. In-model imputation (mi(), me()) is refused for the
same kind of reason: a latent value is a parameter, not an
observation. Use AIC() on the maximum-likelihood fits or
frmtmb::frm_bootstrap() for those.
A family that declares how its likelihood factorizes
(frmtmb::frmtmb_structure(loglik_group = ) or (loglik_row = )) is
not in that position and is not refused. Its columns are the pieces
it declares, at the COARSEST granularity it gives: a family that
groups its likelihood is saying that its rows are not independently
droppable, which is exactly the question a leave-one-out column asks.
The matrix then carries attr(x, "unit") naming what a column is.
loo::loo.matrix() never sees that attribute and its printout says
only "Computed from N by K log-likelihood matrix", which reads
exactly like a per-observation one, so loo(), waic() and
psis() emit a message naming the unit and the column count when
they are handed such a matrix. The elpd is then leave-one-UNIT-out,
and the number itself carries no mark of that.
Examples
# \donttest{
if (requireNamespace("tmbstan", quietly = TRUE) &&
requireNamespace("rstan", quietly = TRUE) &&
!frmtmb.sample:::tmbstan_build_broken()) {
set.seed(9)
dd <- data.frame(x = rnorm(60), g = factor(rep(1:6, 10)))
dd$y <- rnorm(60, 1 + 0.5 * dd$x + rnorm(6, 0, 0.5)[dd$g], 1)
fit <- frm(bf(y ~ x + (1 | g)), family = gaussian(), data = dd)
ds <- frm_sample(fit, chains = 1, iter = 500, refresh = 0)
ll <- log_lik(ds)
dim(ll)
# the column means are the per-observation expected log-densities
head(colMeans(ll))
}
#> frm_sample(): default priors (brms 2.23 defaults; prior = "flat" puts brms's (flat) in every slot)
#> Intercept student_t(3, 0.8, 2.5)
#> sigma student_t(3, 0, 2.5) [natural scale]
#> sd student_t(3, 0, 2.5) [natural sd scale]
#> b (flat), as brms leaves slopes
#> Warning: There were 4 divergent transitions after warmup. See
#> https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
#> to find out why this is a problem and how to eliminate them.
#> Warning: Examine the pairs() plot to diagnose sampling problems
#> Warning: Bulk Effective Samples Size (ESS) is too low, indicating posterior means and medians may be unreliable.
#> Running the chains for more iterations may help. See
#> https://mc-stan.org/misc/warnings.html#bulk-ess
#> Warning: Tail Effective Samples Size (ESS) is too low, indicating posterior variances and tail quantiles may be unreliable.
#> Running the chains for more iterations may help. See
#> https://mc-stan.org/misc/warnings.html#tail-ess
#> [1] -0.8735093 -2.1721647 -1.2481160 -2.5636748 -0.8763703 -3.1446238
# }