Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
160ff12
New function extract_lhs_varnames to refactor other functions
NErler Sep 1, 2025
d1002f1
make function names more descriptive
NErler Sep 1, 2025
45b0784
refactor function for readability
NErler Sep 1, 2025
4f09daf
improve function documentation
NErler Sep 1, 2025
b837e89
automatic file reformatting
NErler Sep 1, 2025
9218898
update documentation
NErler Sep 1, 2025
3c9cf89
add test for extract_lhs_varnames
NErler Sep 1, 2025
ee71ad4
update test due to change in function names
NErler Sep 1, 2025
e25ae85
reformatting by styler
NErler Sep 1, 2025
bedfc20
refactor function and improve documentation
NErler Sep 1, 2025
2a2828b
Refactor all_vars
NErler Sep 3, 2025
9d4f337
Bump version to 1.0.6.9000 and update NEWS
NErler Sep 3, 2025
f921c0e
use new version of all_vars()
NErler Sep 3, 2025
0ade2fe
Add documentation and refactor check_classes function
NErler Sep 3, 2025
00590eb
refactor check_vars_in_data and add documentation
NErler Sep 3, 2025
8e283ee
follow-up commit: use extract_lhs_string instead of extract_lhs
NErler Sep 3, 2025
f62a39a
Refactor extract_id to extract_grouping throughout codebase
NErler Sep 4, 2025
5cc3426
Re-activate check for unused levels
NErler Sep 4, 2025
f03d33f
Added/improved documentation of internal functions
NErler Sep 4, 2025
71a33d0
Added little helper function get_listelement()
NErler Sep 4, 2025
da3f092
New helper function rd_terms_by_grouping()
NErler Sep 4, 2025
18d9e08
refactoring of get_nranef
NErler Sep 4, 2025
a0ad346
Refactoring of remove_grouping()
NErler Sep 4, 2025
b0df0a8
Added function documentation
NErler Sep 4, 2025
4452d3b
remove example for internal function
NErler Sep 7, 2025
db95c61
move function clean_names in syntax file
NErler Sep 7, 2025
5c3ad21
Refactor data check (helper) functions
NErler Sep 7, 2025
7264109
update documentation
NErler Sep 7, 2025
faa8ecb
added markers and comments in syntax
NErler Sep 7, 2025
3d5a5eb
temporary fix in predict() due to new convert_variables()
NErler Sep 7, 2025
f820859
new test file for helpfunctions-checks
NErler Sep 7, 2025
c9654b2
snapshot files, auto generated
NErler Sep 7, 2025
70edb50
refactor get_groups(), add documentation and tests
NErler Sep 7, 2025
71c740e
Fix message printed when model will be run in parallel.
NErler Sep 26, 2025
97cee35
fix summary not displaying the correct mcmc chain settings when argum…
NErler Sep 26, 2025
7429022
add note to remind me of future improvement to do
NErler Oct 14, 2025
7b45804
fixed all_vars, could not handle functions in auxiliary variables, an…
NErler Oct 14, 2025
09b5f48
Fix failing RMD check due to dQuote setting
NErler Oct 15, 2025
4a4e7d6
use old function style
NErler Oct 15, 2025
41e4e88
bugfix for using functions in model formulas
NErler Oct 15, 2025
3ab12c3
update test file for new version of all_vars()
NErler Oct 15, 2025
88ee53b
Initial plan
Copilot Nov 8, 2025
88482f5
Initial plan
Copilot Nov 8, 2025
45cf63c
Fix typo in test description: extact_lhs -> extract_lhs
Copilot Nov 8, 2025
812a9a1
Fix grammatical error in test description
Copilot Nov 8, 2025
7b55f9d
Update R/helpfunctions_checks.R
NErler Nov 8, 2025
ea28e72
Merge pull request #21 from NErler/copilot/sub-pr-19-again
NErler Nov 8, 2025
50eb0c1
Merge pull request #20 from NErler/copilot/sub-pr-19
NErler Nov 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Package: JointAI
Version: 1.0.6
Version: 1.0.6.9000
Title: Joint Analysis and Imputation of Incomplete Data
Authors@R: c(person("Nicole S.", "Erler", email = "n.s.erler@umcutrecht.nl",
role = c("aut", "cre"),
Expand Down
16 changes: 14 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
# JointAI (development version)

## Bug fixes
* Error when using the same function twice in a model formula with different
number of variables (e.g., I(x^2) and I(a/b)) fixed.

## Small improvements
* `all_vars()`: update of the function; can now handle an unspecified number of
input objects and can extract variable names from formulas and character
strings that are valid variable names


--------------------------------------------------------------------------------

# JointAI 1.0.6

* clean-up of helper functions and additional unit tests
* fix typos in argument names in helpfiles
* fix documentation syntax (CRAN NOTEs)

--------------------------------------------------------------------------------



# JointAI 1.0.5

(update request by CRAN)
Expand Down
5 changes: 2 additions & 3 deletions R/divide_matrices.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ divide_matrices <- function(data, fixed, random = NULL, analysis_type,
rd_vcov = rd_vcov, ...) {

# id's and groups ------------------------------------------------------------
# extract the id variable from the random effects formula and get groups
idvar <- extract_id(random, warn = warn)
idvar <- extract_grouping(random, warn = warn)

# re-format data for survival with time-varying covariates:
# the time variables of the longitudinal measurements and the survival times
Expand Down Expand Up @@ -243,7 +242,7 @@ divide_matrices <- function(data, fixed, random = NULL, analysis_type,
}


nranef <- get_nranef(idvar = idvar, random = random, data = data)
nranef <- get_nranef(random = random, data = data)
rd_vcov <- check_rd_vcov(rd_vcov = rd_vcov, nranef = nranef)

list(data = data,
Expand Down
12 changes: 7 additions & 5 deletions R/get_modeltypes.R
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ get_models <- function(fixed, random = NULL, data, auxvars = NULL,
}

# check that all variables are found in the data
allvars <- unique(c(all_vars(c(fixed, random, auxvars)), timevar))
allvars <- all_vars(fixed, random, auxvars, timevar)

if (any(!names(models) %in% names(data))) {
errormsg("Variable(s) %s were not found in the data." ,
Expand All @@ -53,16 +53,18 @@ get_models <- function(fixed, random = NULL, data, auxvars = NULL,


# extract the id variable from the random effects formula and get groups
idvar <- extract_id(random, warn = warn)
idvar <- extract_grouping(random, warn = warn)
groups <- get_groups(idvar, data)

random2 <- remove_grouping(random)


# new version of allvars, without the grouping variable
# NOTE: all_vars() checks if a string is a valid variable name but that is
# not the case for all names of fixed (e.g., Surv(time, status))
allvars <- unique(c(names(fixed),
all_vars(c(remove_lhs(fixed), random2, auxvars)),
names(models), timevar))
all_vars(remove_lhs(fixed), random2, auxvars,
names(models), timevar)
))

group_lvls <- colSums(!identify_level_relations(groups))
max_lvl <- max(group_lvls)
Expand Down
2 changes: 1 addition & 1 deletion R/get_refs.R
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ set_refcat <- function(data, formula, covars, auxvars = NULL) {# nocov start
covars <- all_vars(remove_lhs(formula))
}
if (!is.null(auxvars))
covars <- unique(c(covars, all_vars(auxvars)))
covars <- all_vars(covars, auxvars)

factors <- covars[sapply(data[covars], is.factor)]

Expand Down
96 changes: 68 additions & 28 deletions R/helpfunctions.R
Original file line number Diff line number Diff line change
Expand Up @@ -62,48 +62,88 @@ cvapply <- function(x, fun, ...) {

# variable levels and grouping -------------------------------------------------

get_groups <- function(idvar, data) {
# identify clusters/groups based on the id variables
# - idvar: vector of names of the id variables
# - data: a data.frame
#' Get grouping information
#'
#' A helper function that generates grouping information from a data.frame
#' and a character vector with the names of grouping variables.
#' In all cases, the level "lvlone" is added to indicate the lowest level of
#' the data (i.e., each observation is its own group).
#'
#' @param idvars a character vector with the names of grouping variables
#' @param data a data.frame
#'
#' @returns a list with grouping information for each grouping level:
#' each element is a vector of length `nrow(data)` with the group
#' membership indices of each observation for the corresponding
#' grouping level.
#' @keywords internal
#'
get_groups <- function(idvars, data) {

if (!is.null(idvar)) {
groups <- nlapply(idvar, function(i) {
if (!is.null(idvars)) {
groups <- nlapply(idvars, function(i) {
match(data[, i], unique(data[, i]))
})

# check for unnecessary nesting levels
gr_length <- ivapply(groups, function(x) length(unique(x))) == nrow(data)
if (any(gr_length)) {
if (sum(gr_length) == 1L) {
errormsg("The grouping level %s seem to be unnecessary.
There are only unique observations at this level.",
dQuote(names(gr_length[gr_length])))
} else {
errormsg("The grouping levels %s seem to be unnecessary.
There are only unique observations at these levels.",
paste_and(dQuote(names(gr_length[gr_length]))))
}
}
check_unnecessary_grouping_levels(groups, nrow(data))
check_duplicate_groupings(groups)

# add lowest-level grouping "lvlone"
groups$lvlone <- seq_len(nrow(data))

# check for duplicate levels
gr_dupl <- duplicated(groups)
if (any(gr_dupl)) {
gr_dupl2 <- duplicated(groups, fromLast = TRUE)
errormsg("The grouping levels %s are duplicates.",
paste_and(dQuote(unique(c(names(groups)[gr_dupl],
names(groups)[gr_dupl2])))))
}
} else {
groups <- list(lvlone = seq_len(nrow(data)))
}

groups
return(groups)
}



#' Check for unnecessary grouping levels
#'
#' @param groups a list of grouping information, as obtained from `get_groups()`
#'
#' @returns NULL; throws an error if unnecessary grouping levels are found
#' @keywords internal
#'
check_unnecessary_grouping_levels <- function(groups, nrow_data) {
group_lengths <- ivapply(groups, function(x) length(unique(x)) == nrow_data)

if (any(group_lengths)) {
errormsg(
"The grouping level(s) %s seem(s) to be unnecessary.
There are only unique observations at these levels.",
paste_and(dQuote(names(group_lengths[group_lengths])))
)
}
}

#' Check for duplicate grouping levels
#'
#' @param groups a list of grouping information, as obtained from `get_groups()`
#'
#' @returns NULL; throws an error if duplicate grouping levels are found
#' @keywords internal
#'
check_duplicate_groupings <- function(groups) {
group_duplicates <- duplicated(groups)
if (any(group_duplicates)) {
group_dupl2 <- duplicated(groups, fromLast = TRUE)
errormsg(
"The grouping levels %s are duplicates.",
paste_and(dQuote(unique(c(
names(groups)[group_duplicates],
names(groups)[group_dupl2]
))))
)
}
}





check_cluster <- function(x, grouping) {
# check if a variable varies within one cluster
# - x: a vector
Expand Down
4 changes: 2 additions & 2 deletions R/helpfunctions_JAGS.R
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ run_parallel <- function(n_adapt, n_iter, n_chains, inits, thin = 1L,

if (isTRUE(mess) & isTRUE(parallel))
msg("Parallel sampling with %s workers started (%s).",
length(f$workers), Sys.time())
future::nbrOfWorkers(), Sys.time())

if (isTRUE(mess) & !isTRUE(parallel))
msg("Note: the original model was run in parallel.")
Expand Down Expand Up @@ -165,7 +165,7 @@ run_parallel <- function(n_adapt, n_iter, n_chains, inits, thin = 1L,
}

fit$parallel <- parallel
fit$workers <- length(f$workers)
fit$workers <- future::nbrOfWorkers()

if (!isTRUE(parallel)) {
fit$time_adapt <- difftime_df(fit$time_adapt)
Expand Down
Loading
Loading