Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 17 additions & 2 deletions R/mashPipeline.R
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,17 @@ mashResidualCorrelation <- function(
.mashResidCorNullBased(sumStatsList, inputScale, method, corArgs)
}

# The component contract is a named LIST of covariance matrices. c() collapses
# two NULLs to NULL, which is exactly what a requested-but-empty component set
# produces (flashier finding no factors on weak data, say), so normalise that
# one case rather than handing a NULL to callers who were promised a list --
# length()/names()/c() all keep working on an empty list, and "this component
# ran and yielded nothing" stays distinguishable from a structural failure.
# @noRd
.mashComponentList <- function(x) {
if (is.null(x)) list() else x
}

#' @title Build mash Data-Driven Covariance Components
#' @description Build the raw per-method data-driven covariance components
#' (\code{cov_canonical} / \code{cov_pca} / \code{cov_flash} /
Expand Down Expand Up @@ -638,7 +649,7 @@ mashCovarianceComponents <- function(
components = components,
nPcs = nPcs
)
c(built$canonical, built$dataDriven)
.mashComponentList(c(built$canonical, built$dataDriven))
}

# `components` and `engine` each take a name or that choice's own
Expand Down Expand Up @@ -1057,7 +1068,11 @@ MashComponentParam <- function(
)
}
list(
U = c(refined$U, comps$canonical),
# Same list contract as mashCovarianceComponents(). Defensive here
# rather than load-bearing: the mashr fit above aborts on an empty
# Ulist, so this return is not reached with zero components -- but the
# two component-returning entry points should not differ in shape.
U = .mashComponentList(c(refined$U, comps$canonical)),
w = refined$w,
loglik = refined$loglik
)
Expand Down
23 changes: 23 additions & 0 deletions tests/testthat/test_mashPipeline.R
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,29 @@ test_that("mashCovarianceComponents builds a single requested component", {
)))
})

test_that("a requested component that yields nothing returns an empty list", {
skip_if_not_installed("mashr")
ss <- mashFixture()
# flashier legitimately finds no factors on weak data. c(NULL, NULL) is
# NULL, so the collapse used to leak a NULL out of a function documented
# to return a named list -- length()/names()/c() then behave differently
# for callers, and "ran, found nothing" became indistinguishable from
# "nothing was built at all".
local_mocked_bindings(cov_flash = function(...) NULL, .package = "mashr")
out <- suppressMessages(suppressWarnings(
mashCovarianceComponents(
list(strong = ss),
alpha = 0,
vhat = diag(3),
components = "flash",
setSeed = 1L
)
))
expect_type(out, "list")
expect_length(out, 0L)
expect_false(is.null(out))
})

test_that("mashCovarianceComponents default builds all non-udr components", {
skip_if_not_installed("mashr")
skip_if_not_installed("flashier")
Expand Down
Loading