Git Product home page Git Product logo

nicholasjclark / mvgam Goto Github PK

View Code? Open in Web Editor NEW
89.0 7.0 11.0 582.79 MB

{mvgam} R 📦 to fit Dynamic Bayesian Generalized Additive Models for time series analysis and forecasting

Home Page: https://nicholasjclark.github.io/mvgam/

License: Other

R 98.48% C++ 0.44% TeX 1.08%
bayesian-statistics ecological-modelling forecasting generalised-additive-models multivariate-timeseries rstats timeseries dynamic-factor-models gaussian-process jags

mvgam's Introduction

mvgam R package logoStan Logo

mvgam

R-CMD-check Test status Coverage status CRAN Version CRAN Downloads

The goal of mvgam is to fit Bayesian Dynamic Generalized Additive Models to time series data. The motivation for the package is described in Clark & Wells 2022 (published in Methods in Ecology and Evolution), with additional inspiration on the use of Bayesian probabilistic modelling coming from Michael Betancourt, Michael Dietze and Emily Fox, among many others.

Resources

A series of vignettes cover data formatting, forecasting and several extended case studies of DGAMs. A number of other examples have also been compiled:

Installation

Install the stable version from CRAN using: install.packages('mvgam'), or install the development version from GitHub using: devtools::install_github("nicholasjclark/mvgam"). Note that to condition models on observed data, either JAGS (along with packages rjags and runjags) or Stan must be installed (along with either rstan and/or cmdstanr). Please refer to installation links for JAGS here, for Stan with rstan here, or for Stan with cmdstandr here. You will need a fairly recent version of Stan to ensure all syntax is recognized. If you see warnings such as variable "array" does not exist, this is usually a sign that you need to update Stan.

We highly recommend you use Cmdstan through the cmdstanr interface. This is because Cmdstan is easier to install, is more up to date with new features, and uses less memory than rstan. See this documentation from the Cmdstan team for more information.

Citing mvgam and related software

When using any software please make sure to appropriately acknowledge the hard work that developers and maintainers put into making these packages available. Citations are currently the best way to formally acknowledge this work, so we highly encourage you to cite any packages that you rely on for your research.

When using mvgam, please cite the following:

  • Clark, N.J. and Wells, K. (2022). Dynamic Generalized Additive Models (DGAMs) for forecasting discrete ecological time series. Methods in Ecology and Evolution. DOI: https://doi.org/10.1111/2041-210X.13974

As mvgam acts as an interface to Stan and JAGS, please additionally cite whichever software you use for parameter estimation:

  • Carpenter B., Gelman A., Hoffman M. D., Lee D., Goodrich B., Betancourt M., Brubaker M., Guo J., Li P., and Riddell A. (2017). Stan: A probabilistic programming language. Journal of Statistical Software. 76(1). 10.18637/jss.v076.i01
  • Plummer, M. (2013). JAGS: A program for analysis of Bayesian graphical models using Gibbs sampling. Proceedings of the 3rd International Workshop on Distributed Statistical Computing. 124(125.10).

mvgam relies on several other R packages and, of course, on R itself. To find out how to cite R and its packages, use the citation function. There are some features of mvgam which specifically rely on certain packages. The most important of these is the generation of data necessary to estimate smoothing splines, which entirely rely on mgcv. The rstan and cmdstanr packages together with Rcpp makes Stan conveniently accessible in R, while the rjags and runjags packages together with the coda package make JAGS accessible in R. If you use some of these features, please also consider citing the related packages.

Cheatsheet

mvgam usage cheatsheet

Introducing mvgam for fitting Generalized Additive Models to time series

We can explore the model’s primary functions using a dataset that is available with all R installations. Load the lynx data and plot the series as well as its autocorrelation function

data(lynx)
lynx_full <- data.frame(year = 1821:1934, 
                        population = as.numeric(lynx))
plot(lynx_full$population, type = 'l', ylab = 'Lynx trappings',
     xlab = 'Time', bty = 'l', lwd = 2)
box(bty = 'l', lwd  = 2)

Visualizing the lynx time series in R

acf(lynx_full$population, main = '', bty = 'l', lwd = 2,
    ci.col = 'darkred')
box(bty = 'l', lwd  = 2)

Visualizing the lynx time series in R

Along with serial autocorrelation, there is a clear ~19-year cyclic pattern. Create a season term that can be used to model this effect and give a better representation of the data generating process than we would likely get with a linear model

plot(stl(ts(lynx_full$population, frequency = 19), s.window = 'periodic'),
     lwd = 2, col.range = 'darkred')

Visualizing and decomposing the lynx time series in R

lynx_full$season <- (lynx_full$year%%19) + 1

For mvgam models, we need an indicator of the series name as a factor (if the column series is missing, this will be added automatically by assuming that all observations are from a single time series). A time column is needed to index time

lynx_full$time <- 1:NROW(lynx_full)
lynx_full$series <- factor('series1')

Split the data into training (first 50 years) and testing (next 10 years of data) to evaluate forecasts

lynx_train = lynx_full[1:50, ]
lynx_test = lynx_full[51:60, ]

Inspect the series in a bit more detail using mvgam’s plotting utility

plot_mvgam_series(data = lynx_train, y = 'population')

Plotting time series features with the mvgam R package

Formulate an mvgam model; this model fits a GAM in which a cyclic smooth function for season is estimated jointly with a full time series model for the temporal process (in this case an AR3 process). We assume the outcome follows a Poisson distribution and will condition the model in Stan using MCMC sampling with the Cmdstan interface:

lynx_mvgam <- mvgam(population ~ s(season, bs = 'cc', k = 12),
                    knots = list(season = c(0.5, 19.5)),
                    data = lynx_train,
                    newdata = lynx_test,
                    family = poisson(),
                    trend_model = AR(p = 3),
                    backend = 'cmdstanr')

Have a look at this model’s summary to see what is being estimated. Note that no pathological behaviours have been detected and we achieve good effective sample sizes / mixing for all parameters

summary(lynx_mvgam)
#> GAM formula:
#> population ~ s(season, bs = "cc", k = 12)
#> 
#> Family:
#> poisson
#> 
#> Link function:
#> log
#> 
#> Trend model:
#> AR(p = 3)
#> 
#> N series:
#> 1 
#> 
#> N timepoints:
#> 60 
#> 
#> Status:
#> Fitted using Stan 
#> 4 chains, each with iter = 1000; warmup = 500; thin = 1 
#> Total post-warmup draws = 2000
#> 
#> 
#> GAM coefficient (beta) estimates:
#>               2.5%    50% 97.5% Rhat n_eff
#> (Intercept)   6.20  6.600  6.90    1  1177
#> s(season).1  -0.55 -0.052  0.47    1  1078
#> s(season).2   0.54  1.200  1.90    1   943
#> s(season).3   1.10  1.900  2.60    1   920
#> s(season).4  -0.11  0.540  1.20    1  1126
#> s(season).5  -1.30 -0.580  0.14    1   757
#> s(season).6  -1.10 -0.370  0.44    1  1156
#> s(season).7  -0.24  0.670  1.50    1  1167
#> s(season).8   0.12  1.100  1.90    1   630
#> s(season).9  -0.54  0.050  0.66    1   977
#> s(season).10 -1.40 -0.940 -0.53    1  1130
#> 
#> Approximate significance of GAM smooths:
#>            edf Ref.df Chi.sq p-value   
#> s(season) 9.98     10  38892  0.0012 **
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Latent trend AR parameter estimates:
#>           2.5%  50% 97.5% Rhat n_eff
#> ar1[1]   0.610 0.86  0.99    1  1330
#> ar2[1]   0.055 0.45  0.83    1   496
#> ar3[1]   0.068 0.45  0.95    1   413
#> sigma[1] 0.370 0.46  0.58    1   907
#> 
#> Stan MCMC diagnostics:
#> n_eff / iter looks reasonable for all parameters
#> Rhat looks reasonable for all parameters
#> 0 of 2000 iterations ended with a divergence (0%)
#> 0 of 2000 iterations saturated the maximum tree depth of 12 (0%)
#> E-FMI indicated no pathological behavior
#> 
#> Samples were drawn using NUTS(diag_e) at Wed Jun 12 9:37:37 AM 2024.
#> For each parameter, n_eff is a crude measure of effective sample size,
#> and Rhat is the potential scale reduction factor on split MCMC chains
#> (at convergence, Rhat = 1)

