From 0ecf6f14dd4816a79ffb860f8f430cd6451efcbb Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 12:14:04 -0500 Subject: [PATCH 01/31] Fix collinearity inflation for ordinal models --- R/check_collinearity.R | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index 86a28334a..d77cd7123 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -491,12 +491,24 @@ check_collinearity.zerocount <- function( } } - # check for missing intercept - if (insight::has_intercept(x)) { - v <- v[-1, -1] - term_assign <- term_assign[-1] - } else if (isTRUE(verbose)) { - insight::format_alert("Model has no intercept. VIFs may not be sensible.") + # Filter to true slope parameters (handles multiple intercepts in ordinal models) + if (inherits(x, c("clm", "clmm"))) { + slope_names <- names(x$beta) + keep_idx <- which(colnames(v) %in% slope_names) + } else if (insight::has_intercept(x)) { + # Standard behavior: drop the first column/row (the singular intercept) + keep_idx <- seq_len(ncol(v))[-1] + } else { + keep_idx <- seq_len(ncol(v)) + if (isTRUE(verbose)) { + insight::format_alert("Model without intercept. VIFs may not be sensible.") + } + } + + # Safely subset both the matrix and the assignment vector + if (length(keep_idx) < ncol(v)) { + v <- v[keep_idx, keep_idx, drop = FALSE] + term_assign <- term_assign[keep_idx] } f <- insight::find_formula(x, verbose = FALSE) From c645ff73b1fb24c6cb150407ded9fa3f364f9de0 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 12:23:46 -0500 Subject: [PATCH 02/31] Address gemini-code-review feedback --- R/check_collinearity.R | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index d77cd7123..6fcb1ac0a 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -491,9 +491,10 @@ check_collinearity.zerocount <- function( } } - # Filter to true slope parameters (handles multiple intercepts in ordinal models) +# Filter to true slope parameters (handles multiple intercepts in ordinal models) if (inherits(x, c("clm", "clmm"))) { - slope_names <- names(x$beta) + slope_names <- insight::find_parameters(x)$conditional + if (is.null(slope_names)) slope_names <- names(x$beta) keep_idx <- which(colnames(v) %in% slope_names) } else if (insight::has_intercept(x)) { # Standard behavior: drop the first column/row (the singular intercept) @@ -504,11 +505,13 @@ check_collinearity.zerocount <- function( insight::format_alert("Model without intercept. VIFs may not be sensible.") } } - - # Safely subset both the matrix and the assignment vector + + # Safely subset the matrix and the assignment vector if (length(keep_idx) < ncol(v)) { + if (!is.null(term_assign) && length(term_assign) == ncol(v)) { + term_assign <- term_assign[keep_idx] + } v <- v[keep_idx, keep_idx, drop = FALSE] - term_assign <- term_assign[keep_idx] } f <- insight::find_formula(x, verbose = FALSE) From 75f16f08e056e45c8a17fdc8e9cde26e27d6ebcf Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 12:25:15 -0500 Subject: [PATCH 03/31] Apply styling --- R/check_collinearity.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index 6fcb1ac0a..d4c4f0b9c 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -491,10 +491,12 @@ check_collinearity.zerocount <- function( } } -# Filter to true slope parameters (handles multiple intercepts in ordinal models) + # Filter to true slope parameters (handles multiple intercepts in ordinal models) if (inherits(x, c("clm", "clmm"))) { slope_names <- insight::find_parameters(x)$conditional - if (is.null(slope_names)) slope_names <- names(x$beta) + if (is.null(slope_names)) { + slope_names <- names(x$beta) + } keep_idx <- which(colnames(v) %in% slope_names) } else if (insight::has_intercept(x)) { # Standard behavior: drop the first column/row (the singular intercept) From 3a84e3e64cbef2d541c0da3a3bd2ef561c3c4e51 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 12:49:09 -0500 Subject: [PATCH 04/31] Revert gemini suggestion and fix slope identification --- R/check_collinearity.R | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index d4c4f0b9c..e70e42846 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -493,11 +493,29 @@ check_collinearity.zerocount <- function( # Filter to true slope parameters (handles multiple intercepts in ordinal models) if (inherits(x, c("clm", "clmm"))) { - slope_names <- insight::find_parameters(x)$conditional - if (is.null(slope_names)) { - slope_names <- names(x$beta) - } + slope_names <- names(x$beta) keep_idx <- which(colnames(v) %in% slope_names) + + # Rebuild term_assign securely to prevent NA drops for categorical predictors + tryCatch( + { + f_cond <- insight::find_formula(x)$conditional + d <- insight::get_data(x, verbose = FALSE) + mm <- stats::model.matrix(f_cond, data = d) + assign_attr <- attr(mm, "assign") + + if (!is.null(assign_attr)) { + if (assign_attr[1] == 0) { + assign_attr <- assign_attr[-1] # Drop intercept + } + if (length(assign_attr) == length(keep_idx)) { + term_assign <- assign_attr + } + } + }, + error = function(e) NULL + ) + } else if (insight::has_intercept(x)) { # Standard behavior: drop the first column/row (the singular intercept) keep_idx <- seq_len(ncol(v))[-1] From b7f9fb580894545dd944682e649942bd25454482 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 12:52:01 -0500 Subject: [PATCH 05/31] Remove extra whitespace for air --- R/check_collinearity.R | 2 -- 1 file changed, 2 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index e70e42846..44fe4aafc 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -503,7 +503,6 @@ check_collinearity.zerocount <- function( d <- insight::get_data(x, verbose = FALSE) mm <- stats::model.matrix(f_cond, data = d) assign_attr <- attr(mm, "assign") - if (!is.null(assign_attr)) { if (assign_attr[1] == 0) { assign_attr <- assign_attr[-1] # Drop intercept @@ -515,7 +514,6 @@ check_collinearity.zerocount <- function( }, error = function(e) NULL ) - } else if (insight::has_intercept(x)) { # Standard behavior: drop the first column/row (the singular intercept) keep_idx <- seq_len(ncol(v))[-1] From 091be3829ed6c1d9469a4f51f5480100177a45ae Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 13:12:43 -0500 Subject: [PATCH 06/31] Move processing before rank deficiency checks --- R/check_collinearity.R | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index 44fe4aafc..8d84cace6 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -481,34 +481,22 @@ check_collinearity.zerocount <- function( return(NULL) } - # we have rank-deficiency here. remove NA columns from assignment - if (isTRUE(attributes(v)$rank_deficient) && !is.null(attributes(v)$na_columns_index)) { - term_assign <- term_assign[-attributes(v)$na_columns_index] - if (isTRUE(verbose)) { - insight::format_alert( - "Model matrix is rank deficient. VIFs may not be sensible." - ) - } - } - # Filter to true slope parameters (handles multiple intercepts in ordinal models) if (inherits(x, c("clm", "clmm"))) { + # names(x$beta) returns only non-singular (surviving) slopes slope_names <- names(x$beta) keep_idx <- which(colnames(v) %in% slope_names) - # Rebuild term_assign securely to prevent NA drops for categorical predictors + # Rebuild term_assign to handle categorical predictors AND rank-deficiency tryCatch( { - f_cond <- insight::find_formula(x)$conditional - d <- insight::get_data(x, verbose = FALSE) - mm <- stats::model.matrix(f_cond, data = d) + mm <- insight::get_modelmatrix(x) assign_attr <- attr(mm, "assign") if (!is.null(assign_attr)) { - if (assign_attr[1] == 0) { - assign_attr <- assign_attr[-1] # Drop intercept - } - if (length(assign_attr) == length(keep_idx)) { - term_assign <- assign_attr + # Match surviving slopes in v to columns in the full model matrix + match_idx <- which(colnames(mm) %in% colnames(v)[keep_idx]) + if (length(match_idx) > 0) { + term_assign <- assign_attr[match_idx] } } }, @@ -524,14 +512,24 @@ check_collinearity.zerocount <- function( } } - # Safely subset the matrix and the assignment vector + # Safely subset the matrix (term_assign is already synced for ordinal models) if (length(keep_idx) < ncol(v)) { - if (!is.null(term_assign) && length(term_assign) == ncol(v)) { + if (!inherits(x, c("clm", "clmm")) && !is.null(term_assign) && length(term_assign) == ncol(v)) { term_assign <- term_assign[keep_idx] } v <- v[keep_idx, keep_idx, drop = FALSE] } + # we have rank-deficiency here. remove NA columns from assignment + if (isTRUE(attributes(v)$rank_deficient) && !is.null(attributes(v)$na_columns_index)) { + term_assign <- term_assign[-attributes(v)$na_columns_index] + if (isTRUE(verbose)) { + insight::format_alert( + "Model matrix is rank deficient. VIFs may not be sensible." + ) + } + } + f <- insight::find_formula(x, verbose = FALSE) # hurdle or zeroinfl model can have no zero-inflation formula, in which case From 035e33d83fae9ecc8eb2ee6b9a2cbbed4befdf81 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 13:16:46 -0500 Subject: [PATCH 07/31] Break up condition for linter --- R/check_collinearity.R | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index 8d84cace6..249faee10 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -514,7 +514,11 @@ check_collinearity.zerocount <- function( # Safely subset the matrix (term_assign is already synced for ordinal models) if (length(keep_idx) < ncol(v)) { - if (!inherits(x, c("clm", "clmm")) && !is.null(term_assign) && length(term_assign) == ncol(v)) { + if ( + !inherits(x, c("clm", "clmm")) && + !is.null(term_assign) && + length(term_assign) == ncol(v) + ) { term_assign <- term_assign[keep_idx] } v <- v[keep_idx, keep_idx, drop = FALSE] From b3b266d38398465a8e1aa11b2c9fa78714317cf7 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 13:18:19 -0500 Subject: [PATCH 08/31] Remove dangling spaces for linter --- R/check_collinearity.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index 249faee10..6f71f19fe 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -515,8 +515,8 @@ check_collinearity.zerocount <- function( # Safely subset the matrix (term_assign is already synced for ordinal models) if (length(keep_idx) < ncol(v)) { if ( - !inherits(x, c("clm", "clmm")) && - !is.null(term_assign) && + !inherits(x, c("clm", "clmm")) && + !is.null(term_assign) && length(term_assign) == ncol(v) ) { term_assign <- term_assign[keep_idx] From 577e59a9b785512bc32ab2f58cf3697229dcd06b Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 13:36:08 -0500 Subject: [PATCH 09/31] Improve name-matching --- R/check_collinearity.R | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index 6f71f19fe..fd70fb00c 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -487,14 +487,14 @@ check_collinearity.zerocount <- function( slope_names <- names(x$beta) keep_idx <- which(colnames(v) %in% slope_names) - # Rebuild term_assign to handle categorical predictors AND rank-deficiency + # Rebuild term_assign by matching model matrix columns to surviving slopes tryCatch( { mm <- insight::get_modelmatrix(x) assign_attr <- attr(mm, "assign") if (!is.null(assign_attr)) { - # Match surviving slopes in v to columns in the full model matrix - match_idx <- which(colnames(mm) %in% colnames(v)[keep_idx]) + # Use name-matching to isolate indices for estimated slopes + match_idx <- which(colnames(mm) %in% slope_names) if (length(match_idx) > 0) { term_assign <- assign_attr[match_idx] } @@ -514,11 +514,7 @@ check_collinearity.zerocount <- function( # Safely subset the matrix (term_assign is already synced for ordinal models) if (length(keep_idx) < ncol(v)) { - if ( - !inherits(x, c("clm", "clmm")) && - !is.null(term_assign) && - length(term_assign) == ncol(v) - ) { + if (!inherits(x, c("clm", "clmm")) && !is.null(term_assign) && length(term_assign) == ncol(v)) { term_assign <- term_assign[keep_idx] } v <- v[keep_idx, keep_idx, drop = FALSE] From 6f8c986ce15ba5a1d867924ad6890837fe922286 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 13:37:08 -0500 Subject: [PATCH 10/31] Fix line width again --- R/check_collinearity.R | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index fd70fb00c..3f945319f 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -514,7 +514,11 @@ check_collinearity.zerocount <- function( # Safely subset the matrix (term_assign is already synced for ordinal models) if (length(keep_idx) < ncol(v)) { - if (!inherits(x, c("clm", "clmm")) && !is.null(term_assign) && length(term_assign) == ncol(v)) { + if ( + !inherits(x, c("clm", "clmm")) && + !is.null(term_assign) && + length(term_assign) == ncol(v) + ) { term_assign <- term_assign[keep_idx] } v <- v[keep_idx, keep_idx, drop = FALSE] From adcf453ecaefe33b9bcb0cbe3e464b343fe96c53 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 23 Apr 2026 13:38:39 -0500 Subject: [PATCH 11/31] Fix dangling spaces again --- R/check_collinearity.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index 3f945319f..c3a449084 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -515,8 +515,8 @@ check_collinearity.zerocount <- function( # Safely subset the matrix (term_assign is already synced for ordinal models) if (length(keep_idx) < ncol(v)) { if ( - !inherits(x, c("clm", "clmm")) && - !is.null(term_assign) && + !inherits(x, c("clm", "clmm")) && + !is.null(term_assign) && length(term_assign) == ncol(v) ) { term_assign <- term_assign[keep_idx] From d5d8798a3136395bf96b66b5ba055e562262ad52 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 12:59:16 -0500 Subject: [PATCH 12/31] Add news and increment version --- .Rbuildignore | 64 ++++++++++++++++++++++++----------------------- .gitignore | 1 + DESCRIPTION | 2 +- NEWS.md | 7 ++++++ performance.Rproj | 50 ++++++++++++++++++------------------ 5 files changed, 67 insertions(+), 57 deletions(-) diff --git a/.Rbuildignore b/.Rbuildignore index d46eb037d..f6b27d8db 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -1,31 +1,33 @@ -^logo.png -^README.Rmd -^LICENSE - -^\.Rprofile$ -^.*\.Rproj$ -^\.Rproj\.user$ - -^\.travis.yml -^\_pkgdown.yml -^\_pkgdown.yaml -^paper.bib$ -^GEMINI\.md$ - -^data/. -^docs/. -^vignettes/. -^pkgdown/. -^WIP/. -^papers/. -^.github/. -^CODE_OF_CONDUCT\.md$ -^revdep$ -^tests/testthat/_snaps/. -^cran-comments\.md$ -^\.github$ -\.code-workspace$ -\.lintr$ -^CRAN-SUBMISSION$ -^[.]?air[.]toml$ -^\.vscode$ +^logo.png +^README.Rmd +^LICENSE + +^\.Rprofile$ +^.*\.Rproj$ +^\.Rproj\.user$ + +^\.travis.yml +^\_pkgdown.yml +^\_pkgdown.yaml +^paper.bib$ +^GEMINI\.md$ + +^data/. +^docs/. +^vignettes/. +^pkgdown/. +^WIP/. +^papers/. +^.github/. +^CODE_OF_CONDUCT\.md$ +^revdep$ +^tests/testthat/_snaps/. +^cran-comments\.md$ +^\.github$ +\.code-workspace$ +\.lintr$ +^CRAN-SUBMISSION$ +^[.]?air[.]toml$ +^\.vscode$ +^\.positai$ +^\.claude$ diff --git a/.gitignore b/.gitignore index 1e3341f4a..ae0593944 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ Network Trash Folder Temporary Items .apdisk .Rprofile +.positai diff --git a/DESCRIPTION b/DESCRIPTION index 0edd57763..1727096d1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: performance Title: Assessment of Regression Models Performance -Version: 0.16.0.2 +Version: 0.16.0.3 Authors@R: c(person(given = "Daniel", family = "Lüdecke", diff --git a/NEWS.md b/NEWS.md index 8653afe2b..efaec9d14 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,10 @@ +# performance 0.16.0.3 + +## Bug fixes + +* Fixed issue in `check_collinearity()` that was causing inflated VIF values + when applied to clm and clmm models from the ordinal package. + # performance 0.16.0.2 ## Changes diff --git a/performance.Rproj b/performance.Rproj index 2c52ed395..aacfcbeaa 100644 --- a/performance.Rproj +++ b/performance.Rproj @@ -1,25 +1,25 @@ -Version: 1.0 -ProjectId: af6facf3-033e-40d4-ac22-2830774814a9 - -RestoreWorkspace: No -SaveWorkspace: No -AlwaysSaveHistory: No - -EnableCodeIndexing: Yes -UseSpacesForTab: Yes -NumSpacesForTab: 2 -Encoding: UTF-8 - -RnwWeave: knitr -LaTeX: pdfLaTeX - -StripTrailingWhitespace: Yes - -BuildType: Package -PackageUseDevtools: Yes -PackageInstallArgs: --no-multiarch --with-keep.source -PackageCheckArgs: --as-cran --run-donttest -PackageRoxygenize: rd,collate,namespace - -QuitChildProcessesOnExit: Yes -DisableExecuteRprofile: Yes +Version: 1.0 +ProjectId: af6facf3-033e-40d4-ac22-2830774814a9 + +RestoreWorkspace: No +SaveWorkspace: No +AlwaysSaveHistory: No + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: knitr +LaTeX: pdfLaTeX + +StripTrailingWhitespace: Yes + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source +PackageCheckArgs: --as-cran --run-donttest +PackageRoxygenize: rd,collate,namespace + +QuitChildProcessesOnExit: Yes +DisableExecuteRprofile: Yes From 37cdbdce2883a07215f26c3d62f209408155f4bc Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:12:42 -0500 Subject: [PATCH 13/31] Add clmm test --- .vscode/settings.json | 5 ++++ tests/testthat/test-check_collinearity.R | 34 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..9eb545797 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.autoComplete.extraPaths": [ + "c:\\Users\\jeffg\\.positron\\extensions\\continue.continue-0.0.412-win32-x64" + ] +} \ No newline at end of file diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index c6b192643..7d31d0b7c 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -300,3 +300,37 @@ test_that("check_collinearity, validate adjusted vif against car", { expect_equal(out1[, 1], out2$VIF, tolerance = 1e-3, ignore_attr = TRUE) expect_equal(out1[, 3], out2$SE_factor, tolerance = 1e-3, ignore_attr = TRUE) }) + +test_that("check_collinearity, ordinal clmm models", { + skip_if_not_installed("ordinal") + + set.seed(999) + n <- 500 + + x_continuous <- rnorm(n, mean = 0, sd = 1) + x_binary <- sample(c(-0.5, 0.5), size = n, replace = TRUE, prob = c(0.85, 0.15)) + subject_id <- factor(rep(1:50, each = 10)) + random_intercepts <- rnorm(50, 0, 1) + + latent_y <- 2 * x_continuous + + 3 * x_binary + + random_intercepts[as.numeric(subject_id)] + + rlogis(n) + + y_ordinal <- cut( + latent_y, + breaks = 15, + ordered_result = TRUE + ) + + dat <- data.frame(y_ordinal, x_continuous, x_binary, subject_id) + mod_clmm <- ordinal::clmm( + y_ordinal ~ x_continuous + x_binary + (1 | subject_id), + data = dat + ) + out <- check_collinearity(mod_clmm) + + expect_s3_class(out, "check_collinearity") + expect_identical(out$Term, c("x_continuous", "x_binary")) + expect_equal(out$VIF, c(1.12, 1.12), tolerance = 0.05) +}) \ No newline at end of file From 0f2e0a5adb0a7382e01dd41a142fc7079739e54f Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:14:00 -0500 Subject: [PATCH 14/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index 7d31d0b7c..7cb6e2530 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -303,7 +303,6 @@ test_that("check_collinearity, validate adjusted vif against car", { test_that("check_collinearity, ordinal clmm models", { skip_if_not_installed("ordinal") - set.seed(999) n <- 500 From 1df8633b7ff875a7671005e8c0b35770862a7daa Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:14:16 -0500 Subject: [PATCH 15/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index 7cb6e2530..fdaa7fbb3 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -305,7 +305,6 @@ test_that("check_collinearity, ordinal clmm models", { skip_if_not_installed("ordinal") set.seed(999) n <- 500 - x_continuous <- rnorm(n, mean = 0, sd = 1) x_binary <- sample(c(-0.5, 0.5), size = n, replace = TRUE, prob = c(0.85, 0.15)) subject_id <- factor(rep(1:50, each = 10)) From 93cde3780f83f72883837418894e858d1a7fbbbf Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:14:54 -0500 Subject: [PATCH 16/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index fdaa7fbb3..c2044af6b 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -309,10 +309,11 @@ test_that("check_collinearity, ordinal clmm models", { x_binary <- sample(c(-0.5, 0.5), size = n, replace = TRUE, prob = c(0.85, 0.15)) subject_id <- factor(rep(1:50, each = 10)) random_intercepts <- rnorm(50, 0, 1) - - latent_y <- 2 * x_continuous + - 3 * x_binary + - random_intercepts[as.numeric(subject_id)] + + + latent_y <- 2 * + x_continuous + + 3 * x_binary + + random_intercepts[as.numeric(subject_id)] + rlogis(n) y_ordinal <- cut( From f51ef91dac88fe8bf5a932ee61933751a141ca21 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:15:06 -0500 Subject: [PATCH 17/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index c2044af6b..970e94581 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -315,7 +315,6 @@ test_that("check_collinearity, ordinal clmm models", { 3 * x_binary + random_intercepts[as.numeric(subject_id)] + rlogis(n) - y_ordinal <- cut( latent_y, breaks = 15, From ab7d8aff797e8e090884d07035b1d3106123e13e Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:23:21 -0500 Subject: [PATCH 18/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index 970e94581..fb8312ade 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -320,7 +320,6 @@ test_that("check_collinearity, ordinal clmm models", { breaks = 15, ordered_result = TRUE ) - dat <- data.frame(y_ordinal, x_continuous, x_binary, subject_id) mod_clmm <- ordinal::clmm( y_ordinal ~ x_continuous + x_binary + (1 | subject_id), From a46a0b36a798d1ac66ab8f4981dfa162f8087ff5 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:23:35 -0500 Subject: [PATCH 19/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index fb8312ade..903e2fe55 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -322,7 +322,7 @@ test_that("check_collinearity, ordinal clmm models", { ) dat <- data.frame(y_ordinal, x_continuous, x_binary, subject_id) mod_clmm <- ordinal::clmm( - y_ordinal ~ x_continuous + x_binary + (1 | subject_id), + y_ordinal ~ x_continuous + x_binary + (1 | subject_id), data = dat ) out <- check_collinearity(mod_clmm) From c2a6b5c8ca558a68115a436469ff4f2a88b4c893 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:24:06 -0500 Subject: [PATCH 20/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index 903e2fe55..f3d850ecf 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -326,7 +326,6 @@ test_that("check_collinearity, ordinal clmm models", { data = dat ) out <- check_collinearity(mod_clmm) - expect_s3_class(out, "check_collinearity") expect_identical(out$Term, c("x_continuous", "x_binary")) expect_equal(out$VIF, c(1.12, 1.12), tolerance = 0.05) From e52bb0886a8f533d19dffc2981949ee497c77564 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:24:21 -0500 Subject: [PATCH 21/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index f3d850ecf..6311578e2 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -329,4 +329,4 @@ test_that("check_collinearity, ordinal clmm models", { expect_s3_class(out, "check_collinearity") expect_identical(out$Term, c("x_continuous", "x_binary")) expect_equal(out$VIF, c(1.12, 1.12), tolerance = 0.05) -}) \ No newline at end of file +}) From 16cbe71e5d333437f8acda95090d40d2cff5a8a0 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:51:20 -0500 Subject: [PATCH 22/31] Add clm test and fix bug --- R/check_collinearity.R | 7 +++---- tests/testthat/test-check_collinearity.R | 24 +++++++++++++++++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index c3a449084..ef26f2a60 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -512,12 +512,11 @@ check_collinearity.zerocount <- function( } } - # Safely subset the matrix (term_assign is already synced for ordinal models) + # Safely subset the matrix if (length(keep_idx) < ncol(v)) { if ( - !inherits(x, c("clm", "clmm")) && - !is.null(term_assign) && - length(term_assign) == ncol(v) + !is.null(term_assign) && + length(term_assign) == ncol(v) ) { term_assign <- term_assign[keep_idx] } diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index 6311578e2..fb6aaa91c 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -309,7 +309,6 @@ test_that("check_collinearity, ordinal clmm models", { x_binary <- sample(c(-0.5, 0.5), size = n, replace = TRUE, prob = c(0.85, 0.15)) subject_id <- factor(rep(1:50, each = 10)) random_intercepts <- rnorm(50, 0, 1) - latent_y <- 2 * x_continuous + 3 * x_binary + @@ -330,3 +329,26 @@ test_that("check_collinearity, ordinal clmm models", { expect_identical(out$Term, c("x_continuous", "x_binary")) expect_equal(out$VIF, c(1.12, 1.12), tolerance = 0.05) }) + +test_that("check_collinearity, ordinal clm models", { + skip_if_not_installed("ordinal") + set.seed(999) + n <- 500 + x_continuous <- rnorm(n, mean = 0, sd = 1) + x_binary <- sample(c(-0.5, 0.5), size = n, replace = TRUE, prob = c(0.85, 0.15)) + latent_y <- 2 * x_continuous + 3 * x_binary + rlogis(n) + y_ordinal <- cut( + latent_y, + breaks = 15, + ordered_result = TRUE + ) + dat <- data.frame(y_ordinal, x_continuous, x_binary) + mod_clm <- ordinal::clm( + y_ordinal ~ x_continuous + x_binary, + data = dat + ) + out <- check_collinearity(mod_clm) + expect_s3_class(out, "check_collinearity") + expect_identical(out$Term, c("x_continuous", "x_binary")) + expect_equal(out$VIF, c(1.11, 1.11), tolerance = 0.05) +}) \ No newline at end of file From fe9f4249d73a41d23f343b13c7b0fb99b15e2d9a Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sun, 26 Apr 2026 13:52:54 -0500 Subject: [PATCH 23/31] Style for air --- R/check_collinearity.R | 5 +---- tests/testthat/test-check_collinearity.R | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index ef26f2a60..2ae38ca14 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -514,10 +514,7 @@ check_collinearity.zerocount <- function( # Safely subset the matrix if (length(keep_idx) < ncol(v)) { - if ( - !is.null(term_assign) && - length(term_assign) == ncol(v) - ) { + if (!is.null(term_assign) && length(term_assign) == ncol(v)) { term_assign <- term_assign[keep_idx] } v <- v[keep_idx, keep_idx, drop = FALSE] diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index fb6aaa91c..1cbd1624e 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -344,11 +344,11 @@ test_that("check_collinearity, ordinal clm models", { ) dat <- data.frame(y_ordinal, x_continuous, x_binary) mod_clm <- ordinal::clm( - y_ordinal ~ x_continuous + x_binary, + y_ordinal ~ x_continuous + x_binary, data = dat ) out <- check_collinearity(mod_clm) expect_s3_class(out, "check_collinearity") expect_identical(out$Term, c("x_continuous", "x_binary")) expect_equal(out$VIF, c(1.11, 1.11), tolerance = 0.05) -}) \ No newline at end of file +}) From 4639d89b08ff411ff3c7510e0d3ad5e142b54e2e Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Fri, 1 May 2026 15:58:17 -0500 Subject: [PATCH 24/31] Delete .vscode/settings.json --- .vscode/settings.json | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 9eb545797..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "python.autoComplete.extraPaths": [ - "c:\\Users\\jeffg\\.positron\\extensions\\continue.continue-0.0.412-win32-x64" - ] -} \ No newline at end of file From 8e5caa3f5612840e336ecbaa2d3b61e0e101066a Mon Sep 17 00:00:00 2001 From: jmgirard Date: Sat, 2 May 2026 10:53:42 -0500 Subject: [PATCH 25/31] add offset test --- tests/testthat/test-check_collinearity.R | 56 ++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index 1cbd1624e..e38f90756 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -352,3 +352,59 @@ test_that("check_collinearity, ordinal clm models", { expect_identical(out$Term, c("x_continuous", "x_binary")) expect_equal(out$VIF, c(1.11, 1.11), tolerance = 0.05) }) + +test_that("check_collinearity, ordinal clmm models with offset", { + skip_if_not_installed("ordinal") + set.seed(999) + n <- 500 + x_continuous <- rnorm(n, mean = 0, sd = 1) + x_binary <- sample(c(-0.5, 0.5), size = n, replace = TRUE, prob = c(0.85, 0.15)) + x_offset <- rnorm(n, mean = 0, sd = 0.5) + subject_id <- factor(rep(1:50, each = 10)) + random_intercepts <- rnorm(50, 0, 1) + + latent_y <- 2 * x_continuous + 3 * x_binary + random_intercepts[as.numeric(subject_id)] + x_offset + rlogis(n) + y_ordinal <- cut(latent_y, breaks = 15, ordered_result = TRUE) + + dat <- data.frame(y_ordinal, x_continuous, x_binary, x_offset, subject_id) + + mod_clmm_offset <- ordinal::clmm( + y_ordinal ~ x_continuous + x_binary + offset(x_offset) + (1 | subject_id), + data = dat + ) + + out <- check_collinearity(mod_clmm_offset) + expect_s3_class(out, "check_collinearity") + expect_identical(out$Term, c("x_continuous", "x_binary")) + expect_equal(out$VIF, c(1.12, 1.12), tolerance = 0.05) +}) + +test_that("check_collinearity, ordinal clm models with offset", { + skip_if_not_installed("ordinal") + set.seed(999) + n <- 500 + x_continuous <- rnorm(n, mean = 0, sd = 1) + x_binary <- sample(c(-0.5, 0.5), size = n, replace = TRUE, prob = c(0.85, 0.15)) + x_offset <- rnorm(n, mean = 0, sd = 0.5) + latent_y <- 2 * x_continuous + 3 * x_binary + x_offset + rlogis(n) + y_ordinal <- cut(latent_y, breaks = 15, ordered_result = TRUE) + dat <- data.frame(y_ordinal, x_continuous, x_binary, x_offset) + mod_clm_offset <- ordinal::clm( + y_ordinal ~ x_continuous + x_binary + offset(x_offset), + data = dat + ) + out <- check_collinearity(mod_clm_offset) + expect_s3_class(out, "check_collinearity") + expect_identical(out$Term, c("x_continuous", "x_binary")) + expect_equal(out$VIF, c(1.11, 1.11), tolerance = 0.05) +}) + +test_that("check_collinearity, standard lm models with offset", { + # Standard linear model with an offset + m_lm_offset <- lm(mpg ~ wt + cyl + offset(disp), data = mtcars) + out <- check_collinearity(m_lm_offset) + expect_s3_class(out, "check_collinearity") + # The offset should not be evaluated for collinearity + expect_identical(out$Term, c("wt", "cyl")) + expect_false("disp" %in% out$Term) +}) From 4a2acab8183254fc72e220243990f4ab1e38a59d Mon Sep 17 00:00:00 2001 From: jmgirard Date: Sat, 2 May 2026 10:54:32 -0500 Subject: [PATCH 26/31] remove sanity check comment --- R/check_collinearity.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/check_collinearity.R b/R/check_collinearity.R index 2ae38ca14..fdb684c2d 100644 --- a/R/check_collinearity.R +++ b/R/check_collinearity.R @@ -572,7 +572,6 @@ check_collinearity.zerocount <- function( result <- vector("numeric") na_terms <- vector("numeric") - # sanity check - models with offset(?) may contain too many term assignments if (length(term_assign) > ncol(v)) { term_assign <- term_assign[seq_len(ncol(v))] } From 9f622e94f0fd7f424b729c2a8033db45cbf84dd3 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sat, 2 May 2026 10:55:55 -0500 Subject: [PATCH 27/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index e38f90756..cc9d67425 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -362,8 +362,13 @@ test_that("check_collinearity, ordinal clmm models with offset", { x_offset <- rnorm(n, mean = 0, sd = 0.5) subject_id <- factor(rep(1:50, each = 10)) random_intercepts <- rnorm(50, 0, 1) - - latent_y <- 2 * x_continuous + 3 * x_binary + random_intercepts[as.numeric(subject_id)] + x_offset + rlogis(n) + + latent_y <- 2 * + x_continuous + + 3 * x_binary + + random_intercepts[as.numeric(subject_id)] + + x_offset + + rlogis(n) y_ordinal <- cut(latent_y, breaks = 15, ordered_result = TRUE) dat <- data.frame(y_ordinal, x_continuous, x_binary, x_offset, subject_id) From d84bf41c2b2f3c06947a25fdf65c98dd03471f9c Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sat, 2 May 2026 10:56:45 -0500 Subject: [PATCH 28/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index cc9d67425..103b39c6c 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -370,7 +370,6 @@ test_that("check_collinearity, ordinal clmm models with offset", { x_offset + rlogis(n) y_ordinal <- cut(latent_y, breaks = 15, ordered_result = TRUE) - dat <- data.frame(y_ordinal, x_continuous, x_binary, x_offset, subject_id) mod_clmm_offset <- ordinal::clmm( From 7311745378e701e2c8852923df4e1408fe76ecd6 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sat, 2 May 2026 10:57:33 -0500 Subject: [PATCH 29/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index 103b39c6c..57ec9c1f1 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -371,7 +371,6 @@ test_that("check_collinearity, ordinal clmm models with offset", { rlogis(n) y_ordinal <- cut(latent_y, breaks = 15, ordered_result = TRUE) dat <- data.frame(y_ordinal, x_continuous, x_binary, x_offset, subject_id) - mod_clmm_offset <- ordinal::clmm( y_ordinal ~ x_continuous + x_binary + offset(x_offset) + (1 | subject_id), data = dat From 4a1f33e094c67309aa1776b8c66985b7a081eceb Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sat, 2 May 2026 10:58:23 -0500 Subject: [PATCH 30/31] Style for air Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- tests/testthat/test-check_collinearity.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-check_collinearity.R b/tests/testthat/test-check_collinearity.R index 57ec9c1f1..fadd683a6 100644 --- a/tests/testthat/test-check_collinearity.R +++ b/tests/testthat/test-check_collinearity.R @@ -375,7 +375,6 @@ test_that("check_collinearity, ordinal clmm models with offset", { y_ordinal ~ x_continuous + x_binary + offset(x_offset) + (1 | subject_id), data = dat ) - out <- check_collinearity(mod_clmm_offset) expect_s3_class(out, "check_collinearity") expect_identical(out$Term, c("x_continuous", "x_binary")) From 56d95564b0873889b8098e27373f702b7f19edf0 Mon Sep 17 00:00:00 2001 From: jmgirard Date: Sat, 2 May 2026 11:02:52 -0500 Subject: [PATCH 31/31] ignore .vscode --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ae0593944..270c13cf8 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,5 @@ Network Trash Folder Temporary Items .apdisk .Rprofile -.positai +.positai +.vscode