├── .Rbuildignore ├── .gitignore ├── DESCRIPTION ├── NAMESPACE ├── R ├── .gitignore ├── build_stan.R ├── clean_stan.R ├── collect_stan.R ├── get_condition_post.R ├── get_contrast_matrix.R ├── get_subject_indices.R ├── halfhelmert_contrasts.R ├── halfsum_contrasts.R ├── imports_and_helpers.R ├── kill_stan.R ├── names_from_WB.R ├── normality.R ├── stan_summary.R ├── start_stan.R └── watch_stan.R ├── README.md ├── ezStan.Rproj └── man ├── build_stan.Rd ├── clean_stan.Rd ├── collect_stan.Rd ├── get_condition_post.Rd ├── get_contrast_matrix.Rd ├── get_subject_indices.Rd ├── halfhelmert_contrasts.Rd ├── halfsum_contrasts.Rd ├── kill_stan.Rd ├── names_from_WB.Rd ├── normality.Rd ├── stan_summary.Rd ├── start_stan.Rd └── watch_stan.Rd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | README.md 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: ezStan 2 | Type: Package 3 | Title: Helper Functions for Stan 4 | Version: 0.4.1 5 | Authors@R: person(given='Michael A.',family='Lawrence',email='mike.lwrnc@gmail.com',role=c('aut','cre')) 6 | Description: A collection of functions to facilitate use of Stan/rstan. 7 | License: GPL (>= 3) 8 | Encoding: UTF-8 9 | LazyData: true 10 | Imports: 11 | dplyr (>= 0.5.0), 12 | rstan (>= 2.14.1), 13 | tidyr (>= 0.6.0), 14 | lubridate (>= 1.6.0), 15 | data.table (>= 1.10.4-3), 16 | Suggests: 17 | tibble 18 | RoxygenNote: 7.1.0 19 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(build_stan) 4 | export(clean_stan) 5 | export(collect_stan) 6 | export(get_condition_post) 7 | export(get_contrast_matrix) 8 | export(get_subject_indices) 9 | export(halfhelmert_contrasts) 10 | export(halfsum_contrasts) 11 | export(kill_stan) 12 | export(names_from_WB) 13 | export(normality) 14 | export(stan_summary) 15 | export(start_stan) 16 | export(watch_stan) 17 | import(rstan) 18 | importFrom(data.table,"fread") 19 | importFrom(dplyr,one_of) 20 | importFrom(lubridate,"seconds_to_period") 21 | importFrom(stats,"contr.helmert") 22 | importFrom(stats,"contr.sum") 23 | importFrom(stats,"contrasts") 24 | importFrom(stats,"contrasts<-") 25 | importFrom(stats,"cor") 26 | importFrom(stats,"median") 27 | importFrom(stats,"model.matrix") 28 | importFrom(stats,"pnorm") 29 | importFrom(stats,"sd") 30 | importFrom(stats,"terms") 31 | importFrom(tidyr,gather) 32 | importFrom(utils,installed.packages) 33 | -------------------------------------------------------------------------------- /R/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /R/build_stan.R: -------------------------------------------------------------------------------- 1 | 2 | #' Build a Stan model without locking R/RStudio 3 | #' 4 | #' @param file A character string specifying the path to a Stan file to build. 5 | #' @return an object of the class rstan::stan_model. 6 | #' @export 7 | #' 8 | #' @examples 9 | build_stan = function( 10 | file 11 | ){ 12 | if(!(file.exists(file))){ 13 | stop(paste0('Unable to find file named "',file,'"')) 14 | } 15 | if(!('loggr' %in% installed.packages())){ 16 | warning( 17 | 'IMPORTANT: ezStan now relies on the loggr package to show erros an warnings from Stan. If you\'re seeing this message, you need to install loggr by running: 18 | devtools::install_github("smbache/loggr")' 19 | ) 20 | } 21 | if(!dir.exists('stan_temp')){ 22 | dir.create('stan_temp') 23 | } 24 | unlink('stan_temp/build*') 25 | script_file = 'stan_temp/build.R'#tempfile() 26 | stderr_file = 'stan_temp/build.stderr'#tempfile() 27 | stdout_file = 'stan_temp/build.stdout'#tempfile() 28 | log_file = 'stan_temp/build.log'#tempfile() 29 | flag_file = 'stan_temp/build.flag'#tempfile() 30 | cat( 31 | " 32 | args = commandArgs(trailingOnly=TRUE) 33 | mod_file = args[1] 34 | log_file = args[2] 35 | flag_file = args[3] 36 | if('loggr' %in% installed.packages()){ 37 | suppressMessages(library(loggr,quietly=T)) 38 | my_formatter <- function(event) event$message 39 | log_file(log_file,.formatter = my_formatter,subscriptions=c('message', 'warning','stop')) 40 | } 41 | suppressMessages(library(rstan,quietly=T)) 42 | rstan_options(auto_write = TRUE) 43 | suppressMessages(mod <- rstan::stan_model(mod_file)) 44 | save(flag_file,file=flag_file) 45 | " 46 | , file = script_file 47 | ) 48 | cat('\rBuilding ...') 49 | system2( 50 | command = "Rscript" 51 | , args = c('--vanilla',script_file,file,log_file,flag_file) 52 | , stdout = NULL#stdout_file 53 | , stderr = NULL#stderr_file 54 | , wait = FALSE 55 | ) 56 | done = FALSE 57 | loops = 0 58 | while(!done){ 59 | loops = loops+1 60 | Sys.sleep(1) 61 | cat('\rBuilding ...',rep('.',times=loops),sep='') 62 | if(file.exists(flag_file)){ 63 | done = T 64 | } 65 | if(file.exists(log_file)){ 66 | if(length(scan(log_file,what='character',sep='\n',quiet=T))>0){ 67 | done = T 68 | } 69 | } 70 | } 71 | cat('\r',rep(' ',times=loops+12),sep='') 72 | #cat('\nBuild complete.') 73 | # cat('\r') 74 | # if(file.exists(log_file)){ 75 | # temp = scan(log_file,what='character',quiet=T,sep='\n') 76 | # if(length(temp)>0){ 77 | # cat('Messages from stan_model:',temp,'\n',sep='\n') 78 | # } 79 | # } 80 | cat('\r') 81 | # if(file.exists(stderr_file)){ 82 | # temp = scan(stderr_file,what='character',quiet=T) 83 | # if(length(temp)>0){ 84 | # cat('\nMessages from stan_model:',temp,sep='\n') 85 | # } 86 | # } 87 | Sys.sleep(1) 88 | return(rstan::stan_model(file)) 89 | # mod = NULL 90 | # try(mod <- rstan::stan_model(file)) 91 | # if(!is.null(mod)){ 92 | # cat('\nBuild successful.') 93 | # return(mod) 94 | # }else{ 95 | # stop('Build failed.') 96 | # } 97 | } 98 | -------------------------------------------------------------------------------- /R/clean_stan.R: -------------------------------------------------------------------------------- 1 | #' Delete temporary files created by a Stan session 2 | #' @param report_all_clean A logical value indicating whether this function should print an "All Clean!" message when complete. 3 | #' @return No value is returned 4 | #' @export 5 | #' 6 | #' @examples 7 | clean_stan = function(report_all_clean=T){ 8 | if(dir.exists('stan_temp')){ 9 | file.remove(list.files(path='stan_temp',all.files=T,full.names=T,recursive=T),recursive=T) 10 | file.remove('stan_temp') 11 | } 12 | if(report_all_clean){ 13 | cat("\r","All clean!") 14 | utils::flush.console() 15 | } 16 | return(invisible(NULL)) 17 | } 18 | -------------------------------------------------------------------------------- /R/collect_stan.R: -------------------------------------------------------------------------------- 1 | #' Collect the results from a Stan session 2 | #' 3 | #' @return An object of S4 class stanfit. 4 | #' @export 5 | #' 6 | #' @examples 7 | collect_stan = function(){ 8 | #pre-defining objects we'll get from load() to avoid package build warnings 9 | cores = NULL 10 | chains_per_core = NULL 11 | seed_start = NULL 12 | iter = NULL 13 | stan_args = NULL 14 | load('stan_temp/start_stan_args.rda') 15 | #loads the following objects: 16 | # cores 17 | # chains_per_core 18 | # seed_start 19 | # iter 20 | # stan_args 21 | num_chains = cores*chains_per_core 22 | post_list = list() 23 | j = 1 24 | for(this_chain in 1:num_chains){ 25 | chain_name = sprintf(paste0('chain%0',ceiling(log10(cores)),'d'),this_chain) 26 | log_file = paste0('stan_temp/logs_',chain_name,'.log') 27 | if(file.exists(log_file)){ #check if the log file exists 28 | all_lines = readLines(log_file) 29 | if(length(all_lines)>0){ #log file has contents 30 | cat(paste0('[', chain_name, ':] ')) 31 | cat('\n') 32 | for(line in all_lines){ 33 | cat(line) 34 | cat('\n') 35 | } 36 | cat('\n') 37 | } 38 | } 39 | rda_file = paste0('stan_temp/rdas_',chain_name,'.rda') 40 | if(file.exists(rda_file)){ 41 | load(rda_file) 42 | post_list[[j]] = get('post') 43 | names(post_list)[j] = chain_name 44 | j = j + 1 45 | } 46 | } 47 | return(sflist2stanfit(post_list)) 48 | } 49 | -------------------------------------------------------------------------------- /R/get_condition_post.R: -------------------------------------------------------------------------------- 1 | #' Combine coefficient samples from a Stan model with the design matrix to compute posterior samples on the conditions of the design. 2 | #' 3 | #' @param from_stan A stanfit object from a call to rstan::stan() or rstan::sampling() containing posterior samples from the model. 4 | #' @param par Character string naming the parameter on which to compute the summary table. 5 | #' @param X Optional contrast matrix as yielded from get_contrast_matrix() indicating the contrasts passed as data when creating the above stanfit object. 6 | #' @param W Optional contrast matrix as yielded from get_contrast_matrix() indicating the within-subjects contrasts passed as data when creating the above stanfit object. If present, X is ignored and B must be present. 7 | #' @param B Optional contrast matrix as yielded from get_contrast_matrix() indicating the between-subjects contrasts passed as data when creating the above stanfit object. W must be present, else B is ignored. 8 | #' @param numeric_res Integer value conveying the number of points within the range of a continuous variable to sample. If 0, the values from the data are used. 9 | #' @param collapse_intercept_to_median Logical indicating whether to use the median of the samples for the intercept rather than its full set of samples. Assumes that the intercept is the first parameter. 10 | #' 11 | #' @return If the `tibble` package is installed, a tibble; else, a data frame. 12 | #' @export 13 | #' 14 | #' @examples 15 | #' @importFrom tidyr gather 16 | #' @importFrom dplyr one_of 17 | get_condition_post <- 18 | function( 19 | from_stan 20 | , par 21 | , X = NULL 22 | , W = NULL 23 | , B = NULL 24 | , numeric_res = 0 25 | , collapse_intercept_to_median = FALSE 26 | ){ 27 | if(!is.null(W)){ 28 | W_data = attr(W,'data') 29 | if (inherits(W_data, "tbl_df")) { 30 | W_data = as.data.frame(data) 31 | } 32 | B_data = attr(B,'data') 33 | if (inherits(B_data, "tbl_df")) { 34 | B_data = as.data.frame(B_data) 35 | } 36 | W_vars = attr(terms(attr(W,'formula')),'term.labels') 37 | W_vars = W_vars[!grepl(':',W_vars)] 38 | B_vars = attr(terms(attr(B,'formula')),'term.labels') 39 | B_vars = B_vars[!grepl(':',B_vars)] 40 | data_vars = c(B_vars,W_vars) 41 | new_names = names_from_WB(W,B,reverse=T) 42 | }else{ 43 | data = attr(X,'data') 44 | if (inherits(data, "tbl_df")) { 45 | data = as.data.frame(data) 46 | } 47 | data_vars = attr(terms(attr(X,'formula')),'term.labels') 48 | data_vars = data_vars[!grepl(':',data_vars)] 49 | new_names = dimnames(X)[[2]] 50 | } 51 | temp = list() 52 | for(i in 1:length(data_vars)){ 53 | this_var = data_vars[i] 54 | if(is.null(W)){ 55 | this_fixed_data = data[,names(data)==this_var] 56 | }else{ 57 | if(this_var%in%W_vars){ 58 | this_fixed_data = W_data[,names(W_data)==this_var] 59 | }else{ 60 | this_fixed_data = B_data[,names(B_data)==this_var] 61 | } 62 | } 63 | if(is.numeric(this_fixed_data)*(numeric_res>0)){ 64 | temp[[i]] = seq( 65 | min(this_fixed_data) 66 | , max(this_fixed_data) 67 | , length.out=numeric_res 68 | ) 69 | }else{ 70 | temp[[i]] = sort(unique(this_fixed_data)) 71 | if(is.factor(temp[[i]])){ 72 | contrasts(temp[[i]]) = contrasts(this_fixed_data) 73 | } 74 | } 75 | } 76 | to_return = data.frame(expand.grid(temp)) 77 | names(to_return) = data_vars 78 | both_formula = eval(parse(text=paste('~', paste(data_vars, collapse = '*')))) 79 | unique_mm = get_contrast_matrix( 80 | data = to_return 81 | , formula = both_formula 82 | ) 83 | unique_mm = unique_mm[,new_names] 84 | samples = rstan::extract(from_stan,par)[[1]] 85 | samples = matrix(samples,nrow=dim(samples)[1]) 86 | if(collapse_intercept_to_median){ 87 | samples[,1] = median(samples[,1]) 88 | } 89 | # samples = samples[,match(new_names,dimnames(unique_mm)[[2]])] 90 | mat = matrix(NA,nrow=nrow(to_return),ncol=dim(samples)[1]) 91 | for(i in 1:dim(samples)[1]){ 92 | mat[,i] <- unique_mm%*%samples[i,] 93 | } 94 | to_return = cbind(to_return,as.data.frame(mat)) 95 | to_return = tidyr::gather(to_return,key='sample',value='value',-dplyr::one_of(data_vars)) 96 | to_return$sample = as.numeric(gsub('V','',as.character(to_return$sample),fixed=T)) 97 | if('tibble' %in% installed.packages()){ 98 | to_return = tibble::as_tibble(to_return) 99 | } 100 | return(to_return) 101 | } 102 | 103 | -------------------------------------------------------------------------------- /R/get_contrast_matrix.R: -------------------------------------------------------------------------------- 1 | #' Obtain a contrast matrix for a data set given a specified formula 2 | #' 3 | #' @param data A data frame (or tibble). 4 | #' @param formula A formula starting with `~` expressing the model 5 | #' @param contrast_kind The kind of contrasts to use. Used to quickly convert all factors to a particular non-R-default contrast kind. 6 | #' 7 | #' @return A contrast matrix with the formula & data saved as attributes. 8 | #' @export 9 | #' 10 | #' @examples 11 | get_contrast_matrix = function( 12 | data 13 | , formula 14 | , contrast_kind = NULL 15 | ){ 16 | if (inherits(data, "tbl_df")) { 17 | data = as.data.frame(data) 18 | } 19 | vars = attr(terms(formula),'term.labels') 20 | vars = vars[!grepl(':',vars)] 21 | if(length(vars)==1){ 22 | data = data.frame(data[,vars]) 23 | names(data) = vars 24 | }else{ 25 | data = data[,vars] 26 | } 27 | vars_to_rename = NULL 28 | for(i in vars){ 29 | if(is.character(data[,i])){ 30 | data[,i] = factor(data[,i]) 31 | } 32 | if( is.factor(data[,i])){ 33 | if(length(levels(data[,i]))==2){ 34 | vars_to_rename = c(vars_to_rename,i) 35 | } 36 | if(!is.null(contrast_kind) ){ 37 | contrasts(data[,i]) = contrast_kind 38 | } 39 | } 40 | } 41 | mm = model.matrix(data=data,object=formula) 42 | dimnames(mm)[[2]][dimnames(mm)[[2]]=='(Intercept)'] = '(I)' 43 | for(i in vars_to_rename){ 44 | dimnames(mm)[[2]] = gsub(paste0(i,1),i,dimnames(mm)[[2]]) 45 | } 46 | attr(mm,'formula') = formula 47 | attr(mm,'data') = data 48 | return(mm) 49 | } 50 | -------------------------------------------------------------------------------- /R/get_subject_indices.R: -------------------------------------------------------------------------------- 1 | #' Get a table of start/end indices from subject label vector 2 | #' 3 | #' Given input of an ordered subject label vector, returns the indices into this 4 | #' vector corresponding to the beginning and end of each subject. 5 | #' 6 | #' @param x A vector of subject labels, sorted by said labels. 7 | #' 8 | #' @return A matrix with two columns (start & end) and as many rows as there are unique labels in the input 9 | #' @export 10 | #' 11 | #' @examples 12 | get_subject_indices = function(x){ 13 | x = as.numeric(factor(x)) #converts labels/numbers to 1:length(x) sequence 14 | temp = 1:length(x) 15 | indices = matrix(NA,nrow=max(x),ncol=2) 16 | for(i in 1:max(x)){ 17 | indices[i,1] = min(temp[x==i]) 18 | indices[i,2] = max(temp[x==i]) 19 | } 20 | return(indices) 21 | } 22 | -------------------------------------------------------------------------------- /R/halfhelmert_contrasts.R: -------------------------------------------------------------------------------- 1 | #' Half-Helmert Contrasts 2 | #' 3 | #' contr.helmert()/2. 4 | #' @param ... Arguments passed to contr.helmert 5 | #' 6 | #' @return See Value in contr.helmert(). 7 | #' @export 8 | #' 9 | #' @examples 10 | halfhelmert_contrasts = function( 11 | ... 12 | ){ 13 | contr.helmert(...)*.5 14 | } 15 | -------------------------------------------------------------------------------- /R/halfsum_contrasts.R: -------------------------------------------------------------------------------- 1 | #' Half-sum Contrasts 2 | #' 3 | #' contr.sum()/2. 4 | #' @param ... Arguments passed to contr.sum 5 | #' 6 | #' @return See Value in contr.sum(). 7 | #' @export 8 | #' 9 | #' @examples 10 | halfsum_contrasts = function( 11 | ... 12 | ){ 13 | contr.sum(...)*.5 14 | } 15 | -------------------------------------------------------------------------------- /R/imports_and_helpers.R: -------------------------------------------------------------------------------- 1 | #' @import rstan 2 | #' @importFrom stats "contr.sum" "contr.helmert" "contrasts" "contrasts<-" "cor" "median" "model.matrix" "pnorm" "sd" "terms" 3 | #' @importFrom utils installed.packages 4 | #' @importFrom lubridate "seconds_to_period" 5 | #' @importFrom data.table "fread" 6 | NULL 7 | 8 | # .onAttach <- function(libname, pkgname) { 9 | # packageStartupMessage(' 10 | # [2018-02-16] ezStan changes:\n 11 | # - start_stan: "args" argument eliminated; see ?start_stan for new usage. 12 | # - start_stan: now permits running multiple sequential chains on each core (useful if you want more chains than available cores). 13 | # - watch_stan: now shows warnings and errors as they occur (requires loggr package to be installed via devtools::install_github("mike-lawrence/loggr")). 14 | # - watch_stan: now shows post-warmup divergences and iterations exceeding the max_treedepth. 15 | # - watch_stan: now plays a beep if the beepr package is installed. 16 | # - watch_stan: now has an argument "kill_on_divergence" that kills the sampling if a post-warmup divergence is encountered. 17 | # - collect_stan: now shows any warnings and errors encountered during sampling. 18 | # - build_stan: new function that attempts to build stan models in a separate process, thereby avoiding interface hangs in RStudio. Still a work in progess as scenarios where you encounter the "hash mismatch" warning will still cause a hang. 19 | # ' 20 | # ) 21 | # } 22 | 23 | str_rep = function(x, i) { 24 | paste(rep.int(x, i), collapse = "") 25 | } 26 | time_as_string = function(x) { 27 | tolower(lubridate::seconds_to_period(round(x))) 28 | } 29 | append_string = function(s,i,spacing,one_line_per_chain){ 30 | if(one_line_per_chain){ 31 | w = getOption("width")+1 32 | s = paste0(s,i,str_rep(' ',w-(nchar(i)%%w)+spacing)) 33 | }else{ 34 | s = paste0(s,i,str_rep(' ',spacing)) 35 | } 36 | return(s) 37 | } 38 | 39 | -------------------------------------------------------------------------------- /R/kill_stan.R: -------------------------------------------------------------------------------- 1 | #' Terminate an errant Stan session 2 | #' 3 | #' @return No value is returned 4 | #' @export 5 | #' 6 | #' @examples 7 | kill_stan = function(){ 8 | system2( 9 | command = "killall" 10 | , args = "R" 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /R/names_from_WB.R: -------------------------------------------------------------------------------- 1 | #' Figure out variable names given W & B contrast matrices 2 | #' 3 | #' @param W Optional contrast matrix as yielded from get_contrast_matrix() indicating the within-subjects contrasts passed as data when creating the above stanfit object. If present, X is ignored and B must be present. 4 | #' @param B Optional contrast matrix as yielded from get_contrast_matrix() indicating the between-subjects contrasts passed as data when creating the above stanfit object. W must be present, else B is ignored. 5 | #' @param reverse Logical indicating whether to reverse the order of the names. 6 | #' 7 | #' @return A vector of character strings indicating names of variables. 8 | #' @export 9 | #' 10 | #' @examples 11 | names_from_WB = function(W,B,reverse = F){ 12 | B_names = dimnames(B)[[2]] 13 | W_names = dimnames(W)[[2]] 14 | new_names = rep(NA,length(W_names)*length(B_names)) 15 | k = 1 16 | if(!reverse){ 17 | for(i in B_names){ 18 | for(j in W_names){ 19 | new_names[k] = paste(i,j,sep=':') 20 | k = k + 1 21 | } 22 | } 23 | }else{ 24 | for(i in W_names){ 25 | for(j in B_names){ 26 | new_names[k] = paste(j,i,sep=':') 27 | k = k + 1 28 | } 29 | } 30 | } 31 | new_names = gsub('(I):','',new_names,fixed=T) 32 | new_names = gsub(':(I)','',new_names,fixed=T) 33 | new_names = gsub('(Intercept):','',new_names,fixed=T) 34 | new_names = gsub(':(Intercept)','',new_names,fixed=T) 35 | return(new_names) 36 | } 37 | -------------------------------------------------------------------------------- /R/normality.R: -------------------------------------------------------------------------------- 1 | #' A metric of normality 2 | #' 3 | #' Provides a measure of normality for the input by computing the correlation 4 | #' between the data and the expected data given each data point's quantile and a 5 | #' normal distribution with a mean equal to the data's mean and a standard 6 | #' deviation equal to the data's standard deviation. 7 | #' @param x A numeric vector. 8 | #' 9 | #' @return A single value on the scale -1 to 1. 10 | #' @export 11 | #' 12 | #' @examples 13 | normality = function(x){ 14 | x = sort(x) 15 | m = mean(x) 16 | s = sd(x) 17 | q = pnorm(seq(0,1,length.out=length(x))[2:(length(x)-1)],m,s) 18 | x = x[2:(length(x)-1)] 19 | r = cor(x,q) 20 | return(r) 21 | } 22 | -------------------------------------------------------------------------------- /R/stan_summary.R: -------------------------------------------------------------------------------- 1 | #' Compute a summary table for the posterior samples for a parameter from a Stan model 2 | #' 3 | #' @param from_stan A stanfit object from a call to rstan::stan() or rstan::sampling() containing posterior samples from the model. 4 | #' @param par Character string naming the parameter on which to compute the summary table. 5 | #' @param probs Vector of probabilities to show as columns in the summary table. 6 | #' @param X Optional contrast matrix as yielded from get_contrast_matrix() indicating the contrasts passed as data when creating the above stanfit object. 7 | #' @param W Optional contrast matrix as yielded from get_contrast_matrix() indicating the within-subjects contrasts passed as data when creating the above stanfit object. If present, X is ignored and B must be present. 8 | #' @param B Optional contrast matrix as yielded from get_contrast_matrix() indicating the between-subjects contrasts passed as data when creating the above stanfit object. W must be present, else B is ignored. 9 | #' @param is_cor A logical indicating whether the parameter is a correlation matrix. If TRUE, returns only the upper-diagonal entries. 10 | #' 11 | #' @return A tibble 12 | #' @export 13 | #' 14 | #' @examples 15 | stan_summary = function( 16 | from_stan 17 | , par 18 | , probs = c(.5,.025,.975) 19 | , X = NULL 20 | , W = NULL 21 | , B = NULL 22 | , is_cor = F 23 | ){ 24 | m = monitor(from_stan,probs=probs,print=F) 25 | all_pars = dimnames(m)[[1]] 26 | all_pars_no_squares = str_replace(dimnames(m)[[1]],'\\[.*\\]','') 27 | select_pars = all_pars[all_pars_no_squares%in%par] 28 | requested_pars = par 29 | m %>% 30 | tibble::as_tibble(m) %>% 31 | dplyr::mutate( 32 | par = str_replace(dimnames(m)[[1]],'\\[.*\\]','') 33 | ) %>% 34 | dplyr::filter( 35 | par%in%requested_pars 36 | ) %>% 37 | dplyr::select( 38 | par 39 | , contains('%') 40 | , Rhat 41 | , Bulk_ESS 42 | , Tail_ESS 43 | ) -> 44 | m 45 | 46 | if(!is_cor){ 47 | if(!is.null(X)){ 48 | m$par = dimnames(X)[[2]] 49 | } 50 | if(!is.null(W)){ 51 | m$par = names_from_WB(W,B) 52 | } 53 | }else{ 54 | temp = select_pars 55 | temp = gsub(']','',temp) 56 | temp = unlist(strsplit(temp,'[',fixed=T)) 57 | temp = temp[(1:length(temp))%%2==0] 58 | temp = unlist(strsplit(temp,',',fixed=T)) 59 | v1 = temp[(1:length(temp))%%2==1] 60 | v2 = temp[(1:length(temp))%%2==0] 61 | keep = v2>v1 62 | v1 = v1[keep] 63 | v2 = v2[keep] 64 | if(!is.null(X)){ 65 | v1 = dimnames(X)[[2]][as.numeric(v1)] 66 | v2 = dimnames(X)[[2]][as.numeric(v2)] 67 | } 68 | if(!is.null(W)){ 69 | temp = names_from_WB(W,B) 70 | v1 = temp[as.numeric(v1)] 71 | v2 = temp[as.numeric(v2)] 72 | } 73 | m = m[keep,] 74 | m$par = paste(v1,v2,sep='~') 75 | } 76 | return(m) 77 | } 78 | -------------------------------------------------------------------------------- /R/start_stan.R: -------------------------------------------------------------------------------- 1 | #' Start a Stan session 2 | #' 3 | #' @param data A list of objects expected by the model as data. 4 | #' @param mod A stan model created by rstan::stan_model(). 5 | #' @param cores An integer value specifying the number of cores to run in parallel. 6 | #' @param chains_per_core An integer value specifying the number of chain to run sequentially on each core 7 | #' @param seed_start An integer specifying the seed to use for the first core. All subsequent cores will use seed values that increment seed_start. 8 | #' @param ... Additional named arguments passed to rstan::sampling(). 9 | #' @return No value is returned. 10 | #' @export 11 | #' 12 | #' @examples 13 | start_stan = function( 14 | data 15 | , mod 16 | , cores = parallel::detectCores()/2 17 | , chains_per_core = 1 18 | , seed_start = 1 19 | , ... 20 | ){ 21 | if(!('loggr' %in% installed.packages())){ 22 | warning( 23 | 'IMPORTANT: ezStan now relies on the loggr package to show erros an warnings from Stan. If you\'re seeing this message, you need to install loggr by running: 24 | devtools::install_github("mike-lawrence/loggr")' 25 | ) 26 | } 27 | if(dir.exists('stan_temp')){ 28 | clean_stan(report_all_clean=F) 29 | } 30 | dir.create('stan_temp') 31 | stan_args = list(...) 32 | iter = 2e3 33 | if('iter' %in% names(stan_args)){ 34 | iter = stan_args[['iter']] 35 | } 36 | warmup = iter/2 37 | if('warmup'%in% names(stan_args)){ 38 | warmup = stan_args[['warmup']] 39 | } 40 | max_treedepth = 10 41 | if('control'%in% names(stan_args)){ 42 | control = stan_args[['control']] 43 | if('max_treedepth'%in% names(control)){ 44 | max_treedepth = control[['max_treedepth']] 45 | } 46 | } 47 | save( 48 | cores 49 | , chains_per_core 50 | , seed_start 51 | , iter 52 | , warmup 53 | , max_treedepth 54 | , stan_args 55 | , file = 'stan_temp/start_stan_args.rda' 56 | ) 57 | save( 58 | data 59 | , mod 60 | , file = 'stan_temp/data.rda' 61 | ) 62 | cat( 63 | "chain_num = as.numeric(commandArgs(trailingOnly=TRUE)[1]) 64 | load(file='stan_temp/start_stan_args.rda') 65 | #loads the following objects: 66 | # cores 67 | # chains_per_core 68 | # seed_start 69 | # stan_args 70 | 71 | chain_name = sprintf(paste0('chain%0',ceiling(log10(cores)),'d'),chain_num) 72 | sample_file_name = paste0('stan_temp/samples_',chain_name,'.txt') 73 | rda_file_name = paste0('stan_temp/rdas_',chain_name,'.rda') 74 | log_file_name = paste0('stan_temp/logs_',chain_name,'.log') 75 | 76 | if('loggr' %in% installed.packages()){ 77 | suppressMessages(library(loggr,quietly=T)) 78 | my_formatter <- function(event) event$message 79 | log_file(log_file_name,.formatter = my_formatter,subscriptions=c('message', 'warning','stop')) 80 | } 81 | 82 | load('stan_temp/data.rda') 83 | suppressMessages(library(rstan,quietly=T)) 84 | 85 | stan_args$refresh = 0 86 | stan_args$chains = 1 87 | stan_args$cores = 1 88 | stan_args$iter = iter 89 | stan_args$data = data 90 | stan_args$object = mod 91 | stan_args$seed = seed_start + chain_num - 1 92 | stan_args$sample_file = sample_file_name 93 | 94 | post = NULL 95 | while(is.null(post)){ 96 | try(post <- do.call(rstan::sampling,stan_args)) 97 | } 98 | save(post,file=rda_file_name) 99 | #file.remove(sample_file_name) 100 | 101 | next_chain_num = chain_num + cores 102 | if( next_chain_num<=(chains_per_core*cores) ){ 103 | next_chain_name = sprintf(paste0('chain%0',ceiling(log10(cores)),'d'),next_chain_num) 104 | stdout_file = paste0('stan_temp/stdout_',next_chain_name,'.txt') 105 | stderr_file = paste0('stan_temp/stderr_',next_chain_name,'.txt') 106 | system2( 107 | command = 'Rscript' 108 | , args = c('--vanilla','stan_temp/do_chain.R',next_chain_num) 109 | , stdout = stdout_file 110 | , stderr = stderr_file 111 | , wait = FALSE 112 | ) 113 | 114 | } 115 | " 116 | , file = 'stan_temp/do_chain.R' 117 | ) 118 | cat("\nStarting chains...") 119 | for(i in 1:cores){ 120 | chain_name = sprintf(paste0("chain%0",ceiling(log10(cores)),"d"),i) 121 | stdout_file = paste0('stan_temp/stdout_',chain_name,'.txt') 122 | stderr_file = paste0('stan_temp/stderr_',chain_name,'.txt') 123 | system2( 124 | command = "Rscript" 125 | , args = c('--vanilla','stan_temp/do_chain.R',i) 126 | , stdout = stdout_file 127 | , stderr = stderr_file 128 | , wait = FALSE 129 | ) 130 | } 131 | start_time = as.numeric(Sys.time()) 132 | save(start_time,file='stan_temp/start_time.rda') 133 | cat("\nChains started. Run watch_stan() to watch progress") 134 | return(invisible(NULL)) 135 | } 136 | -------------------------------------------------------------------------------- /R/watch_stan.R: -------------------------------------------------------------------------------- 1 | #' Watch the progress of a Stan session 2 | #' 3 | #' @param update_interval Number of seconds to wait between updates. 4 | #' @param one_line_per_chain A logical value specifying whether the progress for each chain should be printed on seperate lines. 5 | #' @param spacing An integer value specifying the number of extra spaces to add to the end of each chain's progress string. Can be used to fix misalignment when one_line_per_chain is TRUE. 6 | #' @param beep_when_done A logical value specifying whether a sound should be played on completion (requires the beepr package to be installed). 7 | #' @param kill_on_divergence A logical value specifying whether to kill chains if a post-warmup divergence is encountered. 8 | #' @param timeout A numeric value indicating the number of seconds after which the sampling is automatically terminated if not complete. If NA (the default), no timeout is enforced. 9 | #' @return No value is returned. 10 | #' @export 11 | #' 12 | #' @examples 13 | watch_stan = function(update_interval=1,one_line_per_chain=TRUE,spacing=2,beep_when_done=TRUE,kill_on_divergence=FALSE,timeout=NA){ 14 | #pre-defining objects we'll get from load() to avoid package build warnings 15 | cores = NULL 16 | chains_per_core = NULL 17 | seed_start = NULL 18 | iter = NULL 19 | stan_args = NULL 20 | load('stan_temp/start_stan_args.rda') 21 | #loads the following objects: 22 | # cores 23 | # chains_per_core 24 | # seed_start 25 | # iter 26 | # warmup 27 | # max_treedepth 28 | # stan_args 29 | #pre-defining objects we'll get from load() to avoid package build warnings 30 | start_time = NULL 31 | load('stan_temp/start_time.rda') 32 | #loads the following objects: 33 | # start_time 34 | num_chains = cores*chains_per_core 35 | if(!file.exists('stan_temp/watching.rda')){ 36 | watching = list() 37 | watching$chain_names = list() 38 | watching$sample_files = list() 39 | watching$rda_files = list() 40 | watching$log_files = list() 41 | watching$sample_file_sizes = list() 42 | watching$samples_done = list() 43 | watching$time_left = list() 44 | watching$sum_exceed_max = list() 45 | watching$sum_divergences = list() 46 | for(this_chain in 1:num_chains){ 47 | chain_name = sprintf(paste0('chain%0',ceiling(log10(cores)),'d'),this_chain) 48 | watching$chain_names[[this_chain]] = chain_name 49 | watching$sample_files[[this_chain]] = paste0('stan_temp/samples_',chain_name,'.txt') 50 | watching$rda_files[[this_chain]] = paste0('stan_temp/rdas_',chain_name,'.rda') 51 | watching$log_files[[this_chain]] = paste0('stan_temp/logs_',chain_name,'.log') 52 | watching$sample_file_sizes[[this_chain]] = 0 53 | watching$samples_done[[this_chain]] = 0 54 | watching$time_left[[this_chain]] = NA 55 | watching$sum_exceed_max[[this_chain]] = 0 56 | watching$sum_divergences[[this_chain]] = 0 57 | watching$warmup_end_time[[this_chain]] = NA 58 | } 59 | watching$num_done = length(list.files(path="stan_temp",pattern='rdas_')) 60 | watching$dones = c() 61 | watching$chains_with_messages = c() 62 | watching$messages_to_print = c() 63 | }else{ 64 | #pre-defining objects we'll get from load() to avoid package build warnings 65 | watching = NULL 66 | load('stan_temp/watching.rda') 67 | #loads the following objects: 68 | # watching 69 | } 70 | while((watching$num_donetimeout){ 77 | kill_stan() 78 | if('beepr'%in%installed.packages()){ 79 | eval(parse(text='beepr::beep()')) #hiding beepr dependency from package check 80 | } 81 | stop('Timeout reached.') 82 | } 83 | } 84 | for(this_chain in 1:num_chains){ 85 | if(!(this_chain %in% watching$dones)){ #if this chain isn't already done 86 | if(file.exists(watching$sample_files[[this_chain]])){ #only try reading the sample file if it exists 87 | size = file.size(watching$sample_files[[this_chain]]) 88 | if(size>watching$sample_file_sizes[[this_chain]]){ #only try reading if the sample file size has changed 89 | # f = file(description=watching$sample_files[[this_chain]],open='rb') 90 | # seek(con = f, origin = 'start', where = watching$sample_file_sizes[[this_chain]]) 91 | # a = readLines(f) 92 | # close(f) 93 | if(is.na(watching$warmup_end_time[[this_chain]])){ 94 | a = NULL 95 | try(a <- data.table::fread( 96 | file = watching$sample_files[[this_chain]] 97 | , sep = ',' 98 | , select = paste('V',1:7,sep='') 99 | , header = FALSE 100 | , skip = 26 + watching$samples_done[[this_chain]] 101 | , blank.lines.skip = TRUE 102 | , fill = TRUE 103 | , nrows = warmup - watching$samples_done[[this_chain]] 104 | ),silent=T) 105 | }else{ 106 | a = NULL 107 | try(a <- data.table::fread( 108 | file = watching$sample_files[[this_chain]] 109 | , sep = ',' 110 | , select = paste('V',1:7,sep='') 111 | , header = FALSE 112 | , skip = 26 + watching$samples_done[[this_chain]] + 4 113 | , nrows = iter - watching$samples_done[[this_chain]] 114 | , fill = TRUE 115 | , blank.lines.skip = TRUE 116 | ),silent=T) 117 | } 118 | if(!is.null(a)){ 119 | names(a)[1:7] = c('lp__','accept_stat__','stepsize__','treedepth__','n_leapfrog__','divergent__','energy__') 120 | a = a[!is.na(a$divergent__),] 121 | if(nrow(a)>0){ 122 | watching$sample_file_sizes[[this_chain]] = size 123 | watching$samples_done[[this_chain]] = watching$samples_done[[this_chain]] + nrow(a) 124 | watching$sum_exceed_max[[this_chain]] = watching$sum_exceed_max[[this_chain]] + sum(a$treedepth__>max_treedepth) 125 | if(watching$samples_done[[this_chain]]>warmup){ 126 | if(any(a$divergent__==1)){ 127 | watching$sum_divergences[[this_chain]] = watching$sum_divergences[[this_chain]] + sum(a$divergent__) 128 | if(kill_on_divergence){ 129 | kill_stan() 130 | if('beepr'%in%installed.packages()){ 131 | eval(parse(text='beepr::beep()')) #hiding beepr dependency from package check 132 | } 133 | stop('Post-warmup divergence encountered') 134 | } 135 | } 136 | } 137 | if(watching$samples_done[[this_chain]]>0){ 138 | if(watching$samples_done[[this_chain]]>=warmup){ 139 | if(is.na(watching$warmup_end_time[[this_chain]])){ 140 | watching$warmup_end_time[[this_chain]] = time_now 141 | }else{ 142 | time_per_sample = ( time_now - watching$warmup_end_time[[this_chain]] ) / (watching$samples_done[[this_chain]]-warmup) 143 | samples_left = iter - watching$samples_done[[this_chain]] 144 | watching$time_left[[this_chain]] = samples_left*time_per_sample 145 | } 146 | }else{ 147 | time_per_sample = time_elapsed / watching$samples_done[[this_chain]] 148 | samples_left = iter - watching$samples_done[[this_chain]] 149 | watching$time_left[[this_chain]] = samples_left*time_per_sample 150 | } 151 | } 152 | if(watching$samples_done[[this_chain]]==iter){ 153 | watching$dones = c(watching$dones,this_chain) 154 | } 155 | save(watching,file='stan_temp/watching.rda') 156 | } 157 | } 158 | } 159 | }else{ 160 | if(file.exists(watching$rda_files[[this_chain]])){ #might not have caught this finished chain before its sample file was deleted 161 | watching$samples_done[[this_chain]] = iter 162 | watching$dones = c(watching$dones,this_chain) 163 | save(watching,file='stan_temp/watching.rda') 164 | } 165 | } 166 | } 167 | # if(!(this_chain %in% watching$chains_with_messages)){ #if this chain isn't already in the error list 168 | # if(file.exists(watching$log_files[[this_chain]])){ #check if the log file exists 169 | # temp = readLines(watching$log_files[[this_chain]]) 170 | # if(length(temp)>0){ #log file has contents 171 | # watching$chains_with_messages = c(watching$chains_with_messages,this_chain) 172 | # watching$messages_to_print = c( 173 | # watching$messages_to_print 174 | # , paste0( 175 | # '[' 176 | # , watching$chain_names[[this_chain]] 177 | # , ' messages:] ' 178 | # ) 179 | # ) 180 | # for(j in 1:length(temp)){ 181 | # watching$messages_to_print = c( 182 | # watching$messages_to_print 183 | # , temp[j] 184 | # ) 185 | # } 186 | # } 187 | # } 188 | # } 189 | time_left = "?" 190 | if(any(!is.na(unlist(watching$time_left)))){ 191 | time_left = time_as_string(max(unlist(watching$time_left),na.rm=T)) 192 | } 193 | update_text_to_print = '\r' 194 | for(this_chain in 1:num_chains){ 195 | this_chain_message = paste0( 196 | watching$chain_names[[this_chain]] 197 | , ': ' 198 | , watching$samples_done[[this_chain]] 199 | ,'/',iter 200 | ) 201 | if(watching$sum_divergences[[this_chain]]>0){ 202 | this_chain_message = paste0( 203 | this_chain_message 204 | , ' , divergences: ' 205 | , watching$sum_divergences[[this_chain]] 206 | ) 207 | } 208 | if(watching$sum_exceed_max[[this_chain]]>0){ 209 | this_chain_message = paste0( 210 | this_chain_message 211 | , ' , max_treedepth exceeded: ' 212 | , watching$sum_exceed_max[[this_chain]] 213 | ) 214 | } 215 | update_text_to_print = append_string(update_text_to_print,this_chain_message,spacing,one_line_per_chain) 216 | } 217 | temp = paste0('Chains complete: ',watching$num_done,'/',num_chains) 218 | update_text_to_print = append_string(update_text_to_print,temp,spacing,one_line_per_chain) 219 | temp = paste0('Estimated time remaining: ',time_left) 220 | update_text_to_print = append_string(update_text_to_print,temp,spacing,one_line_per_chain) 221 | temp = paste0('Elapsed time: ',time_as_string(time_elapsed)) 222 | update_text_to_print = append_string(update_text_to_print,temp,spacing,one_line_per_chain) 223 | if(!is.na(timeout)){ 224 | temp = paste0('Time to timeout: ',time_as_string(timeout-time_elapsed)) 225 | update_text_to_print = append_string(update_text_to_print,temp,spacing,one_line_per_chain) 226 | } 227 | if(length(watching$messages_to_print)>0){ 228 | for(temp in watching$messages_to_print){ 229 | update_text_to_print = append_string(update_text_to_print,temp,spacing,one_line_per_chain) 230 | } 231 | update_text_to_print = append_string(update_text_to_print,' ',spacing,one_line_per_chain) 232 | } 233 | cat(update_text_to_print) 234 | utils::flush.console() 235 | } 236 | } 237 | #one last check for messages 238 | for(this_chain in 1:num_chains){ 239 | if(!(this_chain %in% watching$chains_with_messages)){ #if this chain isn't already in the error list 240 | if(file.exists(watching$log_files[[this_chain]])){ #check if the log file exists 241 | temp = readLines(watching$log_files[[this_chain]]) 242 | if(length(temp)>0){ #log file has contents 243 | watching$chains_with_messages = c(watching$chains_with_messages,this_chain) 244 | for(j in 1:length(temp)){ 245 | watching$messages_to_print = c( 246 | watching$messages_to_print 247 | , paste0( 248 | '[' 249 | , watching$chain_names[[this_chain]] 250 | , ':] ' 251 | , temp[j] 252 | ) 253 | ) 254 | } 255 | } 256 | } 257 | } 258 | } 259 | update_text_to_print = '\r' 260 | for(this_chain in 1:(num_chains+1)){ 261 | update_text_to_print = append_string(update_text_to_print,' ',spacing,one_line_per_chain) 262 | } 263 | time_elapsed = ( as.numeric(Sys.time()) - start_time ) 264 | temp = paste0('All done! Elapsed time: ',time_as_string(time_elapsed)) 265 | update_text_to_print = append_string(update_text_to_print,temp,spacing,one_line_per_chain) 266 | if(length(watching$messages_to_print)>0){ 267 | for(temp in watching$messages_to_print){ 268 | update_text_to_print = append_string(update_text_to_print,temp,spacing,one_line_per_chain) 269 | } 270 | update_text_to_print = append_string(update_text_to_print,' ',spacing,one_line_per_chain) 271 | } 272 | update_text_to_print = paste0(update_text_to_print,'\n') 273 | cat(update_text_to_print) 274 | utils::flush.console() 275 | if(beep_when_done){ 276 | if('beepr'%in%installed.packages()){ 277 | eval(parse(text='beepr::beep()')) #hiding beepr dependency from package check 278 | } 279 | } 280 | return(invisible(NULL)) 281 | } 282 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Note: 2 | This project is no longer being developed. See [aria](https://github.com/mike-lawrence/aria) for a more reccent project with some shared/extended features. 3 | 4 | # ezStan 5 | An R package containing helper functions for Stan. 6 | 7 | This package contains code I develop through my teaching and personal use of Stan/RStan to make some common tasks easier. It's very much a work in progress and probably won't ever be on CRAN. 8 | 9 | Install via: 10 | ```{r} 11 | 12 | devtools::install_github('mike-lawrence/ezStan') #requires devtools to be installed, which is achieved via install.packages("devtools") 13 | ``` 14 | 15 | Optionally, for better warning & error message handling, install loggr: 16 | ```{r} 17 | devtools::install_github('mike-lawrence/loggr') #temporarily necessary until smbache/loggr is updated and uploaded to CRAN 18 | ``` 19 | 20 | Usage Example: 21 | ```{r} 22 | #compile a model; shouldn't block the RStudio UI like rstan::stan_model() 23 | my_mod = build_stan(my_stan_file) 24 | 25 | #start the model sampling 26 | start_stan( 27 | data = my_data_for_stan 28 | , mod = my_mod 29 | # can add other arguments to rstan::sampling() here if nessary, for example: 30 | , iter = 4e3 31 | ) 32 | 33 | #now watch the sampling (see ?watch_stan for options, like killing when post-warmup divergences are encountered) 34 | watch_stan() 35 | #it's ok to stop the watcher; you can resume watching by simply running watch_stan() again 36 | 37 | #if anything goes awry with the sampling, you can call kill_stan() to stop all chains 38 | 39 | #when the sampling is done, collect results 40 | my_post = collect_stan() 41 | 42 | #once collected, you can recover some disk space by running: 43 | clean_stan() 44 | 45 | #slightly cleaner looking summary table: 46 | stan_summary( 47 | post = my_post 48 | , par = 'some_parameter' 49 | ) 50 | 51 | ``` 52 | -------------------------------------------------------------------------------- /ezStan.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: No 9 | NumSpacesForTab: 4 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | StripTrailingWhitespace: Yes 16 | 17 | BuildType: Package 18 | PackageUseDevtools: Yes 19 | PackageInstallArgs: --no-multiarch --with-keep.source 20 | PackageRoxygenize: rd,collate,namespace 21 | -------------------------------------------------------------------------------- /man/build_stan.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/build_stan.R 3 | \name{build_stan} 4 | \alias{build_stan} 5 | \title{Build a Stan model without locking R/RStudio} 6 | \usage{ 7 | build_stan(file) 8 | } 9 | \arguments{ 10 | \item{file}{A character string specifying the path to a Stan file to build.} 11 | } 12 | \value{ 13 | an object of the class rstan::stan_model. 14 | } 15 | \description{ 16 | Build a Stan model without locking R/RStudio 17 | } 18 | -------------------------------------------------------------------------------- /man/clean_stan.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/clean_stan.R 3 | \name{clean_stan} 4 | \alias{clean_stan} 5 | \title{Delete temporary files created by a Stan session} 6 | \usage{ 7 | clean_stan(report_all_clean = T) 8 | } 9 | \arguments{ 10 | \item{report_all_clean}{A logical value indicating whether this function should print an "All Clean!" message when complete.} 11 | } 12 | \value{ 13 | No value is returned 14 | } 15 | \description{ 16 | Delete temporary files created by a Stan session 17 | } 18 | -------------------------------------------------------------------------------- /man/collect_stan.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/collect_stan.R 3 | \name{collect_stan} 4 | \alias{collect_stan} 5 | \title{Collect the results from a Stan session} 6 | \usage{ 7 | collect_stan() 8 | } 9 | \value{ 10 | An object of S4 class stanfit. 11 | } 12 | \description{ 13 | Collect the results from a Stan session 14 | } 15 | -------------------------------------------------------------------------------- /man/get_condition_post.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_condition_post.R 3 | \name{get_condition_post} 4 | \alias{get_condition_post} 5 | \title{Combine coefficient samples from a Stan model with the design matrix to compute posterior samples on the conditions of the design.} 6 | \usage{ 7 | get_condition_post( 8 | from_stan, 9 | par, 10 | X = NULL, 11 | W = NULL, 12 | B = NULL, 13 | numeric_res = 0, 14 | collapse_intercept_to_median = FALSE 15 | ) 16 | } 17 | \arguments{ 18 | \item{from_stan}{A stanfit object from a call to rstan::stan() or rstan::sampling() containing posterior samples from the model.} 19 | 20 | \item{par}{Character string naming the parameter on which to compute the summary table.} 21 | 22 | \item{X}{Optional contrast matrix as yielded from get_contrast_matrix() indicating the contrasts passed as data when creating the above stanfit object.} 23 | 24 | \item{W}{Optional contrast matrix as yielded from get_contrast_matrix() indicating the within-subjects contrasts passed as data when creating the above stanfit object. If present, X is ignored and B must be present.} 25 | 26 | \item{B}{Optional contrast matrix as yielded from get_contrast_matrix() indicating the between-subjects contrasts passed as data when creating the above stanfit object. W must be present, else B is ignored.} 27 | 28 | \item{numeric_res}{Integer value conveying the number of points within the range of a continuous variable to sample. If 0, the values from the data are used.} 29 | 30 | \item{collapse_intercept_to_median}{Logical indicating whether to use the median of the samples for the intercept rather than its full set of samples. Assumes that the intercept is the first parameter.} 31 | } 32 | \value{ 33 | If the `tibble` package is installed, a tibble; else, a data frame. 34 | } 35 | \description{ 36 | Combine coefficient samples from a Stan model with the design matrix to compute posterior samples on the conditions of the design. 37 | } 38 | -------------------------------------------------------------------------------- /man/get_contrast_matrix.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_contrast_matrix.R 3 | \name{get_contrast_matrix} 4 | \alias{get_contrast_matrix} 5 | \title{Obtain a contrast matrix for a data set given a specified formula} 6 | \usage{ 7 | get_contrast_matrix(data, formula, contrast_kind = NULL) 8 | } 9 | \arguments{ 10 | \item{data}{A data frame (or tibble).} 11 | 12 | \item{formula}{A formula starting with `~` expressing the model} 13 | 14 | \item{contrast_kind}{The kind of contrasts to use. Used to quickly convert all factors to a particular non-R-default contrast kind.} 15 | } 16 | \value{ 17 | A contrast matrix with the formula & data saved as attributes. 18 | } 19 | \description{ 20 | Obtain a contrast matrix for a data set given a specified formula 21 | } 22 | -------------------------------------------------------------------------------- /man/get_subject_indices.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_subject_indices.R 3 | \name{get_subject_indices} 4 | \alias{get_subject_indices} 5 | \title{Get a table of start/end indices from subject label vector} 6 | \usage{ 7 | get_subject_indices(x) 8 | } 9 | \arguments{ 10 | \item{x}{A vector of subject labels, sorted by said labels.} 11 | } 12 | \value{ 13 | A matrix with two columns (start & end) and as many rows as there are unique labels in the input 14 | } 15 | \description{ 16 | Given input of an ordered subject label vector, returns the indices into this 17 | vector corresponding to the beginning and end of each subject. 18 | } 19 | -------------------------------------------------------------------------------- /man/halfhelmert_contrasts.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/halfhelmert_contrasts.R 3 | \name{halfhelmert_contrasts} 4 | \alias{halfhelmert_contrasts} 5 | \title{Half-Helmert Contrasts} 6 | \usage{ 7 | halfhelmert_contrasts(...) 8 | } 9 | \arguments{ 10 | \item{...}{Arguments passed to contr.helmert} 11 | } 12 | \value{ 13 | See Value in contr.helmert(). 14 | } 15 | \description{ 16 | contr.helmert()/2. 17 | } 18 | -------------------------------------------------------------------------------- /man/halfsum_contrasts.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/halfsum_contrasts.R 3 | \name{halfsum_contrasts} 4 | \alias{halfsum_contrasts} 5 | \title{Half-sum Contrasts} 6 | \usage{ 7 | halfsum_contrasts(...) 8 | } 9 | \arguments{ 10 | \item{...}{Arguments passed to contr.sum} 11 | } 12 | \value{ 13 | See Value in contr.sum(). 14 | } 15 | \description{ 16 | contr.sum()/2. 17 | } 18 | -------------------------------------------------------------------------------- /man/kill_stan.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/kill_stan.R 3 | \name{kill_stan} 4 | \alias{kill_stan} 5 | \title{Terminate an errant Stan session} 6 | \usage{ 7 | kill_stan() 8 | } 9 | \value{ 10 | No value is returned 11 | } 12 | \description{ 13 | Terminate an errant Stan session 14 | } 15 | -------------------------------------------------------------------------------- /man/names_from_WB.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/names_from_WB.R 3 | \name{names_from_WB} 4 | \alias{names_from_WB} 5 | \title{Figure out variable names given W & B contrast matrices} 6 | \usage{ 7 | names_from_WB(W, B, reverse = F) 8 | } 9 | \arguments{ 10 | \item{W}{Optional contrast matrix as yielded from get_contrast_matrix() indicating the within-subjects contrasts passed as data when creating the above stanfit object. If present, X is ignored and B must be present.} 11 | 12 | \item{B}{Optional contrast matrix as yielded from get_contrast_matrix() indicating the between-subjects contrasts passed as data when creating the above stanfit object. W must be present, else B is ignored.} 13 | 14 | \item{reverse}{Logical indicating whether to reverse the order of the names.} 15 | } 16 | \value{ 17 | A vector of character strings indicating names of variables. 18 | } 19 | \description{ 20 | Figure out variable names given W & B contrast matrices 21 | } 22 | -------------------------------------------------------------------------------- /man/normality.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/normality.R 3 | \name{normality} 4 | \alias{normality} 5 | \title{A metric of normality} 6 | \usage{ 7 | normality(x) 8 | } 9 | \arguments{ 10 | \item{x}{A numeric vector.} 11 | } 12 | \value{ 13 | A single value on the scale -1 to 1. 14 | } 15 | \description{ 16 | Provides a measure of normality for the input by computing the correlation 17 | between the data and the expected data given each data point's quantile and a 18 | normal distribution with a mean equal to the data's mean and a standard 19 | deviation equal to the data's standard deviation. 20 | } 21 | -------------------------------------------------------------------------------- /man/stan_summary.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/stan_summary.R 3 | \name{stan_summary} 4 | \alias{stan_summary} 5 | \title{Compute a summary table for the posterior samples for a parameter from a Stan model} 6 | \usage{ 7 | stan_summary( 8 | from_stan, 9 | par, 10 | probs = c(0.5, 0.025, 0.975), 11 | X = NULL, 12 | W = NULL, 13 | B = NULL, 14 | is_cor = F 15 | ) 16 | } 17 | \arguments{ 18 | \item{from_stan}{A stanfit object from a call to rstan::stan() or rstan::sampling() containing posterior samples from the model.} 19 | 20 | \item{par}{Character string naming the parameter on which to compute the summary table.} 21 | 22 | \item{probs}{Vector of probabilities to show as columns in the summary table.} 23 | 24 | \item{X}{Optional contrast matrix as yielded from get_contrast_matrix() indicating the contrasts passed as data when creating the above stanfit object.} 25 | 26 | \item{W}{Optional contrast matrix as yielded from get_contrast_matrix() indicating the within-subjects contrasts passed as data when creating the above stanfit object. If present, X is ignored and B must be present.} 27 | 28 | \item{B}{Optional contrast matrix as yielded from get_contrast_matrix() indicating the between-subjects contrasts passed as data when creating the above stanfit object. W must be present, else B is ignored.} 29 | 30 | \item{is_cor}{A logical indicating whether the parameter is a correlation matrix. If TRUE, returns only the upper-diagonal entries.} 31 | } 32 | \value{ 33 | A tibble 34 | } 35 | \description{ 36 | Compute a summary table for the posterior samples for a parameter from a Stan model 37 | } 38 | -------------------------------------------------------------------------------- /man/start_stan.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/start_stan.R 3 | \name{start_stan} 4 | \alias{start_stan} 5 | \title{Start a Stan session} 6 | \usage{ 7 | start_stan( 8 | data, 9 | mod, 10 | cores = parallel::detectCores()/2, 11 | chains_per_core = 1, 12 | seed_start = 1, 13 | ... 14 | ) 15 | } 16 | \arguments{ 17 | \item{data}{A list of objects expected by the model as data.} 18 | 19 | \item{mod}{A stan model created by rstan::stan_model().} 20 | 21 | \item{cores}{An integer value specifying the number of cores to run in parallel.} 22 | 23 | \item{chains_per_core}{An integer value specifying the number of chain to run sequentially on each core} 24 | 25 | \item{seed_start}{An integer specifying the seed to use for the first core. All subsequent cores will use seed values that increment seed_start.} 26 | 27 | \item{...}{Additional named arguments passed to rstan::sampling().} 28 | } 29 | \value{ 30 | No value is returned. 31 | } 32 | \description{ 33 | Start a Stan session 34 | } 35 | -------------------------------------------------------------------------------- /man/watch_stan.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/watch_stan.R 3 | \name{watch_stan} 4 | \alias{watch_stan} 5 | \title{Watch the progress of a Stan session} 6 | \usage{ 7 | watch_stan( 8 | update_interval = 1, 9 | one_line_per_chain = TRUE, 10 | spacing = 2, 11 | beep_when_done = TRUE, 12 | kill_on_divergence = FALSE, 13 | timeout = NA 14 | ) 15 | } 16 | \arguments{ 17 | \item{update_interval}{Number of seconds to wait between updates.} 18 | 19 | \item{one_line_per_chain}{A logical value specifying whether the progress for each chain should be printed on seperate lines.} 20 | 21 | \item{spacing}{An integer value specifying the number of extra spaces to add to the end of each chain's progress string. Can be used to fix misalignment when one_line_per_chain is TRUE.} 22 | 23 | \item{beep_when_done}{A logical value specifying whether a sound should be played on completion (requires the beepr package to be installed).} 24 | 25 | \item{kill_on_divergence}{A logical value specifying whether to kill chains if a post-warmup divergence is encountered.} 26 | 27 | \item{timeout}{A numeric value indicating the number of seconds after which the sampling is automatically terminated if not complete. If NA (the default), no timeout is enforced.} 28 | } 29 | \value{ 30 | No value is returned. 31 | } 32 | \description{ 33 | Watch the progress of a Stan session 34 | } 35 | --------------------------------------------------------------------------------