As with any MCMC software, we can inspect traceplots. Here for the GAM smoothing parameters, using mvgam’s reliance on the excellent bayesplot library:

mcmc_plot(lynx_mvgam, variable = 'rho', regex = TRUE, type = 'trace')
#> No divergences to plot.

Smoothing parameter posterior distributions estimated with Stan in mvgam

and for the latent trend parameters

mcmc_plot(lynx_mvgam, variable = 'trend_params', regex = TRUE, type = 'trace')
#> No divergences to plot.

Dynamic temporal autocorrelation parameters estimated with Stan in mvgam

Use posterior predictive checks, which capitalize on the extensive functionality of the bayesplot package, to see if the model can simulate data that looks realistic and unbiased. First, examine histograms for posterior retrodictions (yhat) and compare to the histogram of the observations (y)

pp_check(lynx_mvgam, type = "hist", ndraws = 5)
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Posterior predictive checks for discrete time series in R

Next examine simulated empirical Cumulative Distribution Functions (CDF) for posterior predictions

pp_check(lynx_mvgam, type = "ecdf_overlay", ndraws = 25)

Posterior predictive checks for discrete time series in R

Rootograms are popular graphical tools for checking a discrete model’s ability to capture dispersion properties of the response variable. Posterior predictive hanging rootograms can be displayed using the ppc() function. In the plot below, we bin the unique observed values into 25 bins to prevent overplotting and help with interpretation. This plot compares the frequencies of observed vs predicted values for each bin. For example, if the gray bars (representing observed frequencies) tend to stretch below zero, this suggests the model’s simulations predict the values in that particular bin less frequently than they are observed in the data. A well-fitting model that can generate realistic simulated data will provide a rootogram in which the lower boundaries of the grey bars are generally near zero. For this plot we use the S3 function ppc.mvgam(), which is not as versatile as pp_check() but allows us to bin rootograms to avoid overplotting

ppc(lynx_mvgam, type = "rootogram", n_bins = 25)

Posterior predictive rootograms for discrete time series in R

All plots indicate the model is well calibrated against the training data. Inspect the estimated cyclic smooth, which is shown as a ribbon plot of posterior empirical quantiles. We can also overlay posterior quantiles of partial residuals (shown in red), which represent the leftover variation that the model expects would remain if this smooth term was dropped but all other parameters remained unchanged. A strong pattern in the partial residuals suggests there would be strong patterns left unexplained in the model if we were to drop this term, giving us further confidence that this function is important in the model

plot(lynx_mvgam, type = 'smooths', residuals = TRUE)

Plotting GAM smooth functions in mvgam and R

First derivatives of smooths can be plotted to inspect how the slope of the function changes. To plot these we use the more flexible plot_mvgam_smooth() function

plot_mvgam_smooth(lynx_mvgam, series = 1, 
                  smooth = 'season', 
                  derivatives = TRUE)

Plotting GAM smooth functions in mvgam and R

As for many types of regression models, it is often more useful to plot model effects on the outcome scale. mvgam has support for the wonderful marginaleffects package, allowing a wide variety of posterior contrasts, averages, conditional and marginal predictions to be calculated and plotted. Below is the conditional effect of season plotted on the outcome scale, for example:

require(ggplot2)
plot_predictions(lynx_mvgam, condition = 'season', points = 0.5) +
  theme_classic()

Using marginaleffects and mvgam to plot GAM smooth functions in R

We can also view the mvgam’s posterior predictions for the entire series (testing and training)

plot(lynx_mvgam, type = 'forecast', newdata = lynx_test)
#> Out of sample DRPS:
#> 1879.7308065

Plotting forecast distributions using mvgam in R

And the estimated latent trend component, again using the more flexible plot_mvgam_...() option to show first derivatives of the estimated trend

plot_mvgam_trend(lynx_mvgam, newdata = lynx_test, derivatives = TRUE)

Plotting dynamic trend components using mvgam in R

A key aspect of ecological forecasting is to understand how different components of a model contribute to forecast uncertainty. We can estimate relative contributions to forecast uncertainty for the GAM component and the latent trend component using mvgam

plot_mvgam_uncertainty(lynx_mvgam, newdata = lynx_test, legend_position = 'none')
text(1, 0.2, cex = 1.5, label="GAM component", 
     pos = 4, col="white", family = 'serif')
text(1, 0.8, cex = 1.5, label="Trend component", 
     pos = 4, col="#7C0000", family = 'serif')

Decomposing uncertainty contributions to forecasts in mvgam in R

Both components contribute to forecast uncertainty. Diagnostics of the model can also be performed using mvgam. Have a look at the model’s residuals, which are posterior empirical quantiles of Dunn-Smyth randomised quantile residuals so should follow approximate normality. We are primarily looking for a lack of autocorrelation, which would suggest our AR3 model is appropriate for the latent trend

plot(lynx_mvgam, type = 'residuals')

Plotting Dunn-Smyth residuals for time series analysis in mvgam and R

Extended observation families

mvgam was originally designed to analyse and forecast non-negative integer-valued data. These data are traditionally challenging to analyse with existing time-series analysis packages. But further development of mvgam has resulted in support for a growing number of observation families. Currently, the package can handle data for the following:

  • gaussian() for real-valued data
  • student_t() for heavy-tailed real-valued data
  • lognormal() for non-negative real-valued data
  • Gamma() for non-negative real-valued data
  • betar() for proportional data on (0,1)
  • bernoulli() for binary data
  • poisson() for count data
  • nb() for overdispersed count data
  • binomial() for count data with known number of trials
  • beta_binomial() for overdispersed count data with known number of trials
  • nmix() for count data with imperfect detection (unknown number of trials)
  • tweedie() for overdispersed count data

Note that only poisson(), nb(), and tweedie() are available if using JAGS. All families, apart from tweedie(), are supported if using Stan. See ??mvgam_families for more information. Below is a simple example for simulating and modelling proportional data with Beta observations over a set of seasonal series with independent Gaussian Process dynamic trends:

set.seed(100)
data <- sim_mvgam(family = betar(),
                  T = 80,
                  trend_model = 'GP',
                  prop_trend = 0.5, 
                  seasonality = 'shared')
plot_mvgam_series(data = data$data_train, series = 'all')

mod <- mvgam(y ~ s(season, bs = 'cc', k = 7) +
               s(season, by = series, m = 1, k = 5),
             trend_model = 'GP',
             data = data$data_train,
             newdata = data$data_test,
             family = betar())

Inspect the summary to see that the posterior now also contains estimates for the Beta precision parameters $\phi$. We can suppress a summary of the $\beta$ coefficients, which is useful when there are many spline coefficients to report:

