├── .Rbuildignore ├── .gitignore ├── CRAN-SUBMISSION ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── distribution.R ├── modeling.R ├── package_info.R ├── plot.R └── preparation.R ├── README.Rmd ├── README.md ├── README.qmd ├── altdoc └── quarto_website.yml ├── calc_pry1.R ├── cran-comments.md ├── custom_sim.R ├── data ├── fit_multivariate.rda ├── ord_fit_mean.rda ├── ord_fit_phi.rda ├── pew.rda └── sim_data.rda ├── docs ├── CITATION.html ├── LICENSE.html ├── NEWS.html ├── freeze.rds ├── index.html ├── man │ ├── dordbeta.html │ ├── dordbeta_files │ │ └── figure-html │ │ │ └── unnamed-chunk-1-1.png │ ├── fit_imputed.html │ ├── fit_multivariate.html │ ├── normalize.html │ ├── ord_fit_mean.html │ ├── ord_fit_phi.html │ ├── ordbetareg.html │ ├── pew.html │ ├── plot_heiss.html │ ├── posterior_epred_ordbeta.brmsfit.html │ ├── pp_check_ordbeta.html │ ├── pp_check_ordbeta_files │ │ └── figure-html │ │ │ ├── unnamed-chunk-1-1.png │ │ │ ├── unnamed-chunk-1-2.png │ │ │ └── unnamed-chunk-1-3.png │ ├── rordbeta.html │ ├── sim_data.html │ └── sim_ordbeta.html ├── quarto_website.yml ├── screen1.png ├── screen2.png ├── search.json ├── site_libs │ ├── bootstrap │ │ ├── bootstrap-icons.css │ │ ├── bootstrap-icons.woff │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.js │ ├── clipboard │ │ └── clipboard.min.js │ ├── quarto-html │ │ ├── anchor.min.js │ │ ├── popper.min.js │ │ ├── quarto-syntax-highlighting.css │ │ ├── quarto.js │ │ ├── tippy.css │ │ └── tippy.umd.min.js │ ├── quarto-nav │ │ ├── headroom.min.js │ │ └── quarto-nav.js │ └── quarto-search │ │ ├── autocomplete.umd.js │ │ ├── fuse.min.js │ │ └── quarto-search.js └── vignettes │ ├── package_introduction.html │ ├── package_introduction_files │ ├── figure-html │ │ ├── load_data-1.png │ │ ├── plot_cut-1.png │ │ ├── plot_heiss-1.png │ │ ├── plot_phi_sim-1.png │ │ ├── post_predict-1.png │ │ ├── post_predict-2.png │ │ └── sim_plot-1.png │ └── libs │ │ ├── bootstrap │ │ ├── bootstrap-icons.css │ │ ├── bootstrap-icons.woff │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.js │ │ ├── clipboard │ │ └── clipboard.min.js │ │ └── quarto-html │ │ ├── anchor.min.js │ │ ├── popper.min.js │ │ ├── quarto-syntax-highlighting.css │ │ ├── quarto.js │ │ ├── tippy.css │ │ └── tippy.umd.min.js │ ├── screen1.png │ └── screen2.png ├── man ├── dordbeta.Rd ├── fit_multivariate.Rd ├── normalize.Rd ├── ord_fit_mean.Rd ├── ord_fit_phi.Rd ├── ordbetareg-package.Rd ├── ordbetareg.Rd ├── pew.Rd ├── plot_heiss.Rd ├── posterior_epred_ordbeta.brmsfit.Rd ├── pp_check_ordbeta.Rd ├── rordbeta.Rd ├── sim_data.Rd └── sim_ordbeta.Rd ├── ordbetareg_power.R ├── ordbetareg_transparent_v2.png ├── ordbetaregpack.Rproj ├── orig.Rmd ├── test_krueger.R ├── test_package.R └── vignettes ├── .gitignore ├── package_introduction.qmd ├── screen1.png └── screen2.png /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | README.Rmd 5 | orig.Rmd 6 | test_krueger.R 7 | test_package.R 8 | ^CRAN-SUBMISSION$ 9 | ^cran-comments\.md$ 10 | prop_modeling_version.R 11 | calc_pry1.R 12 | test 13 | test.stan 14 | test2.stan 15 | ols_ordbeta_compare.png 16 | discrete.png 17 | continuous.png 18 | ex_code_plot.R 19 | README.md 20 | anim_ggplot.gif 21 | overwrite_datasets.R 22 | package_introduction_with_transform.Rmd 23 | issue* 24 | ^docs$ 25 | ^altdoc$ 26 | ^_quarto$ 27 | ^doc$ 28 | ^Meta$ 29 | README* 30 | coffee* 31 | custom_sim.R 32 | heiss_plot_bsky.jpeg 33 | for_jason.jpeg 34 | heiss_example.jpeg 35 | model_beta_ordbeta_phi.rds 36 | ordbetareg_power.R 37 | ordbetareg_transparent_v2.png 38 | screen1.png 39 | screen2.png 40 | styles.css 41 | tshirt_coffe.png 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | inst/doc 6 | altdoc/freeze.rds 7 | ^_quarto$ 8 | /doc/ 9 | /Meta/ 10 | -------------------------------------------------------------------------------- /CRAN-SUBMISSION: -------------------------------------------------------------------------------- 1 | Version: 0.8 2 | Date: 2025-02-03 16:59:05 UTC 3 | SHA: 69f22acf93db32515bb1b42f34ed5c50e2114811 4 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: ordbetareg 2 | Type: Package 3 | Title: Ordered Beta Regression Models with 'brms' 4 | Version: 0.8 5 | Authors@R: person( 6 | "Robert", "Kubinec", 7 | email = "bobkubinec@gmail.com", 8 | role = c("aut", "cre"), 9 | comment = c(ORCID = "0000-0001-6655-4119")) 10 | Description: Implements ordered beta regression models, which are for modeling continuous variables with upper and lower bounds, such as 11 | survey sliders, dose-response relationships and indexes. For more information, see 12 | Kubinec (2023) . The package is a front-end to the R package 'brms', which 13 | facilitates a range of regression specifications, including hierarchical, dynamic and 14 | multivariate modeling. 15 | BugReports: https://github.com/saudiwin/ordbetareg_pack/issues 16 | License: MIT + file LICENSE 17 | Encoding: UTF-8 18 | LazyData: true 19 | LazyDataCompression: xz 20 | Roxygen: list(markdown = TRUE) 21 | RoxygenNote: 7.3.2 22 | Depends: 23 | R (>= 3.5), 24 | brms (>= 2.18.0), 25 | stats 26 | Imports: 27 | dplyr, 28 | ggplot2 (>= 3.4.0), 29 | tidyr, 30 | scales, 31 | stringr, 32 | abind, 33 | checkmate, 34 | insight, 35 | rstantools 36 | Suggests: 37 | rmarkdown, 38 | quarto, 39 | knitr, 40 | gt, 41 | modelsummary (>= 1.4.1), 42 | marginaleffects (>= 0.10.0), 43 | haven, 44 | Hmisc, 45 | collapse, 46 | ggthemes, 47 | glmmTMB, 48 | mice, 49 | bayestestR, 50 | gganimate, 51 | transformr, 52 | DeclareDesign 53 | VignetteBuilder: 54 | quarto 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2022 2 | COPYRIGHT HOLDER: ordbetareg authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2022 ordbetareg authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(posterior_epred_ordbeta,brmsfit) 4 | export(dordbeta) 5 | export(normalize) 6 | export(ordbetareg) 7 | export(plot_heiss) 8 | export(posterior_epred_ordbeta) 9 | export(pp_check_ordbeta) 10 | export(rordbeta) 11 | export(sim_ordbeta) 12 | import(ggplot2) 13 | importFrom(abind,abind) 14 | importFrom(brms,bf) 15 | importFrom(brms,brm) 16 | importFrom(brms,brm_multiple) 17 | importFrom(brms,custom_family) 18 | importFrom(brms,make_stancode) 19 | importFrom(brms,posterior_epred) 20 | importFrom(brms,posterior_predict) 21 | importFrom(brms,set_prior) 22 | importFrom(brms,stanvar) 23 | importFrom(dplyr,"%>%") 24 | importFrom(dplyr,arrange) 25 | importFrom(dplyr,as_tibble) 26 | importFrom(dplyr,bind_cols) 27 | importFrom(dplyr,bind_rows) 28 | importFrom(dplyr,group_by) 29 | importFrom(dplyr,lag) 30 | importFrom(dplyr,left_join) 31 | importFrom(dplyr,mutate) 32 | importFrom(dplyr,n) 33 | importFrom(dplyr,pull) 34 | importFrom(dplyr,select) 35 | importFrom(dplyr,slice) 36 | importFrom(dplyr,summarize) 37 | importFrom(dplyr,tibble) 38 | importFrom(scales,label_number) 39 | importFrom(scales,label_percent) 40 | importFrom(stats,as.formula) 41 | importFrom(stats,dbeta) 42 | importFrom(stats,median) 43 | importFrom(stats,plogis) 44 | importFrom(stats,qlogis) 45 | importFrom(stats,quantile) 46 | importFrom(stats,rbeta) 47 | importFrom(stats,rnorm) 48 | importFrom(stats,runif) 49 | importFrom(stats,update) 50 | importFrom(stats,var) 51 | importFrom(stringr,str_extract) 52 | importFrom(stringr,str_wrap) 53 | importFrom(tidyr,gather) 54 | importFrom(tidyr,unchop) 55 | importFrom(tidyr,unnest) 56 | importFrom(utils,packageVersion) 57 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # ordbetareg v0.8 2 | 3 | - add `plot_heiss` function to examine how scale components change given levels of a factor 4 | - add `prior_manual` option to allow for a complete over-ride of priors in the model 5 | (enables cutpoint modeling) 6 | - add `posterior_epred_ordbeta` function to allow for predicting the different 7 | components of the scale (bottom, middle and top) 8 | - update vignette with sections on power analysis and modeling cutpoints 9 | - assorted bug fixes to multivariate/missing data modeling 10 | 11 | # ordbetareg v0.7.3.1 12 | 13 | - removed `transformr` from imports because of many dependencies 14 | - fixed bug if the intercept was specified and centered due to `brms` code change 15 | - fixed bug that prevented the `make_stancode` option from working. changed that option 16 | to `return_stancode` 17 | - fixed `pp_check_ordbeta` for multivariate DVs 18 | 19 | # ordbetareg v0.7.2 20 | 21 | - `glmmTMB` no longer has standard errors from the `marginaleffects` package. Updated vignette to reflect this. 22 | 23 | # ordbetareg v0.7.1 24 | 25 | - Removed dependency on `faux` as `faux` was taken off CRAN. This also means the 26 | `sim_ordbeta` function cannot generate correlated covariates. 27 | - Fixed a bug with the `transform` option to the `slopes` function in `marginaleffects` 28 | in the vignette. 29 | - `marginaleffects` now supports standard errors for ordered beta regression models 30 | in glmmTMB, so vignette was updated to reflect this. 31 | 32 | # ordbetareg v0.7.0 33 | 34 | - Allow intercepts to receive separate priors from main effects (permitting zero-ing out intercepts). 35 | - Changed log-likelihood calculation to match `brms` documentation and permit point-wise 36 | `loo` calculation. 37 | - Added animated density plots for continuous responses. 38 | - Improved plot formatting and added theme and label options. 39 | - Updated vignette to include information about `glmmTMB` as an alternative for estimation 40 | and new plot functions. 41 | - Added distribution `rordbeta` and `dordbeta` functions. 42 | - Removed legacy `0 + Intercept` function code. Now accepts any kind of formula. 43 | 44 | # ordbetareg v0.5.0 45 | 46 | - Fixed bug disabling extra priors for phi regression/modeling. 47 | - Added `pp_check_ordbetareg` function for accurate posterior predictive plots for discrete and continuous outcomes. 48 | - Updated vignette and documentation. 49 | 50 | # ordbetareg v0.3.0 51 | 52 | - Fixed bugs relating to processing of bounded outcomes. 53 | - Allow for user-specific bounds to be set for normalization of outcome to 0/1 (issue #2). 54 | - Enable multiple imputation and multivariate responses with `brms`. 55 | - Fix documentation issues (issues #4 and #5). 56 | 57 | # ordbetareg v0.2.1 58 | 59 | - Added RNG code for the `induced_dirichlet` prior on cutpoints to allow for 60 | the `sample_prior` option to work with `brm`, allowing for Bayes factors 61 | and other post-estimation tools with the `bridgesampling` package (issue #1). 62 | - Fixed error in terms of correctly accounting for reverse transform of the 63 | ordered cutpoint vector. 64 | - Made the `ldpf` function more stable by using logs instead of direct 65 | multiplication. 66 | - Fixed typos in the vignette. 67 | 68 | # ordbetareg v0.2 69 | 70 | - Initial release. 71 | -------------------------------------------------------------------------------- /R/distribution.R: -------------------------------------------------------------------------------- 1 | # functions that define the ordered beta statistical distribution 2 | 3 | 4 | 5 | #' Probability Density Function for the Ordered Beta Distribution 6 | #' 7 | #' 8 | #' This function will return the density of given variates of the 9 | #' ordered beta distribution conditional on values for 10 | #' the mean (`mu`), dispersion (`phi`) and cutpoints 11 | #' governing the ratio of degenerate (discrete) to continuous 12 | #' responses. 13 | #' 14 | #' @export 15 | #' @param x Variates of the ordered beta distribution (should be in the \[0,1\] interval). 16 | #' @param mu Value of the mean of the distribution. 17 | #' Should be in the \(0,1\) interval (cannot be strictly equal to 0 or 1). If 18 | #' length is greater than 1, should be of length x. 19 | #' @param phi Value of the dispersion parameter. Should be strictly greater than 0. If 20 | #' length is greater than 1, should be of length x. 21 | #' @param cutpoints A vector of two numeric values for the cutpoints. Second value should 22 | #' @param log where to return the log density 23 | #' be strictly greater than the first value. 24 | #' @return Returns a vector of length `x` of the density of the ordered beta distribution 25 | #' conditional on `mu` and `phi`. 26 | #' @examples 27 | #' 28 | #' # examine density (likelihood) of different possible values 29 | #' # given fixed values for ordered beta parameters 30 | #' 31 | #' x <- seq(0, 1, by=0.01) 32 | #' 33 | #' x_dens <- dordbeta(x, mu = 0.3, phi=2, cutpoints=c(-2, 2)) 34 | #' 35 | #' # Most likely value for x is approx 1 36 | #' # Note discontinuity in density function between continuous/discrete values 37 | #' # density function is a combined PMF/PDF, so not a real PDF 38 | #' # can though be used for MLE 39 | #' 40 | #' plot(x_dens, x) 41 | #' 42 | #' # discrete values should be compared to each other: 43 | #' # prob of discrete 0 > prob of discrete 1 44 | #' 45 | #' x_dens[x==0] > x_dens[x==1] 46 | dordbeta <- function(x=.9, 47 | mu=0.5, 48 | phi=1, 49 | cutpoints=c(-1,1), 50 | log=FALSE) { 51 | 52 | if(!all(mu>0 & mu<1) ) { 53 | 54 | stop("Please pass a numeric value for mu that is between 0 and 1.") 55 | 56 | } 57 | 58 | if(!all(phi>0)) { 59 | 60 | stop("Please pass a numeric value for phi that is greater than 0.") 61 | 62 | } 63 | 64 | if(!(length(mu) %in% c(1,length(x)))) { 65 | 66 | stop("Please pass a vector for mu that is either length 1 or length N.") 67 | 68 | } 69 | 70 | if(!(length(phi) %in% c(1,length(x)))) { 71 | 72 | stop("Please pass a vector for phi that is either length 1 or length N.") 73 | 74 | } 75 | 76 | mu_ql <- qlogis(mu) 77 | 78 | if(length(mu_ql)==1) { 79 | mu_ql <- rep(mu_ql, length(x)) 80 | } 81 | 82 | # probabilities for three possible categories (0, proportion, 1) 83 | low <- 1-plogis(mu_ql - cutpoints[1]) 84 | middle <- plogis(mu_ql - cutpoints[1]) - plogis(mu_ql - cutpoints[2]) 85 | high <- plogis(mu_ql - cutpoints[2]) 86 | 87 | # we'll assume the same eta was used to generate outcomes 88 | 89 | out_beta <- dbeta(x = x,mu * phi, (1 - mu) * phi) 90 | 91 | # now determine which one we get for each observation 92 | outcomes <- sapply(1:length(x), function(i) { 93 | 94 | if(x[i]==0) { 95 | 96 | out <- low[i] 97 | 98 | } else if(x[i]==1) { 99 | 100 | out <- high[i] 101 | 102 | } else { 103 | 104 | out <- middle[i] * out_beta[i] 105 | 106 | } 107 | 108 | return(out) 109 | 110 | }) 111 | 112 | if(log) { 113 | 114 | return(log(outcomes)) 115 | 116 | } else { 117 | 118 | return(outcomes) 119 | 120 | } 121 | 122 | } 123 | 124 | #' Generate Ordered Beta Variates 125 | #' 126 | #' 127 | #' This function will generate ordered beta random variates given 128 | #' values for the mean (`mu`), dispersion (`phi`) and cutpoints 129 | #' governing the ratio of degenerate (discrete) to continuous 130 | #' responses. 131 | #' 132 | #' @export 133 | #' @param n Number of variates to generate. 134 | #' @param mu Value of the mean of the distribution. 135 | #' Should be in the \(0,1\) interval (cannot be strictly equal to 0 or 1). If 136 | #' length is greater than 1, should be of length `n`. 137 | #' @param phi Value of the dispersion parameter. Should be strictly greater than 0. If 138 | #' length is greater than 1, should be of length `n`. 139 | #' @param cutpoints A vector of two numeric values for the cutpoints. Second value should 140 | #' be strictly greater than the first value. 141 | #' @return A vector of length `n` of variates from the ordered beta distribution. 142 | #' @examples 143 | #' 144 | #' # generate 100 random variates with an average of 0.7 145 | #' # all will be in the closed interval \[0,1\] 146 | #' 147 | #' ordbeta_var <- rordbeta(n=100, mu=0.7, phi=2) 148 | #' 149 | #' # Will be approx mean = 0.7 with high positive skew 150 | #' 151 | #' summary(ordbeta_var) 152 | rordbeta <- function(n=100, 153 | mu=0.5, 154 | phi=1, 155 | cutpoints=c(-1,1)) { 156 | 157 | if(!all(mu>0 & mu<1) ) { 158 | 159 | stop("Please pass a numeric value for mu that is between 0 and 1.") 160 | 161 | } 162 | 163 | if(!all(phi>0)) { 164 | 165 | stop("Please pass a numeric value for phi that is greater than 0.") 166 | 167 | } 168 | 169 | if(!(length(mu) %in% c(1,n))) { 170 | 171 | stop("Please pass a vector for mu that is either length 1 or length N.") 172 | 173 | } 174 | 175 | if(!(length(phi) %in% c(1,n))) { 176 | 177 | stop("Please pass a vector for phi that is either length 1 or length N.") 178 | 179 | } 180 | 181 | mu_ql <- qlogis(mu) 182 | 183 | if(length(mu_ql)==1) { 184 | mu_ql <- rep(mu_ql, n) 185 | } 186 | 187 | # probabilities for three possible categories (0, proportion, 1) 188 | low <- 1-plogis(mu_ql - cutpoints[1]) 189 | middle <- plogis(mu_ql - cutpoints[1]) - plogis(mu_ql - cutpoints[2]) 190 | high <- plogis(mu_ql - cutpoints[2]) 191 | 192 | # we'll assume the same eta was used to generate outcomes 193 | 194 | out_beta <- rbeta(n = n,mu * phi, (1 - mu) * phi) 195 | 196 | # now determine which one we get for each observation 197 | outcomes <- sapply(1:n, function(i) { 198 | 199 | sample(1:3,size=1,prob=c(low[i],middle[i],high[i])) 200 | 201 | }) 202 | 203 | # if we get underflow/overflow, assign the max/min based on double floating point precision 204 | 205 | out_beta[out_beta==1] <- out_beta[out_beta==1] - .Machine$double.eps 206 | out_beta[out_beta==0] <- out_beta[out_beta==0] + .Machine$double.eps 207 | 208 | # now combine binary (0/1) with proportion (beta) 209 | 210 | final_out <- sapply(1:n,function(i) { 211 | if(outcomes[i]==1) { 212 | return(0) 213 | } else if(outcomes[i]==2) { 214 | return(out_beta[i]) 215 | } else { 216 | return(1) 217 | } 218 | }) 219 | 220 | 221 | return(final_out) 222 | 223 | } 224 | -------------------------------------------------------------------------------- /R/package_info.R: -------------------------------------------------------------------------------- 1 | utils::globalVariables(c('filter','resp','deparse0','SW', 2 | 'is_equal','all_vars','is.brmsfit', 3 | 'ulapply','combine_family_info', 4 | 'group_to_factor','family_names', 5 | 'get_dpar','softmax','component', 6 | '.draw','rownum','pred', 7 | 'y','median_prop','y_agg',"y_plot", 8 | 'yplot','ymin','ymax','prop_ci_nice', 9 | 'restructure','prepare_predictions', 10 | 'collapse_comma',"is.bprepl","posterior_epred_trunc", 11 | "is.bprepnl", 12 | '.dpar_family','dpar_class', 13 | 'dpar_id','get_nlpar','is.mixfamily', 14 | 'conv_cats_dpars','seq_dim','p')) 15 | 16 | #' ordbetareg: A Model for Continuous Data with Lower and Upper Bounds 17 | #' 18 | #' The `ordbetareg` package is essentially a wrapper around `brms` that 19 | #' enables the ordered beta regression model to be fit. This model has 20 | #' advantages over other alternatives for continous data with upper 21 | #' and lower bounds, such as survey sliders, indexes, 22 | #' dose-response relationships, 23 | #' and visual analog scales (among others). The package allows for all of the 24 | #' many `brms` regression modeling functions to be used with the ordered 25 | #' beta regression distribution. 26 | #' 27 | #' To learn more about how the package works, see the vignette by using 28 | #' the command `browseVignettes(package='ordbetareg')`. 29 | #' 30 | #' For more info about the distribution, see 31 | #' this paper: https://osf.io/preprints/socarxiv/2sx6y/ 32 | #' 33 | #' To cite the package, please cite the following paper: 34 | #' 35 | #' Kubinec, Robert. "Ordered Beta Regression: A Parsimonious, Well-Fitting Model for Continuous Data with Lower and Upper Bounds." **Political Analysis**. 2022. 36 | #' @keywords internal 37 | "_PACKAGE" 38 | 39 | 40 | #' Pew American Trends Panel Wave 28 41 | #' 42 | #' A dataset with the non-missing responses for the 28th wave of the 43 | #' Pew American Trends Panel survey. 44 | #' 45 | #' @format A data frame with 140 variables and 2,538 observations. 46 | #' @source https://www.pewresearch.org/social-trends/dataset/american-trends-panel-wave-28/] 47 | "pew" 48 | 49 | #' Fitted Ordered Beta Regression Model 50 | #' 51 | #' A fitted ordered beta regression model to the mean of the thermometer 52 | #' column from the pew data. 53 | #' @format an `ordbetareg` object 54 | "ord_fit_mean" 55 | 56 | #' Fitted Ordered Beta Regression Model (Phi Regression) 57 | #' 58 | #' A fitted ordered beta regression model to the dispersion parameter 59 | #' of the thermometer 60 | #' column from the pew data. 61 | #' @format an `ordbetareg` object 62 | "ord_fit_phi" 63 | 64 | #' Fitted Ordered Beta Regression Model (Multivariate regression) 65 | #' 66 | #' A fitted ordered beta regression model with two responses, 67 | #' one an ordered beta regression and the other a Gaussian/Normal 68 | #' outcome. Useful for examining mediation analysis. 69 | #' @format an `ordbetareg` object 70 | "fit_multivariate" 71 | 72 | #' Simulated Ordered Beta Regression Values 73 | #' 74 | #' The simulated draws used in the vignette for calculating statistical 75 | #' power. 76 | #' @format A dataframe 77 | "sim_data" 78 | -------------------------------------------------------------------------------- /R/preparation.R: -------------------------------------------------------------------------------- 1 | #' Normalize Outcome/Response to \\[0,1\\] Interval 2 | #' 3 | #' This function takes a continuous (double) column of data and converts it 4 | #' to have 0 as the lower bound and 1 as the upper bound. 5 | #' 6 | #' Beta regression can only be done with a response that is continuous with a 7 | #' lower bound of 0 and an upper bound of 1. However, it is 8 | #' straightforward to transform any lower and upper-bounded continuous 9 | #' variable to the \\[0,1\\] interval. This function does the transformation 10 | #' and saves the original bounds as attributes so that the bounds can be 11 | #' reverse-transformed. 12 | #' 13 | #' @param outcome Any non-character vector. Factors will be converted 14 | #' to numeric via coercion. 15 | #' @param true_bounds Specify this parameter with the lower and upper bound 16 | #' if the observed min/max of the outcome should not be used. Useful when 17 | #' an upper or lower bound exists but the observed data is less than/more than 18 | #' that bound. The normalization function will respect these bounds. 19 | #' @return A numeric vector with an upper bound of 1 and a lower bound of 20 | #' 0. The original bounds are saved in the attributes "lower_bound" and 21 | #' "upper_bound". 22 | #' @examples 23 | #' # set up arbitrary upper and lower-bounded vector 24 | #' outcome <- runif(1000, min=-33, max=445) 25 | #' 26 | #' # normalize to \\[0,1\\] 27 | #' 28 | #' trans_outcome <- normalize(outcome=outcome) 29 | #' summary(trans_outcome) 30 | #' 31 | #' # only works with numeric vectors and factors 32 | #' 33 | #' try(normalize(outcome=c('a','b'))) 34 | #' 35 | #' @export 36 | normalize <- function(outcome,true_bounds=NULL) { 37 | 38 | if(is.character(outcome)) { 39 | 40 | stop("Please do not pass a character vector as a response/outcome.\nThat really doesn't make any sense.") 41 | 42 | } 43 | 44 | if(is.factor(outcome)) { 45 | 46 | message("Converting factor response variable to numeric.") 47 | 48 | outcome <- as.numeric(outcome) 49 | 50 | } 51 | 52 | if(is.na(min(outcome, na.rm=T)) || is.infinite(min(outcome, na.rm=T))) { 53 | 54 | stop("The vector does not have enough non-missing data.") 55 | 56 | } 57 | 58 | if(!is.null(true_bounds)) { 59 | 60 | min_out <- true_bounds[1] 61 | max_out <- true_bounds[2] 62 | } else { 63 | 64 | min_out <- min(outcome, na.rm=T) 65 | max_out <- max(outcome, na.rm=T) 66 | 67 | message(paste0("Normalizing using the observed bounds of ",min_out, " - ", 68 | max_out,". If these are incorrect, please pass the bounds to use to the true_bounds parameter.")) 69 | 70 | } 71 | 72 | trans_out <- (outcome - min_out) / (max_out - min_out) 73 | 74 | # handle values very close to 0 75 | 76 | attr(trans_out, "upper_bound") <- max_out 77 | attr(trans_out, "lower_bound") <- min_out 78 | 79 | return(trans_out) 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "README" 3 | output: github_document 4 | --- 5 | 6 | ```{r setup, include=FALSE} 7 | knitr::opts_chunk$set(echo = TRUE) 8 | ``` 9 | 10 | ```{r logo,echo=FALSE,out.height="100px",out.width="100px"} 11 | knitr::include_graphics("ordbetareg_transparent_v2.png") 12 | ``` 13 | 14 | This is the repository for the `ordbetareg` package, which can be used to fit the ordered beta regression model as specified in Kubinec (2022) (see paper https://osf.io/preprints/socarxiv/2sx6y/). This package is [on CRAN](https://cran.r-project.org/web/packages/ordbetareg/index.html), but to use the development version, use this install command (requires the `remotes` package to be installed first): 15 | 16 | ``` 17 | remotes::install_github("saudiwin/ordbetareg_pack",build_vignettes=TRUE,dependencies = TRUE) 18 | ``` 19 | 20 | To learn more about beta regression modeling and where this package fits in, you can read [my blog post on the topic](https://www.robertkubinec.com/post/limited_dvs/). To learn how to use the package, see the introduction vignette by using the `vignette("package_introduction", package="ordbetareg")` command at the R console or viewing the [vignette on CRAN](https://cran.r-project.org/web/packages/ordbetareg/vignettes/package_introduction.html). 21 | 22 | If you use the package, please cite it as: 23 | 24 | Kubinec, Robert. "Ordered Beta Regression: A Parsimonious, Well-Fitting Model for Continuous Data with Lower and Upper Bounds." *Political Analysis*. 2022. https://doi.org/10.1017/pan.2022.20. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ordered Beta Regression 2 | 3 | 4 | This website contains information about the R package `ordbetareg` and 5 | general information about other implementations of the ordered beta 6 | regression model in other statistical packages. If you just want to get 7 | started with the R package `ordbetareg`, see the 8 | [vignette](https://saudiwin.github.io/ordbetareg_pack/vignettes/package_introduction.html). The package website has further details on [other implementations of the model in other statistical software packages](https://saudiwin.github.io/ordbetareg_pack/). 9 | 10 | ## About the Model 11 | 12 | Proportion data–percentiles, slider scales, visual-analog scales–have 13 | long puzzled statisticians because they combine two things: a continuous 14 | measure (the proportion) and a discrete measure (the top value of 1 or 15 | 100% and the bottom value of 0 or 0%). Conventional statistical models 16 | like the oft-used OLS regression implicitly assume these boundaries do 17 | not exist; this means an OLS regression can predict to absurd values 18 | like 115% of patients being sick or -5% of legislators being elected. 19 | 20 | To address this issue, I developed the ordered beta regression model 21 | that combines two things: beta regression, which is defined for any 22 | bounded continuous scale, and ordered logit, which works for discrete 23 | categories. By doing this, you can fit an ordered beta regression for 24 | any percentile/proportion for *both* the middle continuous part and the 25 | bounds of the scale. This model can also predict within this scale as 26 | well. 27 | 28 | I describe the model and how to do this type of [modeling in this blog 29 | post](https://www.robertkubinec.com/post/limited_dvs/). For more detail 30 | on the inner workings of the model, I refer you to the paper: 31 | 32 | Kubinec, Robert. 2023. “Ordered Beta Regression: A Parsimonious, 33 | Well-Fitting Model for Continuous Data with Lower and Upper Bounds.” 34 | Political Analysis 31(4): 519–36. doi: 35 | . 36 | 37 | For an ungated version of the article, see the pre-publication draft on 38 | OSF: 39 | 40 | 41 | 42 | ## Support ordbetareg 43 | 44 | If you’ve enjoyed using the package, consider buying some `ordbetareg` 45 | swag! Click the image below to go to the online store (I receive a few 46 | dollars from each order). 47 | 48 | ## How to Use the Model 49 | 50 | At present, ordered beta regression is available in Stan 51 | (Section 3.5), R 52 | (Section 3.1), Python 53 | (Section 3.2), Stata 54 | (Section 3.3), and Julia 55 | (Section 3.4). R has the 56 | strongest support for the model with multiple implementations, but it is 57 | perfectly usable in other statistical frameworks. Below I briefly 58 | describe the available packages. 59 | 60 | ### R 61 | 62 | Ordered beta regression is currently available in R in two different 63 | packages: 64 | [`ordbetareg`](https://cran.r-project.org/web/packages/ordbetareg/index.html) 65 | and 66 | [`glmmTMB`](https://cran.r-project.org/web/packages/glmmTMB/index.html). 67 | The primary difference between these two packages is estimation method 68 | and the number of ordered beta-specific functions. `ordbetareg` is a 69 | package that estimates a Bayesian implementation as described in the 70 | paper above by using `brms`, a powerful R package for Bayesian 71 | regression. `ordbetareg` is maintained by me and you can see the full 72 | source code here on 73 | [Github](https://github.com/saudiwin/ordbetareg_pack) (and report [an 74 | issue with the 75 | package](https://github.com/saudiwin/ordbetareg_pack/issues) if you have 76 | one). The package includes auxiliary functions like power analysis and 77 | plots that are specific to proportion responses—check out [the package 78 | vignette](https://cran.r-project.org/web/packages/ordbetareg/index.html) 79 | for more details. 80 | 81 | `glmmTMB`, by contrast, is a general purpose regression package that 82 | specializes in mixed multilevel models. It implements a broad array of 83 | regression models using maximum likelihood. This means that a model 84 | estimated in `glmmTMB` will almost certainly be faster than `ordbetareg` 85 | (although if you set up `ordbetareg` to use 86 | [`cmdstanr`](https://mc-stan.org/cmdstanr/) it can get pretty fast). On 87 | the other hand, maximum likelihood estimation has some limitations. 88 | Arguably the most important one is that it can be tricky to fit a model 89 | that doesn’t have observations at the bounds, i.e. either 0 (0%) or 1 90 | (100%). The Bayesian implementation in `ordbetareg` has no problem doing 91 | this. 92 | 93 | That being said, I think `glmmTMB` is a fine package and believe it 94 | works fine for many scholars’ problems. `brms`, which `ordbetareg` is 95 | based on, offers a lot more features, including native support for 96 | multiple imputation, time series modeling and even latent 97 | variable/factor analysis modeling, but it does require more setup and 98 | the models are generally slower. 99 | 100 | For either package, I highly recommend using 101 | [`marginaleffects`](https://marginaleffects.com/), and in particular the 102 | `avg_slopes` function, to convert the coefficient estimates in the 103 | package to the bounded scale (i.e. between 0 and 1). This function 104 | allows you to then interpret your model estimates as the effect of a 105 | covariate in terms of percentage change/proportion change in the bounded 106 | response/outcome. `marginaleffects` is available [on 107 | CRAN](https://cran.r-project.org/web/packages/marginaleffects/index.html) 108 | and works great with both `ordbetareg` and `glmmTMB`. 109 | 110 | With both of these packages available, ordered beta regression has 111 | **very strong** support in R. If you don’t have a preference for a 112 | particular software package and want to use this model, I would 113 | recommend R. 114 | 115 | ### Python 116 | 117 | Ordered beta regression is implemented using the `scipy` package with 118 | maximum likelihood estimation. At present, you’ll need to clone (i.e., 119 | download) this [Github 120 | repository](https://github.com/saudiwin/ordbetareg_py) to install the 121 | package as it is not yet available with `pip` or `conda forge`. The 122 | package includes plotting functions specific to proportion outcomes and 123 | reports coefficient values in the untransformed (logit) scale. 124 | `marginaleffects` support for this package is coming soon. 125 | 126 | ### Stata 127 | 128 | There is initial support for Stata as a set of .ado files that can be 129 | downloaded and installed in the `ado` folder in your Stata machine. More 130 | details are available on the [Github 131 | repository](https://github.com/saudiwin/ordbetareg_stata). This package 132 | supports using the `margins` command to convert coefficients to the 133 | bounded outcome scale, although it does not support all Stata features 134 | as of yet. There are plans to make this package available via SSC in the 135 | near future. 136 | 137 | ### Julia 138 | 139 | Ordered beta regression is available via the 140 | [`SubjectiveScalesModels.jl`](https://github.com/DominiqueMakowski/SubjectiveScalesModels.jl/tree/825f3fc089e64361e72e1a336003ea78320dc496) 141 | package, which offers support for a variety of regression models useful 142 | in cognitive psychology and other fields with sliders/visual analog 143 | scales. The package includes neat visualizations and is maintained by 144 | [Dominique Makowksi](https://dominiquemakowski.github.io/). 145 | 146 | ### Stan 147 | 148 | As is evident in the paper, the original model was written in Stan code. 149 | While I no longer am maintaining the Stan code, [it is available in the 150 | paper 151 | repository](https://github.com/saudiwin/ordbetareg/blob/master/beta_logit.stan) 152 | and works great. If you want to incorporate the likelihood or other Stan 153 | code into your Stan model, feel free! 154 | -------------------------------------------------------------------------------- /README.qmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Ordered Beta Regression" 3 | about: 4 | template: jolla 5 | format: 6 | html: 7 | css: styles.css 8 | --- 9 | 10 | This website contains information about the R package `ordbetareg` and general information about other implementations of the ordered beta regression model in other statistical packages. If you just want to get started with the R package `ordbetareg`, see the [vignette](vignettes/package_introduction.html). 11 | 12 | ## About the Model 13 | 14 | Proportion data--percentiles, slider scales, visual-analog scales--have long puzzled statisticians because they combine two things: a continuous measure (the proportion) and a discrete measure (the top value of 1 or 100% and the bottom value of 0 or 0%). Conventional statistical models like the oft-used OLS regression implicitly assume these boundaries do not exist; this means an OLS regression can predict to absurd values like 115% of patients being sick or -5% of legislators being elected. 15 | 16 | To address this issue, I developed the ordered beta regression model that combines two things: beta regression, which is defined for any bounded continuous scale, and ordered logit, which works for discrete categories. By doing this, you can fit an ordered beta regression for any percentile/proportion for *both* the middle continuous part and the bounds of the scale. This model can also predict within this scale as well. 17 | 18 | I describe the model and how to do this type of [modeling in this blog post](https://www.robertkubinec.com/post/limited_dvs/). For more detail on the inner workings of the model, I refer you to the paper: 19 | 20 | Kubinec, Robert. 2023. “Ordered Beta Regression: A Parsimonious, Well-Fitting Model for Continuous Data with Lower and Upper Bounds.” Political Analysis 31(4): 519–36. doi: . 21 | 22 | For an ungated version of the article, see the pre-publication draft on OSF: 23 | 24 | 25 | 26 | ## Support ordbetareg 27 | 28 | If you've enjoyed using the package, consider buying some `ordbetareg` swag! Click the image below to go to the online store (I receive a few dollars from each order). 29 | 30 | ::: {layout-ncol=2} 31 | [![](screen1.png){fig-align="center" width="2in"}](https://rmk-designs.printify.me/product/15852280/this-is-not-normal-ordbetareg-t-shirt) 32 | 33 | [![](screen2.png){fig-align="center" width="2in"}](https://rmk-designs.printify.me/product/15852502/this-is-not-normal-ordbetareg-coffee-mug) 34 | ::: 35 | 36 | 37 | 38 | 39 | 40 | ## How to Use the Model 41 | 42 | At present, ordered beta regression is available in Stan (@sec-stan), R (@sec-r), Python (@sec-python), Stata (@sec-stata), and Julia (@sec-julia). R has the strongest support for the model with multiple implementations, but it is perfectly usable in other statistical frameworks. Below I briefly describe the available packages. 43 | 44 | ### R {#sec-r} 45 | 46 | Ordered beta regression is currently available in R in two different packages: [`ordbetareg`](https://cran.r-project.org/web/packages/ordbetareg/index.html) and [`glmmTMB`](https://cran.r-project.org/web/packages/glmmTMB/index.html). The primary difference between these two packages is estimation method and the number of ordered beta-specific functions. `ordbetareg` is a package that estimates a Bayesian implementation as described in the paper above by using `brms`, a powerful R package for Bayesian regression. `ordbetareg` is maintained by me and you can see the full source code here on [Github](https://github.com/saudiwin/ordbetareg_pack) (and report [an issue with the package](https://github.com/saudiwin/ordbetareg_pack/issues) if you have one). The package includes auxiliary functions like power analysis and plots that are specific to proportion responses—check out [the package vignette](https://cran.r-project.org/web/packages/ordbetareg/index.html) for more details. 47 | 48 | `glmmTMB`, by contrast, is a general purpose regression package that specializes in mixed multilevel models. It implements a broad array of regression models using maximum likelihood. This means that a model estimated in `glmmTMB` will almost certainly be faster than `ordbetareg` (although if you set up `ordbetareg` to use [`cmdstanr`](https://mc-stan.org/cmdstanr/) it can get pretty fast). On the other hand, maximum likelihood estimation has some limitations. Arguably the most important one is that it can be tricky to fit a model that doesn't have observations at the bounds, i.e. either 0 (0%) or 1 (100%). The Bayesian implementation in `ordbetareg` has no problem doing this. 49 | 50 | That being said, I think `glmmTMB` is a fine package and believe it works fine for many scholars' problems. `brms`, which `ordbetareg` is based on, offers a lot more features, including native support for multiple imputation, time series modeling and even latent variable/factor analysis modeling, but it does require more setup and the models are generally slower. 51 | 52 | For either package, I highly recommend using [`marginaleffects`](https://marginaleffects.com/), and in particular the `avg_slopes` function, to convert the coefficient estimates in the package to the bounded scale (i.e. between 0 and 1). This function allows you to then interpret your model estimates as the effect of a covariate in terms of percentage change/proportion change in the bounded response/outcome. `marginaleffects` is available [on CRAN](https://cran.r-project.org/web/packages/marginaleffects/index.html) and works great with both `ordbetareg` and `glmmTMB`. 53 | 54 | With both of these packages available, ordered beta regression has **very strong** support in R. If you don't have a preference for a particular software package and want to use this model, I would recommend R. 55 | 56 | ### Python {#sec-python} 57 | 58 | Ordered beta regression is implemented using the `scipy` package with maximum likelihood estimation. At present, you'll need to clone (i.e., download) this [Github repository](https://github.com/saudiwin/ordbetareg_py) to install the package as it is not yet available with `pip` or `conda forge`. The package includes plotting functions specific to proportion outcomes and reports coefficient values in the untransformed (logit) scale. `marginaleffects` support for this package is coming soon. 59 | 60 | ### Stata {#sec-stata} 61 | 62 | There is initial support for Stata as a set of .ado files that can be downloaded and installed in the `ado` folder in your Stata machine. More details are available on the [Github repository](https://github.com/saudiwin/ordbetareg_stata). This package supports using the `margins` command to convert coefficients to the bounded outcome scale, although it does not support all Stata features as of yet. There are plans to make this package available via SSC in the near future. 63 | 64 | ### Julia {#sec-julia} 65 | 66 | Ordered beta regression is available via the [`SubjectiveScalesModels.jl`](https://github.com/DominiqueMakowski/SubjectiveScalesModels.jl/tree/825f3fc089e64361e72e1a336003ea78320dc496) package, which offers support for a variety of regression models useful in cognitive psychology and other fields with sliders/visual analog scales. The package includes neat visualizations and is maintained by [Dominique Makowksi](https://dominiquemakowski.github.io/). 67 | 68 | ### Stan {#sec-stan} 69 | 70 | As is evident in the paper, the original model was written in Stan code. While I no longer am maintaining the Stan code, [it is available in the paper repository](https://github.com/saudiwin/ordbetareg/blob/master/beta_logit.stan) and works great. If you want to incorporate the likelihood or other Stan code into your Stan model, feel free! 71 | -------------------------------------------------------------------------------- /altdoc/quarto_website.yml: -------------------------------------------------------------------------------- 1 | project: 2 | type: website 3 | 4 | website: 5 | title: "$ALTDOC_PACKAGE_NAME" 6 | navbar: 7 | search: true 8 | right: 9 | - icon: github 10 | href: $ALTDOC_PACKAGE_URL_GITHUB 11 | aria-label: $ALTDOC_PACKAGE_NAME GitHub 12 | sidebar: 13 | collapse-level: 1 14 | contents: 15 | - text: Home 16 | file: index.qmd 17 | - section: $ALTDOC_VIGNETTE_BLOCK 18 | - section: $ALTDOC_MAN_BLOCK 19 | - text: News 20 | file: $ALTDOC_NEWS 21 | - text: Changelog 22 | file: $ALTDOC_CHANGELOG 23 | - text: License 24 | file: $ALTDOC_LICENSE 25 | - text: Licence 26 | file: $ALTDOC_LICENCE 27 | - text: Code of conduct 28 | file: $ALTDOC_CODE_OF_CONDUCT 29 | - text: Citation 30 | file: $ALTDOC_CITATION 31 | 32 | # format: 33 | # html: 34 | # theme: cosmo 35 | # number-sections: false -------------------------------------------------------------------------------- /calc_pry1.R: -------------------------------------------------------------------------------- 1 | # calculate Pr(Y=1) and Pr(Y=0) (i.e. the discrete categories of the scale) 2 | # for different groups in the data 3 | # useful for treatment/control comparisons 4 | 5 | library(ordbetareg) 6 | library(dplyr) 7 | library(tidyr) 8 | library(posterior) 9 | library(tidybayes) 10 | library(ggplot2) 11 | 12 | data("pew") 13 | 14 | # prepare data 15 | 16 | model_data <- select(pew,therm, 17 | education="F_EDUCCAT2_FINAL", 18 | region="F_CREGION_FINAL", 19 | income="F_INCOME_FINAL") 20 | 21 | # we will look at probabilities of y=1 or y=0 for different levels of education 22 | 23 | table(model_data$education) 24 | 25 | ord_fit_mean <- ordbetareg(formula=therm ~ education + income + 26 | (1|region), 27 | data=model_data, 28 | cores=2,chains=2) 29 | 30 | # function that will return tibble with Pr(y=1) and Pr(y=0) 31 | 32 | return_pr_01 <- function(object) { 33 | 34 | # get linear prediction 35 | 36 | ord_pred <- add_linpred_draws(newdata=object$data, 37 | object=object,ndraws=200) 38 | 39 | # need top and bottom cutpoints 40 | 41 | cut_draws <- spread_draws(object,cutzero,cutone) 42 | 43 | # merge with linear prediction 44 | 45 | ord_pred <- left_join(ord_pred, 46 | distinct(select(cut_draws,.draw, 47 | cutzero,cutone)), by=c(".draw")) 48 | 49 | # calculate pr_0 and pr_1 50 | 51 | ord_pred <- group_by(ord_pred, .draw) %>% 52 | mutate(pr_y_1=plogis(.linpred - cutone), 53 | pr_y_0=1 - plogis(.linpred - cutzero)) 54 | 55 | return(ungroup(ord_pred)) 56 | 57 | } 58 | 59 | # what we want is the difference in Pr(Y=1) between two groups in the data: 60 | # education = Less than high school and education = Postgraduate 61 | # collapse to this quantity for each posterior draw (group by .draw) 62 | 63 | ord_pry1y0 <- return_pr_01(ord_fit_mean) 64 | 65 | change_groups_y1 <- group_by(ord_pry1y0, .draw) %>% 66 | summarize(group_diff=mean(pr_y_1[education=="Postgraduate"]) - mean(pr_y_1[education=="Less than high school"])) 67 | 68 | # look at the estimate of the difference in terms of posterior draws: 69 | 70 | hist(change_groups_y1$group_diff) 71 | summary(change_groups_y1$group_diff) 72 | 73 | # difference is mostly positive (more 1s in the postgraduate group relative to low-education group) 74 | 75 | # test with 5% - 95% interval 76 | 77 | quantile(change_groups_y1$group_diff, prob=c(.05, .95)) 78 | 79 | # this interval is only positive values 80 | 81 | # also plot with ggplot & tidybayes 82 | 83 | change_groups_y1 %>% 84 | ggplot(aes(x=group_diff)) + 85 | stat_halfeye() 86 | 87 | # now the same for Pr(y=0) 88 | 89 | change_groups_y0 <- group_by(ord_pry1y0, .draw) %>% 90 | summarize(group_diff=mean(pr_y_0[education=="Postgraduate"]) - mean(pr_y_0[education=="Less than high school"])) 91 | 92 | # look at the estimate of the difference in terms of posterior draws: 93 | 94 | hist(change_groups_y0$group_diff) 95 | summary(change_groups_y0$group_diff) 96 | 97 | # difference is more negative (more 0s in the postgraduate group relative to low-education group) 98 | 99 | # test with 5% - 95% interval 100 | 101 | quantile(change_groups_y0$group_diff, prob=c(.05, .95)) 102 | 103 | # this interval includes only negative values 104 | 105 | # also plot with ggplot & tidybayes 106 | 107 | change_groups_y0 %>% 108 | ggplot(aes(x=group_diff)) + 109 | stat_halfeye() 110 | 111 | # combined plot 112 | 113 | bind_rows(list(`Pr(Y=1)`=change_groups_y1, 114 | `Pr(Y=0)`=change_groups_y0), 115 | .id="Outcome Type") %>% 116 | ggplot(aes(x=group_diff)) + 117 | stat_halfeye(aes(fill=`Outcome Type`), 118 | alpha=0.5) + 119 | labs(x="Difference in Probability of Outcome from Low to High Education", 120 | y="") 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## R CMD check results 2 | 3 | 0 errors | 0 warnings | 1 notes 4 | 5 | * checking CRAN incoming feasibility ... NOTE 6 | Maintainer: 'Robert Kubinec ' 7 | 8 | New maintainer: 9 | Robert Kubinec 10 | Old maintainer(s): 11 | Robert Kubinec 12 | 13 | I updated my email address, and I trimmed the data folder. 14 | 15 | -------------------------------------------------------------------------------- /custom_sim.R: -------------------------------------------------------------------------------- 1 | # custom sim with rordbeta 2 | 3 | library(ordbetareg) 4 | library(glmmTMB) 5 | library(tidyverse) 6 | 7 | # we'll use glmmTMB because it's much faster and we're only going to use it 8 | # for point estimation 9 | 10 | # parameters to loop over and get power curves for 11 | 12 | treat_effects <- seq(0,1, by=0.2) 13 | num_subjects <- seq(10,20,by=2) 14 | num_measures_per_day <- seq(2,5,by=1) 15 | num_days <- 10 16 | # measure of how much variance there is within-subject over time 17 | # equivalent to sd of random intercepts 18 | sd_within_subject <- .2 19 | 20 | # need to simulate dispersion (phi) 21 | # higher values = more concentration (sort of like lower variance) 22 | 23 | phi <- 1 24 | 25 | # p-value threshold to use 26 | 27 | p_threshold <- 0.05 28 | 29 | # make a big grid 30 | 31 | all_params <- expand.grid(treat_effects,num_subjects, 32 | num_measures_per_day, 33 | num_days, 34 | sd_within_subject, 35 | phi) 36 | 37 | names(all_params) <- c("treat_effects","num_subjects", 38 | "num_measures_per_day","num_days","sd_within_subject", 39 | "phi") 40 | 41 | # number of sims per parameter combination 42 | # 100 would be a bit more conservative 43 | sims <- 10 44 | 45 | # parallel processing to make it run faster 46 | # set to just vanilla lapply if on Windows 47 | 48 | over_params <- parallel::mclapply(1:nrow(all_params), function(i) { 49 | 50 | this_param <- slice(all_params, i) 51 | 52 | # now loop over iterations 53 | 54 | over_sims <- lapply(1:sims, function (s) { 55 | 56 | # first draw random intercept for each respondent 57 | 58 | var_int <- rnorm(n=this_param$num_subjects, 59 | mean=0,sd=this_param$sd_within_subject) 60 | 61 | # make vector with one intercept per participant 62 | # multiple measures = multiple obs per respondent 63 | 64 | var_int_all <- rep(var_int,each=this_param$num_measures_per_day*this_param$num_days) 65 | 66 | # simulate outcome with function rordbeta given parameters 67 | 68 | # first simulate predictor as random uniform 69 | 70 | covar <- runif(n=this_param$num_subjects * this_param$num_measures_per_day * this_param$num_days, 71 | min = 0, 72 | max=1) 73 | 74 | # note: no overall intercept, we assume it is zero but that could be changed 75 | 76 | # logit function for linear model 77 | 78 | linear_model <- plogis(this_param$treat_effects * covar + 79 | var_int_all) 80 | 81 | out_ordbeta <- rordbeta(n=this_param$num_subjects * this_param$num_measures_per_day * this_param$num_days, 82 | mu = linear_model, 83 | phi=this_param$phi) 84 | 85 | # fit a glmmTMB model for speed 86 | 87 | to_model <- tibble(out_ordbeta=out_ordbeta, 88 | covar=covar, 89 | subject=rep(1:this_param$num_subjects, 90 | each=this_param$num_measures_per_day * this_param$num_days), 91 | days=rep(1:this_param$num_days, times=this_param$num_measures_per_day*this_param$num_subjects)) 92 | 93 | # fit varying intercepts model if > 1 obs per respondent 94 | 95 | if(this_param$num_measures_per_day>1 || this_param$num_days>1) { 96 | 97 | glmtmb_fit <- glmmTMB(out_ordbeta ~ covar + (1|subject),data = to_model, 98 | family=ordbeta) 99 | 100 | } else { 101 | 102 | glmtmb_fit <- glmmTMB(out_ordbeta ~ covar,data = to_model, 103 | family=ordbeta) 104 | 105 | } 106 | 107 | # now get estimates and see if coef is significant and what the bias is 108 | 109 | glm_sum <- summary(glmtmb_fit) 110 | 111 | # simulation results 112 | 113 | sim_res <- tibble(param_vals=i, 114 | iteration=s, 115 | treat_est=glm_sum$coefficients$cond['covar','Estimate'], 116 | treat_pvalue=glm_sum$coefficients$cond['covar','Pr(>|z|)'], 117 | treat_err=glm_sum$coefficients$cond['covar','Std. Error'], 118 | true_treat=this_param$treat_effects, 119 | true_phi=this_param$phi, 120 | num_subjects=this_param$num_subjects, 121 | num_measures_per_day=this_param$num_measures_per_day, 122 | sd_within_subject=this_param$sd_within_subject, 123 | p_threshold=p_threshold) 124 | 125 | # power = p < threshold 126 | 127 | sim_res <- mutate(sim_res, 128 | treat_sig=as.numeric(treat_pvalue < p_threshold)) 129 | 130 | return(sim_res) 131 | 132 | 133 | 134 | }) %>% bind_rows 135 | 136 | return(over_sims) 137 | 138 | },mc.cores=parallel::detectCores()) %>% bind_rows 139 | 140 | # estimate power by param combination 141 | 142 | power_est <- group_by(over_params, param_vals,true_treat, 143 | true_phi,num_subjects, num_measures_per_day, 144 | sd_within_subject,p_threshold) %>% 145 | summarize(power_est=mean(treat_sig)) 146 | 147 | # plot power curve for varying N 148 | # with different lines for different true treatment effects 149 | # facet wrap by num_measures_per_day 150 | 151 | library(ggplot2) 152 | 153 | power_est %>% 154 | mutate(num_measures_per_day=factor(num_measures_per_day), 155 | true_treat=paste0("Effect size: ",true_treat)) %>% 156 | ggplot(aes(y=power_est, 157 | x=num_subjects)) + 158 | geom_line(aes(linetype=num_measures_per_day, 159 | group=num_measures_per_day)) + 160 | facet_wrap(~true_treat) 161 | -------------------------------------------------------------------------------- /data/fit_multivariate.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/data/fit_multivariate.rda -------------------------------------------------------------------------------- /data/ord_fit_mean.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/data/ord_fit_mean.rda -------------------------------------------------------------------------------- /data/ord_fit_phi.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/data/ord_fit_phi.rda -------------------------------------------------------------------------------- /data/pew.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/data/pew.rda -------------------------------------------------------------------------------- /data/sim_data.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/data/sim_data.rda -------------------------------------------------------------------------------- /docs/freeze.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/freeze.rds -------------------------------------------------------------------------------- /docs/man/dordbeta_files/figure-html/unnamed-chunk-1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/man/dordbeta_files/figure-html/unnamed-chunk-1-1.png -------------------------------------------------------------------------------- /docs/man/pp_check_ordbeta_files/figure-html/unnamed-chunk-1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/man/pp_check_ordbeta_files/figure-html/unnamed-chunk-1-1.png -------------------------------------------------------------------------------- /docs/man/pp_check_ordbeta_files/figure-html/unnamed-chunk-1-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/man/pp_check_ordbeta_files/figure-html/unnamed-chunk-1-2.png -------------------------------------------------------------------------------- /docs/man/pp_check_ordbeta_files/figure-html/unnamed-chunk-1-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/man/pp_check_ordbeta_files/figure-html/unnamed-chunk-1-3.png -------------------------------------------------------------------------------- /docs/quarto_website.yml: -------------------------------------------------------------------------------- 1 | project: 2 | type: website 3 | 4 | website: 5 | title: "$ALTDOC_PACKAGE_NAME" 6 | navbar: 7 | search: true 8 | right: 9 | - icon: github 10 | href: $ALTDOC_PACKAGE_URL_GITHUB 11 | aria-label: $ALTDOC_PACKAGE_NAME GitHub 12 | sidebar: 13 | collapse-level: 1 14 | contents: 15 | - text: Home 16 | file: index.qmd 17 | - section: $ALTDOC_VIGNETTE_BLOCK 18 | - section: $ALTDOC_MAN_BLOCK 19 | - text: News 20 | file: $ALTDOC_NEWS 21 | - text: Changelog 22 | file: $ALTDOC_CHANGELOG 23 | - text: License 24 | file: $ALTDOC_LICENSE 25 | - text: Licence 26 | file: $ALTDOC_LICENCE 27 | - text: Code of conduct 28 | file: $ALTDOC_CODE_OF_CONDUCT 29 | - text: Citation 30 | file: $ALTDOC_CITATION 31 | 32 | # format: 33 | # html: 34 | # theme: cosmo 35 | # number-sections: false -------------------------------------------------------------------------------- /docs/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/screen1.png -------------------------------------------------------------------------------- /docs/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/screen2.png -------------------------------------------------------------------------------- /docs/site_libs/bootstrap/bootstrap-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/site_libs/bootstrap/bootstrap-icons.woff -------------------------------------------------------------------------------- /docs/site_libs/clipboard/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.11 3 | * https://clipboardjs.com/ 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1.anchorjs-link,.anchorjs-link:focus{opacity:1}",A.sheet.cssRules.length),A.sheet.insertRule("[data-anchorjs-icon]::after{content:attr(data-anchorjs-icon)}",A.sheet.cssRules.length),A.sheet.insertRule('@font-face{font-family:anchorjs-icons;src:url(data:n/a;base64,AAEAAAALAIAAAwAwT1MvMg8yG2cAAAE4AAAAYGNtYXDp3gC3AAABpAAAAExnYXNwAAAAEAAAA9wAAAAIZ2x5ZlQCcfwAAAH4AAABCGhlYWQHFvHyAAAAvAAAADZoaGVhBnACFwAAAPQAAAAkaG10eASAADEAAAGYAAAADGxvY2EACACEAAAB8AAAAAhtYXhwAAYAVwAAARgAAAAgbmFtZQGOH9cAAAMAAAAAunBvc3QAAwAAAAADvAAAACAAAQAAAAEAAHzE2p9fDzz1AAkEAAAAAADRecUWAAAAANQA6R8AAAAAAoACwAAAAAgAAgAAAAAAAAABAAADwP/AAAACgAAA/9MCrQABAAAAAAAAAAAAAAAAAAAAAwABAAAAAwBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAMCQAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAg//0DwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAAIAAAACgAAxAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADAAAAAIAAgAAgAAACDpy//9//8AAAAg6cv//f///+EWNwADAAEAAAAAAAAAAAAAAAAACACEAAEAAAAAAAAAAAAAAAAxAAACAAQARAKAAsAAKwBUAAABIiYnJjQ3NzY2MzIWFxYUBwcGIicmNDc3NjQnJiYjIgYHBwYUFxYUBwYGIwciJicmNDc3NjIXFhQHBwYUFxYWMzI2Nzc2NCcmNDc2MhcWFAcHBgYjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAAADACWAAEAAAAAAAEACAAAAAEAAAAAAAIAAwAIAAEAAAAAAAMACAAAAAEAAAAAAAQACAAAAAEAAAAAAAUAAQALAAEAAAAAAAYACAAAAAMAAQQJAAEAEAAMAAMAAQQJAAIABgAcAAMAAQQJAAMAEAAMAAMAAQQJAAQAEAAMAAMAAQQJAAUAAgAiAAMAAQQJAAYAEAAMYW5jaG9yanM0MDBAAGEAbgBjAGgAbwByAGoAcwA0ADAAMABAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAH//wAP) format("truetype")}',A.sheet.cssRules.length)),h=document.querySelectorAll("[id]"),t=[].map.call(h,function(A){return A.id}),i=0;i\]./()*\\\n\t\b\v\u00A0]/g,"-").replace(/-{2,}/g,"-").substring(0,this.options.truncate).replace(/^-+|-+$/gm,"").toLowerCase()},this.hasAnchorJSLink=function(A){var e=A.firstChild&&-1<(" "+A.firstChild.className+" ").indexOf(" anchorjs-link "),A=A.lastChild&&-1<(" "+A.lastChild.className+" ").indexOf(" anchorjs-link ");return e||A||!1}}}); 9 | // @license-end -------------------------------------------------------------------------------- /docs/site_libs/quarto-html/popper.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @popperjs/core v2.11.7 - MIT License 3 | */ 4 | 5 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Popper={})}(this,(function(e){"use strict";function t(e){if(null==e)return window;if("[object Window]"!==e.toString()){var t=e.ownerDocument;return t&&t.defaultView||window}return e}function n(e){return e instanceof t(e).Element||e instanceof Element}function r(e){return e instanceof t(e).HTMLElement||e instanceof HTMLElement}function o(e){return"undefined"!=typeof ShadowRoot&&(e instanceof t(e).ShadowRoot||e instanceof ShadowRoot)}var i=Math.max,a=Math.min,s=Math.round;function f(){var e=navigator.userAgentData;return null!=e&&e.brands&&Array.isArray(e.brands)?e.brands.map((function(e){return e.brand+"/"+e.version})).join(" "):navigator.userAgent}function c(){return!/^((?!chrome|android).)*safari/i.test(f())}function p(e,o,i){void 0===o&&(o=!1),void 0===i&&(i=!1);var a=e.getBoundingClientRect(),f=1,p=1;o&&r(e)&&(f=e.offsetWidth>0&&s(a.width)/e.offsetWidth||1,p=e.offsetHeight>0&&s(a.height)/e.offsetHeight||1);var u=(n(e)?t(e):window).visualViewport,l=!c()&&i,d=(a.left+(l&&u?u.offsetLeft:0))/f,h=(a.top+(l&&u?u.offsetTop:0))/p,m=a.width/f,v=a.height/p;return{width:m,height:v,top:h,right:d+m,bottom:h+v,left:d,x:d,y:h}}function u(e){var n=t(e);return{scrollLeft:n.pageXOffset,scrollTop:n.pageYOffset}}function l(e){return e?(e.nodeName||"").toLowerCase():null}function d(e){return((n(e)?e.ownerDocument:e.document)||window.document).documentElement}function h(e){return p(d(e)).left+u(e).scrollLeft}function m(e){return t(e).getComputedStyle(e)}function v(e){var t=m(e),n=t.overflow,r=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+r)}function y(e,n,o){void 0===o&&(o=!1);var i,a,f=r(n),c=r(n)&&function(e){var t=e.getBoundingClientRect(),n=s(t.width)/e.offsetWidth||1,r=s(t.height)/e.offsetHeight||1;return 1!==n||1!==r}(n),m=d(n),y=p(e,c,o),g={scrollLeft:0,scrollTop:0},b={x:0,y:0};return(f||!f&&!o)&&(("body"!==l(n)||v(m))&&(g=(i=n)!==t(i)&&r(i)?{scrollLeft:(a=i).scrollLeft,scrollTop:a.scrollTop}:u(i)),r(n)?((b=p(n,!0)).x+=n.clientLeft,b.y+=n.clientTop):m&&(b.x=h(m))),{x:y.left+g.scrollLeft-b.x,y:y.top+g.scrollTop-b.y,width:y.width,height:y.height}}function g(e){var t=p(e),n=e.offsetWidth,r=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-r)<=1&&(r=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:r}}function b(e){return"html"===l(e)?e:e.assignedSlot||e.parentNode||(o(e)?e.host:null)||d(e)}function x(e){return["html","body","#document"].indexOf(l(e))>=0?e.ownerDocument.body:r(e)&&v(e)?e:x(b(e))}function w(e,n){var r;void 0===n&&(n=[]);var o=x(e),i=o===(null==(r=e.ownerDocument)?void 0:r.body),a=t(o),s=i?[a].concat(a.visualViewport||[],v(o)?o:[]):o,f=n.concat(s);return i?f:f.concat(w(b(s)))}function O(e){return["table","td","th"].indexOf(l(e))>=0}function j(e){return r(e)&&"fixed"!==m(e).position?e.offsetParent:null}function E(e){for(var n=t(e),i=j(e);i&&O(i)&&"static"===m(i).position;)i=j(i);return i&&("html"===l(i)||"body"===l(i)&&"static"===m(i).position)?n:i||function(e){var t=/firefox/i.test(f());if(/Trident/i.test(f())&&r(e)&&"fixed"===m(e).position)return null;var n=b(e);for(o(n)&&(n=n.host);r(n)&&["html","body"].indexOf(l(n))<0;){var i=m(n);if("none"!==i.transform||"none"!==i.perspective||"paint"===i.contain||-1!==["transform","perspective"].indexOf(i.willChange)||t&&"filter"===i.willChange||t&&i.filter&&"none"!==i.filter)return n;n=n.parentNode}return null}(e)||n}var D="top",A="bottom",L="right",P="left",M="auto",k=[D,A,L,P],W="start",B="end",H="viewport",T="popper",R=k.reduce((function(e,t){return e.concat([t+"-"+W,t+"-"+B])}),[]),S=[].concat(k,[M]).reduce((function(e,t){return e.concat([t,t+"-"+W,t+"-"+B])}),[]),V=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function q(e){var t=new Map,n=new Set,r=[];function o(e){n.add(e.name),[].concat(e.requires||[],e.requiresIfExists||[]).forEach((function(e){if(!n.has(e)){var r=t.get(e);r&&o(r)}})),r.push(e)}return e.forEach((function(e){t.set(e.name,e)})),e.forEach((function(e){n.has(e.name)||o(e)})),r}function C(e){return e.split("-")[0]}function N(e,t){var n=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(n&&o(n)){var r=t;do{if(r&&e.isSameNode(r))return!0;r=r.parentNode||r.host}while(r)}return!1}function I(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function _(e,r,o){return r===H?I(function(e,n){var r=t(e),o=d(e),i=r.visualViewport,a=o.clientWidth,s=o.clientHeight,f=0,p=0;if(i){a=i.width,s=i.height;var u=c();(u||!u&&"fixed"===n)&&(f=i.offsetLeft,p=i.offsetTop)}return{width:a,height:s,x:f+h(e),y:p}}(e,o)):n(r)?function(e,t){var n=p(e,!1,"fixed"===t);return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}(r,o):I(function(e){var t,n=d(e),r=u(e),o=null==(t=e.ownerDocument)?void 0:t.body,a=i(n.scrollWidth,n.clientWidth,o?o.scrollWidth:0,o?o.clientWidth:0),s=i(n.scrollHeight,n.clientHeight,o?o.scrollHeight:0,o?o.clientHeight:0),f=-r.scrollLeft+h(e),c=-r.scrollTop;return"rtl"===m(o||n).direction&&(f+=i(n.clientWidth,o?o.clientWidth:0)-a),{width:a,height:s,x:f,y:c}}(d(e)))}function F(e,t,o,s){var f="clippingParents"===t?function(e){var t=w(b(e)),o=["absolute","fixed"].indexOf(m(e).position)>=0&&r(e)?E(e):e;return n(o)?t.filter((function(e){return n(e)&&N(e,o)&&"body"!==l(e)})):[]}(e):[].concat(t),c=[].concat(f,[o]),p=c[0],u=c.reduce((function(t,n){var r=_(e,n,s);return t.top=i(r.top,t.top),t.right=a(r.right,t.right),t.bottom=a(r.bottom,t.bottom),t.left=i(r.left,t.left),t}),_(e,p,s));return u.width=u.right-u.left,u.height=u.bottom-u.top,u.x=u.left,u.y=u.top,u}function U(e){return e.split("-")[1]}function z(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function X(e){var t,n=e.reference,r=e.element,o=e.placement,i=o?C(o):null,a=o?U(o):null,s=n.x+n.width/2-r.width/2,f=n.y+n.height/2-r.height/2;switch(i){case D:t={x:s,y:n.y-r.height};break;case A:t={x:s,y:n.y+n.height};break;case L:t={x:n.x+n.width,y:f};break;case P:t={x:n.x-r.width,y:f};break;default:t={x:n.x,y:n.y}}var c=i?z(i):null;if(null!=c){var p="y"===c?"height":"width";switch(a){case W:t[c]=t[c]-(n[p]/2-r[p]/2);break;case B:t[c]=t[c]+(n[p]/2-r[p]/2)}}return t}function Y(e){return Object.assign({},{top:0,right:0,bottom:0,left:0},e)}function G(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function J(e,t){void 0===t&&(t={});var r=t,o=r.placement,i=void 0===o?e.placement:o,a=r.strategy,s=void 0===a?e.strategy:a,f=r.boundary,c=void 0===f?"clippingParents":f,u=r.rootBoundary,l=void 0===u?H:u,h=r.elementContext,m=void 0===h?T:h,v=r.altBoundary,y=void 0!==v&&v,g=r.padding,b=void 0===g?0:g,x=Y("number"!=typeof b?b:G(b,k)),w=m===T?"reference":T,O=e.rects.popper,j=e.elements[y?w:m],E=F(n(j)?j:j.contextElement||d(e.elements.popper),c,l,s),P=p(e.elements.reference),M=X({reference:P,element:O,strategy:"absolute",placement:i}),W=I(Object.assign({},O,M)),B=m===T?W:P,R={top:E.top-B.top+x.top,bottom:B.bottom-E.bottom+x.bottom,left:E.left-B.left+x.left,right:B.right-E.right+x.right},S=e.modifiersData.offset;if(m===T&&S){var V=S[i];Object.keys(R).forEach((function(e){var t=[L,A].indexOf(e)>=0?1:-1,n=[D,A].indexOf(e)>=0?"y":"x";R[e]+=V[n]*t}))}return R}var K={placement:"bottom",modifiers:[],strategy:"absolute"};function Q(){for(var e=arguments.length,t=new Array(e),n=0;n=0?-1:1,i="function"==typeof n?n(Object.assign({},t,{placement:e})):n,a=i[0],s=i[1];return a=a||0,s=(s||0)*o,[P,L].indexOf(r)>=0?{x:s,y:a}:{x:a,y:s}}(n,t.rects,i),e}),{}),s=a[t.placement],f=s.x,c=s.y;null!=t.modifiersData.popperOffsets&&(t.modifiersData.popperOffsets.x+=f,t.modifiersData.popperOffsets.y+=c),t.modifiersData[r]=a}},se={left:"right",right:"left",bottom:"top",top:"bottom"};function fe(e){return e.replace(/left|right|bottom|top/g,(function(e){return se[e]}))}var ce={start:"end",end:"start"};function pe(e){return e.replace(/start|end/g,(function(e){return ce[e]}))}function ue(e,t){void 0===t&&(t={});var n=t,r=n.placement,o=n.boundary,i=n.rootBoundary,a=n.padding,s=n.flipVariations,f=n.allowedAutoPlacements,c=void 0===f?S:f,p=U(r),u=p?s?R:R.filter((function(e){return U(e)===p})):k,l=u.filter((function(e){return c.indexOf(e)>=0}));0===l.length&&(l=u);var d=l.reduce((function(t,n){return t[n]=J(e,{placement:n,boundary:o,rootBoundary:i,padding:a})[C(n)],t}),{});return Object.keys(d).sort((function(e,t){return d[e]-d[t]}))}var le={name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name;if(!t.modifiersData[r]._skip){for(var o=n.mainAxis,i=void 0===o||o,a=n.altAxis,s=void 0===a||a,f=n.fallbackPlacements,c=n.padding,p=n.boundary,u=n.rootBoundary,l=n.altBoundary,d=n.flipVariations,h=void 0===d||d,m=n.allowedAutoPlacements,v=t.options.placement,y=C(v),g=f||(y===v||!h?[fe(v)]:function(e){if(C(e)===M)return[];var t=fe(e);return[pe(e),t,pe(t)]}(v)),b=[v].concat(g).reduce((function(e,n){return e.concat(C(n)===M?ue(t,{placement:n,boundary:p,rootBoundary:u,padding:c,flipVariations:h,allowedAutoPlacements:m}):n)}),[]),x=t.rects.reference,w=t.rects.popper,O=new Map,j=!0,E=b[0],k=0;k=0,S=R?"width":"height",V=J(t,{placement:B,boundary:p,rootBoundary:u,altBoundary:l,padding:c}),q=R?T?L:P:T?A:D;x[S]>w[S]&&(q=fe(q));var N=fe(q),I=[];if(i&&I.push(V[H]<=0),s&&I.push(V[q]<=0,V[N]<=0),I.every((function(e){return e}))){E=B,j=!1;break}O.set(B,I)}if(j)for(var _=function(e){var t=b.find((function(t){var n=O.get(t);if(n)return n.slice(0,e).every((function(e){return e}))}));if(t)return E=t,"break"},F=h?3:1;F>0;F--){if("break"===_(F))break}t.placement!==E&&(t.modifiersData[r]._skip=!0,t.placement=E,t.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function de(e,t,n){return i(e,a(t,n))}var he={name:"preventOverflow",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name,o=n.mainAxis,s=void 0===o||o,f=n.altAxis,c=void 0!==f&&f,p=n.boundary,u=n.rootBoundary,l=n.altBoundary,d=n.padding,h=n.tether,m=void 0===h||h,v=n.tetherOffset,y=void 0===v?0:v,b=J(t,{boundary:p,rootBoundary:u,padding:d,altBoundary:l}),x=C(t.placement),w=U(t.placement),O=!w,j=z(x),M="x"===j?"y":"x",k=t.modifiersData.popperOffsets,B=t.rects.reference,H=t.rects.popper,T="function"==typeof y?y(Object.assign({},t.rects,{placement:t.placement})):y,R="number"==typeof T?{mainAxis:T,altAxis:T}:Object.assign({mainAxis:0,altAxis:0},T),S=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,V={x:0,y:0};if(k){if(s){var q,N="y"===j?D:P,I="y"===j?A:L,_="y"===j?"height":"width",F=k[j],X=F+b[N],Y=F-b[I],G=m?-H[_]/2:0,K=w===W?B[_]:H[_],Q=w===W?-H[_]:-B[_],Z=t.elements.arrow,$=m&&Z?g(Z):{width:0,height:0},ee=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},te=ee[N],ne=ee[I],re=de(0,B[_],$[_]),oe=O?B[_]/2-G-re-te-R.mainAxis:K-re-te-R.mainAxis,ie=O?-B[_]/2+G+re+ne+R.mainAxis:Q+re+ne+R.mainAxis,ae=t.elements.arrow&&E(t.elements.arrow),se=ae?"y"===j?ae.clientTop||0:ae.clientLeft||0:0,fe=null!=(q=null==S?void 0:S[j])?q:0,ce=F+ie-fe,pe=de(m?a(X,F+oe-fe-se):X,F,m?i(Y,ce):Y);k[j]=pe,V[j]=pe-F}if(c){var ue,le="x"===j?D:P,he="x"===j?A:L,me=k[M],ve="y"===M?"height":"width",ye=me+b[le],ge=me-b[he],be=-1!==[D,P].indexOf(x),xe=null!=(ue=null==S?void 0:S[M])?ue:0,we=be?ye:me-B[ve]-H[ve]-xe+R.altAxis,Oe=be?me+B[ve]+H[ve]-xe-R.altAxis:ge,je=m&&be?function(e,t,n){var r=de(e,t,n);return r>n?n:r}(we,me,Oe):de(m?we:ye,me,m?Oe:ge);k[M]=je,V[M]=je-me}t.modifiersData[r]=V}},requiresIfExists:["offset"]};var me={name:"arrow",enabled:!0,phase:"main",fn:function(e){var t,n=e.state,r=e.name,o=e.options,i=n.elements.arrow,a=n.modifiersData.popperOffsets,s=C(n.placement),f=z(s),c=[P,L].indexOf(s)>=0?"height":"width";if(i&&a){var p=function(e,t){return Y("number"!=typeof(e="function"==typeof e?e(Object.assign({},t.rects,{placement:t.placement})):e)?e:G(e,k))}(o.padding,n),u=g(i),l="y"===f?D:P,d="y"===f?A:L,h=n.rects.reference[c]+n.rects.reference[f]-a[f]-n.rects.popper[c],m=a[f]-n.rects.reference[f],v=E(i),y=v?"y"===f?v.clientHeight||0:v.clientWidth||0:0,b=h/2-m/2,x=p[l],w=y-u[c]-p[d],O=y/2-u[c]/2+b,j=de(x,O,w),M=f;n.modifiersData[r]=((t={})[M]=j,t.centerOffset=j-O,t)}},effect:function(e){var t=e.state,n=e.options.element,r=void 0===n?"[data-popper-arrow]":n;null!=r&&("string"!=typeof r||(r=t.elements.popper.querySelector(r)))&&N(t.elements.popper,r)&&(t.elements.arrow=r)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function ve(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function ye(e){return[D,L,A,P].some((function(t){return e[t]>=0}))}var ge={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(e){var t=e.state,n=e.name,r=t.rects.reference,o=t.rects.popper,i=t.modifiersData.preventOverflow,a=J(t,{elementContext:"reference"}),s=J(t,{altBoundary:!0}),f=ve(a,r),c=ve(s,o,i),p=ye(f),u=ye(c);t.modifiersData[n]={referenceClippingOffsets:f,popperEscapeOffsets:c,isReferenceHidden:p,hasPopperEscaped:u},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":p,"data-popper-escaped":u})}},be=Z({defaultModifiers:[ee,te,oe,ie]}),xe=[ee,te,oe,ie,ae,le,he,me,ge],we=Z({defaultModifiers:xe});e.applyStyles=ie,e.arrow=me,e.computeStyles=oe,e.createPopper=we,e.createPopperLite=be,e.defaultModifiers=xe,e.detectOverflow=J,e.eventListeners=ee,e.flip=le,e.hide=ge,e.offset=ae,e.popperGenerator=Z,e.popperOffsets=te,e.preventOverflow=he,Object.defineProperty(e,"__esModule",{value:!0})})); 6 | 7 | -------------------------------------------------------------------------------- /docs/site_libs/quarto-html/quarto-syntax-highlighting.css: -------------------------------------------------------------------------------- 1 | /* quarto syntax highlight colors */ 2 | :root { 3 | --quarto-hl-ot-color: #003B4F; 4 | --quarto-hl-at-color: #657422; 5 | --quarto-hl-ss-color: #20794D; 6 | --quarto-hl-an-color: #5E5E5E; 7 | --quarto-hl-fu-color: #4758AB; 8 | --quarto-hl-st-color: #20794D; 9 | --quarto-hl-cf-color: #003B4F; 10 | --quarto-hl-op-color: #5E5E5E; 11 | --quarto-hl-er-color: #AD0000; 12 | --quarto-hl-bn-color: #AD0000; 13 | --quarto-hl-al-color: #AD0000; 14 | --quarto-hl-va-color: #111111; 15 | --quarto-hl-bu-color: inherit; 16 | --quarto-hl-ex-color: inherit; 17 | --quarto-hl-pp-color: #AD0000; 18 | --quarto-hl-in-color: #5E5E5E; 19 | --quarto-hl-vs-color: #20794D; 20 | --quarto-hl-wa-color: #5E5E5E; 21 | --quarto-hl-do-color: #5E5E5E; 22 | --quarto-hl-im-color: #00769E; 23 | --quarto-hl-ch-color: #20794D; 24 | --quarto-hl-dt-color: #AD0000; 25 | --quarto-hl-fl-color: #AD0000; 26 | --quarto-hl-co-color: #5E5E5E; 27 | --quarto-hl-cv-color: #5E5E5E; 28 | --quarto-hl-cn-color: #8f5902; 29 | --quarto-hl-sc-color: #5E5E5E; 30 | --quarto-hl-dv-color: #AD0000; 31 | --quarto-hl-kw-color: #003B4F; 32 | } 33 | 34 | /* other quarto variables */ 35 | :root { 36 | --quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 37 | } 38 | 39 | pre > code.sourceCode > span { 40 | color: #003B4F; 41 | } 42 | 43 | code span { 44 | color: #003B4F; 45 | } 46 | 47 | code.sourceCode > span { 48 | color: #003B4F; 49 | } 50 | 51 | div.sourceCode, 52 | div.sourceCode pre.sourceCode { 53 | color: #003B4F; 54 | } 55 | 56 | code span.ot { 57 | color: #003B4F; 58 | font-style: inherit; 59 | } 60 | 61 | code span.at { 62 | color: #657422; 63 | font-style: inherit; 64 | } 65 | 66 | code span.ss { 67 | color: #20794D; 68 | font-style: inherit; 69 | } 70 | 71 | code span.an { 72 | color: #5E5E5E; 73 | font-style: inherit; 74 | } 75 | 76 | code span.fu { 77 | color: #4758AB; 78 | font-style: inherit; 79 | } 80 | 81 | code span.st { 82 | color: #20794D; 83 | font-style: inherit; 84 | } 85 | 86 | code span.cf { 87 | color: #003B4F; 88 | font-weight: bold; 89 | font-style: inherit; 90 | } 91 | 92 | code span.op { 93 | color: #5E5E5E; 94 | font-style: inherit; 95 | } 96 | 97 | code span.er { 98 | color: #AD0000; 99 | font-style: inherit; 100 | } 101 | 102 | code span.bn { 103 | color: #AD0000; 104 | font-style: inherit; 105 | } 106 | 107 | code span.al { 108 | color: #AD0000; 109 | font-style: inherit; 110 | } 111 | 112 | code span.va { 113 | color: #111111; 114 | font-style: inherit; 115 | } 116 | 117 | code span.bu { 118 | font-style: inherit; 119 | } 120 | 121 | code span.ex { 122 | font-style: inherit; 123 | } 124 | 125 | code span.pp { 126 | color: #AD0000; 127 | font-style: inherit; 128 | } 129 | 130 | code span.in { 131 | color: #5E5E5E; 132 | font-style: inherit; 133 | } 134 | 135 | code span.vs { 136 | color: #20794D; 137 | font-style: inherit; 138 | } 139 | 140 | code span.wa { 141 | color: #5E5E5E; 142 | font-style: italic; 143 | } 144 | 145 | code span.do { 146 | color: #5E5E5E; 147 | font-style: italic; 148 | } 149 | 150 | code span.im { 151 | color: #00769E; 152 | font-style: inherit; 153 | } 154 | 155 | code span.ch { 156 | color: #20794D; 157 | font-style: inherit; 158 | } 159 | 160 | code span.dt { 161 | color: #AD0000; 162 | font-style: inherit; 163 | } 164 | 165 | code span.fl { 166 | color: #AD0000; 167 | font-style: inherit; 168 | } 169 | 170 | code span.co { 171 | color: #5E5E5E; 172 | font-style: inherit; 173 | } 174 | 175 | code span.cv { 176 | color: #5E5E5E; 177 | font-style: italic; 178 | } 179 | 180 | code span.cn { 181 | color: #8f5902; 182 | font-style: inherit; 183 | } 184 | 185 | code span.sc { 186 | color: #5E5E5E; 187 | font-style: inherit; 188 | } 189 | 190 | code span.dv { 191 | color: #AD0000; 192 | font-style: inherit; 193 | } 194 | 195 | code span.kw { 196 | color: #003B4F; 197 | font-weight: bold; 198 | font-style: inherit; 199 | } 200 | 201 | .prevent-inlining { 202 | content: ".tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1} -------------------------------------------------------------------------------- /docs/site_libs/quarto-nav/headroom.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * headroom.js v0.12.0 - Give your page some headroom. Hide your header until you need it 3 | * Copyright (c) 2020 Nick Williams - http://wicky.nillia.ms/headroom.js 4 | * License: MIT 5 | */ 6 | 7 | !function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(t=t||self).Headroom=n()}(this,function(){"use strict";function t(){return"undefined"!=typeof window}function d(t){return function(t){return t&&t.document&&function(t){return 9===t.nodeType}(t.document)}(t)?function(t){var n=t.document,o=n.body,s=n.documentElement;return{scrollHeight:function(){return Math.max(o.scrollHeight,s.scrollHeight,o.offsetHeight,s.offsetHeight,o.clientHeight,s.clientHeight)},height:function(){return t.innerHeight||s.clientHeight||o.clientHeight},scrollY:function(){return void 0!==t.pageYOffset?t.pageYOffset:(s||o.parentNode||o).scrollTop}}}(t):function(t){return{scrollHeight:function(){return Math.max(t.scrollHeight,t.offsetHeight,t.clientHeight)},height:function(){return Math.max(t.offsetHeight,t.clientHeight)},scrollY:function(){return t.scrollTop}}}(t)}function n(t,s,e){var n,o=function(){var n=!1;try{var t={get passive(){n=!0}};window.addEventListener("test",t,t),window.removeEventListener("test",t,t)}catch(t){n=!1}return n}(),i=!1,r=d(t),l=r.scrollY(),a={};function c(){var t=Math.round(r.scrollY()),n=r.height(),o=r.scrollHeight();a.scrollY=t,a.lastScrollY=l,a.direction=ls.tolerance[a.direction],e(a),l=t,i=!1}function h(){i||(i=!0,n=requestAnimationFrame(c))}var u=!!o&&{passive:!0,capture:!1};return t.addEventListener("scroll",h,u),c(),{destroy:function(){cancelAnimationFrame(n),t.removeEventListener("scroll",h,u)}}}function o(t){return t===Object(t)?t:{down:t,up:t}}function s(t,n){n=n||{},Object.assign(this,s.options,n),this.classes=Object.assign({},s.options.classes,n.classes),this.elem=t,this.tolerance=o(this.tolerance),this.offset=o(this.offset),this.initialised=!1,this.frozen=!1}return s.prototype={constructor:s,init:function(){return s.cutsTheMustard&&!this.initialised&&(this.addClass("initial"),this.initialised=!0,setTimeout(function(t){t.scrollTracker=n(t.scroller,{offset:t.offset,tolerance:t.tolerance},t.update.bind(t))},100,this)),this},destroy:function(){this.initialised=!1,Object.keys(this.classes).forEach(this.removeClass,this),this.scrollTracker.destroy()},unpin:function(){!this.hasClass("pinned")&&this.hasClass("unpinned")||(this.addClass("unpinned"),this.removeClass("pinned"),this.onUnpin&&this.onUnpin.call(this))},pin:function(){this.hasClass("unpinned")&&(this.addClass("pinned"),this.removeClass("unpinned"),this.onPin&&this.onPin.call(this))},freeze:function(){this.frozen=!0,this.addClass("frozen")},unfreeze:function(){this.frozen=!1,this.removeClass("frozen")},top:function(){this.hasClass("top")||(this.addClass("top"),this.removeClass("notTop"),this.onTop&&this.onTop.call(this))},notTop:function(){this.hasClass("notTop")||(this.addClass("notTop"),this.removeClass("top"),this.onNotTop&&this.onNotTop.call(this))},bottom:function(){this.hasClass("bottom")||(this.addClass("bottom"),this.removeClass("notBottom"),this.onBottom&&this.onBottom.call(this))},notBottom:function(){this.hasClass("notBottom")||(this.addClass("notBottom"),this.removeClass("bottom"),this.onNotBottom&&this.onNotBottom.call(this))},shouldUnpin:function(t){return"down"===t.direction&&!t.top&&t.toleranceExceeded},shouldPin:function(t){return"up"===t.direction&&t.toleranceExceeded||t.top},addClass:function(t){this.elem.classList.add.apply(this.elem.classList,this.classes[t].split(" "))},removeClass:function(t){this.elem.classList.remove.apply(this.elem.classList,this.classes[t].split(" "))},hasClass:function(t){return this.classes[t].split(" ").every(function(t){return this.classList.contains(t)},this.elem)},update:function(t){t.isOutOfBounds||!0!==this.frozen&&(t.top?this.top():this.notTop(),t.bottom?this.bottom():this.notBottom(),this.shouldUnpin(t)?this.unpin():this.shouldPin(t)&&this.pin())}},s.options={tolerance:{up:0,down:0},offset:0,scroller:t()?window:null,classes:{frozen:"headroom--frozen",pinned:"headroom--pinned",unpinned:"headroom--unpinned",top:"headroom--top",notTop:"headroom--not-top",bottom:"headroom--bottom",notBottom:"headroom--not-bottom",initial:"headroom"}},s.cutsTheMustard=!!(t()&&function(){}.bind&&"classList"in document.documentElement&&Object.assign&&Object.keys&&requestAnimationFrame),s}); 8 | -------------------------------------------------------------------------------- /docs/site_libs/quarto-nav/quarto-nav.js: -------------------------------------------------------------------------------- 1 | const headroomChanged = new CustomEvent("quarto-hrChanged", { 2 | detail: {}, 3 | bubbles: true, 4 | cancelable: false, 5 | composed: false, 6 | }); 7 | 8 | const announceDismiss = () => { 9 | const annEl = window.document.getElementById("quarto-announcement"); 10 | if (annEl) { 11 | annEl.remove(); 12 | 13 | const annId = annEl.getAttribute("data-announcement-id"); 14 | window.localStorage.setItem(`quarto-announce-${annId}`, "true"); 15 | } 16 | }; 17 | 18 | const announceRegister = () => { 19 | const annEl = window.document.getElementById("quarto-announcement"); 20 | if (annEl) { 21 | const annId = annEl.getAttribute("data-announcement-id"); 22 | const isDismissed = 23 | window.localStorage.getItem(`quarto-announce-${annId}`) || false; 24 | if (isDismissed) { 25 | announceDismiss(); 26 | return; 27 | } else { 28 | annEl.classList.remove("hidden"); 29 | } 30 | 31 | const actionEl = annEl.querySelector(".quarto-announcement-action"); 32 | if (actionEl) { 33 | actionEl.addEventListener("click", function (e) { 34 | e.preventDefault(); 35 | // Hide the bar immediately 36 | announceDismiss(); 37 | }); 38 | } 39 | } 40 | }; 41 | 42 | window.document.addEventListener("DOMContentLoaded", function () { 43 | let init = false; 44 | 45 | announceRegister(); 46 | 47 | // Manage the back to top button, if one is present. 48 | let lastScrollTop = window.pageYOffset || document.documentElement.scrollTop; 49 | const scrollDownBuffer = 5; 50 | const scrollUpBuffer = 35; 51 | const btn = document.getElementById("quarto-back-to-top"); 52 | const hideBackToTop = () => { 53 | btn.style.display = "none"; 54 | }; 55 | const showBackToTop = () => { 56 | btn.style.display = "inline-block"; 57 | }; 58 | if (btn) { 59 | window.document.addEventListener( 60 | "scroll", 61 | function () { 62 | const currentScrollTop = 63 | window.pageYOffset || document.documentElement.scrollTop; 64 | 65 | // Shows and hides the button 'intelligently' as the user scrolls 66 | if (currentScrollTop - scrollDownBuffer > lastScrollTop) { 67 | hideBackToTop(); 68 | lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop; 69 | } else if (currentScrollTop < lastScrollTop - scrollUpBuffer) { 70 | showBackToTop(); 71 | lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop; 72 | } 73 | 74 | // Show the button at the bottom, hides it at the top 75 | if (currentScrollTop <= 0) { 76 | hideBackToTop(); 77 | } else if ( 78 | window.innerHeight + currentScrollTop >= 79 | document.body.offsetHeight 80 | ) { 81 | showBackToTop(); 82 | } 83 | }, 84 | false 85 | ); 86 | } 87 | 88 | function throttle(func, wait) { 89 | var timeout; 90 | return function () { 91 | const context = this; 92 | const args = arguments; 93 | const later = function () { 94 | clearTimeout(timeout); 95 | timeout = null; 96 | func.apply(context, args); 97 | }; 98 | 99 | if (!timeout) { 100 | timeout = setTimeout(later, wait); 101 | } 102 | }; 103 | } 104 | 105 | function headerOffset() { 106 | // Set an offset if there is are fixed top navbar 107 | const headerEl = window.document.querySelector("header.fixed-top"); 108 | if (headerEl) { 109 | return headerEl.clientHeight; 110 | } else { 111 | return 0; 112 | } 113 | } 114 | 115 | function footerOffset() { 116 | const footerEl = window.document.querySelector("footer.footer"); 117 | if (footerEl) { 118 | return footerEl.clientHeight; 119 | } else { 120 | return 0; 121 | } 122 | } 123 | 124 | function dashboardOffset() { 125 | const dashboardNavEl = window.document.getElementById( 126 | "quarto-dashboard-header" 127 | ); 128 | if (dashboardNavEl !== null) { 129 | return dashboardNavEl.clientHeight; 130 | } else { 131 | return 0; 132 | } 133 | } 134 | 135 | function updateDocumentOffsetWithoutAnimation() { 136 | updateDocumentOffset(false); 137 | } 138 | 139 | function updateDocumentOffset(animated) { 140 | // set body offset 141 | const topOffset = headerOffset(); 142 | const bodyOffset = topOffset + footerOffset() + dashboardOffset(); 143 | const bodyEl = window.document.body; 144 | bodyEl.setAttribute("data-bs-offset", topOffset); 145 | bodyEl.style.paddingTop = topOffset + "px"; 146 | 147 | // deal with sidebar offsets 148 | const sidebars = window.document.querySelectorAll( 149 | ".sidebar, .headroom-target" 150 | ); 151 | sidebars.forEach((sidebar) => { 152 | if (!animated) { 153 | sidebar.classList.add("notransition"); 154 | // Remove the no transition class after the animation has time to complete 155 | setTimeout(function () { 156 | sidebar.classList.remove("notransition"); 157 | }, 201); 158 | } 159 | 160 | if (window.Headroom && sidebar.classList.contains("sidebar-unpinned")) { 161 | sidebar.style.top = "0"; 162 | sidebar.style.maxHeight = "100vh"; 163 | } else { 164 | sidebar.style.top = topOffset + "px"; 165 | sidebar.style.maxHeight = "calc(100vh - " + topOffset + "px)"; 166 | } 167 | }); 168 | 169 | // allow space for footer 170 | const mainContainer = window.document.querySelector(".quarto-container"); 171 | if (mainContainer) { 172 | mainContainer.style.minHeight = "calc(100vh - " + bodyOffset + "px)"; 173 | } 174 | 175 | // link offset 176 | let linkStyle = window.document.querySelector("#quarto-target-style"); 177 | if (!linkStyle) { 178 | linkStyle = window.document.createElement("style"); 179 | linkStyle.setAttribute("id", "quarto-target-style"); 180 | window.document.head.appendChild(linkStyle); 181 | } 182 | while (linkStyle.firstChild) { 183 | linkStyle.removeChild(linkStyle.firstChild); 184 | } 185 | if (topOffset > 0) { 186 | linkStyle.appendChild( 187 | window.document.createTextNode(` 188 | section:target::before { 189 | content: ""; 190 | display: block; 191 | height: ${topOffset}px; 192 | margin: -${topOffset}px 0 0; 193 | }`) 194 | ); 195 | } 196 | if (init) { 197 | window.dispatchEvent(headroomChanged); 198 | } 199 | init = true; 200 | } 201 | 202 | // initialize headroom 203 | var header = window.document.querySelector("#quarto-header"); 204 | if (header && window.Headroom) { 205 | const headroom = new window.Headroom(header, { 206 | tolerance: 5, 207 | onPin: function () { 208 | const sidebars = window.document.querySelectorAll( 209 | ".sidebar, .headroom-target" 210 | ); 211 | sidebars.forEach((sidebar) => { 212 | sidebar.classList.remove("sidebar-unpinned"); 213 | }); 214 | updateDocumentOffset(); 215 | }, 216 | onUnpin: function () { 217 | const sidebars = window.document.querySelectorAll( 218 | ".sidebar, .headroom-target" 219 | ); 220 | sidebars.forEach((sidebar) => { 221 | sidebar.classList.add("sidebar-unpinned"); 222 | }); 223 | updateDocumentOffset(); 224 | }, 225 | }); 226 | headroom.init(); 227 | 228 | let frozen = false; 229 | window.quartoToggleHeadroom = function () { 230 | if (frozen) { 231 | headroom.unfreeze(); 232 | frozen = false; 233 | } else { 234 | headroom.freeze(); 235 | frozen = true; 236 | } 237 | }; 238 | } 239 | 240 | window.addEventListener( 241 | "hashchange", 242 | function (e) { 243 | if ( 244 | getComputedStyle(document.documentElement).scrollBehavior !== "smooth" 245 | ) { 246 | window.scrollTo(0, window.pageYOffset - headerOffset()); 247 | } 248 | }, 249 | false 250 | ); 251 | 252 | // Observe size changed for the header 253 | const headerEl = window.document.querySelector("header.fixed-top"); 254 | if (headerEl && window.ResizeObserver) { 255 | const observer = new window.ResizeObserver(() => { 256 | setTimeout(updateDocumentOffsetWithoutAnimation, 0); 257 | }); 258 | observer.observe(headerEl, { 259 | attributes: true, 260 | childList: true, 261 | characterData: true, 262 | }); 263 | } else { 264 | window.addEventListener( 265 | "resize", 266 | throttle(updateDocumentOffsetWithoutAnimation, 50) 267 | ); 268 | } 269 | setTimeout(updateDocumentOffsetWithoutAnimation, 250); 270 | 271 | // fixup index.html links if we aren't on the filesystem 272 | if (window.location.protocol !== "file:") { 273 | const links = window.document.querySelectorAll("a"); 274 | for (let i = 0; i < links.length; i++) { 275 | if (links[i].href) { 276 | links[i].dataset.originalHref = links[i].href; 277 | links[i].href = links[i].href.replace(/\/index\.html/, "/"); 278 | } 279 | } 280 | 281 | // Fixup any sharing links that require urls 282 | // Append url to any sharing urls 283 | const sharingLinks = window.document.querySelectorAll( 284 | "a.sidebar-tools-main-item, a.quarto-navigation-tool, a.quarto-navbar-tools, a.quarto-navbar-tools-item" 285 | ); 286 | for (let i = 0; i < sharingLinks.length; i++) { 287 | const sharingLink = sharingLinks[i]; 288 | const href = sharingLink.getAttribute("href"); 289 | if (href) { 290 | sharingLink.setAttribute( 291 | "href", 292 | href.replace("|url|", window.location.href) 293 | ); 294 | } 295 | } 296 | 297 | // Scroll the active navigation item into view, if necessary 298 | const navSidebar = window.document.querySelector("nav#quarto-sidebar"); 299 | if (navSidebar) { 300 | // Find the active item 301 | const activeItem = navSidebar.querySelector("li.sidebar-item a.active"); 302 | if (activeItem) { 303 | // Wait for the scroll height and height to resolve by observing size changes on the 304 | // nav element that is scrollable 305 | const resizeObserver = new ResizeObserver((_entries) => { 306 | // The bottom of the element 307 | const elBottom = activeItem.offsetTop; 308 | const viewBottom = navSidebar.scrollTop + navSidebar.clientHeight; 309 | 310 | // The element height and scroll height are the same, then we are still loading 311 | if (viewBottom !== navSidebar.scrollHeight) { 312 | // Determine if the item isn't visible and scroll to it 313 | if (elBottom >= viewBottom) { 314 | navSidebar.scrollTop = elBottom; 315 | } 316 | 317 | // stop observing now since we've completed the scroll 318 | resizeObserver.unobserve(navSidebar); 319 | } 320 | }); 321 | resizeObserver.observe(navSidebar); 322 | } 323 | } 324 | } 325 | }); 326 | -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/figure-html/load_data-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/vignettes/package_introduction_files/figure-html/load_data-1.png -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/figure-html/plot_cut-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/vignettes/package_introduction_files/figure-html/plot_cut-1.png -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/figure-html/plot_heiss-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/vignettes/package_introduction_files/figure-html/plot_heiss-1.png -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/figure-html/plot_phi_sim-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/vignettes/package_introduction_files/figure-html/plot_phi_sim-1.png -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/figure-html/post_predict-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/vignettes/package_introduction_files/figure-html/post_predict-1.png -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/figure-html/post_predict-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/vignettes/package_introduction_files/figure-html/post_predict-2.png -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/figure-html/sim_plot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/vignettes/package_introduction_files/figure-html/sim_plot-1.png -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/libs/bootstrap/bootstrap-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/vignettes/package_introduction_files/libs/bootstrap/bootstrap-icons.woff -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/libs/clipboard/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.11 3 | * https://clipboardjs.com/ 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1.anchorjs-link,.anchorjs-link:focus{opacity:1}",A.sheet.cssRules.length),A.sheet.insertRule("[data-anchorjs-icon]::after{content:attr(data-anchorjs-icon)}",A.sheet.cssRules.length),A.sheet.insertRule('@font-face{font-family:anchorjs-icons;src:url(data:n/a;base64,AAEAAAALAIAAAwAwT1MvMg8yG2cAAAE4AAAAYGNtYXDp3gC3AAABpAAAAExnYXNwAAAAEAAAA9wAAAAIZ2x5ZlQCcfwAAAH4AAABCGhlYWQHFvHyAAAAvAAAADZoaGVhBnACFwAAAPQAAAAkaG10eASAADEAAAGYAAAADGxvY2EACACEAAAB8AAAAAhtYXhwAAYAVwAAARgAAAAgbmFtZQGOH9cAAAMAAAAAunBvc3QAAwAAAAADvAAAACAAAQAAAAEAAHzE2p9fDzz1AAkEAAAAAADRecUWAAAAANQA6R8AAAAAAoACwAAAAAgAAgAAAAAAAAABAAADwP/AAAACgAAA/9MCrQABAAAAAAAAAAAAAAAAAAAAAwABAAAAAwBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAMCQAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAg//0DwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAAIAAAACgAAxAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADAAAAAIAAgAAgAAACDpy//9//8AAAAg6cv//f///+EWNwADAAEAAAAAAAAAAAAAAAAACACEAAEAAAAAAAAAAAAAAAAxAAACAAQARAKAAsAAKwBUAAABIiYnJjQ3NzY2MzIWFxYUBwcGIicmNDc3NjQnJiYjIgYHBwYUFxYUBwYGIwciJicmNDc3NjIXFhQHBwYUFxYWMzI2Nzc2NCcmNDc2MhcWFAcHBgYjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAAADACWAAEAAAAAAAEACAAAAAEAAAAAAAIAAwAIAAEAAAAAAAMACAAAAAEAAAAAAAQACAAAAAEAAAAAAAUAAQALAAEAAAAAAAYACAAAAAMAAQQJAAEAEAAMAAMAAQQJAAIABgAcAAMAAQQJAAMAEAAMAAMAAQQJAAQAEAAMAAMAAQQJAAUAAgAiAAMAAQQJAAYAEAAMYW5jaG9yanM0MDBAAGEAbgBjAGgAbwByAGoAcwA0ADAAMABAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAH//wAP) format("truetype")}',A.sheet.cssRules.length)),h=document.querySelectorAll("[id]"),t=[].map.call(h,function(A){return A.id}),i=0;i\]./()*\\\n\t\b\v\u00A0]/g,"-").replace(/-{2,}/g,"-").substring(0,this.options.truncate).replace(/^-+|-+$/gm,"").toLowerCase()},this.hasAnchorJSLink=function(A){var e=A.firstChild&&-1<(" "+A.firstChild.className+" ").indexOf(" anchorjs-link "),A=A.lastChild&&-1<(" "+A.lastChild.className+" ").indexOf(" anchorjs-link ");return e||A||!1}}}); 9 | // @license-end -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/libs/quarto-html/popper.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @popperjs/core v2.11.7 - MIT License 3 | */ 4 | 5 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Popper={})}(this,(function(e){"use strict";function t(e){if(null==e)return window;if("[object Window]"!==e.toString()){var t=e.ownerDocument;return t&&t.defaultView||window}return e}function n(e){return e instanceof t(e).Element||e instanceof Element}function r(e){return e instanceof t(e).HTMLElement||e instanceof HTMLElement}function o(e){return"undefined"!=typeof ShadowRoot&&(e instanceof t(e).ShadowRoot||e instanceof ShadowRoot)}var i=Math.max,a=Math.min,s=Math.round;function f(){var e=navigator.userAgentData;return null!=e&&e.brands&&Array.isArray(e.brands)?e.brands.map((function(e){return e.brand+"/"+e.version})).join(" "):navigator.userAgent}function c(){return!/^((?!chrome|android).)*safari/i.test(f())}function p(e,o,i){void 0===o&&(o=!1),void 0===i&&(i=!1);var a=e.getBoundingClientRect(),f=1,p=1;o&&r(e)&&(f=e.offsetWidth>0&&s(a.width)/e.offsetWidth||1,p=e.offsetHeight>0&&s(a.height)/e.offsetHeight||1);var u=(n(e)?t(e):window).visualViewport,l=!c()&&i,d=(a.left+(l&&u?u.offsetLeft:0))/f,h=(a.top+(l&&u?u.offsetTop:0))/p,m=a.width/f,v=a.height/p;return{width:m,height:v,top:h,right:d+m,bottom:h+v,left:d,x:d,y:h}}function u(e){var n=t(e);return{scrollLeft:n.pageXOffset,scrollTop:n.pageYOffset}}function l(e){return e?(e.nodeName||"").toLowerCase():null}function d(e){return((n(e)?e.ownerDocument:e.document)||window.document).documentElement}function h(e){return p(d(e)).left+u(e).scrollLeft}function m(e){return t(e).getComputedStyle(e)}function v(e){var t=m(e),n=t.overflow,r=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+r)}function y(e,n,o){void 0===o&&(o=!1);var i,a,f=r(n),c=r(n)&&function(e){var t=e.getBoundingClientRect(),n=s(t.width)/e.offsetWidth||1,r=s(t.height)/e.offsetHeight||1;return 1!==n||1!==r}(n),m=d(n),y=p(e,c,o),g={scrollLeft:0,scrollTop:0},b={x:0,y:0};return(f||!f&&!o)&&(("body"!==l(n)||v(m))&&(g=(i=n)!==t(i)&&r(i)?{scrollLeft:(a=i).scrollLeft,scrollTop:a.scrollTop}:u(i)),r(n)?((b=p(n,!0)).x+=n.clientLeft,b.y+=n.clientTop):m&&(b.x=h(m))),{x:y.left+g.scrollLeft-b.x,y:y.top+g.scrollTop-b.y,width:y.width,height:y.height}}function g(e){var t=p(e),n=e.offsetWidth,r=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-r)<=1&&(r=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:r}}function b(e){return"html"===l(e)?e:e.assignedSlot||e.parentNode||(o(e)?e.host:null)||d(e)}function x(e){return["html","body","#document"].indexOf(l(e))>=0?e.ownerDocument.body:r(e)&&v(e)?e:x(b(e))}function w(e,n){var r;void 0===n&&(n=[]);var o=x(e),i=o===(null==(r=e.ownerDocument)?void 0:r.body),a=t(o),s=i?[a].concat(a.visualViewport||[],v(o)?o:[]):o,f=n.concat(s);return i?f:f.concat(w(b(s)))}function O(e){return["table","td","th"].indexOf(l(e))>=0}function j(e){return r(e)&&"fixed"!==m(e).position?e.offsetParent:null}function E(e){for(var n=t(e),i=j(e);i&&O(i)&&"static"===m(i).position;)i=j(i);return i&&("html"===l(i)||"body"===l(i)&&"static"===m(i).position)?n:i||function(e){var t=/firefox/i.test(f());if(/Trident/i.test(f())&&r(e)&&"fixed"===m(e).position)return null;var n=b(e);for(o(n)&&(n=n.host);r(n)&&["html","body"].indexOf(l(n))<0;){var i=m(n);if("none"!==i.transform||"none"!==i.perspective||"paint"===i.contain||-1!==["transform","perspective"].indexOf(i.willChange)||t&&"filter"===i.willChange||t&&i.filter&&"none"!==i.filter)return n;n=n.parentNode}return null}(e)||n}var D="top",A="bottom",L="right",P="left",M="auto",k=[D,A,L,P],W="start",B="end",H="viewport",T="popper",R=k.reduce((function(e,t){return e.concat([t+"-"+W,t+"-"+B])}),[]),S=[].concat(k,[M]).reduce((function(e,t){return e.concat([t,t+"-"+W,t+"-"+B])}),[]),V=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function q(e){var t=new Map,n=new Set,r=[];function o(e){n.add(e.name),[].concat(e.requires||[],e.requiresIfExists||[]).forEach((function(e){if(!n.has(e)){var r=t.get(e);r&&o(r)}})),r.push(e)}return e.forEach((function(e){t.set(e.name,e)})),e.forEach((function(e){n.has(e.name)||o(e)})),r}function C(e){return e.split("-")[0]}function N(e,t){var n=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(n&&o(n)){var r=t;do{if(r&&e.isSameNode(r))return!0;r=r.parentNode||r.host}while(r)}return!1}function I(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function _(e,r,o){return r===H?I(function(e,n){var r=t(e),o=d(e),i=r.visualViewport,a=o.clientWidth,s=o.clientHeight,f=0,p=0;if(i){a=i.width,s=i.height;var u=c();(u||!u&&"fixed"===n)&&(f=i.offsetLeft,p=i.offsetTop)}return{width:a,height:s,x:f+h(e),y:p}}(e,o)):n(r)?function(e,t){var n=p(e,!1,"fixed"===t);return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}(r,o):I(function(e){var t,n=d(e),r=u(e),o=null==(t=e.ownerDocument)?void 0:t.body,a=i(n.scrollWidth,n.clientWidth,o?o.scrollWidth:0,o?o.clientWidth:0),s=i(n.scrollHeight,n.clientHeight,o?o.scrollHeight:0,o?o.clientHeight:0),f=-r.scrollLeft+h(e),c=-r.scrollTop;return"rtl"===m(o||n).direction&&(f+=i(n.clientWidth,o?o.clientWidth:0)-a),{width:a,height:s,x:f,y:c}}(d(e)))}function F(e,t,o,s){var f="clippingParents"===t?function(e){var t=w(b(e)),o=["absolute","fixed"].indexOf(m(e).position)>=0&&r(e)?E(e):e;return n(o)?t.filter((function(e){return n(e)&&N(e,o)&&"body"!==l(e)})):[]}(e):[].concat(t),c=[].concat(f,[o]),p=c[0],u=c.reduce((function(t,n){var r=_(e,n,s);return t.top=i(r.top,t.top),t.right=a(r.right,t.right),t.bottom=a(r.bottom,t.bottom),t.left=i(r.left,t.left),t}),_(e,p,s));return u.width=u.right-u.left,u.height=u.bottom-u.top,u.x=u.left,u.y=u.top,u}function U(e){return e.split("-")[1]}function z(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function X(e){var t,n=e.reference,r=e.element,o=e.placement,i=o?C(o):null,a=o?U(o):null,s=n.x+n.width/2-r.width/2,f=n.y+n.height/2-r.height/2;switch(i){case D:t={x:s,y:n.y-r.height};break;case A:t={x:s,y:n.y+n.height};break;case L:t={x:n.x+n.width,y:f};break;case P:t={x:n.x-r.width,y:f};break;default:t={x:n.x,y:n.y}}var c=i?z(i):null;if(null!=c){var p="y"===c?"height":"width";switch(a){case W:t[c]=t[c]-(n[p]/2-r[p]/2);break;case B:t[c]=t[c]+(n[p]/2-r[p]/2)}}return t}function Y(e){return Object.assign({},{top:0,right:0,bottom:0,left:0},e)}function G(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function J(e,t){void 0===t&&(t={});var r=t,o=r.placement,i=void 0===o?e.placement:o,a=r.strategy,s=void 0===a?e.strategy:a,f=r.boundary,c=void 0===f?"clippingParents":f,u=r.rootBoundary,l=void 0===u?H:u,h=r.elementContext,m=void 0===h?T:h,v=r.altBoundary,y=void 0!==v&&v,g=r.padding,b=void 0===g?0:g,x=Y("number"!=typeof b?b:G(b,k)),w=m===T?"reference":T,O=e.rects.popper,j=e.elements[y?w:m],E=F(n(j)?j:j.contextElement||d(e.elements.popper),c,l,s),P=p(e.elements.reference),M=X({reference:P,element:O,strategy:"absolute",placement:i}),W=I(Object.assign({},O,M)),B=m===T?W:P,R={top:E.top-B.top+x.top,bottom:B.bottom-E.bottom+x.bottom,left:E.left-B.left+x.left,right:B.right-E.right+x.right},S=e.modifiersData.offset;if(m===T&&S){var V=S[i];Object.keys(R).forEach((function(e){var t=[L,A].indexOf(e)>=0?1:-1,n=[D,A].indexOf(e)>=0?"y":"x";R[e]+=V[n]*t}))}return R}var K={placement:"bottom",modifiers:[],strategy:"absolute"};function Q(){for(var e=arguments.length,t=new Array(e),n=0;n=0?-1:1,i="function"==typeof n?n(Object.assign({},t,{placement:e})):n,a=i[0],s=i[1];return a=a||0,s=(s||0)*o,[P,L].indexOf(r)>=0?{x:s,y:a}:{x:a,y:s}}(n,t.rects,i),e}),{}),s=a[t.placement],f=s.x,c=s.y;null!=t.modifiersData.popperOffsets&&(t.modifiersData.popperOffsets.x+=f,t.modifiersData.popperOffsets.y+=c),t.modifiersData[r]=a}},se={left:"right",right:"left",bottom:"top",top:"bottom"};function fe(e){return e.replace(/left|right|bottom|top/g,(function(e){return se[e]}))}var ce={start:"end",end:"start"};function pe(e){return e.replace(/start|end/g,(function(e){return ce[e]}))}function ue(e,t){void 0===t&&(t={});var n=t,r=n.placement,o=n.boundary,i=n.rootBoundary,a=n.padding,s=n.flipVariations,f=n.allowedAutoPlacements,c=void 0===f?S:f,p=U(r),u=p?s?R:R.filter((function(e){return U(e)===p})):k,l=u.filter((function(e){return c.indexOf(e)>=0}));0===l.length&&(l=u);var d=l.reduce((function(t,n){return t[n]=J(e,{placement:n,boundary:o,rootBoundary:i,padding:a})[C(n)],t}),{});return Object.keys(d).sort((function(e,t){return d[e]-d[t]}))}var le={name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name;if(!t.modifiersData[r]._skip){for(var o=n.mainAxis,i=void 0===o||o,a=n.altAxis,s=void 0===a||a,f=n.fallbackPlacements,c=n.padding,p=n.boundary,u=n.rootBoundary,l=n.altBoundary,d=n.flipVariations,h=void 0===d||d,m=n.allowedAutoPlacements,v=t.options.placement,y=C(v),g=f||(y===v||!h?[fe(v)]:function(e){if(C(e)===M)return[];var t=fe(e);return[pe(e),t,pe(t)]}(v)),b=[v].concat(g).reduce((function(e,n){return e.concat(C(n)===M?ue(t,{placement:n,boundary:p,rootBoundary:u,padding:c,flipVariations:h,allowedAutoPlacements:m}):n)}),[]),x=t.rects.reference,w=t.rects.popper,O=new Map,j=!0,E=b[0],k=0;k=0,S=R?"width":"height",V=J(t,{placement:B,boundary:p,rootBoundary:u,altBoundary:l,padding:c}),q=R?T?L:P:T?A:D;x[S]>w[S]&&(q=fe(q));var N=fe(q),I=[];if(i&&I.push(V[H]<=0),s&&I.push(V[q]<=0,V[N]<=0),I.every((function(e){return e}))){E=B,j=!1;break}O.set(B,I)}if(j)for(var _=function(e){var t=b.find((function(t){var n=O.get(t);if(n)return n.slice(0,e).every((function(e){return e}))}));if(t)return E=t,"break"},F=h?3:1;F>0;F--){if("break"===_(F))break}t.placement!==E&&(t.modifiersData[r]._skip=!0,t.placement=E,t.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function de(e,t,n){return i(e,a(t,n))}var he={name:"preventOverflow",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name,o=n.mainAxis,s=void 0===o||o,f=n.altAxis,c=void 0!==f&&f,p=n.boundary,u=n.rootBoundary,l=n.altBoundary,d=n.padding,h=n.tether,m=void 0===h||h,v=n.tetherOffset,y=void 0===v?0:v,b=J(t,{boundary:p,rootBoundary:u,padding:d,altBoundary:l}),x=C(t.placement),w=U(t.placement),O=!w,j=z(x),M="x"===j?"y":"x",k=t.modifiersData.popperOffsets,B=t.rects.reference,H=t.rects.popper,T="function"==typeof y?y(Object.assign({},t.rects,{placement:t.placement})):y,R="number"==typeof T?{mainAxis:T,altAxis:T}:Object.assign({mainAxis:0,altAxis:0},T),S=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,V={x:0,y:0};if(k){if(s){var q,N="y"===j?D:P,I="y"===j?A:L,_="y"===j?"height":"width",F=k[j],X=F+b[N],Y=F-b[I],G=m?-H[_]/2:0,K=w===W?B[_]:H[_],Q=w===W?-H[_]:-B[_],Z=t.elements.arrow,$=m&&Z?g(Z):{width:0,height:0},ee=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},te=ee[N],ne=ee[I],re=de(0,B[_],$[_]),oe=O?B[_]/2-G-re-te-R.mainAxis:K-re-te-R.mainAxis,ie=O?-B[_]/2+G+re+ne+R.mainAxis:Q+re+ne+R.mainAxis,ae=t.elements.arrow&&E(t.elements.arrow),se=ae?"y"===j?ae.clientTop||0:ae.clientLeft||0:0,fe=null!=(q=null==S?void 0:S[j])?q:0,ce=F+ie-fe,pe=de(m?a(X,F+oe-fe-se):X,F,m?i(Y,ce):Y);k[j]=pe,V[j]=pe-F}if(c){var ue,le="x"===j?D:P,he="x"===j?A:L,me=k[M],ve="y"===M?"height":"width",ye=me+b[le],ge=me-b[he],be=-1!==[D,P].indexOf(x),xe=null!=(ue=null==S?void 0:S[M])?ue:0,we=be?ye:me-B[ve]-H[ve]-xe+R.altAxis,Oe=be?me+B[ve]+H[ve]-xe-R.altAxis:ge,je=m&&be?function(e,t,n){var r=de(e,t,n);return r>n?n:r}(we,me,Oe):de(m?we:ye,me,m?Oe:ge);k[M]=je,V[M]=je-me}t.modifiersData[r]=V}},requiresIfExists:["offset"]};var me={name:"arrow",enabled:!0,phase:"main",fn:function(e){var t,n=e.state,r=e.name,o=e.options,i=n.elements.arrow,a=n.modifiersData.popperOffsets,s=C(n.placement),f=z(s),c=[P,L].indexOf(s)>=0?"height":"width";if(i&&a){var p=function(e,t){return Y("number"!=typeof(e="function"==typeof e?e(Object.assign({},t.rects,{placement:t.placement})):e)?e:G(e,k))}(o.padding,n),u=g(i),l="y"===f?D:P,d="y"===f?A:L,h=n.rects.reference[c]+n.rects.reference[f]-a[f]-n.rects.popper[c],m=a[f]-n.rects.reference[f],v=E(i),y=v?"y"===f?v.clientHeight||0:v.clientWidth||0:0,b=h/2-m/2,x=p[l],w=y-u[c]-p[d],O=y/2-u[c]/2+b,j=de(x,O,w),M=f;n.modifiersData[r]=((t={})[M]=j,t.centerOffset=j-O,t)}},effect:function(e){var t=e.state,n=e.options.element,r=void 0===n?"[data-popper-arrow]":n;null!=r&&("string"!=typeof r||(r=t.elements.popper.querySelector(r)))&&N(t.elements.popper,r)&&(t.elements.arrow=r)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function ve(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function ye(e){return[D,L,A,P].some((function(t){return e[t]>=0}))}var ge={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(e){var t=e.state,n=e.name,r=t.rects.reference,o=t.rects.popper,i=t.modifiersData.preventOverflow,a=J(t,{elementContext:"reference"}),s=J(t,{altBoundary:!0}),f=ve(a,r),c=ve(s,o,i),p=ye(f),u=ye(c);t.modifiersData[n]={referenceClippingOffsets:f,popperEscapeOffsets:c,isReferenceHidden:p,hasPopperEscaped:u},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":p,"data-popper-escaped":u})}},be=Z({defaultModifiers:[ee,te,oe,ie]}),xe=[ee,te,oe,ie,ae,le,he,me,ge],we=Z({defaultModifiers:xe});e.applyStyles=ie,e.arrow=me,e.computeStyles=oe,e.createPopper=we,e.createPopperLite=be,e.defaultModifiers=xe,e.detectOverflow=J,e.eventListeners=ee,e.flip=le,e.hide=ge,e.offset=ae,e.popperGenerator=Z,e.popperOffsets=te,e.preventOverflow=he,Object.defineProperty(e,"__esModule",{value:!0})})); 6 | 7 | -------------------------------------------------------------------------------- /docs/vignettes/package_introduction_files/libs/quarto-html/quarto-syntax-highlighting.css: -------------------------------------------------------------------------------- 1 | /* quarto syntax highlight colors */ 2 | :root { 3 | --quarto-hl-ot-color: #003B4F; 4 | --quarto-hl-at-color: #657422; 5 | --quarto-hl-ss-color: #20794D; 6 | --quarto-hl-an-color: #5E5E5E; 7 | --quarto-hl-fu-color: #4758AB; 8 | --quarto-hl-st-color: #20794D; 9 | --quarto-hl-cf-color: #003B4F; 10 | --quarto-hl-op-color: #5E5E5E; 11 | --quarto-hl-er-color: #AD0000; 12 | --quarto-hl-bn-color: #AD0000; 13 | --quarto-hl-al-color: #AD0000; 14 | --quarto-hl-va-color: #111111; 15 | --quarto-hl-bu-color: inherit; 16 | --quarto-hl-ex-color: inherit; 17 | --quarto-hl-pp-color: #AD0000; 18 | --quarto-hl-in-color: #5E5E5E; 19 | --quarto-hl-vs-color: #20794D; 20 | --quarto-hl-wa-color: #5E5E5E; 21 | --quarto-hl-do-color: #5E5E5E; 22 | --quarto-hl-im-color: #00769E; 23 | --quarto-hl-ch-color: #20794D; 24 | --quarto-hl-dt-color: #AD0000; 25 | --quarto-hl-fl-color: #AD0000; 26 | --quarto-hl-co-color: #5E5E5E; 27 | --quarto-hl-cv-color: #5E5E5E; 28 | --quarto-hl-cn-color: #8f5902; 29 | --quarto-hl-sc-color: #5E5E5E; 30 | --quarto-hl-dv-color: #AD0000; 31 | --quarto-hl-kw-color: #003B4F; 32 | } 33 | 34 | /* other quarto variables */ 35 | :root { 36 | --quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 37 | } 38 | 39 | pre > code.sourceCode > span { 40 | color: #003B4F; 41 | } 42 | 43 | code span { 44 | color: #003B4F; 45 | } 46 | 47 | code.sourceCode > span { 48 | color: #003B4F; 49 | } 50 | 51 | div.sourceCode, 52 | div.sourceCode pre.sourceCode { 53 | color: #003B4F; 54 | } 55 | 56 | code span.ot { 57 | color: #003B4F; 58 | font-style: inherit; 59 | } 60 | 61 | code span.at { 62 | color: #657422; 63 | font-style: inherit; 64 | } 65 | 66 | code span.ss { 67 | color: #20794D; 68 | font-style: inherit; 69 | } 70 | 71 | code span.an { 72 | color: #5E5E5E; 73 | font-style: inherit; 74 | } 75 | 76 | code span.fu { 77 | color: #4758AB; 78 | font-style: inherit; 79 | } 80 | 81 | code span.st { 82 | color: #20794D; 83 | font-style: inherit; 84 | } 85 | 86 | code span.cf { 87 | color: #003B4F; 88 | font-weight: bold; 89 | font-style: inherit; 90 | } 91 | 92 | code span.op { 93 | color: #5E5E5E; 94 | font-style: inherit; 95 | } 96 | 97 | code span.er { 98 | color: #AD0000; 99 | font-style: inherit; 100 | } 101 | 102 | code span.bn { 103 | color: #AD0000; 104 | font-style: inherit; 105 | } 106 | 107 | code span.al { 108 | color: #AD0000; 109 | font-style: inherit; 110 | } 111 | 112 | code span.va { 113 | color: #111111; 114 | font-style: inherit; 115 | } 116 | 117 | code span.bu { 118 | font-style: inherit; 119 | } 120 | 121 | code span.ex { 122 | font-style: inherit; 123 | } 124 | 125 | code span.pp { 126 | color: #AD0000; 127 | font-style: inherit; 128 | } 129 | 130 | code span.in { 131 | color: #5E5E5E; 132 | font-style: inherit; 133 | } 134 | 135 | code span.vs { 136 | color: #20794D; 137 | font-style: inherit; 138 | } 139 | 140 | code span.wa { 141 | color: #5E5E5E; 142 | font-style: italic; 143 | } 144 | 145 | code span.do { 146 | color: #5E5E5E; 147 | font-style: italic; 148 | } 149 | 150 | code span.im { 151 | color: #00769E; 152 | font-style: inherit; 153 | } 154 | 155 | code span.ch { 156 | color: #20794D; 157 | font-style: inherit; 158 | } 159 | 160 | code span.dt { 161 | color: #AD0000; 162 | font-style: inherit; 163 | } 164 | 165 | code span.fl { 166 | color: #AD0000; 167 | font-style: inherit; 168 | } 169 | 170 | code span.co { 171 | color: #5E5E5E; 172 | font-style: inherit; 173 | } 174 | 175 | code span.cv { 176 | color: #5E5E5E; 177 | font-style: italic; 178 | } 179 | 180 | code span.cn { 181 | color: #8f5902; 182 | font-style: inherit; 183 | } 184 | 185 | code span.sc { 186 | color: #5E5E5E; 187 | font-style: inherit; 188 | } 189 | 190 | code span.dv { 191 | color: #AD0000; 192 | font-style: inherit; 193 | } 194 | 195 | code span.kw { 196 | color: #003B4F; 197 | font-weight: bold; 198 | font-style: inherit; 199 | } 200 | 201 | .prevent-inlining { 202 | content: ".tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1} -------------------------------------------------------------------------------- /docs/vignettes/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/vignettes/screen1.png -------------------------------------------------------------------------------- /docs/vignettes/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/docs/vignettes/screen2.png -------------------------------------------------------------------------------- /man/dordbeta.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/distribution.R 3 | \name{dordbeta} 4 | \alias{dordbeta} 5 | \title{Probability Density Function for the Ordered Beta Distribution} 6 | \usage{ 7 | dordbeta(x = 0.9, mu = 0.5, phi = 1, cutpoints = c(-1, 1), log = FALSE) 8 | } 9 | \arguments{ 10 | \item{x}{Variates of the ordered beta distribution (should be in the [0,1] interval).} 11 | 12 | \item{mu}{Value of the mean of the distribution. 13 | Should be in the \(0,1\) interval (cannot be strictly equal to 0 or 1). If 14 | length is greater than 1, should be of length x.} 15 | 16 | \item{phi}{Value of the dispersion parameter. Should be strictly greater than 0. If 17 | length is greater than 1, should be of length x.} 18 | 19 | \item{cutpoints}{A vector of two numeric values for the cutpoints. Second value should} 20 | 21 | \item{log}{where to return the log density 22 | be strictly greater than the first value.} 23 | } 24 | \value{ 25 | Returns a vector of length \code{x} of the density of the ordered beta distribution 26 | conditional on \code{mu} and \code{phi}. 27 | } 28 | \description{ 29 | This function will return the density of given variates of the 30 | ordered beta distribution conditional on values for 31 | the mean (\code{mu}), dispersion (\code{phi}) and cutpoints 32 | governing the ratio of degenerate (discrete) to continuous 33 | responses. 34 | } 35 | \examples{ 36 | 37 | # examine density (likelihood) of different possible values 38 | # given fixed values for ordered beta parameters 39 | 40 | x <- seq(0, 1, by=0.01) 41 | 42 | x_dens <- dordbeta(x, mu = 0.3, phi=2, cutpoints=c(-2, 2)) 43 | 44 | # Most likely value for x is approx 1 45 | # Note discontinuity in density function between continuous/discrete values 46 | # density function is a combined PMF/PDF, so not a real PDF 47 | # can though be used for MLE 48 | 49 | plot(x_dens, x) 50 | 51 | # discrete values should be compared to each other: 52 | # prob of discrete 0 > prob of discrete 1 53 | 54 | x_dens[x==0] > x_dens[x==1] 55 | } 56 | -------------------------------------------------------------------------------- /man/fit_multivariate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package_info.R 3 | \docType{data} 4 | \name{fit_multivariate} 5 | \alias{fit_multivariate} 6 | \title{Fitted Ordered Beta Regression Model (Multivariate regression)} 7 | \format{ 8 | an \code{ordbetareg} object 9 | } 10 | \usage{ 11 | fit_multivariate 12 | } 13 | \description{ 14 | A fitted ordered beta regression model with two responses, 15 | one an ordered beta regression and the other a Gaussian/Normal 16 | outcome. Useful for examining mediation analysis. 17 | } 18 | \keyword{datasets} 19 | -------------------------------------------------------------------------------- /man/normalize.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/preparation.R 3 | \name{normalize} 4 | \alias{normalize} 5 | \title{Normalize Outcome/Response to \[0,1\] Interval} 6 | \usage{ 7 | normalize(outcome, true_bounds = NULL) 8 | } 9 | \arguments{ 10 | \item{outcome}{Any non-character vector. Factors will be converted 11 | to numeric via coercion.} 12 | 13 | \item{true_bounds}{Specify this parameter with the lower and upper bound 14 | if the observed min/max of the outcome should not be used. Useful when 15 | an upper or lower bound exists but the observed data is less than/more than 16 | that bound. The normalization function will respect these bounds.} 17 | } 18 | \value{ 19 | A numeric vector with an upper bound of 1 and a lower bound of 20 | 0. The original bounds are saved in the attributes "lower_bound" and 21 | "upper_bound". 22 | } 23 | \description{ 24 | This function takes a continuous (double) column of data and converts it 25 | to have 0 as the lower bound and 1 as the upper bound. 26 | } 27 | \details{ 28 | Beta regression can only be done with a response that is continuous with a 29 | lower bound of 0 and an upper bound of 1. However, it is 30 | straightforward to transform any lower and upper-bounded continuous 31 | variable to the \[0,1\] interval. This function does the transformation 32 | and saves the original bounds as attributes so that the bounds can be 33 | reverse-transformed. 34 | } 35 | \examples{ 36 | # set up arbitrary upper and lower-bounded vector 37 | outcome <- runif(1000, min=-33, max=445) 38 | 39 | # normalize to \\[0,1\\] 40 | 41 | trans_outcome <- normalize(outcome=outcome) 42 | summary(trans_outcome) 43 | 44 | # only works with numeric vectors and factors 45 | 46 | try(normalize(outcome=c('a','b'))) 47 | 48 | } 49 | -------------------------------------------------------------------------------- /man/ord_fit_mean.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package_info.R 3 | \docType{data} 4 | \name{ord_fit_mean} 5 | \alias{ord_fit_mean} 6 | \title{Fitted Ordered Beta Regression Model} 7 | \format{ 8 | an \code{ordbetareg} object 9 | } 10 | \usage{ 11 | ord_fit_mean 12 | } 13 | \description{ 14 | A fitted ordered beta regression model to the mean of the thermometer 15 | column from the pew data. 16 | } 17 | \keyword{datasets} 18 | -------------------------------------------------------------------------------- /man/ord_fit_phi.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package_info.R 3 | \docType{data} 4 | \name{ord_fit_phi} 5 | \alias{ord_fit_phi} 6 | \title{Fitted Ordered Beta Regression Model (Phi Regression)} 7 | \format{ 8 | an \code{ordbetareg} object 9 | } 10 | \usage{ 11 | ord_fit_phi 12 | } 13 | \description{ 14 | A fitted ordered beta regression model to the dispersion parameter 15 | of the thermometer 16 | column from the pew data. 17 | } 18 | \keyword{datasets} 19 | -------------------------------------------------------------------------------- /man/ordbetareg-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package_info.R 3 | \docType{package} 4 | \name{ordbetareg-package} 5 | \alias{ordbetareg-package} 6 | \title{ordbetareg: A Model for Continuous Data with Lower and Upper Bounds} 7 | \description{ 8 | The \code{ordbetareg} package is essentially a wrapper around \code{brms} that 9 | enables the ordered beta regression model to be fit. This model has 10 | advantages over other alternatives for continous data with upper 11 | and lower bounds, such as survey sliders, indexes, 12 | dose-response relationships, 13 | and visual analog scales (among others). The package allows for all of the 14 | many \code{brms} regression modeling functions to be used with the ordered 15 | beta regression distribution. 16 | } 17 | \details{ 18 | To learn more about how the package works, see the vignette by using 19 | the command \code{browseVignettes(package='ordbetareg')}. 20 | 21 | For more info about the distribution, see 22 | this paper: https://osf.io/preprints/socarxiv/2sx6y/ 23 | 24 | To cite the package, please cite the following paper: 25 | 26 | Kubinec, Robert. "Ordered Beta Regression: A Parsimonious, Well-Fitting Model for Continuous Data with Lower and Upper Bounds." \strong{Political Analysis}. 2022. 27 | } 28 | \seealso{ 29 | Useful links: 30 | \itemize{ 31 | \item Report bugs at \url{https://github.com/saudiwin/ordbetareg_pack/issues} 32 | } 33 | 34 | } 35 | \author{ 36 | \strong{Maintainer}: Robert Kubinec \email{bobkubinec@gmail.com} (\href{https://orcid.org/0000-0001-6655-4119}{ORCID}) 37 | 38 | } 39 | \keyword{internal} 40 | -------------------------------------------------------------------------------- /man/ordbetareg.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/modeling.R 3 | \name{ordbetareg} 4 | \alias{ordbetareg} 5 | \title{Fit Ordered Beta Regression Model} 6 | \usage{ 7 | ordbetareg( 8 | formula = NULL, 9 | data = NULL, 10 | true_bounds = NULL, 11 | phi_reg = "none", 12 | use_brm_multiple = FALSE, 13 | coef_prior_mean = 0, 14 | coef_prior_SD = 5, 15 | intercept_prior_mean = NULL, 16 | intercept_prior_SD = NULL, 17 | phi_prior = 0.1, 18 | dirichlet_prior = c(1, 1, 1), 19 | phi_coef_prior_mean = 0, 20 | phi_coef_prior_SD = 5, 21 | phi_intercept_prior_mean = NULL, 22 | phi_intercept_prior_SD = NULL, 23 | extra_prior = NULL, 24 | manual_prior = NULL, 25 | init = "0", 26 | return_stancode = FALSE, 27 | ... 28 | ) 29 | } 30 | \arguments{ 31 | \item{formula}{Either an R formula in the form response/DV ~ var1 + var2 32 | etc. \emph{or} formula object as created/called by the \code{brms} 33 | \link[brms:brmsformula]{brms::bf} function.} 34 | 35 | \item{data}{An R data frame or tibble containing the variables in the formula} 36 | 37 | \item{true_bounds}{If the true bounds of the outcome/response 38 | don't exist in the data, pass a length 2 numeric vector 39 | of the minimum and maximum bounds to properly normalize 40 | the outcome/response} 41 | 42 | \item{phi_reg}{Whether you are including a linear model predicting 43 | the dispersion parameter, phi, and/or for the response. If you are 44 | including models for both, pass option 'both'. If you only have an 45 | intercept for the outcome (i.e. a 1 in place of covariates), pass 'only'. 46 | If you only have intercepts for phi (such as a varying intercepts/random effects) 47 | model, pass the value "intercepts". To set priors on these intercepts, 48 | use the \code{extra-prior} option with the \link[brms:set_prior]{brms::set_prior} function (class="sd"). 49 | If no model of any kind for phi, the default, pass 'none'.} 50 | 51 | \item{use_brm_multiple}{(T/F) Whether the model should use 52 | \link[brms:brm_multiple]{brms::brm_multiple} for multiple 53 | imputation over multiple dataframes passed 54 | as a list to the \code{data} argument} 55 | 56 | \item{coef_prior_mean}{The mean of the Normal distribution prior on the 57 | regression coefficients (for predicting the mean of the response). 58 | Default is 0.} 59 | 60 | \item{coef_prior_SD}{The SD of the Normal distribution prior on the 61 | regression coefficients (for predicting the mean of the response). 62 | Default is 5, which makes the prior weakly informative on the 63 | logit scale.} 64 | 65 | \item{intercept_prior_mean}{The mean of the Normal distribution prior 66 | for the intercept. By default is NULL, which means the intercept 67 | receives the same prior as \code{coef_prior_mean}. To zero out the 68 | intercept, set this parameter to 0 and \code{coef_prior_SD} to a 69 | very small number (0.01 or smaller). NOTE: the default intercept 70 | in \code{brms} is centered (mean-subtracted) by default. To use a 71 | traditional intercept, either add \code{0 + Intercept} to the 72 | formula or specify \code{center=FALSE} in the \code{bf} formula function for 73 | \code{brms}. See \code{\link[brms:brmsformula]{brms::brmsformula()}} for more info.} 74 | 75 | \item{intercept_prior_SD}{The SD of the Normal distribution prior 76 | for the intercept. By default is NULL, which means the intercept 77 | receives the same prior SD as \code{coef_prior_SD}.} 78 | 79 | \item{phi_prior}{The mean parameter of the exponential prior on 80 | phi, which determines the dispersion of the beta distribution. The 81 | default is .1, which equals a mean of 10 and is thus weakly 82 | informative on the interval (0.4, 30). If the response has very low variance (i.e. tightly) 83 | clusters around a specific value, then decreasing this prior (and increasing the expected value) 84 | may be 85 | helpful. Checking the value of phi in the output of the model command 86 | will reveal if a value of 0.1 (mean of 10) is too small.} 87 | 88 | \item{dirichlet_prior}{A vector of three integers 89 | corresponding to the prior parameters for the dirchlet distribution 90 | (alpha parameter) governing the location of the cutpoints between 91 | the components of the response (continuous vs. degenerate). 92 | The default is 1 which puts equal probability on 93 | degenerate versus continuous responses. Likely only needs to be 94 | changed in a repeated sampling situation to stabilize the cutpoint 95 | locations across samples.} 96 | 97 | \item{phi_coef_prior_mean}{The mean of the Normal distribution prior on the 98 | regression coefficients for predicting phi, the dispersion parameter. 99 | Only useful if a linear model is being fit to phi. 100 | Default is 0.} 101 | 102 | \item{phi_coef_prior_SD}{The SD of the Normal distribution prior on the 103 | regression coefficients for predicting phi, the dispersion parameter. 104 | Only useful if a linear model is being fit to phi. 105 | Default is 5, which makes the prior weakly informative on the 106 | exponential scale.} 107 | 108 | \item{phi_intercept_prior_mean}{The mean of the Normal distribution prior 109 | for the phi (dispersion) regression intercept. By default is NULL, 110 | which means the intercept 111 | receives the same prior as \code{phi_coef_prior_mean}. To zero out the 112 | intercept, set this parameter to 0 and \code{phi_coef_prior_SD} to a 113 | very small number (0.01 or smaller).} 114 | 115 | \item{phi_intercept_prior_SD}{The SD of the Normal distribution prior 116 | for the phi (dispersion) regression intercept. By default is NULL, 117 | which means the intercept 118 | receives the same prior SD as \code{phi_coef_prior_SD}.} 119 | 120 | \item{extra_prior}{An additional prior, such as a prior for a specific 121 | regression coefficient, added to the outcome regression by passing one of the \code{brms} 122 | functions \link[brms:set_prior]{brms::set_prior} or \link[brms:set_prior]{brms::prior_string} with appropriate 123 | values.} 124 | 125 | \item{manual_prior}{If you want to set your own custom priors with \code{brms}, 126 | use this option to pass any valid \code{brms} priors such as those created with 127 | \link[brms:set_prior]{brms::set_prior} or \link[brms:set_prior]{brms::prior_string}. Note that this option replaces 128 | any other priors set. Useful especially when doing something unorthodox 129 | like modeling cutpoints.} 130 | 131 | \item{init}{This parameter is used to determine starting values for 132 | the Stan sampler to begin Markov Chain Monte Carlo sampling. It is 133 | set by default at 0 because the non-linear nature of beta regression 134 | means that it is possible to begin with extreme values depending on the 135 | scale of the covariates. Setting this to 0 helps the sampler find 136 | starting values. It does, on the other hand, limit the ability to detect 137 | convergence issues with Rhat statistics. If that is a concern, such as 138 | with an experimental feature of \code{brms}, set this to \code{"random"} to get 139 | more robust starting values (just be sure to scale the covariates so they are 140 | not too large in absolute size).} 141 | 142 | \item{return_stancode}{If \code{TRUE}, will pass back the \emph{only} the Stan code for the 143 | model as a character vector rather than fitting the model.} 144 | 145 | \item{...}{All other arguments passed on to the \code{brm} function} 146 | } 147 | \value{ 148 | A \code{brms} object fitted with the ordered beta regression distribution. 149 | } 150 | \description{ 151 | This function allows you to estimate an ordered beta regression model 152 | via a formula syntax. 153 | } 154 | \details{ 155 | This function is a wrapper around the \link[brms:brm]{brms::brm} function, which is a 156 | powerful Bayesian regression modeling engine using Stan. To fully explore 157 | the options available, including dynamic and hierarchical modeling, please 158 | see the documentation for the \code{brm} function above. As the ordered beta 159 | regression model is currently not available in \code{brms} natively, this modeling 160 | function allows a \code{brms} model to be fit with the ordered beta regression 161 | distribution. 162 | 163 | For more information about the model, see the paper here: https://osf.io/preprints/socarxiv/2sx6y/. 164 | 165 | This function allows you to set priors on the dispersion parameter, 166 | the cutpoints, and the regression coefficients (see below for options). 167 | However, to add specific priors on individual covariates, you would need 168 | to use the \link[brms:set_prior]{brms::set_prior} function by specifying an individual covariate 169 | (see function documentation) and passing the result of the function call 170 | to the \code{extra_prior} argument. 171 | 172 | This function will also automatically normalize the outcome so that it 173 | lies in the \[0,1\] interval, as required by beta regression. For furthur 174 | information, see the documentation for the \link{normalize} function. 175 | 176 | Priors can be set on a variety of coefficients in the model, see 177 | the description of parameters \code{coef_prior_mean} and \code{intercept_prior_mean}, 178 | in addition to setting a custom prior with the \code{extra_prior} option. 179 | When setting priors on intercepts, it is important to note that 180 | by default, all intercepts in brms are centered (the means are 181 | subtracted from the data). As a result, a prior set on the default 182 | intercept will have a different interpretation than a traditional 183 | intercept (i.e. the value of the outcome when the covariates are 184 | all zero). To change this setting, use the \code{\link[brms:brmsformula]{brms::bf()}} function 185 | as a wrapper around the formula with the option \code{center=FALSE} to 186 | set priors on a traditional non-centered intercept. 187 | 188 | Note that while \code{brms} also supports adding \code{0 + Intercept} to the 189 | formula to address this issue, \code{ordbetareg} does not support this 190 | syntax. Instead, use \code{center=FALSE} as an option to \code{\link[brms:brmsformula]{brms::bf()}}. 191 | } 192 | \examples{ 193 | # load survey data that comes with the package 194 | 195 | library(dplyr) 196 | data("pew") 197 | 198 | # prepare data 199 | 200 | model_data <- select(pew,therm, 201 | education="F_EDUCCAT2_FINAL", 202 | region="F_CREGION_FINAL", 203 | income="F_INCOME_FINAL") 204 | 205 | # It takes a while to fit the models. Run the code 206 | # below if you want to load a saved fitted model from the 207 | # package, otherwise use the model-fitting code 208 | 209 | data("ord_fit_mean") 210 | 211 | \donttest{ 212 | # fit the actual model 213 | 214 | if(.Platform$OS.type!="windows") { 215 | 216 | ord_fit_mean <- ordbetareg(formula=therm ~ education + income + 217 | (1|region), 218 | data=model_data, 219 | cores=2,chains=2) 220 | 221 | } 222 | 223 | 224 | } 225 | 226 | # access values of the coefficients 227 | 228 | summary(ord_fit_mean) 229 | } 230 | -------------------------------------------------------------------------------- /man/pew.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package_info.R 3 | \docType{data} 4 | \name{pew} 5 | \alias{pew} 6 | \title{Pew American Trends Panel Wave 28} 7 | \format{ 8 | A data frame with 140 variables and 2,538 observations. 9 | } 10 | \source{ 11 | https://www.pewresearch.org/social-trends/dataset/american-trends-panel-wave-28/] 12 | } 13 | \usage{ 14 | pew 15 | } 16 | \description{ 17 | A dataset with the non-missing responses for the 28th wave of the 18 | Pew American Trends Panel survey. 19 | } 20 | \keyword{datasets} 21 | -------------------------------------------------------------------------------- /man/plot_heiss.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot.R 3 | \name{plot_heiss} 4 | \alias{plot_heiss} 5 | \title{Heiss Plot for Predicted Proportions of Bounded Scale Components} 6 | \usage{ 7 | plot_heiss( 8 | object, 9 | grouping_fac = NULL, 10 | recode_group_labels = NULL, 11 | ndraws = NULL, 12 | show_category_perc_labels = TRUE, 13 | category_label_font_size = 3, 14 | category_label_accuracy = 1, 15 | strip_text_font = element_text(face = "plain", size = 9), 16 | plot_title = "Predicted Proportions of Bounded Scale Components", 17 | plot_subtitle = paste0("By Unique Values of ", grouping_fac), 18 | plot_caption = NULL, 19 | plot_caption_width = 70, 20 | calc_func = mean, 21 | lb = 0.05, 22 | upb = 0.95, 23 | plot_font_size = 11, 24 | plot_font = "", 25 | y_axis_label = "Predicted Proportions", 26 | legend_name = "Scale Components", 27 | component_colors = c("#ef8737", "#bb292c", "#62205f"), 28 | component_labels = c("0", "(0-1)", "1"), 29 | ... 30 | ) 31 | } 32 | \arguments{ 33 | \item{object}{A fitted \code{\link[=ordbetareg]{ordbetareg()}} model object.} 34 | 35 | \item{grouping_fac}{A character string indicating the name of the discrete column in the data used for grouping predictions. Must be a valid column name that was passed to \code{\link[=ordbetareg]{ordbetareg()}}.} 36 | 37 | \item{recode_group_labels}{Optional. A character vector of new labels for the grouping factor levels. Must match the number and order of unique levels/values in \code{grouping_fac}.} 38 | 39 | \item{ndraws}{Optional. The number of posterior draws to use for predictions. If \code{NULL}, all available draws are used.} 40 | 41 | \item{show_category_perc_labels}{Logical. Whether to display category percentage labels on the plot. Defaults to \code{TRUE}.} 42 | 43 | \item{category_label_font_size}{The \code{ggplot2} font size for the labels on the 44 | scale components (if \code{show_category_perc_labels} is \code{TRUE}). Defaults to 3.} 45 | 46 | \item{category_label_accuracy}{The accuracy, or amount of rounding, 47 | for component label ranges on the plot (if \code{show_category_perc_labels} is \code{TRUE}). 48 | Default is 1. See \code{\link[scales:label_percent]{scales::label_percent()}} for more info on meaning of 49 | \code{accuracy} parameter.} 50 | 51 | \item{strip_text_font}{A \code{ggplot2::element_text} object defining the font style for facet strip text. Defaults to \code{element_text(face = "plain", size = 9)}.} 52 | 53 | \item{plot_title}{Title of the plot. Defaults to "Predicted Proportions of Bounded Scale Components".} 54 | 55 | \item{plot_subtitle}{Subtitle of the plot. Defaults to a message indicating the grouping variable.} 56 | 57 | \item{plot_caption}{Caption text for the plot. If NULL, the default, will use a detailed but static description of the plot contents.} 58 | 59 | \item{plot_caption_width}{Width (in characters) at which the caption is wrapped. Defaults to 60.} 60 | 61 | \item{calc_func}{A function used to calculate the central tendency of predictions. Defaults to \code{mean}.} 62 | 63 | \item{lb}{Lower bound for uncertainty intervals. Defaults to 0.05 (5th percentile).} 64 | 65 | \item{upb}{Upper bound for uncertainty intervals. Defaults to 0.95 (95th percentile).} 66 | 67 | \item{plot_font_size}{Base font size for the plot. Defaults to 11.} 68 | 69 | \item{plot_font}{Base font family for the plot. Defaults to an empty string (uses system default).} 70 | 71 | \item{y_axis_label}{Label for the y-axis. Defaults to "Predicted Proportions".} 72 | 73 | \item{legend_name}{Legend title. Defaults to "Scale Components".} 74 | 75 | \item{component_colors}{A character vector of colors for the plot components (bottom, continuous, top). Defaults to \code{c("#ef8737", "#bb292c", "#62205f")}.} 76 | 77 | \item{component_labels}{A character vector of labels for the scale/outcome components (bottom, continuous, top). Defaults to \code{c("0", "(0-1)", "1")}.} 78 | 79 | \item{...}{Additional arguments passed to [posterior_epred_ordbeta())]. 80 | 81 | [posterior_epred_ordbeta())]: R:posterior_epred_ordbeta())} 82 | } 83 | \value{ 84 | A \code{ggplot2} object representing the predicted proportions of the components. 85 | } 86 | \description{ 87 | The Heiss plot, developed by the statistician Andrew Heiss, is a plot of the predicted proportions of components on a bounded scale that are grouped by the unique levels of a grouping variable or factor (such as a random effect) in the model. 88 | The plot excels at showing how the scale components--that is, the bottom, middle continuous, and top ends of the scale--vary with a discrete variable while also capturing posterior uncertainty. 89 | This plot was the winner of the 2023 ordbetareg Visualization Prize. 90 | } 91 | \details{ 92 | For more details of the plot, see: 93 | 94 | Heiss, Andrew and Ye, Meng. "Enforcing Boundaries: China's Overseas NGO Law and Operational Constraints for Global Civil Society." Working Paper, 2023. \url{https://stats.andrewheiss.com/compassionate-clam/notebook/manuscript.html}. 95 | } 96 | \examples{ 97 | # Load a fitted model object and create a plot for 98 | # distinct values of the factor education 99 | # 100 | # data('ord_fit_mean') 101 | # 102 | # plot_heiss(ord_fit_mean,ndraws=100) 103 | # 104 | # See introductory package vignette for more information on function options 105 | 106 | } 107 | -------------------------------------------------------------------------------- /man/posterior_epred_ordbeta.brmsfit.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/modeling.R 3 | \name{posterior_epred_ordbeta.brmsfit} 4 | \alias{posterior_epred_ordbeta.brmsfit} 5 | \alias{posterior_epred_ordbeta} 6 | \title{Calculate Probability of Response Components} 7 | \usage{ 8 | \method{posterior_epred_ordbeta}{brmsfit}( 9 | object, 10 | component = "all", 11 | newdata = NULL, 12 | re_formula = NULL, 13 | re.form = NULL, 14 | resp = NULL, 15 | dpar = NULL, 16 | nlpar = NULL, 17 | ndraws = NULL, 18 | draw_ids = NULL, 19 | sort = FALSE, 20 | ... 21 | ) 22 | } 23 | \arguments{ 24 | \item{object}{An ordbetareg/brms object} 25 | 26 | \item{component}{The type of response component, i.e., the probability 27 | of the bottom end of the scale, the top end, or the middle (i.e.) 28 | continuous values.} 29 | 30 | \item{newdata}{see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}} 31 | 32 | \item{re_formula}{see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}} 33 | 34 | \item{re.form}{see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}} 35 | 36 | \item{resp}{see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}} 37 | 38 | \item{dpar}{see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}} 39 | 40 | \item{nlpar}{see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}} 41 | 42 | \item{ndraws}{see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}} 43 | 44 | \item{draw_ids}{see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}} 45 | 46 | \item{sort}{see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}} 47 | 48 | \item{...}{see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}} 49 | } 50 | \value{ 51 | An S x N matrix where S is the number of posterior draws 52 | and N is the number of observations. 53 | } 54 | \description{ 55 | This function is an alternative to the \code{brms} default \code{posterior_epred} 56 | to allow for predictions of 57 | the probability of the bottom, top, or middle (i.e. continuous) parts 58 | of the response. Useful when wanting to understand what the effect of a covariate 59 | is on bottom or top values of the scale. 60 | } 61 | \details{ 62 | To predict the top, bottom, or "middle" (i.e. continuous) components of the 63 | response, set the \code{component} argument to "top", "bottom" or "continuous". By 64 | default, \code{component} is set to "all", which will replicate behavior of the 65 | default \code{posterior_epred} function. 66 | 67 | All other arguments besides \code{component} are the same as the 68 | standard generic \code{posterior_predict}. 69 | For more information on the relevant arguments for \code{posterior_epred}, 70 | see \link[brms:posterior_epred.brmsfit]{brms::posterior_epred}. 71 | } 72 | \examples{ 73 | 74 | data('ord_fit_mean') 75 | 76 | # use function to calculate probability of top end of scale 77 | 78 | pr_1s <- posterior_epred_ordbeta(ord_fit_mean,component="top") 79 | 80 | # use function to calculate probability of bottom end of scale 81 | 82 | pr_0s <- posterior_epred_ordbeta(ord_fit_mean,component="top") 83 | 84 | # use function to calculate probability of continuous / 85 | # beta-distributed part of scale 86 | 87 | pr_beta <- posterior_epred_ordbeta(ord_fit_mean,component="top") 88 | 89 | } 90 | -------------------------------------------------------------------------------- /man/pp_check_ordbeta.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot.R 3 | \name{pp_check_ordbeta} 4 | \alias{pp_check_ordbeta} 5 | \title{Accurate Posterior Predictive Plots for Ordbetareg Models} 6 | \usage{ 7 | pp_check_ordbeta( 8 | model = NULL, 9 | dv = NULL, 10 | type = "both", 11 | ndraws = 10, 12 | cores = NULL, 13 | group = NULL, 14 | new_theme = NULL, 15 | outcome_label = NULL, 16 | animate = FALSE, 17 | reverse_bounds = TRUE, 18 | facet_scales = "fixed" 19 | ) 20 | } 21 | \arguments{ 22 | \item{model}{A fitted \link{ordbetareg} model.} 23 | 24 | \item{dv}{If you fit a model with multiple DVs/responses, 25 | pass the name of the DV as a character value. 26 | Note: this must be the same as the name of the column 27 | in the data used to fit the model.} 28 | 29 | \item{type}{Default is "both" for creating both a 30 | discrete (bar) and continuous (density) plot. Can also be 31 | "discrete" for only the bar plot for discrete values (0/1) or 32 | "continuous" for continuous values (density plot).} 33 | 34 | \item{ndraws}{Number of posterior draws to use to calculate estimates and show in plot. 35 | Defaults to 10.} 36 | 37 | \item{cores}{Number of cores to use to produce posterior predictive distribution. Defaults to NULL or 1 core.} 38 | 39 | \item{group}{A factor variable of the same number of 40 | rows as the data that is used to broduce grouped 41 | (faceted) plots of the posterior distribution.} 42 | 43 | \item{new_theme}{Any additional themes to be added to ggplot2 (default is NULL).} 44 | 45 | \item{outcome_label}{A character value that will replace the name of the outcome in the plot 46 | (default is the name of the response variable in the data frame).} 47 | 48 | \item{animate}{Whether to animate each posterior draw for continuous 49 | distributions (defaults to FALSE). Requires installation of the 50 | \code{gganimate} and \code{transformr} R packages.} 51 | 52 | \item{reverse_bounds}{Whether to plot data using the original bounds in the data 53 | (i.e. not 0 and 1).} 54 | 55 | \item{facet_scales}{The option passed on to the \code{facet_wrap} function in 56 | \code{ggplot2} for the type of scale for facetting if passing a variable for 57 | \code{group}. Defaults to \code{"fixed"} scales but can be set to \code{"free_y"} to allow 58 | probability density/bar count scales to vary or \code{"free"} to allow both x 59 | and y axes to vary (i.e., also outcome axis ticks).} 60 | } 61 | \value{ 62 | If "both", prints both plots and returns a list of both plots as 63 | \code{ggplot2} objects. Otherwise, prints and returnst the specific plot 64 | as a \code{ggplot2} object. 65 | } 66 | \description{ 67 | The standard \link[brms:pp_check.brmsfit]{brms::pp_check} plot available via \code{brms} 68 | is not accurate for ordbetareg models because an 69 | ordered beta regression has both continuous and discrete 70 | components. This function implements a bar plot and a density 71 | plot for the continuous and discrete elements separately, 72 | and will return accurate posterior predictive plots 73 | relative to the data. 74 | } 75 | \examples{ 76 | 77 | # need a fitted ordbetareg model 78 | 79 | data("ord_fit_mean") 80 | 81 | out_plots <- pp_check_ordbeta(ord_fit_mean) 82 | 83 | # view discrete bar plot 84 | 85 | out_plots$discrete 86 | 87 | # view continuous density plot 88 | 89 | out_plots$continuous 90 | 91 | # change title using ggplot2 ggtitle function 92 | 93 | out_plots$discrete + ggplot2::ggtitle("New title") 94 | 95 | } 96 | -------------------------------------------------------------------------------- /man/rordbeta.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/distribution.R 3 | \name{rordbeta} 4 | \alias{rordbeta} 5 | \title{Generate Ordered Beta Variates} 6 | \usage{ 7 | rordbeta(n = 100, mu = 0.5, phi = 1, cutpoints = c(-1, 1)) 8 | } 9 | \arguments{ 10 | \item{n}{Number of variates to generate.} 11 | 12 | \item{mu}{Value of the mean of the distribution. 13 | Should be in the \(0,1\) interval (cannot be strictly equal to 0 or 1). If 14 | length is greater than 1, should be of length \code{n}.} 15 | 16 | \item{phi}{Value of the dispersion parameter. Should be strictly greater than 0. If 17 | length is greater than 1, should be of length \code{n}.} 18 | 19 | \item{cutpoints}{A vector of two numeric values for the cutpoints. Second value should 20 | be strictly greater than the first value.} 21 | } 22 | \value{ 23 | A vector of length \code{n} of variates from the ordered beta distribution. 24 | } 25 | \description{ 26 | This function will generate ordered beta random variates given 27 | values for the mean (\code{mu}), dispersion (\code{phi}) and cutpoints 28 | governing the ratio of degenerate (discrete) to continuous 29 | responses. 30 | } 31 | \examples{ 32 | 33 | # generate 100 random variates with an average of 0.7 34 | # all will be in the closed interval \[0,1\] 35 | 36 | ordbeta_var <- rordbeta(n=100, mu=0.7, phi=2) 37 | 38 | # Will be approx mean = 0.7 with high positive skew 39 | 40 | summary(ordbeta_var) 41 | } 42 | -------------------------------------------------------------------------------- /man/sim_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package_info.R 3 | \docType{data} 4 | \name{sim_data} 5 | \alias{sim_data} 6 | \title{Simulated Ordered Beta Regression Values} 7 | \format{ 8 | A dataframe 9 | } 10 | \usage{ 11 | sim_data 12 | } 13 | \description{ 14 | The simulated draws used in the vignette for calculating statistical 15 | power. 16 | } 17 | \keyword{datasets} 18 | -------------------------------------------------------------------------------- /man/sim_ordbeta.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/modeling.R 3 | \name{sim_ordbeta} 4 | \alias{sim_ordbeta} 5 | \title{Power Calculation via Simulation of the Ordered Beta Regression Model} 6 | \usage{ 7 | sim_ordbeta( 8 | N = 1000, 9 | k = 5, 10 | iter = 1000, 11 | cores = 1, 12 | phi = 1, 13 | cutpoints = c(-1, 1), 14 | beta_coef = NULL, 15 | beta_type = "continuous", 16 | treat_assign = 0.5, 17 | return_data = FALSE, 18 | seed = as.numeric(Sys.time()), 19 | ... 20 | ) 21 | } 22 | \arguments{ 23 | \item{N}{The sample size for the simulation. Include a vector of integers 24 | to examine power/results for multiple sample sizes.} 25 | 26 | \item{k}{The number of covariates/predictors.} 27 | 28 | \item{iter}{The number of simulations to run. For power calculation, 29 | should be at least 500 (yes, this will take some time).} 30 | 31 | \item{cores}{The number of cores to use to parallelize the simulation.} 32 | 33 | \item{phi}{Value of the dispersion parameter in the beta distribution.} 34 | 35 | \item{cutpoints}{Value of the two cutpoints for the ordered model. 36 | By default are the values -1 and +1 (these are interpreted in the 37 | logit scale and so should not be too large). The farther apart, 38 | the fewer degenerate (0 or 1) responses there will be in the distribution.} 39 | 40 | \item{beta_coef}{If not null, a vector of length \code{k} of the true 41 | predictor coefficients/treatment values to use for the simulation. 42 | Otherwise, coefficients are drawn from a random uniform distribution 43 | from -1 to 1 for each predictor.} 44 | 45 | \item{beta_type}{Can be either \code{continuous} or \code{binary}. Use the latter 46 | for conventional treatments with two values.} 47 | 48 | \item{treat_assign}{If \code{beta_type} is set to \code{binary}, 49 | you can use this parameter to set the proportion 50 | of \code{N} assigned to treatment. By default, 51 | the parameter is set to 0.5 for 52 | equal/balanced treatment control groups.} 53 | 54 | \item{return_data}{Whether to return the simulated dqta as a list 55 | in the \code{data} column of the returned data frame.} 56 | 57 | \item{seed}{The seed to use to make the results reproducible. Set 58 | automatically to a date-time stamp.} 59 | 60 | \item{...}{Any other arguments are passed on to the \link[brms:brm]{brms::brm} function 61 | to control modeling options.} 62 | } 63 | \value{ 64 | a tibble data frame with columns of simulated and estimated values and 65 | rows for each simulation iteration X coefficient combination. I.e., 66 | if there are five predictors, and 1,000 iterations, the resulting data frame 67 | will have 1,000 rows. If there are multiple values for \code{N}, 68 | then each value 69 | of \code{N} will have its own set of iterations, making the final size of the 70 | data a multiple of the number of sample sizes to iterate over. The 71 | data frame will have the following columns: 72 | 1. 73 | } 74 | \description{ 75 | This function allows you to calculate power curves (or anything else) 76 | via simulating the ordered beta regression model. 77 | } 78 | \details{ 79 | This function implements the simulation found in Kubinec (2022). This 80 | simulation allows you to vary the sample size, number & type of predictors, 81 | values of the predictors (or treatment values), and the power to target. 82 | The function returns a data frame 83 | with one row per simulation draw and covariate \code{k}. 84 | } 85 | \examples{ 86 | # This function takes a while to run as it has 87 | # to fit an ordered beta regression to each 88 | # draw. The package comes with a saved 89 | # simulation dataset you can inspect to see what the 90 | # result looks like 91 | 92 | data("sim_data") 93 | 94 | library(dplyr) 95 | 96 | # will take a while to run this 97 | \donttest{ 98 | 99 | if(.Platform$OS.type!="windows") { 100 | 101 | sim_data <- sim_ordbeta(N=c(250,750), 102 | k=1, 103 | beta_coef = .5, 104 | iter=5,cores=2, 105 | beta_type="binary", 106 | treat_assign=0.3) 107 | 108 | } 109 | 110 | } 111 | 112 | # to get the power values by N, simply summarize/group 113 | # by N with functions from the R package dplyr 114 | 115 | sim_data \%>\% 116 | group_by(N) \%>\% 117 | summarize(mean_power=mean(power)) 118 | 119 | 120 | } 121 | -------------------------------------------------------------------------------- /ordbetareg_power.R: -------------------------------------------------------------------------------- 1 | 2 | library(DeclareDesign) 3 | library(ordbetareg) 4 | library(fabricatr) 5 | library(future) 6 | library(marginaleffects) 7 | library(broom) 8 | library(glmmTMB) 9 | 10 | set.seed(20241125) 11 | 12 | options(parallelly.fork.enable = TRUE) # required for use in RStudio 13 | 14 | # parallelism 15 | plan(multicore,workers=4) 16 | 17 | # helper function for glmmTMB ordbetareg fit 18 | 19 | tidy_avg_slopes <- function(x) { 20 | tidy(avg_slopes(x)) 21 | } 22 | 23 | # create two designs with identical treatment effects/expected values 24 | # first uses rordbeta to generate proportion in [0,1] 25 | # second uses rordbeta to generate proportion in [0,1], then 26 | # dichotomizes to 0 or 1 by rounding at 0.5 27 | # compare to see which has greater power given same number of obsevations 28 | # & check for bias 29 | 30 | # first, a simulated proportion using rordbeta (ordered beta distribution) 31 | # see https://osf.io/preprints/socarxiv/2sx6y 32 | # cutpoints = number of observations at the bound (i.e. 0 or 1) 33 | # phi = dispersion, a value of 1 means relatively "flat" 34 | 35 | design_rordbeta <- 36 | declare_model( 37 | N = 100, 38 | potential_outcomes(Y ~ rordbeta(N, mu = .5 + .05*Z,phi = 1, 39 | cutpoints=c(-3,3) 40 | )) 41 | ) + 42 | declare_inquiry(ATE = 0.05) + 43 | declare_assignment(Z = complete_ra(N, m = 50)) + 44 | declare_measurement(Y = reveal_outcomes(Y ~ Z)) + 45 | declare_estimator(Y ~ Z, .method = glmmTMB::glmmTMB, inquiry = "ATE", 46 | family=glmmTMB::ordbeta, 47 | .summary= tidy_avg_slopes, 48 | term="Z") 49 | 50 | # equivalent to dichotomizing (if a proportion) 51 | design_dichot <- 52 | declare_model( 53 | N = 100, 54 | potential_outcomes(Y ~ as.numeric(rordbeta(N, mu = .5 + .05*Z,phi = 1, 55 | cutpoints=c(-3,3))>0.5)) 56 | ) + 57 | declare_inquiry(ATE = 0.05) + 58 | declare_assignment(Z = complete_ra(N, m = 50)) + 59 | declare_measurement(Y = reveal_outcomes(Y ~ Z)) + 60 | declare_estimator(Y ~ Z, .method = lm_robust, inquiry = "ATE") 61 | 62 | # NB: DeclareDesign is using lm_robust as it's default estimation 63 | # However, it should be unbiased for the mean/expected value for the 64 | # binomial/dichotomized model 65 | 66 | diagnosands <- 67 | declare_diagnosands(bias = mean(estimate - estimand), 68 | power = mean(p.value <= 0.05)) 69 | 70 | # compare in terms of bias on the ATE & Power 71 | 72 | diagnose_design(design_rordbeta, diagnosands = diagnosands) 73 | diagnose_design(design_dichot, diagnosands = diagnosands) 74 | 75 | # rordbeta has greater power than dichotomization (about 50% more power) 76 | # dichotomized response also has bias - bias is positive, possibly because ATE is positive and 77 | # greater than 0.5 78 | -------------------------------------------------------------------------------- /ordbetareg_transparent_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/ordbetareg_transparent_v2.png -------------------------------------------------------------------------------- /ordbetaregpack.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: 39a1ae9d-42b1-4f0d-91f8-a74705f63771 3 | 4 | RestoreWorkspace: Default 5 | SaveWorkspace: Default 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: pdfLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /orig.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction to `ordbetareg`" 3 | author: Robert Kubinec 4 | output: html_document 5 | --- 6 | 7 | ```{r setup, include=F} 8 | 9 | require(brms) 10 | require(dplyr) 11 | require(tidyr) 12 | require(ggplot2) 13 | require(haven) 14 | 15 | knitr::opts_chunk$set(echo=T) 16 | 17 | set.seed(71520177) 18 | 19 | ``` 20 | 21 | 22 | # Overview 23 | 24 | This notebook contains instructions for running the ordered beta regression model in the R package `brms`, a front-end to the Stan Hamiltonian Markov Chain Monte Carlo sampler. The ordered beta regression model is designed explicitly for slider scale/visual analog scale data of the type you will often find in online surveys among other areas. I refer you to a paper on the model if you are not familiar with it: https://osf.io/preprints/socarxiv/2sx6y/. 25 | 26 | The ordered beta regression model is not natively supported in `brms` and so instead I define it here using the [custom response option](https://cran.r-project.org/web/packages/brms/vignettes/brms_customfamilies.html) of `brms`. 27 | 28 | # Data Preparation 29 | 30 | First, I load data from the Pew Forum that asks a question about respondents' views towards college professors (for a more complete explication, see the paper referenced above). 31 | 32 | ```{r load_data} 33 | 34 | pew <- read_sav("data/W28_Aug17/ATP W28.sav") %>% 35 | mutate(therm=na_if(THERMO_THERMBC_W28,999)) %>% 36 | filter(!is.na(therm)) 37 | 38 | pew %>% 39 | ggplot(aes(x=therm)) + 40 | geom_histogram(bins=100) + 41 | theme_minimal() + 42 | theme(panel.grid=element_blank()) + 43 | scale_x_continuous(breaks=c(0,25,50,75,100), 44 | labels=c("0","Colder","50","Warmer","100")) + 45 | ylab("") + 46 | xlab("") + 47 | labs(caption=paste0("Figure shows the distribution of ",sum(!is.na(pew$therm))," non-missing survey responses.")) 48 | 49 | ``` 50 | 51 | The distributions of feelings towards college professors contains both degenerate (0 and 100) and continuous responses between 0 and 100. To model it, we first need to rescale the outcome so that it will have bounds between 0 and 1 instead of 0 and 100. This is done very easily by subtracting the minimum value, in this case 0, and then dividing by the difference between the minimum and maximum (i.e., 100). I also do some other data processing tasks: 52 | 53 | ```{r munge_data} 54 | 55 | model_data <- select(pew,therm,age="F_AGECAT_FINAL", 56 | sex="F_SEX_FINAL", 57 | income="F_INCOME_FINAL", 58 | ideology="F_IDEO_FINAL", 59 | race="F_RACETHN_RECRUITMENT", 60 | education="F_EDUCCAT2_FINAL", 61 | region="F_CREGION_FINAL", 62 | approval="POL1DT_W28", 63 | born_again="F_BORN_FINAL", 64 | relig="F_RELIG_FINAL", 65 | news="NEWS_PLATFORMA_W28") %>% 66 | mutate_all(zap_missing) %>% 67 | drop_na %>% 68 | mutate(therm=(therm - min(therm,na.rm = T))/(max(therm,na.rm=T) - 69 | min(therm,na.rm = T)), 70 | news=as_factor(news,levels="labels"), 71 | age=c(scale(age)), 72 | race=as_factor(race,levels="labels"), 73 | ideology=as_factor(ideology,levels="labels"), 74 | income=as_factor(income,levels="labels"), 75 | approval=as_factor(approval,levels="labels"), 76 | sex=as_factor(sex,levels="labels"), 77 | education=as_factor(education,levels="labels"), 78 | born_again=as_factor(born_again,levels="labels"), 79 | relig=as_factor(relig,levels="labels")) %>% 80 | mutate_at(c("race","ideology","income","approval","sex","education","born_again","relig"), function(c) { 81 | factor(c, exclude=levels(c)[length(levels(c))]) 82 | }) %>% 83 | # need to make these ordered factors for BRMS 84 | mutate(education=ordered(education), 85 | income=ordered(income)) 86 | 87 | ``` 88 | 89 | The completed dataset has `r nrow(model_data)` observations. 90 | 91 | # Define Custom Family 92 | 93 | To model this data in `brms`, I have to define some code using the `custom_family` function to create a new distribution, `ord_beta_reg`. You need to run the following code in R before trying to use the custom family as it defines the log-likelihood and the priors (you can of course add additional priors of your own). You can access this code as an R script `define_ord_betareg.R` in the [Github repository containing this Rmarkdown file](https://github.com/saudiwin/ordbetareg). 94 | 95 | ```{r customfam} 96 | 97 | # custom family 98 | 99 | ord_beta_reg <- custom_family("ord_beta_reg", 100 | dpars=c("mu","phi","cutzero","cutone"), 101 | links=c("logit","log",NA,NA), 102 | lb=c(NA,0,NA,NA), 103 | type="real") 104 | 105 | # stan code for density of the model 106 | 107 | stan_funs <- "real ord_beta_reg_lpdf(real y, real mu, real phi, real cutzero, real cutone) { 108 | 109 | //auxiliary variables 110 | real mu_logit = logit(mu); 111 | vector[2] thresh; 112 | thresh[1] = cutzero; 113 | thresh[2] = cutzero + exp(cutone); 114 | 115 | if(y==0) { 116 | return log1m_inv_logit(mu_logit - thresh[1]); 117 | } else if(y==1) { 118 | return log_inv_logit(mu_logit - thresh[2]); 119 | } else { 120 | return log(inv_logit(mu_logit - thresh[1]) - inv_logit(mu_logit - thresh[2])) + 121 | beta_proportion_lpdf(y|mu,phi); 122 | } 123 | }" 124 | 125 | stanvars <- stanvar(scode=stan_funs,block="functions") 126 | 127 | # For pulling posterior predictions 128 | 129 | posterior_predict_ord_beta_reg <- function(i, draws, ...) { 130 | mu <- draws$dpars$mu[, i] 131 | phi <- draws$dpars$phi 132 | cutzero <- draws$dpars$cutzero 133 | cutone <- draws$dpars$cutone 134 | N <- length(phi) 135 | 136 | thresh1 <- cutzero 137 | thresh2 <- cutzero + exp(cutone) 138 | 139 | pr_y0 <- 1 - plogis(qlogis(mu) - thresh1) 140 | pr_y1 <- plogis(qlogis(mu) - thresh2) 141 | pr_cont <- plogis(qlogis(mu)-thresh1) - plogis(qlogis(mu) - thresh2) 142 | out_beta <- rbeta(n=N,mu*phi,(1-mu)*phi) 143 | 144 | # now determine which one we get for each observation 145 | outcomes <- sapply(1:N, function(i) { 146 | sample(1:3,size=1,prob=c(pr_y0[i],pr_cont[i],pr_y1[i])) 147 | }) 148 | 149 | final_out <- sapply(1:length(outcomes),function(i) { 150 | if(outcomes[i]==1) { 151 | return(0) 152 | } else if(outcomes[i]==2) { 153 | return(out_beta[i]) 154 | } else { 155 | return(1) 156 | } 157 | }) 158 | 159 | final_out 160 | 161 | } 162 | 163 | # for calculating marginal effects/conditional expectations 164 | 165 | posterior_epred_ord_beta_reg <- function(draws) { 166 | 167 | cutzero <- draws$dpars$cutzero 168 | cutone <- draws$dpars$cutone 169 | 170 | mu <- draws$dpars$mu 171 | 172 | thresh1 <- cutzero 173 | thresh2 <- cutzero + exp(cutone) 174 | 175 | low <- 1 - plogis(qlogis(mu) - thresh1) 176 | middle <- plogis(qlogis(mu)-thresh1) - plogis(qlogis(mu) - thresh2) 177 | high <- plogis(qlogis(mu) - thresh2) 178 | 179 | low*0 + middle*mu + high 180 | } 181 | 182 | # for calcuating LOO and Bayes Factors 183 | 184 | log_lik_ord_beta_reg <- function(i, draws) { 185 | 186 | mu <- draws$dpars$mu[,i] 187 | phi <- draws$dpars$phi 188 | y <- draws$data$Y[i] 189 | cutzero <- draws$dpars$cutzero 190 | cutone <- draws$dpars$cutone 191 | 192 | thresh1 <- cutzero 193 | thresh2 <- cutzero + exp(cutone) 194 | 195 | if(y==0) { 196 | out <- log(1 - plogis(qlogis(mu) - thresh1)) 197 | } else if(y==1) { 198 | out <- log(plogis(qlogis(mu) - thresh2)) 199 | } else { 200 | out <- log(plogis(qlogis(mu)-thresh1) - plogis(qlogis(mu) - thresh2)) + dbeta(y,mu*phi,(1-mu)*phi,log=T) 201 | } 202 | 203 | out 204 | 205 | } 206 | 207 | # Feel free to add any other priors / change the priors on b, 208 | # which represent regression coefficients on the logit 209 | # scale 210 | 211 | priors <- set_prior("normal(0,5)",class="b") + 212 | prior(constant(0),class="b",coef="Intercept") + 213 | prior_string("target += normal_lpdf((cutzero + exp(cutone)) - cutzero|0,3) + cutone",check=F) + 214 | set_prior("exponential(.1)",class="phi") 215 | 216 | priors_phireg <- set_prior("normal(0,5)",class="b") + 217 | prior(constant(0),class="b",coef="Intercept") + 218 | prior_string("target += normal_lpdf((cutzero + exp(cutone)) - cutzero|0,3) + cutone",check=F) 219 | 220 | ``` 221 | 222 | # Run In BRMS 223 | 224 | Given these new functions, we can then run a `brms` model as usual. The one catch is that we need to include the `priors` object in the `priors` argument and the `stanvars` object in the `stanvars` argument of the function, which comes from the code block above. The second and *very important* item is that the model formula must start with `0 + Intercept` for the independent (right-hand side) variables. You can add anything you want after that first term. This is to ensure no multi-collinearity between the ordinal cutpoints in the model and the intercept. 225 | 226 | Other than that, everything is the same and you can use any cool `brms` features. To demonstrate some of these, I will model education and income as ordinal predictors by using the `mo()` function. By doing so, we can get a single effect for education and income instead of having to use dummies for separate education/income categories. As a result, I can include an interaction between the two variables to see if wealthier more educated people have better views towards college professors than poorer better educated people. Finally, I include varying (random) census region intercepts. 227 | 228 | One note is that I use the `cmdstanr` backend for `brms` to use the latest version of Stan, but you can remove that option to use the Stan that comes with `brms` (`rstan`). 229 | 230 | ```{r run_brms} 231 | 232 | brms_fit <- brm(therm ~ 0 + Intercept + mo(education)*mo(income) + (1|region), data=model_data, 233 | family=ord_beta_reg, 234 | cores=2,chains=2, 235 | prior = priors, 236 | refresh=0, 237 | backend="cmdstanr", 238 | stanvars=stanvars) 239 | 240 | ``` 241 | 242 | The running time for this model, which has pretty complicated predictors, is about 7 minutes. So the model is currently robust enough to handle datasets of reasonable size. Performance will improve if I can get the model into `brms` proper. The one divergent transition referenced above is due to the well-known funnel problem of the variance of the random intercepts, and I will ignore it for the purposes of this vignette. 243 | 244 | # Post-Estimation 245 | 246 | The first thing we can do is extract the model cutpoints and overlay them on the empirical distribution to see how the model is dividing the outcome into discrete-ish categories. We have to do transformation of the cutpoints using the inverse logit function in R (`plogis`) to get back values in the scale of the response, and I have to exponentiate and add the first cutpoint to get the correct value for the second cutpoint: 247 | 248 | ```{r plot_cut} 249 | 250 | all_draws <- extract_draws(brms_fit) 251 | 252 | cutzero <- plogis(all_draws$dpars$cutzero) 253 | cutone <- plogis(all_draws$dpars$cutzero + exp(all_draws$dpars$cutone)) 254 | 255 | pew %>% 256 | ggplot(aes(x=therm)) + 257 | geom_histogram(bins=100) + 258 | theme_minimal() + 259 | theme(panel.grid=element_blank()) + 260 | scale_x_continuous(breaks=c(0,25,50,75,100), 261 | labels=c("0","Colder","50","Warmer","100")) + 262 | geom_vline(xintercept = mean(cutzero)*100,linetype=2) + 263 | geom_vline(xintercept = mean(cutone)*100,linetype=2) + 264 | ylab("") + 265 | xlab("") + 266 | labs(caption=paste0("Figure shows the distribution of ",sum(!is.na(pew$therm))," non-missing survey responses.")) 267 | 268 | 269 | ``` 270 | 271 | We can see in the plot above that the model does a good job isolating values that are very close to 0 and 1 from values that are more continuous in nature. 272 | 273 | We can plot the full predictive distribution relative to the original outcome: 274 | 275 | ```{r post_predict} 276 | 277 | pp_check(brms_fit) + theme_minimal() 278 | 279 | ``` 280 | 281 | We can see the coefficients from the model with the following command (these are on the logit scale with the exception of phi, the scale/dispersion parameter): 282 | 283 | ```{r coef_plot} 284 | plot(brms_fit,ask=F,theme=theme(panel.grid=element_blank(), 285 | panel.background = element_blank())) 286 | ``` 287 | 288 | # Marginal Effects 289 | 290 | We can also look at marginal effects, or the average change in the outcome given a unit change in one of the variables, by using the `conditional_effects` function in `brms`. This function plots the effect of `income` and `education` separately and then the interaction of the two. 291 | 292 | ```{r marg_effect} 293 | 294 | plot(conditional_effects(brms_fit),theme=theme(axis.text.x = element_text(angle=90), 295 | panel.grid=element_blank(), 296 | panel.background = element_blank()),ask=F) 297 | 298 | ``` 299 | 300 | Broadly speaking, these plots show that richer people have less favorable views on college professors, less educated people have less favorable views towards college professors, and the effect is even stronger if we consider the interaction. Wealthier less educated people are dramatically more likely to have less favorable views towards college professors than poor people with a postgraduate education. The difference is equivalent to 0.4, or 40 points on the original 0 to 100 scale. 301 | 302 | # Understanding Clustering/Polarization of Respondents 303 | 304 | As I explain in the paper, one of the main advantages of using a Beta regression model is its ability to model the dispersion among respondents not just in terms of variance (i.e. heteroskedasticity) but also the shape of dispersion, whether it is U or inverted-U shaped. Conceptually, a U shape would imply that respondents are bipolar, moving towards the extremes. An inverted-U shape would imply that respondents tend to cluster around a central value. We can predict these responses conditionally in the sample by adding predictors for `phi`, the scale/dispersion parameter in the Beta distribution. Higher values of `phi` imply a uni-modal distribution clustered around a central value, with increasing `phi` implying more clustering. Lower values of `phi` imply a bi-modal distribution with values at the extremes. Notably, these effects are calculated independently of the expected value, or mean, of the distribution, so values of `phi` will produce different shapes depending on the average value. 305 | 306 | The one change we need to make to fit this model is to add a formula predicting `phi` in the code below (this formula does not need the `0 + Intercept` syntax of the main model). We wrap both formulas in the `bf` function to indicate they are both distributional parameters. We also need to change the prior argument to `priors_phireg` as the priors of the model have changed as a result of adding predictors. For this model, we will put in an interaction between age and sex to see if younger/older men/women tend to have clustered or more heterogenous views on college professors. To make the model run a bit faster, we drop the `mo` terms around `education` and `income` so that they are evaluated as dummy variables. 307 | 308 | 309 | ```{r run_brms_phi} 310 | 311 | brms_fit_phireg <- brm(bf(therm ~ 0 + Intercept + education + income, 312 | phi ~ age*sex), 313 | data=model_data, 314 | family=ord_beta_reg, 315 | cores=2,chains=2, 316 | prior = priors_phireg, 317 | refresh=100, 318 | stanvars=stanvars, 319 | backend="cmdstanr") 320 | 321 | ``` 322 | 323 | We cannot use the `conditional_effects` option to plot because the dispersion parameter `phi` by definition does not affect the expected outcome (i.e., the average). Instead, we can uses the regular `plot` function to visualize the parameters: 324 | 325 | ```{r plot_phi} 326 | 327 | plot(brms_fit_phireg,pars = "phi",fixed = F,ask=F,combo=c("intervals","hist")) 328 | 329 | ``` 330 | 331 | There is weak to moderate evidence in this plot that women have more homogenous views and that older women have more homogenous views than younger women. However, there is substantial uncertainty in this estimate and the estimate itself is not very large. We are using the log link for `phi`, so to get the value of the effect on `phi`, we can exponentiate the coefficient, i.e. `exp(0.1)=``r round(exp(0.1),2)`. We can compare what the distributions look like by plotting histograms of simulated outcomes using the Beta distribution and the average response in our data, which is `r round(mean(model_data$therm,na.rm=T),2)`. We will examine a 1-unit change in `phi` (much larger than our estimated effects) from 2 to 3 by using the average response for `mu` and plotting the density of both distributions: 332 | 333 | ```{r plot_phi_sim} 334 | 335 | # parameterization of beta distribution in terms of mu/phi (mean/dispersion) 336 | rbeta_mean <- function(N,mu,phi) { 337 | rbeta(N,mu*phi,(1-mu)*phi) 338 | } 339 | 340 | tibble(phi_small=rbeta_mean(10000,mean(model_data$therm,na.rm=T),2), 341 | phi_big=rbeta_mean(10000,mean(model_data$therm,na.rm=T),3)) %>% 342 | gather("phi_type","simulated_value") %>% 343 | ggplot(aes(x=simulated_value)) + 344 | geom_density(alpha=0.5,aes(fill=phi_type)) + 345 | theme(panel.background = element_blank(), 346 | panel.grid=element_blank()) 347 | 348 | ``` 349 | 350 | We can see that the `phi_big` distribution is more clustered around a central value while `phi_small` shows more movement towards the extremes of the distribution. However, the movement is modest, as the value of the coefficient suggests. 351 | -------------------------------------------------------------------------------- /test_krueger.R: -------------------------------------------------------------------------------- 1 | library(tidyverse) 2 | library(dagitty) 3 | library(ggdag) 4 | 5 | dag_kruger <- dagitty("dag{ A -> P; 6 | C -> P; 7 | P -> S; 8 | A -> S; 9 | P -> Ahat; 10 | A -> Phat; 11 | S -> Phat; 12 | S -> Ahat; 13 | S -> B 14 | }") 15 | 16 | theme_set(theme_minimal()) 17 | 18 | thematic::auto_config_set( 19 | thematic::auto_config( 20 | bg = "#D8DEE9", 21 | fg = "#2E3440", 22 | accent = "#5E81AC" 23 | ) 24 | ) 25 | thematic::thematic_on() 26 | 27 | in_range <- function(x, lower, upper) { 28 | case_when( 29 | x > upper ~ upper, 30 | x < lower ~ lower, 31 | TRUE ~ x 32 | ) 33 | } 34 | 35 | n = 10000 36 | uncertainty = 0.1 37 | 38 | # change this so that perceived - ability, but reported is censored. 39 | 40 | over_unc <- lapply(seq(0.01,3, by=0.01), function(u) { 41 | 42 | sim <- crossing( 43 | participant = 1:n 44 | ) %>% 45 | mutate( 46 | ability = runif(n(), 0, 1), 47 | ability2 = 1.89*ability, 48 | test_score=ability + rnorm(n(), 0, sd=u), 49 | perceived_ab=ability + rnorm(n(), 0, sd=u), 50 | # confidence = rnorm(n(), 0, sd=uncertainty), 51 | # perceived_accurate=ability + confidence, 52 | perceived_ab=plogis(ability), 53 | perceived_test=plogis(test_score), 54 | # actual = ability %>% in_range(0, 1), 55 | # bias = perceived - actual, 56 | # real_bias = perceived_accurate - ability 57 | ) 58 | 59 | tibble(unc=u, 60 | est=lm(perceived_test ~ perceived_ab, data=sim)$coefficients[2]) 61 | 62 | 63 | }) %>% bind_rows 64 | 65 | plot(over_unc$unc, over_unc$est) 66 | 67 | sim <- crossing( 68 | participant = 1:n 69 | ) %>% 70 | mutate( 71 | ability = rbeta(n(),1,1), 72 | ability2 = 1.89*ability, 73 | step1 = qlogis(ability), 74 | err=rnorm(n(),sd=1), 75 | perceived_ab=, 76 | # confidence = rnorm(n(), 0, sd=uncertainty), 77 | # perceived_accurate=ability + confidence, 78 | # actual = ability %>% in_range(0, 1), 79 | # bias = perceived - actual, 80 | # real_bias = perceived_accurate - ability 81 | ) 82 | 83 | this_sim <- sim_ordbeta(N=1000, k=1, beta_coef=3,return_data=T,iter=1,cutpoints = c(-2,2)) 84 | 85 | m1 <- ordbetareg(outcome ~ Var1 + I(Var1^2) + I(Var1^3), data=this_sim$data[[1]],backend="cmdstanr", 86 | threads=threading(16),chains=1) 87 | summary(m1) 88 | summary(lm(outcome ~ Var1 + I(Var1^2) + I(Var1^3), data=this_sim$data[[1]])) 89 | 90 | ggplot(this_sim$data[[1]], aes(y=outcome, x=Var1)) + geom_point() + stat_smooth(method="lm") + 91 | geom_abline(slope=1, intercept=0, linetype=2, colour="red") 92 | ggplot(this_sim$data[[1]], aes(y=outcome, x=Var1)) + geom_point() + stat_smooth() + 93 | geom_abline(slope=1, intercept=0, linetype=2, colour="red") 94 | ggplot(this_sim$data[[1]], aes(y=outcome, x=Var1)) + geom_point() + stat_smooth(method = "lm",formula = y ~ poly(x,2)) + 95 | geom_abline(slope=1, intercept=0, linetype=2, colour="red") 96 | 97 | 98 | 99 | sim_quantiles <- sim %>% 100 | summarise( 101 | quantile_breaks = quantile(actual), 102 | quantile = names(quantile_breaks), 103 | percieved_quantile_breaks = quantile(perceived), 104 | percieved_quantile = names(percieved_quantile_breaks) 105 | ) 106 | 107 | # sim <- sim %>% 108 | # mutate( 109 | # actual_q = cut(actual, 110 | # breaks = sim_quantiles$quantile_breaks, 111 | # labels = sim_quantiles$quantile[-1], 112 | # include.lowest = TRUE), 113 | # perceived_q = cut(perceived, 114 | # breaks = sim_quantiles$percieved_quantile_breaks, 115 | # labels = sim_quantiles$percieved_quantile[-1], 116 | # include.lowest = TRUE) 117 | # ) 118 | # 119 | # sim_q_summary <- sim %>% 120 | # group_by(actual_q) %>% 121 | # summarise(across(c(actual, perceived), 122 | # mean)) %>% 123 | # mutate(quartile_mean = actual) %>% 124 | # pivot_longer(c(actual, perceived)) 125 | 126 | # model this with ordered beta regression 127 | 128 | library(ordbetareg) 129 | library(brms) 130 | 131 | sim_perc <- mutate(sim, perc=cut(actual, 132 | breaks=quantile(actual, prob=seq(0,1,by=0.01)), 133 | labels=names(quantile(actual, prob=seq(0,1,by=0.01)))[-1], 134 | include.lowest=TRUE), 135 | perc=as.numeric(stringr::str_remove(perc,"%")), 136 | bias=actual-perceived) 137 | 138 | est_mod <- ordbetareg(formula=perceived ~ actual + I(actual^2) + 139 | I(actual^3),data=sim_perc, 140 | backend="cmdstanr",chains=1, 141 | threads=threading(10)) 142 | 143 | # est_mod <- ordbetareg(formula=perceived_actual ~ actual,data=sim_perc, 144 | # backend="cmdstanr",chains=1, 145 | # threads=threading(10)) 146 | 147 | summary(est_mod) 148 | 149 | check_mod <- lm(bias ~ ability*confidence, data=sim_perc) 150 | 151 | summary(check_mod) 152 | 153 | summary(lm(perceived ~ actual + I(actual^2) + I(actual^3), data=sim_perc)) 154 | 155 | summary(lm(confidence ~ ability + perceived, data=sim_perc)) 156 | 157 | summary(lm(confidence ~ ability + perceived_accurate, data=sim_perc)) 158 | 159 | summary(lm(perceived_accurate ~ ability*confidence, data=sim_perc)) 160 | 161 | summary(lm(perceived ~ actual + ability, data=sim_perc)) 162 | -------------------------------------------------------------------------------- /test_package.R: -------------------------------------------------------------------------------- 1 | library(ordbetareg) 2 | library(tidyverse) 3 | 4 | data("pew") 5 | 6 | # prepare data 7 | 8 | model_data <- select(pew,therm, 9 | education="F_EDUCCAT2_FINAL", 10 | region="F_CREGION_FINAL", 11 | income="F_INCOME_FINAL", 12 | THERMO_THERMBA_W28) %>% 13 | mutate(THERMO_THERMBA_W28=as.numeric(THERMO_THERMBA_W28)) 14 | 15 | 16 | # fit the actual model 17 | 18 | ord_fit_one_mod <- ordbetareg(formula=bf(THERMO_THERMBA_W28~ education), 19 | data=model_data, 20 | cores=2,chains=2,sample_prior="only", 21 | backend="cmdstanr") 22 | 23 | ord_fit_one_mod_newbounds <- ordbetareg(formula=bf(THERMO_THERMBA_W28~ education), 24 | data=model_data, 25 | true_bounds=c(0,100), 26 | cores=2,chains=2,sample_prior="only", 27 | backend="cmdstanr") 28 | 29 | ord_fit_two_mods <- ordbetareg(formula=bf(THERMO_THERMBA_W28~ education) + 30 | bf(therm ~ region), 31 | data=model_data, 32 | cores=2,chains=2,sample_prior="only", 33 | backend="cmdstanr") 34 | 35 | ord_fit_two_mods_dfam <- ordbetareg(formula=bf(THERMO_THERMBA_W28~ education, family="gaussian") + 36 | bf(therm ~ region), 37 | data=list(model_data, 38 | model_data), 39 | use_brm_multiple = T, 40 | cores=2,chains=2,sample_prior="only", 41 | backend="cmdstanr") 42 | 43 | ord_fit_two_mods_mi <- ordbetareg(formula=bf(THERMO_THERMBA_W28~ education) + 44 | bf(therm ~ region), 45 | data=list(model_data,model_data), 46 | use_brm_multiple = T, 47 | cores=2,chains=2,sample_prior="only", 48 | backend="cmdstanr") 49 | 50 | ord_fit_phireg <- ordbetareg(formula=bf(THERMO_THERMBA_W28~ 1, 51 | phi ~ (1|education)), 52 | phi_reg = "intercepts", 53 | extra_prior = set_prior("normal(0,2)",class="sd",dpar="phi"), 54 | data=model_data, 55 | cores=2,chains=2,sample_prior="only", 56 | backend="cmdstanr") 57 | 58 | # check with cmdstnar 59 | 60 | require(cmdstanr) 61 | 62 | dirich <- cmdstan_model("~/test.stan",quiet=F,compile = T,force_recompile=T) 63 | 64 | dirich_fit <- dirich$sample(data=list(K=3)) 65 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/vignettes/screen1.png -------------------------------------------------------------------------------- /vignettes/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saudiwin/ordbetareg_pack/b1540a9857b19470bd267a777907af0a2ca3de81/vignettes/screen2.png --------------------------------------------------------------------------------