--- title: "Fitting Vasicek-Type Regression Models to the Data in vasicekreg" author: - Josmar Mazucheli - Bruna Alves output: rmarkdown::html_vignette: toc: true bibliography: references.bib link-citations: true vignette: > %\VignetteIndexEntry{Fitting Vasicek-Type Regression Models to the Data in vasicekreg} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.align = "center", fig.height = 4.8, fig.width = 7.0, message = FALSE, warning = FALSE ) library(gamlss) library(vasicekreg) control <- gamlss.control(n.cyc = 200, trace = FALSE) options(width = 90) set.seed(2026) ``` # Scope The `vasicekreg` package provides distribution functions and GAMLSS families for responses bounded by the unit interval. This vignette is deliberately application-oriented: its main purpose is to fit models to all four data sets distributed with the package and to explain what the fitted parameters mean. Detailed derivations of the densities and a systematic study of random generation are left to a separate vignette. The package is built on the Vasicek construction [@Vasicek2002; @MazucheliEtAl2022] and on the Generalized Additive Models for Location, Scale and Shape (GAMLSS) framework [@RigbyStasinopoulos2005; @StasinopoulosRigby2007]. The logistic and hyperbolic-secant kernels are motivated by @Witzany2013 and @FischerEtAl2017, respectively. The available regression families are summarized below. | Family | Kernel | Support | Parameterization | Meaning of `mu` | |:--|:--|:--|:--|:--| | `NVASIM` | Standard normal | $(0,1)$ | Mean | $E(Y)$ | | `NVASIQ` | Standard normal | $(0,1)$ | Fixed quantile | $Q_Y(\mathtt{quantile})$ | | `LVASIQ` | Logistic | $(0,1)$ | Fixed quantile | $Q_Y(\mathtt{quantile})$ | | `HVASIQ` | Hyperbolic secant | $(0,1)$ | Fixed quantile | $Q_Y(\mathtt{quantile})$ | | `ZANVASIM` | Standard normal | $[0,1)$ | Zero-augmented mean | $E(Y\mid Y>0)$ | | `OANVASIM` | Standard normal | $(0,1]$ | One-augmented mean | $E(Y\mid Y<1)$ | | `ZOANVASIM` | Standard normal | $[0,1]$ | Zero-and-one-augmented mean | $E(Y\mid 00)$ is an estimated distributional parameter. # Conditional and marginal interpretation The distinction between the continuous-component mean and the marginal mean is essential for the augmented models. If `mu`, `nu`, and `tau` denote fitted values on the response scale, then the mixture parameterizations used here follow the general treatment of boundary-inflated models in @OspinaFerrari2010 and @OspinaFerrari2012. | Family | Boundary probabilities | Marginal mean | |:--|:--|:--| | `ZANVASIM` | $P(Y=0)=\nu$ | $(1-\nu)\mu$ | | `OANVASIM` | $P(Y=1)=\nu$ | $\nu+(1-\nu)\mu$ | | `ZOANVASIM` | $P(Y=0)=\nu$, $P(Y=1)=(1-\nu)\tau$ | $(1-\nu)\{\tau+(1-\tau)\mu\}$ | Consequently, a coefficient in the `mu` predictor of an augmented model describes the conditional mean in the open interval. Its effect on the marginal mean also depends on the fitted boundary probabilities. # Helpers used in the examples The following small functions keep the output compact. All reported values are calculated when the vignette is built; no numerical results are hard-coded. ```{r helpers} model_fit_table <- function(models, n) { rows <- lapply(names(models), function(label) { object <- models[[label]] data.frame( model = label, family = as.character(object$family[1L]), parameters = object$df.fit, logLik = -object$G.deviance / 2, AIC = gamlss::GAIC(object, k = 2), BIC = gamlss::GAIC(object, k = log(n)), converged = isTRUE(object$converged), row.names = NULL ) }) do.call(rbind, rows) } coefficient_table <- function(object, parameters) { rows <- lapply(parameters, function(parameter) { estimate <- stats::coef(object, what = parameter) data.frame( parameter = parameter, term = names(estimate), estimate = unname(estimate), row.names = NULL ) }) do.call(rbind, rows) } fitted_summary <- function(values) { rows <- lapply(names(values), function(quantity) { x <- values[[quantity]] data.frame( quantity = quantity, minimum = min(x), first_quartile = unname(stats::quantile(x, 0.25)), median = stats::median(x), mean = mean(x), third_quartile = unname(stats::quantile(x, 0.75)), maximum = max(x), row.names = NULL ) }) do.call(rbind, rows) } response_profile <- function(x) { c( observations = length(x), zero = sum(x == 0), interior = sum(x > 0 & x < 1), one = sum(x == 1) ) } ``` # Overview of the included data ```{r load-data} data("bodyfat", package = "vasicekreg") data("transport", package = "vasicekreg") data("trees", package = "vasicekreg") data("aep", package = "vasicekreg") bodyfat_responses <- c("ARMS", "LEGS", "BODY", "ANDROID", "GYNECOID") aep$inappropriate <- with(aep, noinap / los) profiles <- rbind( do.call(rbind, lapply(bodyfat_responses, function(response) { data.frame( data = "bodyfat", response = response, t(response_profile(bodyfat[[response]])), row.names = NULL ) })), data.frame( data = "transport", response = "propbiked", t(response_profile(transport$propbiked)), row.names = NULL ), data.frame( data = "trees", response = "prop", t(response_profile(trees$prop)), row.names = NULL ), data.frame( data = "aep", response = "noinap / los", t(response_profile(aep$inappropriate)), row.names = NULL ) ) knitr::kable(profiles, caption = "Observed support of the bounded responses.") ``` This empirical support determines the admissible families. The five `bodyfat` responses lie strictly inside the interval and can be fitted by any of the four base families. `transport`, `trees`, and `aep` require boundary models because their responses contain zero, one, or both, respectively. Replacing an exact boundary value by an arbitrary small offset is unnecessary and would change the observed data. # Body-fat proportions: mean and quantile regression The `bodyfat` data contain five body-fat proportions measured on 298 individuals, together with age, body mass index (BMI), sex, and physical activity. A value such as `ARMS = 0.163` represents 16.3%, so the responses are already on the unit-interval scale and must not be divided by 100. The data source and previous analyses are described by @PetterleEtAl2020, @MazucheliEtAl2021, and @MazucheliEtAl2022. The five responses are repeated measurements on the same individuals. The models below analyze each response separately and therefore do not estimate their cross-response dependence. A joint multivariate interpretation would require a different model. We center the continuous covariates to give the intercept a more useful interpretation and explicitly label the factor levels. ```{r bodyfat-prepare} bodyfat_analysis <- within(bodyfat, { AGE_centered <- AGE - mean(AGE) BMI_centered <- BMI - mean(BMI) SEX <- factor(SEX, levels = c(1, 2), labels = c("female", "male")) IPAQ <- factor( IPAQ, levels = c(0, 1, 2), labels = c("sedentary", "insufficiently_active", "active") ) }) bodyfat_ranges <- data.frame( response = bodyfat_responses, minimum = vapply(bodyfat_analysis[bodyfat_responses], min, numeric(1)), maximum = vapply(bodyfat_analysis[bodyfat_responses], max, numeric(1)), row.names = NULL ) knitr::kable(bodyfat_ranges, digits = 3, caption = "Ranges of the five body-fat proportions.") ``` ## Mean regression for all five responses For each body-fat response, the same linear predictor is used for the conditional mean and `sigma` is held constant. Keeping the formula common facilitates comparisons of coefficient patterns across anatomical regions, but AIC and BIC values from different responses should not be interpreted as a competition among the responses. ```{r bodyfat-mean-fits} fit_bodyfat_mean <- setNames( lapply(bodyfat_responses, function(response) { mu_formula <- stats::reformulate( c("AGE_centered", "BMI_centered", "SEX", "IPAQ"), response = response ) gamlss( formula = mu_formula, sigma.formula = ~ 1, family = NVASIM(), data = bodyfat_analysis, control = control ) }), bodyfat_responses ) bodyfat_mean_statistics <- model_fit_table( fit_bodyfat_mean, n = nrow(bodyfat_analysis) ) knitr::kable( bodyfat_mean_statistics, digits = 3, caption = "Normal-kernel Vasicek mean regressions for the body-fat responses." ) ``` For these `NVASIM` fits, `fitted(object, what = "mu")` returns the fitted conditional mean on the response scale. The coefficients themselves are on the logit scale. The complete coefficient table for any response can be obtained as follows. ```{r bodyfat-mean-coefficients} knitr::kable( coefficient_table(fit_bodyfat_mean[["ARMS"]], c("mu", "sigma")), digits = 4, caption = "Coefficient estimates for the ARMS mean-regression model." ) ``` ## Comparing the three quantile kernels We next fit median regressions to `ARMS` with identical predictors and the normal, logistic, and hyperbolic-secant kernels. Here, `mu` is the fitted conditional median, not the conditional mean. The same construction can be used for another fixed level by changing `quantile_level`. ```{r bodyfat-quantile-fits} quantile_level <- 0.50 fit_arms_nq <- gamlss( ARMS ~ AGE_centered + BMI_centered + SEX + IPAQ, sigma.formula = ~ 1, family = NVASIQ(quantile = quantile_level), data = bodyfat_analysis, control = control ) fit_arms_lq <- gamlss( ARMS ~ AGE_centered + BMI_centered + SEX + IPAQ, sigma.formula = ~ 1, family = LVASIQ(quantile = quantile_level), data = bodyfat_analysis, control = control ) fit_arms_hq <- gamlss( ARMS ~ AGE_centered + BMI_centered + SEX + IPAQ, sigma.formula = ~ 1, family = HVASIQ(quantile = quantile_level), data = bodyfat_analysis, control = control ) arms_models <- c( list(NVASIM_mean = fit_bodyfat_mean[["ARMS"]]), list( NVASIQ_median = fit_arms_nq, LVASIQ_median = fit_arms_lq, HVASIQ_median = fit_arms_hq ) ) knitr::kable( model_fit_table(arms_models, n = nrow(bodyfat_analysis)), digits = 3, caption = "Likelihood-based summaries for the ARMS models." ) ``` Because all four models use the same response observations, their maximized likelihoods, AICs, and BICs can be compared. Such a comparison concerns the complete fitted distributions. It does not make `mu` directly comparable between the mean model and the median models. ```{r bodyfat-fitted-summary} knitr::kable( fitted_summary(list( NVASIM_conditional_mean = fitted(arms_models$NVASIM_mean, what = "mu"), NVASIQ_conditional_median = fitted(arms_models$NVASIQ_median, what = "mu"), LVASIQ_conditional_median = fitted(arms_models$LVASIQ_median, what = "mu"), HVASIQ_conditional_median = fitted(arms_models$HVASIQ_median, what = "mu") )), digits = 4, caption = "Summaries of fitted means and medians for ARMS." ) ``` # Bicycle-trip proportions: zero augmentation The `transport` data contain 60 respondents from a stratified transportation study. The response is the proportion of trips to campus made by bicycle. The data originate from the consulting study reported by @Korosteleva2019 and were subsequently analyzed by @MenezesEtAl2021; the object distributed by `vasicekreg` retains the values supplied by `uwquantreg` [@uwquantreg]. Because `propbiked` contains exact zeros but no ones, `ZANVASIM` is the appropriate augmented family. We use the covariate structure from the previous analysis: gender, parking-permit duration, and institutional status in the positive-component mean; gender and distance in the zero probability. Centering the continuous predictors changes the intercepts but not the fitted values or slopes. ```{r transport-fit} transport_analysis <- within(transport, { gender <- stats::relevel(factor(gender), ref = "F") status <- stats::relevel(factor(status), ref = "faculty") parking_centered <- parking - mean(parking) distance_centered <- distance - mean(distance) }) fit_transport <- gamlss( propbiked ~ gender + parking_centered + status, sigma.formula = ~ 1, nu.formula = ~ gender + distance_centered, family = ZANVASIM(), data = transport_analysis, control = control ) knitr::kable( model_fit_table( list(ZANVASIM = fit_transport), n = nrow(transport_analysis) ), digits = 3, caption = "Likelihood-based summary for the transport model." ) knitr::kable( coefficient_table(fit_transport, c("mu", "sigma", "nu")), digits = 4, caption = "Coefficient estimates for the zero-augmented transport model." ) ``` Here, `mu` is the fitted mean bicycle-trip proportion among positive responses and `nu` is the fitted probability of no bicycle trips. The fitted marginal mean combines both components. ```{r transport-fitted} transport_mu <- fitted(fit_transport, what = "mu") transport_nu <- fitted(fit_transport, what = "nu") transport_marginal_mean <- (1 - transport_nu) * transport_mu knitr::kable( fitted_summary(list( positive_component_mean = transport_mu, probability_zero = transport_nu, marginal_mean = transport_marginal_mean )), digits = 4, caption = "Fitted quantities from the transport model." ) ``` Although `ntrips` and `nbiked` are available, this analysis treats each respondent's proportion as one mixed continuous--discrete response. It is not a binomial model for `nbiked` conditional on `ntrips`, and respondents with larger denominators do not automatically receive larger weights. # Tree-survival proportions: one augmentation The `trees` data record two-year survival proportions in 26 parks. Their provenance is the same as that of `transport` [@Korosteleva2019; @MenezesEtAl2021; @uwquantreg]. The response contains exact ones but no zeros, so we use `OANVASIM`. The continuous-component mean depends on pest-control frequency, fertilization frequency, precipitation, and wind speed. The probability of complete survival depends on wind speed. Precipitation and wind are centered to make the intercepts refer to their sample-average values. ```{r trees-fit} trees_analysis <- within(trees, { precip_centered <- precip - mean(precip) wind_centered <- wind - mean(wind) }) fit_trees <- gamlss( prop ~ pest + fertilization + precip_centered + wind_centered, sigma.formula = ~ 1, nu.formula = ~ wind_centered, family = OANVASIM(), data = trees_analysis, control = control ) knitr::kable( model_fit_table( list(OANVASIM = fit_trees), n = nrow(trees_analysis) ), digits = 3, caption = "Likelihood-based summary for the tree-survival model." ) knitr::kable( coefficient_table(fit_trees, c("mu", "sigma", "nu")), digits = 4, caption = "Coefficient estimates for the one-augmented tree-survival model." ) ``` For this model, `mu` is the fitted mean survival proportion conditional on a value below one and `nu` is the probability of complete survival. Therefore, the marginal mean is `nu + (1 - nu) * mu`. ```{r trees-fitted} trees_mu <- fitted(fit_trees, what = "mu") trees_nu <- fitted(fit_trees, what = "nu") trees_marginal_mean <- trees_nu + (1 - trees_nu) * trees_mu knitr::kable( fitted_summary(list( continuous_component_mean = trees_mu, probability_one = trees_nu, marginal_mean = trees_marginal_mean )), digits = 4, caption = "Fitted quantities from the tree-survival model." ) ``` The small sample of 26 parks warrants caution, particularly because several distributional parameters are estimated. As in the transport example, the model treats each park-level proportion as one response; it is not a binomial model that uses `planted` as a number of trials. # Inappropriate hospital-stay proportions: two-boundary augmentation The `aep` data contain 1,383 patients admitted to Hospital del Mar in Barcelona in 1988 and 1990. For each patient, `noinap` is the number of days classified as inappropriate and `los` is the total length of stay. The data were studied by @GangeEtAl1996 and the object in `vasicekreg` retains the structure supplied by `gamlss.data` [@gamlssdata]. We define $$ Y_i=\mathrm{noinap}_i / \mathrm{los}_i. $$ The response includes both zero and one and is therefore fitted with `ZOANVASIM`. In its sequential boundary parameterization, $$ P(Y_i=0)=\nu_i,\qquad P(Y_i=1)=(1-\nu_i)\tau_i,\qquad P(0