summary(mod, include_betas = FALSE)
#> GAM formula:
#> y ~ s(season, bs = "cc", k = 7) + s(season, by = series, m = 1, 
#>     k = 5)
#> 
#> Family:
#> beta
#> 
#> Link function:
#> logit
#> 
#> Trend model:
#> GP
#> 
#> N series:
#> 3 
#> 
#> N timepoints:
#> 80 
#> 
#> Status:
#> Fitted using Stan 
#> 4 chains, each with iter = 1000; warmup = 500; thin = 1 
#> Total post-warmup draws = 2000
#> 
#> 
#> Observation precision parameter estimates:
#>        2.5% 50% 97.5% Rhat n_eff
#> phi[1]  5.6 8.3    12    1  1528
#> phi[2]  5.8 8.8    13    1  1347
#> phi[3]  5.7 8.4    12    1  1430
#> 
#> GAM coefficient (beta) estimates:
#>              2.5%  50% 97.5% Rhat n_eff
#> (Intercept) -0.15 0.19  0.45    1  1001
#> 
#> Approximate significance of GAM smooths:
#>                            edf Ref.df Chi.sq p-value    
#> s(season)                4.297      5  39.37  <2e-16 ***
#> s(season):seriesseries_1 0.936      4   0.50    0.98    
#> s(season):seriesseries_2 0.935      4   0.39    0.99    
#> s(season):seriesseries_3 0.645      4   2.81    0.86    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Latent trend marginal deviation (alpha) and length scale (rho) estimates:
#>              2.5%  50% 97.5% Rhat n_eff
#> alpha_gp[1] 0.078 0.42  0.90 1.00   877
#> alpha_gp[2] 0.380 0.73  1.30 1.00  1130
#> alpha_gp[3] 0.170 0.47  0.97 1.00   886
#> rho_gp[1]   1.200 3.70 14.00 1.01   797
#> rho_gp[2]   1.700 7.30 34.00 1.02   463
#> rho_gp[3]   1.300 4.70 20.00 1.00   722
#> 
#> Stan MCMC diagnostics:
#> n_eff / iter looks reasonable for all parameters
#> Rhat looks reasonable for all parameters
#> 4 of 2000 iterations ended with a divergence (0.2%)
#>  *Try running with larger adapt_delta to remove the divergences
#> 0 of 2000 iterations saturated the maximum tree depth of 12 (0%)
#> E-FMI indicated no pathological behavior
#> 
#> Samples were drawn using NUTS(diag_e) at Wed Jun 12 9:38:57 AM 2024.
#> For each parameter, n_eff is a crude measure of effective sample size,
#> and Rhat is the potential scale reduction factor on split MCMC chains
#> (at convergence, Rhat = 1)

Plot the hindcast and forecast distributions for each series

layout(matrix(1:4, nrow = 2, byrow = TRUE))
for(i in 1:3){
  plot(mod, type = 'forecast', series = i)
}

There are many more extended uses of mvgam, including the ability to fit hierarchical GAMs that include dynamic coefficient models, dynamic factor and Vector Autoregressive processes. See the package documentation for more details. The package can also be used to generate all necessary data structures, initial value functions and modelling code necessary to fit DGAMs using Stan or JAGS. This can be helpful if users wish to make changes to the model to better suit their own bespoke research / analysis goals. The following resources can be helpful to troubleshoot:

License

This project is licensed under an MIT open source license

Interested in contributing?

I’m actively seeking PhD students and other researchers to work in the areas of ecological forecasting, multivariate model evaluation and development of mvgam. Please reach out if you are interested (n.clark’at’uq.edu.au)

mvgam's People

Contributors

henrykironde avatar nicholasjclark avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

mvgam's Issues

Functional curve forecasting

Hyndman has some nice examples of how functional curves can be forecasted by:

  1. Constructing basis functions to represent the curve
  2. Fitting independent ARIMA models to estimate how the basis coefficients change over time

See details at: https://robjhyndman.com/seminars/oadr/

We should be able to adopt something similar by the following:

  1. Construct thin plate basis functions for the curve
  2. Use regularisation to penalise the initial coefficient values
  3. Set up dynamic factors to model how the basis coefficients change over time, inducing correlations among the coefficients
  4. Alternatively, could allow each coefficient to have its own trend model but link them with a hierarchical Gaussian Process to force the coefficients to change smoothly over time
  5. Produce forecasts for basis coefficients that respect their induced correlations

This could also be used for spatiotemporal modelling (allowing multivariate spatial basis functions to change over time and be forecast).

Might not be a feature of mvgam (maybe a bit too specialised?), rather could form the basis for a second package that depends on mvgam for much of the setup

Allow `se()` special for `gaussian` and `student` families to incorporate sampling error

{brms} specials are highly useful for many real-world contexts. As a start to incorporating these, the se() might be the most practically useful. {mvgam} currently has no straightforward way to capture this uncertainty (apart from creating pseudo-series that are then forced to share the same process models and observation params, but this won't necessarily let the uncertainty change with time as it should when incorporating known sampling variability)

Allow hierarchical or no pooling of trend components

Right now autoregressive and trend variance parameters are 'hierarchical', but the hyperparameters are are fixed (i.e. ar1 ~ normal(0, 0.5)). It would be useful to allow options to learn these hierarchically, i.e.

ar1 ~ normal(ar1mu, ar1sigma);
ar1mu ~ normal(0.5, 0.1);
ar1sigma ~ exponential(5);

This is probably more relevant for variance parameters as different series may have wildly different dynamics

Multiplicative effects

Many time series show multiplicative effects, such as seasonality that changes as the underlying level changes. These are easily captured in GAMs using a tensor product (i.e. te(time, season)), but this strategy suffers from poor extrapolations of the marginal time basis. For identity links (gaussian, lognormal, student-t), it may be useful to allow effects to be multiplied by the trend estimate

Work on PR for `gratia`

Would likely require methods for:

  • vcov() with same arguments as vcov.gam()
  • update get_vcov() to return the Bayesian covariance matrix; shouldn't impact marginaleffects functionality
  • predict(object, type = 'terms')
  • predict(object, type = 'lpmatrix')
  • residuals(object, type = 'working')
  • weights(object)
  • smoothCon() and PredictMat() functions for gp() and dynamic() terms

... actually will be easier to

  1. use expand.grid() to get pred values for terms of interest. Accept an argument that allows multidimensional effects to be drawn either as heatmaps or in plot_predictions() style with faceting
  2. fix all other vars to representative row idxs by replicating a single entry in original data (only filling in the vars of interest). This will work with df or list types and the values for non-focal vars won't matter as we will use 'terms' predictions
  3. use predict(type = 'terms') to get partial contribution from effect of interest
  4. plot by sending to gratia::draw_smooth_estimates() and with modified multidimensional plot functions, i.e. plot_smooth.bivariate_smooth_facet() perhaps

Add metrics of stability following Ives et al examples

ar1 = 0.5
ar2 = -0.1
ar3 = -0.1
series <- arima.sim(model = list(ar = c(ar1, ar2, ar3)),
                    n = 100, sd = 1)

# Characteristic return rate (Ives et al 2003; 2010) is an 
# indicator of how quickly a dynamic process returns to 
# its stationary distribution following a perturbance; values
# approaching 1 (or greater than 1) indicate a longer time needed
# before the process returns to stability. To calculate this, all we 
# need is the AR coefficients in matrix form
B <- matrix(c(ar1, 1, 0,
              ar2, 0, 1,
              ar3, 0, 0),
           nrow = 3)

# Calculate eigenvalues of the AR coefficient matrix
evalues <- eigen(B)$values

# Complex eigenvalues indicate quasi-cyclic dynamics in
# which the process shows a characteristic periodicity
if(is.complex(evalues)){
  return_rate <- Re(evalues[1])
  period <- 2 * (pi / Im(evalues[1]))
} else {
  return_rate <- evalues[1]
  period <- NA
}
return_rate
period

plot(series)

Allow linear predictors for the latent states

Possibly the best way to do this would be:

  1. Set up the GAM component for the observation model as usual, using dynamic factors for the trends (number of factors specified by the user when supplying the loading matrix)
  2. Set up a second model using dynamic factors with simple observation model that can be easily stripped from the code
  3. Replace names for trend model penalty matrices and Xp so they can be added to the data in the original observation model
  4. Replace b with b_trend in model code so they can be separated; same for lambda etc....
  5. Pull the trend GAM component Stan code into the observation model Stan code and make final changes to the trend models
  6. Return the trend mgcv model for easier prediction
  7. Think about how to visualise smooths etc...

Add topic tags

I suggest adding the topics time-series-analysis, vector-autoregression in the About section. GitHub topic search is brittle, so that the results of topic searches for vector-autoregression and vectorautoregression (a topic already listed) are distinct.

Add hierarchical latent GP trends

Should just require propagating a "global" GP trend that is added to each linear predictor:

//Stan GP functions generated by package mvgam
functions {
real lambda_gp(real L, int m) {
real lam;
lam = ((mpi())/(2L))^2;
return lam;
}
vector phi_SE(real L, int m, vector x) {
vector[rows(x)] fi;
fi = 1/sqrt(L) * sin(mpi()/(2L) * (x+L));
return fi;
}

real spd_SE(real alpha, real rho, real w) {
real S;
S = (alpha^2) * sqrt(2pi()) * rho * exp(-0.5(rho^2)*(w^2));
return S;
}
}

//Stan model code generated by package mvgam
data {
int<lower=0> total_obs; // total number of observations
int<lower=0> n; // number of timepoints per series
int<lower=0> n_sp; // number of smoothing parameters
int<lower=0> n_series; // number of series
int<lower=0> num_basis; // total number of basis coefficients
vector[num_basis] zero; // prior locations for basis coefficients
real p_taus[1]; // prior precisions for parametric coefficients
real p_coefs[1]; // prior locations for parametric coefficients
matrix[num_basis, total_obs] X; // transposed mgcv GAM design matrix
int<lower=0> ytimes[n, n_series]; // time-ordered matrix (which col in X belongs to each [time, series] observation?)
matrix[8,8] S1; // mgcv smooth penalty matrix S1
int<lower=0, upper=1> y_observed[n, n_series]; // indices of missing vs observed
int<lower=-1> y[n, n_series]; // time-ordered observations, with -1 indicating missing
}

transformed data {
vector<lower=1>[n] times;
real<lower=0> boundary;
int<lower=1> num_gp_basis;
num_gp_basis = max(40, n);
matrix[n, num_gp_basis] gp_phi;

for (t in 1:n){
times[t] = t;
}

boundary = (5.0/4) * max(times);
for (m in 1:num_gp_basis){
gp_phi[,m] = phi_SE(boundary, m, times);
}
}

parameters {
// raw basis coefficients
row_vector[num_basis] b_raw;

// gp parameters
vector<lower=0>[n_series] alpha_gp;
vector<lower=0>[n_series] rho_gp;
real<lower=0> alpha_gp_global;
real<lower=0> rho_gp_global;

// gp coefficient weights
matrix[num_gp_basis, n_series] b_gp;
vector[num_gp_basis] b_gp_global;

// smoothing parameters
vector<lower=0>[n_sp] lambda;
}

transformed parameters {
// GAM contribution to expectations (log scale)
vector[total_obs] eta;

// expectations
matrix[n, n_series] mus;

// gp spectral densities
matrix[n, n_series] trend;
vector[n] trend_global;
matrix[num_gp_basis, n_series] diag_SPD;
matrix[num_gp_basis, n_series] SPD_beta;
vector[num_gp_basis] diag_SPD_global;
vector[num_gp_basis] SPD_beta_global;

// basis coefficients
row_vector[num_basis] b;

b[1:num_basis] = b_raw[1:num_basis];
eta = to_vector(b * X);

// gp trend estimates
for (m in 1:num_gp_basis){
for (s in 1:n_series){
diag_SPD[m, s] = sqrt(spd_SE(alpha_gp[s], rho_gp[s], sqrt(lambda_gp(boundary, m))));
}
}
SPD_beta = diag_SPD .* b_gp;
trend = gp_phi * SPD_beta;

// global gp trend
for (m in 1:num_gp_basis){
diag_SPD_global[m] = sqrt(spd_SE(alpha_gp_global, rho_gp_global, sqrt(lambda_gp(boundary, m))));
}
SPD_beta_global = diag_SPD_global .* b_gp_global;
trend_global = gp_phi * SPD_beta_global;

for(s in 1:n_series){
mus[1:n, s] = eta[ytimes[1:n, s]] + trend[1:n, s] + trend_global;
}
}

model {
// parametric effect priors (regularised for identifiability)
for (i in 1:1) {
b_raw[i] ~ normal(p_coefs[i], 1 / p_taus[i]);
}

// prior for s(season)...
b_raw[2:9] ~ multi_normal_prec(zero[2:9],S1[1:8,1:8] * lambda[1]);

// priors for smoothing parameters
lambda ~ exponential(0.05);

// priors for gp parameters
for (s in 1:n_series){
b_gp[1:num_gp_basis, s] ~ normal(0, 1);
}
b_gp_global ~ normal(0, 1);
alpha_gp ~ normal(0, 0.5);
alpha_gp_global ~ normal(0, 0.5);
rho_gp ~ inv_gamma(4, 24);
rho_gp_global ~ inv_gamma(4, 24);

// likelihood functions
for (i in 1:n) {
for (s in 1:n_series) {
if (y_observed[i, s])
y[i, s] ~ poisson_log(mus[i, s]);
}
}
}

generated quantities {
vector[n_sp] rho;
array[n, n_series] int ypred;
rho = log(lambda);

// posterior predictions
for(s in 1:n_series){
ypred[1:n, s] = poisson_log_rng(mus[1:n, s]);
}
}

Relax need for a `trend_model` in State Space models

There are many contexts in which we'd like to model some underlying process that doesn't necessarily need autoregressive dynamics. Shared latent states for one, or accommodating sampling uncertainty for another. Or perhaps we want to use a GP of time without any extra AR parameters

Include options for term-specific predictions

In mgcv, the predict function allows certain terms to be omitted. Should be able to include this in mvgam by finding the coefficients associated with excluded terms and setting their values to zero in the linear predictor matrix

Prepare for next version release

Preparing for next release

Submit to CRAN

  • usethis::use_version('minor')
  • devtools::submit_cran()
  • Approve email
  • Wait for CRAN...

Accepted 🎉

  • Finish & publish blog post
  • Add link to blog post in pkgdown news menu
  • usethis::use_github_release()
  • usethis::use_dev_version(push = TRUE)
  • Tweet

Allow intercept-free models

The formula interface should allow for simple SS models where there is no intercept in either the observation or the latent state models. Something like:

   formula = y ~ -1,
   trend_formula = ~ -1, 
   trend_model = 'AR1',
   ...

Because we need an underlying (and valid) mgcv model for generating predictions, we'll need to include the intercept in the model and either fix it at zero (as a transformed parameter) or regularize it to zero

stan model fails on test case

The following code, taken from the documentation, results in an error when run using Stan:

library(dplyr)
library(mvgam)
trend <- cumsum(rnorm(53, -0.1, 0.5))
observations <- rnbinom(53, size = 10, mu = 4 + exp(trend))
data.frame(y = observations) %>%
  dplyr::mutate(lag1 = lag(y),
                lag2 = lag(y, 2),
                lag3 = lag(y, 3)) %>%
  dplyr::slice_tail(n = 50) -> sim_dat
sim_dat$time <- 1:50
data_train <- sim_dat[1:40,]
data_test <- sim_dat[41:50,]
m_mvgam <- mvgam(formula = y ~ 1,
                 data = data_train,
                 newdata = data_test,
                 family = 'nb',
                 trend_model = 'AR3',
                 chains = 4,
                 burnin = 1000,
                 use_stan = TRUE)
Warning: gam.fit3 algorithm did not convergeUsing rstan as the backend

Compiling the Stan program...

SYNTAX ERROR, MESSAGE(S) FROM PARSER:
Variable "vector" does not exist.
 error in 'model5bd849a9767d_d92ad29b307e0dec0b2df017db20274c' at line 89, column 6
  -------------------------------------------------
    87: 
    88: // likelihood functions
    89: vector[n_nonmissing] flat_trends;
             ^
    90: real flat_rs[n_nonmissing];
  -------------------------------------------------

Error in stanc(file = file, model_code = model_code, model_name = model_name,  : 
  failed to parse Stan model 'd92ad29b307e0dec0b2df017db20274c' due to the above error.

Write a FAQ man page

The {mgcv} equivalent is very useful, though not widely known about: https://www.rdocumentation.org/packages/mgcv/versions/1.9-1/topics/mgcv.FAQ. Things to cover

  1. How to compare models using loo_compare() and lfo_cv() (loo less reliable for models with autoregressive processes; use in initial model development and comparison before adding latent dynamics, or stick with lfo)
  2. How to extract posterior estimates
  3. How to model seasonality (options include cyclic smooths and discussion of knot placement, or fourier terms)
  4. How to model time-varying seasonality, and time-varying periodicity
  5. Dealing with irregularly sampled time series (options include GPs and continuous time AR models)
  6. How to report results from mvgam() models (point to the blog post on using {marginaleffects} but caution that this doesn't yet work with linear functionals)
  7. How to check model assumptions (residual plots and ppc_check())
  8. How to choose k

Allow bounds on fixed effect priors

Incorporating bounds on fixed effect priors can be done using the truncation syntax i.e.

b[1] ~ normal(0, 1)T[0, ]

This would require checks on inits to ensure they respect the correct bounding, but would be a useful feature to allow more domain expertise to be included in the prior

Vignette plans

  1. Gaussian Process example with emphasis on length scale priors
  2. Dynamic factors (use tick data); explain how sign-flipping is dealt with
  3. Types of predictions (link, expected, response) and the brms-like wrappers
  4. Hierarchical, time-varying seasonality (hierarchical slopes on fourier series predictors)
  5. Spatiotemporal processes (geostatistical, multiple species, species-level spatial smooths, species-level GPs of time, dynamic factor temporal processes to capture how the spatial fields deviating from the mean spatiotemporal process over time); would highlight how one can fix the observation level params and capture very complex spatiotemporal processes while leveraging data effectively

Other docs needed to form a useful cheatsheet

A cheatsheet is necessary for such a flexible package. Other docs needed to support this are

  • A GP() wrapper for consistency with other trend models
  • A help man page on formulae (similar to mgcv::formula.gam) with expanded docs on dynamic() and gp()
  • A vignette on dynamic factors
  • A vignette on piecewise trends, particularly specifying the cap variable in piecewise logistic models

Future enhancement wishlist

  • Matern covariance functions for GP effects / trends
  • 2d FFT GPs (or even 1d) for complex but stationary GP effects (https://arxiv.org/pdf/2301.08836.pdf)
  • Multivariate normal observation models
  • Move all plots to ggplot2 for broader customisability
  • RW MRF basis for inclusion in dynamic terms and time-varying seasonality
  • Stepwise flat trends (https://github.com/facebook/prophet/pull/1794/files)
  • Linear predictors for other observation parameters (distributional regression)
  • Some easy to follow guidelines on hypothesis testing for ecological models
  • Correlated random effects (add cor option in bs = 're' smooths to estimate random intercepts or slopes from a noncentered mvnormal); handy for 'residual' correlations such as JSDMs

To do

  • Add unit tests for a large variety of model forms (te, t2, distributed lag, etc...)
  • Allow linear predictors for the latent states
    -Funcional curve forecasting:
  1. Construct thin plate basis functions for the curve
  2. Use regularisation to penalise the initial coefficient values
  3. Set up dynamic factors to model how the basis coefficients change over time
  4. Produce forecasts for basis coefficients that respect their induced correlations
  5. This could also be used for spatiotemporal modelling (spatial basis functions change over time dynamically); but might not be a feature of mvgam, rather a second package that depends on mvgam for much of the setup
  • Respect upper bounds for forecasts, prediction, particle filtering
    trunc_poiss = function(lambda, bound){
    out <- vector(length = length(lambda))
    for(i in 1:length(out)){
    func <- ecdf(rpois(10000, lambda = lambda[i]))
    unif_bound <- func(bound)
    out[i] <- qpois(runif(1, 0, unif_bound), lambda = lambda[i])
    }
    out
    }
    trunc_poiss(c(10,8, 21, 11, 5), 20
  • Add hierarchical latent GP trends:
    //Stan GP functions generated by package mvgam
    functions {
    real lambda_gp(real L, int m) {
    real lam;
    lam = ((mpi())/(2L))^2;
    return lam;
    }

vector phi_SE(real L, int m, vector x) {
vector[rows(x)] fi;
fi = 1/sqrt(L) * sin(mpi()/(2L) * (x+L));
return fi;
}

real spd_SE(real alpha, real rho, real w) {
real S;
S = (alpha^2) * sqrt(2pi()) * rho * exp(-0.5(rho^2)*(w^2));
return S;
}
}

//Stan model code generated by package mvgam
data {
int<lower=0> total_obs; // total number of observations
int<lower=0> n; // number of timepoints per series
int<lower=0> n_sp; // number of smoothing parameters
int<lower=0> n_series; // number of series
int<lower=0> num_basis; // total number of basis coefficients
vector[num_basis] zero; // prior locations for basis coefficients
real p_taus[1]; // prior precisions for parametric coefficients
real p_coefs[1]; // prior locations for parametric coefficients
matrix[num_basis, total_obs] X; // transposed mgcv GAM design matrix
int<lower=0> ytimes[n, n_series]; // time-ordered matrix (which col in X belongs to each [time, series] observation?)
matrix[8,8] S1; // mgcv smooth penalty matrix S1
int<lower=0, upper=1> y_observed[n, n_series]; // indices of missing vs observed
int<lower=-1> y[n, n_series]; // time-ordered observations, with -1 indicating missing
}

transformed data {
vector<lower=1>[n] times;
real<lower=0> boundary;
int<lower=1> num_gp_basis;
num_gp_basis = max(40, n);
matrix[n, num_gp_basis] gp_phi;

for (t in 1:n){
times[t] = t;
}

boundary = (5.0/4) * max(times);
for (m in 1:num_gp_basis){
gp_phi[,m] = phi_SE(boundary, m, times);
}
}

parameters {
// raw basis coefficients
row_vector[num_basis] b_raw;

// gp parameters
vector<lower=0>[n_series] alpha_gp;
vector<lower=0>[n_series] rho_gp;
real<lower=0> alpha_gp_global;
real<lower=0> rho_gp_global;

// gp coefficient weights
matrix[num_gp_basis, n_series] b_gp;
vector[num_gp_basis] b_gp_global;

// smoothing parameters
vector<lower=0>[n_sp] lambda;
}

transformed parameters {
// GAM contribution to expectations (log scale)
vector[total_obs] eta;

// expectations
matrix[n, n_series] mus;

// gp spectral densities
matrix[n, n_series] trend;
vector[n] trend_global;
matrix[num_gp_basis, n_series] diag_SPD;
matrix[num_gp_basis, n_series] SPD_beta;
vector[num_gp_basis] diag_SPD_global;
vector[num_gp_basis] SPD_beta_global;

// basis coefficients
row_vector[num_basis] b;

b[1:num_basis] = b_raw[1:num_basis];
eta = to_vector(b * X);

// gp trend estimates
for (m in 1:num_gp_basis){
for (s in 1:n_series){
diag_SPD[m, s] = sqrt(spd_SE(alpha_gp[s], rho_gp[s], sqrt(lambda_gp(boundary, m))));
}
}
SPD_beta = diag_SPD .* b_gp;
trend = gp_phi * SPD_beta;

// global gp trend
for (m in 1:num_gp_basis){
diag_SPD_global[m] = sqrt(spd_SE(alpha_gp_global, rho_gp_global, sqrt(lambda_gp(boundary, m))));
}
SPD_beta_global = diag_SPD_global .* b_gp_global;
trend_global = gp_phi * SPD_beta_global;

for(s in 1:n_series){
mus[1:n, s] = eta[ytimes[1:n, s]] + trend[1:n, s] + trend_global;
}
}

model {
// parametric effect priors (regularised for identifiability)
for (i in 1:1) {
b_raw[i] ~ normal(p_coefs[i], 1 / p_taus[i]);
}

// prior for s(season)...
b_raw[2:9] ~ multi_normal_prec(zero[2:9],S1[1:8,1:8] * lambda[1]);

// priors for smoothing parameters
lambda ~ exponential(0.05);

// priors for gp parameters
for (s in 1:n_series){
b_gp[1:num_gp_basis, s] ~ normal(0, 1);
}
b_gp_global ~ normal(0, 1);
alpha_gp ~ normal(0, 0.5);
alpha_gp_global ~ normal(0, 0.5);
rho_gp ~ inv_gamma(4, 24);
rho_gp_global ~ inv_gamma(4, 24);

// likelihood functions
for (i in 1:n) {
for (s in 1:n_series) {
if (y_observed[i, s])
y[i, s] ~ poisson_log(mus[i, s]);
}
}
}

generated quantities {
vector[n_sp] rho;
array[n, n_series] int ypred;
rho = log(lambda);

// posterior predictions
for(s in 1:n_series){
ypred[1:n, s] = poisson_log_rng(mus[1:n, s]);
}
}

Multivariate models with seperate distributed lag effects not working

Hi Nick,

I am trying to fit a multivariate model with trend-specific distributed lag effects (e.g., ' te(precip, lag, by = trend, k = c(6, 6))').
However, this appears to be failing due to internal formatting errors in mvgam.
I assume this is because 'trend' needs to be in the same matrix format as the two variables ('precip', 'lag' in the previous e.g.,), but when I try to do this, the model fits but errors right at the end with "Error in PredictMat(object$smooth[[k]], data) : Can't find by variable".

I've attached a reproducible example below, any help or ideas would be greatly appreciated!

Cheers,
Matt.

library(mvgam)
# Loading required package: mgcv
# Loading required package: nlme
# This is mgcv 1.8-41. For overview type 'help("mgcv-package")'.
# Loading required package: Rcpp
# Loading required package: brms
# Loading 'brms' package (version 2.20.4). Useful instructions
# can be found by typing help('brms'). A more detailed introduction
# to the package is available through vignette('brms_overview').
# 
# Attaching package: 'brms'
# The following objects are masked from 'package:mgcv':
# 
#     s, t2
# The following object is masked from 'package:stats':
# 
#     ar
# Loading required package: marginaleffects
# Loading required package: insight
# Welcome to mvgam. Please cite as: Clark, NJ, and Wells, K. 2022. Dynamic Generalized Additive Models (DGAMs) for forecasting discrete ecological time series. Methods in Ecology and Evolution, 2022, https://doi.org/10.1111/2041-210X.13974

# Simon Wood’s lag matrix function 
lagard <- function(x, n.lag = 6) {
  n <- length(x); X <- matrix(NA, n, n.lag)
  for (i in 1:n.lag) X[i:n, i] <- x[i:n - i + 1]
  X
}

# simulate time series data at 3 sites
dat <- sim_mvgam(T = 100, n_series = 3, n_lv = 2,
                 family = 'poisson',
                 mu = runif(6, -1, 2),
                 trend_rel = 0.6)

# our data is
data_train <- dat$data_train

# add fake precip covariate
data_train$precip <- rnorm(nrow(data_train))

# make training data a list with lag
data_train_list <- list(lag=matrix(0:5, nrow(data_train), 6, byrow = TRUE),
                         y = data_train$y,
                         time = data_train$time,
                         series = data_train$series)

# calculate lagged values
data_train_list$precip <- lagard(data_train$precip)

# remove first 5 rows due to NA's (from lag)
data_train_list_trim <- lapply(data_train_list, tail, -6)


## fit SS model - same lag response across series 
mod1 <- mvgam(y ~ -1,
              trend_formula = ~ te(precip, lag, k = c(6, 6)),
              data = data_train_list_trim,
              family = poisson(),
              trend_model = 'AR1')
# Using cmdstanr as the backend
# 
# ld: warning: duplicate -rpath '/Users/ree140/.cmdstan/cmdstan-2.33.1/stan/lib/stan_math/lib/tbb' ignored
# Init values were only set for a subset of parameters. 
# Missing init values for the following parameters:
#  - chain 1: b_raw_trend, sigma, ar1, LV, lambda_trend
#  - chain 2: b_raw_trend, sigma, ar1, LV, lambda_trend
#  - chain 3: b_raw_trend, sigma, ar1, LV, lambda_trend
#  - chain 4: b_raw_trend, sigma, ar1, LV, lambda_trend
# Running MCMC with 4 parallel chains...
# 
# Chain 1 Iteration:   1 / 1000 [  0%]  (Warmup)
# Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 1 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -4.44659e-323. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 1
# Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 1 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -2.63581e-20. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 1
# Chain 2 Iteration:   1 / 1000 [  0%]  (Warmup)
# Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 2 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is 8.67362e-18. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 2
# Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 2 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -2.0383e-17. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 2
# Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 2 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is 1.1293e-15. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 2
# Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 2 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is 6.28641e+26. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 2
# Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 2 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -6.59195e-17. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 2
# Chain 3 Iteration:   1 / 1000 [  0%]  (Warmup)
# Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 3 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -1.29127e+20. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 3
# Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 3 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -5.53377e-16. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 3
# Chain 4 Iteration:   1 / 1000 [  0%]  (Warmup)
# Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 4 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is 1.19726e+52. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 4
# Chain 1 Iteration: 100 / 1000 [ 10%]  (Warmup) 
# Chain 2 Iteration: 100 / 1000 [ 10%]  (Warmup) 
# Chain 3 Iteration: 100 / 1000 [ 10%]  (Warmup) 
# Chain 4 Iteration: 100 / 1000 [ 10%]  (Warmup)
# Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 4 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -4.16334e-17. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d38251cb7.stan', line 85, column 2 to line 91, column 63)
# Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 4
# Chain 1 Iteration: 200 / 1000 [ 20%]  (Warmup) 
# Chain 4 Iteration: 200 / 1000 [ 20%]  (Warmup) 
# Chain 3 Iteration: 200 / 1000 [ 20%]  (Warmup) 
# Chain 1 Iteration: 300 / 1000 [ 30%]  (Warmup) 
# Chain 2 Iteration: 200 / 1000 [ 20%]  (Warmup) 
# Chain 4 Iteration: 300 / 1000 [ 30%]  (Warmup) 
# Chain 3 Iteration: 300 / 1000 [ 30%]  (Warmup) 
# Chain 1 Iteration: 400 / 1000 [ 40%]  (Warmup) 
# Chain 2 Iteration: 300 / 1000 [ 30%]  (Warmup) 
# Chain 4 Iteration: 400 / 1000 [ 40%]  (Warmup) 
# Chain 3 Iteration: 400 / 1000 [ 40%]  (Warmup) 
# Chain 2 Iteration: 400 / 1000 [ 40%]  (Warmup) 
# Chain 4 Iteration: 500 / 1000 [ 50%]  (Warmup) 
# Chain 4 Iteration: 501 / 1000 [ 50%]  (Sampling) 
# Chain 1 Iteration: 500 / 1000 [ 50%]  (Warmup) 
# Chain 1 Iteration: 501 / 1000 [ 50%]  (Sampling) 
# Chain 2 Iteration: 500 / 1000 [ 50%]  (Warmup) 
# Chain 2 Iteration: 501 / 1000 [ 50%]  (Sampling) 
# Chain 3 Iteration: 500 / 1000 [ 50%]  (Warmup) 
# Chain 3 Iteration: 501 / 1000 [ 50%]  (Sampling) 
# Chain 4 Iteration: 600 / 1000 [ 60%]  (Sampling) 
# Chain 1 Iteration: 600 / 1000 [ 60%]  (Sampling) 
# Chain 2 Iteration: 600 / 1000 [ 60%]  (Sampling) 
# Chain 4 Iteration: 700 / 1000 [ 70%]  (Sampling) 
# Chain 1 Iteration: 700 / 1000 [ 70%]  (Sampling) 
# Chain 3 Iteration: 600 / 1000 [ 60%]  (Sampling) 
# Chain 2 Iteration: 700 / 1000 [ 70%]  (Sampling) 
# Chain 4 Iteration: 800 / 1000 [ 80%]  (Sampling) 
# Chain 1 Iteration: 800 / 1000 [ 80%]  (Sampling) 
# Chain 2 Iteration: 800 / 1000 [ 80%]  (Sampling) 
# Chain 3 Iteration: 700 / 1000 [ 70%]  (Sampling) 
# Chain 4 Iteration: 900 / 1000 [ 90%]  (Sampling) 
# Chain 1 Iteration: 900 / 1000 [ 90%]  (Sampling) 
# Chain 2 Iteration: 900 / 1000 [ 90%]  (Sampling) 
# Chain 4 Iteration: 1000 / 1000 [100%]  (Sampling) 
# Chain 4 finished in 3.3 seconds.
# Chain 1 Iteration: 1000 / 1000 [100%]  (Sampling) 
# Chain 1 finished in 3.4 seconds.
# Chain 2 Iteration: 1000 / 1000 [100%]  (Sampling) 
# Chain 3 Iteration: 800 / 1000 [ 80%]  (Sampling) 
# Chain 2 finished in 3.5 seconds.
# Chain 3 Iteration: 900 / 1000 [ 90%]  (Sampling) 
# Chain 3 Iteration: 1000 / 1000 [100%]  (Sampling) 
# Chain 3 finished in 4.4 seconds.
# 
# All 4 chains finished successfully.
# Mean chain execution time: 3.7 seconds.
# Total execution time: 4.5 seconds.
# Warning: 2 of 2000 (0.0%) transitions ended with a divergence.
# See https://mc-stan.org/misc/warnings for details.

# works fine

## now try to fit SS model - different lag responses across series 
mod2 <- mvgam(y ~ -1,
              trend_formula = ~ te(precip, lag, by = trend, k = c(6, 6)),
              data = data_train_list_trim,
              family = poisson(),
              trend_model = 'AR1')
# Error in ss_gam$smooth: $ operator is invalid for atomic vectors

# I assume this is because it require the 'trend' var to be in the same format as precip and lag (matrices), so make a new 'trend matrix' in the data 

# series in matrix format (note: I tried overwriting the current series column, that doesn't work)
data_train_list$ser_mat <- matrix(data_train$series, nrow(data_train), 24, byrow = FALSE)

# and need to trim again 
data_train_list_trim <- lapply(data_train_list, tail, -6)

# refit with new ser_mat column  
mod3 <- mvgam(y ~ -1,
              trend_formula = ~ te(precip, lag, by = ser_mat, k = c(6, 6)),
              data = data_train_list_trim,
              family = poisson(),
              trend_model = 'AR1')
# Using cmdstanr as the backend
# 
# ld: warning: duplicate -rpath '/Users/ree140/.cmdstan/cmdstan-2.33.1/stan/lib/stan_math/lib/tbb' ignored
# Init values were only set for a subset of parameters. 
# Missing init values for the following parameters:
#  - chain 1: b_raw_trend, sigma, ar1, LV, lambda_trend
#  - chain 2: b_raw_trend, sigma, ar1, LV, lambda_trend
#  - chain 3: b_raw_trend, sigma, ar1, LV, lambda_trend
#  - chain 4: b_raw_trend, sigma, ar1, LV, lambda_trend
# Running MCMC with 4 parallel chains...
# 
# Chain 1 Iteration:   1 / 1000 [  0%]  (Warmup)
# Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 1 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is 1.51788e-18. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 1
# Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 1 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -1.86483e-17. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 1
# Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 1 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -5.77493e+60. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 1
# Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 1 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -3.55618e-17. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 1
# Chain 2 Iteration:   1 / 1000 [  0%]  (Warmup)
# Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 2 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -1.17285e-14. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 2
# Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 2 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is 1.54564e-15. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 2
# Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 2 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -1.60694e+60. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 2
# Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 2 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is 3.22409e-12. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 2
# Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 2 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is 5.32907e-15. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 2
# Chain 3 Iteration:   1 / 1000 [  0%]  (Warmup)
# Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 3 Exception: multi_normal_prec_lpdf: Precision matrix is not symmetric. Precision matrix[1,19] = 1.6793e+144, but Precision matrix[19,1] = 1.6793e+144 (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 3
# Chain 4 Iteration:   1 / 1000 [  0%]  (Warmup)
# Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 4 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is 2.60209e-17. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 4
# Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 4 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is 1.35525e-17. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 4
# Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 4 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -4.24092e-27. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 4
# Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
# Chain 4 Exception: multi_normal_prec_lpdf: LDLT_Factor of precision parameter is not positive definite.  last conditional variance is -3.90313e-17. (in '/var/folders/91/5zwgbxk952vd2qjj7rg9ky540000gp/T/RtmpeZkEjf/model-888d5ca9263a.stan', line 85, column 2 to line 91, column 63)
# Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
# Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
# Chain 4
# Chain 1 Iteration: 100 / 1000 [ 10%]  (Warmup) 
# Chain 2 Iteration: 100 / 1000 [ 10%]  (Warmup) 
# Chain 3 Iteration: 100 / 1000 [ 10%]  (Warmup) 
# Chain 4 Iteration: 100 / 1000 [ 10%]  (Warmup) 
# Chain 1 Iteration: 200 / 1000 [ 20%]  (Warmup) 
# Chain 4 Iteration: 200 / 1000 [ 20%]  (Warmup) 
# Chain 1 Iteration: 300 / 1000 [ 30%]  (Warmup) 
# Chain 2 Iteration: 200 / 1000 [ 20%]  (Warmup) 
# Chain 3 Iteration: 200 / 1000 [ 20%]  (Warmup) 
# Chain 4 Iteration: 300 / 1000 [ 30%]  (Warmup) 
# Chain 1 Iteration: 400 / 1000 [ 40%]  (Warmup) 
# Chain 2 Iteration: 300 / 1000 [ 30%]  (Warmup) 
# Chain 3 Iteration: 300 / 1000 [ 30%]  (Warmup) 
# Chain 4 Iteration: 400 / 1000 [ 40%]  (Warmup) 
# Chain 1 Iteration: 500 / 1000 [ 50%]  (Warmup) 
# Chain 1 Iteration: 501 / 1000 [ 50%]  (Sampling) 
# Chain 2 Iteration: 400 / 1000 [ 40%]  (Warmup) 
# Chain 3 Iteration: 400 / 1000 [ 40%]  (Warmup) 
# Chain 4 Iteration: 500 / 1000 [ 50%]  (Warmup) 
# Chain 4 Iteration: 501 / 1000 [ 50%]  (Sampling) 
# Chain 1 Iteration: 600 / 1000 [ 60%]  (Sampling) 
# Chain 2 Iteration: 500 / 1000 [ 50%]  (Warmup) 
# Chain 2 Iteration: 501 / 1000 [ 50%]  (Sampling) 
# Chain 3 Iteration: 500 / 1000 [ 50%]  (Warmup) 
# Chain 3 Iteration: 501 / 1000 [ 50%]  (Sampling) 
# Chain 1 Iteration: 700 / 1000 [ 70%]  (Sampling) 
# Chain 2 Iteration: 600 / 1000 [ 60%]  (Sampling) 
# Chain 4 Iteration: 600 / 1000 [ 60%]  (Sampling) 
# Chain 3 Iteration: 600 / 1000 [ 60%]  (Sampling) 
# Chain 2 Iteration: 700 / 1000 [ 70%]  (Sampling) 
# Chain 1 Iteration: 800 / 1000 [ 80%]  (Sampling) 
# Chain 3 Iteration: 700 / 1000 [ 70%]  (Sampling) 
# Chain 4 Iteration: 700 / 1000 [ 70%]  (Sampling) 
# Chain 2 Iteration: 800 / 1000 [ 80%]  (Sampling) 
# Chain 1 Iteration: 900 / 1000 [ 90%]  (Sampling) 
# Chain 2 Iteration: 900 / 1000 [ 90%]  (Sampling) 
# Chain 3 Iteration: 800 / 1000 [ 80%]  (Sampling) 
# Chain 4 Iteration: 800 / 1000 [ 80%]  (Sampling) 
# Chain 1 Iteration: 1000 / 1000 [100%]  (Sampling) 
# Chain 1 finished in 3.6 seconds.
# Chain 2 Iteration: 1000 / 1000 [100%]  (Sampling) 
# Chain 3 Iteration: 900 / 1000 [ 90%]  (Sampling) 
# Chain 2 finished in 3.6 seconds.
# Chain 4 Iteration: 900 / 1000 [ 90%]  (Sampling) 
# Chain 3 Iteration: 1000 / 1000 [100%]  (Sampling) 
# Chain 3 finished in 4.0 seconds.
# Chain 4 Iteration: 1000 / 1000 [100%]  (Sampling) 
# Chain 4 finished in 4.2 seconds.
# 
# All 4 chains finished successfully.
# Mean chain execution time: 3.9 seconds.
# Total execution time: 4.3 seconds.
# Error in PredictMat(object$smooth[[k]], data): Can't find by variable

Created on 2023-11-16 with reprex v2.0.2

Add gps for other covariates and use Hilbert equations for predictions

Can add gp() function as in brms by including an attribute for the linear predictor matrix (non_mgcv_terms). Will then only need to update the mvgam_predict function and make sure all uses of the Xp matrix have the attribute included before calling mvgam_predict.

For gps, can use existing stan functions and improve efficiency of predictions using basis function weights:

# Evaluate Laplacian eigenfunction for a given GP basis function
phi = function(boundary, m, centred_covariate) { 
  1 / sqrt(boundary) * sin((m * pi)/(2 * boundary) * (centred_covariate + boundary)) 
}

# Evaluate eigenvalues for a given GP basis function
lambda = function(boundary, m) {
  ((m * pi)/(2 * boundary))^2 
}

# Spectral density squared exponential Gaussian Process kernel
spd = function(alpha_gp, rho_gp, eigenvalues) { 
  (alpha_gp^2) * sqrt(2 * pi) * rho_gp * exp(-0.5 * (rho_gp^2) * (eigenvalues^2)) 
}


library(mvgam)
simdat = sim_mvgam(n_series = 1, trend_model = 'GP')
mod = mvgam(y ~ 1,
            trend_model = 'GP',
            data = simdat$data_train,
            run_model = T)
code(mod)

object <- mod
new_covariate <- 1:100
covariate <- 'time'

# Need original covariate from observed data (centred)
covariate_cent <- unique(object$obs_data[[covariate]]) - 
  mean(unique(object$obs_data[[covariate]]))

# Need to know number of original GP basis functions
# (best to store this with the model as could be confusing when multiple
# GPs are used in one model)
num_basis_line <- object$model_file[grep('num_gp_basis = ', object$model_file)]
num_gp_basis <- as.numeric(unlist(regmatches(num_basis_line, 
                                        gregexpr("[[:digit:]]+", num_basis_line))))

# Get draws of GP parameters, including the basis function weights
gp_pars <- as.data.frame(object, variable = 'trend_params')

# Get vector of eigenvalues of covariance matrix
eigenvalues <- vector()
for(m in 1:num_gp_basis){
  eigenvalues[m] <- lambda(boundary = (5.0/4) * 
                             (max(covariate_cent) - min(covariate_cent)),
         m = m)
}

# Get vector of eigenfunctions
eigenfunctions <- matrix(NA, nrow = length(new_covariate),
                         ncol = num_gp_basis)
for(m in 1:num_gp_basis){
  eigenfunctions[, m] <- phi(boundary = (5.0/4) * (max(covariate_cent) - min(covariate_cent)),
                           m = m,
                           centred_covariate = new_covariate - 
                             mean(unique(object$obs_data[[covariate]])))
}

# Compute diagonal of covariance matrix
gp_preds <- matrix(NA, nrow = NROW(gp_pars), ncol = NROW(eigenfunctions))
for(i in 1:NROW(gp_pars)){
  diag_SPD <- sqrt(spd(alpha_gp = gp_pars$`alpha_gp[1]`[i], 
                       rho_gp = gp_pars$`rho_gp[1]`[i], 
                       sqrt(eigenvalues)))
  b_gp <- unlist(gp_pars[i, grep('b_gp', colnames(gp_pars))])
  gp_preds[i, ] <- (diag_SPD * b_gp) %*% t(eigenfunctions)
}

# Compute GP draw
plot(gp_preds[1,], type = 'l',
     ylim = range(gp_preds))
for(i in 2:50){
  lines(gp_preds[i,])
}

# Draws should be identical
trends <- mvgam:::mcmc_chains(object$model_output, 'trend')
plot(trends[1,], type = 'l')
lines(gp_preds[1,], col = 'red')

Handling multiple observation types in a single series / time

Hi Nicholas,

I am wondering if it is possible to have multiple levels of an observation covariate in a single time point for a series.

For example, two different types of traps set per session. Normally in mgcv I would split this into multiple rows in the dataframe (with same series and time value), but mvgam doesn't seem to like this -- producing 'Error: One or more series in data is missing observations for one or more timepoints'.

Although perhaps this is less an issue with mvgam, and more how state-space need to be formulated?

Cheers,
Matt.

Add option to ignore residuals

A logical argument to calculate residuals or not would speed up model fitting, especially useful for lfo_cv runs. Would also lead to smaller model objects in terms of memory.

Could also reduce the parameters being monitored during a lfo_cv refit to also speed up post-processing (only really need mus and family-specific parameters for calculating log-likelihoods; or ypred for other types of scores).

Finally, could avoid adding estimates to the mgcv model and avoid returning the mgcv model to also improve speed in lfo_cv runs

Installation Help

Hello!

First, this package looks incredible, thank you for all the work you have put into it.

I am having quite a bit of trouble installing mvgam, unfortunately.

I'm quite confident is due to my system configuration, particularly my GXX complier, not anything wrong with the package itself.

I have been able to install all dependencies of mvgam; however, when I attempt to install mvgam, I am hit will a large stack trace with many errors such as the following. The full log is attached in case it is helpful:
mvgam_install_log.log

/home/a108669/miniconda3/envs/R2/bin/../lib/gcc/x86_64-conda-linux-gnu/13.2.0/../../../../x86_64-conda-linux-gnu/bin/ld: RcppExports.o:RcppExports.cp:(.text$_ZN10tinyformat6detail15formatTruncatedIiEEvRSoRKT_i[_ZN10tinyformat6detail15 formatTruncatedIiEEvRSoRKT_i]+0x2f4): undefined reference to _Unwind_Resume'`

undefined reference to _Unwind_Resume'
undefined reference to R_MakeUnwindCont'
undefined reference to Rf_protect'`


I know this issue is outside the scope of the package, but do you happen to have any nudges the right way? I've tried various versions of gcc installed via conda and internet research is not seeming to offer me solutions. Any help would be immensely appreciated.

Thank you!

-Matt

Error on check package, due to .check_ncores()

Caused by error in .check_ncores():
! 4 simultaneous processes spawned
Backtrace:

1. ├─testthat::test_check("mvgam")
  2. │ └─testthat::test_dir(...)
  3. │   └─testthat:::test_files(...)
  4. │     └─testthat:::test_files_serial(...)
  5. │       └─testthat:::test_files_setup_state(...)
  6. │         └─testthat::source_test_setup(".", env)
  7. │           └─testthat::source_dir(path, "^setup.*\\.[rR]$", env = env, wrap = FALSE)
  8. │             └─base::lapply(...)
  9. │               └─testthat (local) FUN(X[[i]], ...)
 10. │                 └─testthat::source_file(path, env = env, chdir = chdir, wrap = wrap)
 11. │                   ├─base::withCallingHandlers(...)
 12. │                   └─base::eval(exprs, env)
 13. │                     └─base::eval(exprs, env)
 14. │                       └─mvgam::mvgam(...) at tests/testthat/setup.R:16:0
 15. │                         └─mvgam:::get_mvgam_resids(...)
 16. │                           └─parallel::makePSOCKcluster(n_cores)
 17. │                             └─parallel:::.check_ncores(length(names))
 18. │                               └─base::stop(msg, call. = TRUE)
 19. └─base::.handleSimpleError(...)
 20.   └─testthat (local) h(simpleError(msg, call))
 21.     └─rlang::abort(...)
Execution halted

For some reason we are failing to run that part parallel:::.check_ncores(length(names)) not sure why.

Add pathfinder support

New versions of Cmdstan and cmdstanr allow support for the pathfinder algorithm, which employs Quasi-Newton Variational Inference (https://www.youtube.com/watch?v=TPptuDp-w2E). Requires Cmdstan version 2.33 or higher, as well as cmdstanr version 0.6.1.9000 or higher (this currently only applies to the GitHub version). Will need to check that the resulting object of class CmdStanPathfinder can still be used in the same way as the variational object (class CmdStanVB). Use ??cmdstanr::model to find details

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.