12 Tests for competitor methods
Tests for processClusterLassoInputs():
testthat::test_that("processClusterLassoInputs works", {
set.seed(82612)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
ret <- processClusterLassoInputs(X=x, y=y, clusters=good_clusters, nlambda=10)
testthat::expect_true(is.list(ret))
testthat::expect_identical(names(ret), c("x", "clusters", "prototypes",
"var_names"))
# X
testthat::expect_true(is.matrix(ret$x))
testthat::expect_true(all(!is.na(ret$x)))
testthat::expect_true(is.numeric(ret$x))
testthat::expect_equal(ncol(ret$x), 11)
testthat::expect_equal(nrow(ret$x), 15)
testthat::expect_true(all(abs(ret$x - x) < 10^(-9)))
# clusters
testthat::expect_true(is.list(ret$clusters))
testthat::expect_equal(length(ret$clusters), 5)
testthat::expect_equal(5, length(names(ret$clusters)))
testthat::expect_equal(5, length(unique(names(ret$clusters))))
testthat::expect_true("red_cluster" %in% names(ret$clusters))
testthat::expect_true("green_cluster" %in% names(ret$clusters))
testthat::expect_true(all(!is.na(names(ret$clusters))))
testthat::expect_true(all(!is.null(names(ret$clusters))))
testthat::expect_true(all(names(ret$clusters) != ""))
clust_feats <- integer()
true_list <- list(1:4, 5:8, 9, 10, 11)
for(i in 1:length(ret$clusters)){
testthat::expect_true(is.integer(ret$clusters[[i]]))
testthat::expect_equal(length(intersect(clust_feats, ret$clusters[[i]])), 0)
testthat::expect_true(all(ret$clusters[[i]] %in% 1:11))
testthat::expect_equal(length(ret$clusters[[i]]),
length(unique(ret$clusters[[i]])))
testthat::expect_true(all(ret$clusters[[i]] == true_list[[i]]))
clust_feats <- c(clust_feats, ret$clusters[[i]])
}
testthat::expect_equal(length(clust_feats), 11)
testthat::expect_equal(11, length(unique(clust_feats)))
testthat::expect_equal(11, length(intersect(clust_feats, 1:11)))
# prototypes
testthat::expect_true(is.integer(ret$prototypes))
testthat::expect_true(all(ret$prototypes %in% 1:11))
testthat::expect_equal(length(ret$prototypes), 5)
testthat::expect_true(ret$prototypes[1] %in% 1:4)
testthat::expect_true(ret$prototypes[2] %in% 5:8)
testthat::expect_equal(ret$prototypes[3], 9)
testthat::expect_equal(ret$prototypes[4], 10)
testthat::expect_equal(ret$prototypes[5], 11)
# var_names
testthat::expect_equal(length(ret$var_names), 1)
testthat::expect_true(is.na(ret$var_names))
# X as a data.frame
X_df <- datasets::mtcars
res <- processClusterLassoInputs(X=X_df, y=stats::rnorm(nrow(X_df)),
clusters=1:3, nlambda=10)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("x", "clusters", "prototypes",
"var_names"))
X_df_model <- stats::model.matrix(~ ., X_df)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
# X
testthat::expect_true(is.matrix(res$x))
testthat::expect_true(all(!is.na(res$x)))
testthat::expect_true(is.numeric(res$x))
testthat::expect_equal(ncol(res$x), ncol(X_df_model))
testthat::expect_equal(nrow(res$x), nrow(X_df))
testthat::expect_true(all(abs(res$x - X_df_model) < 10^(-9)))
# var_names
testthat::expect_equal(length(res$var_names), ncol(X_df_model))
testthat::expect_true(is.character(res$var_names))
testthat::expect_identical(res$var_names, colnames(X_df_model))
# X as a dataframe with factors (number of columns of final design matrix
# after one-hot encoding factors won't match number of columns of df2)
# cyl, gear, and carb are factors with more than 2 levels
df2 <- X_df
df2$cyl <- as.factor(df2$cyl)
df2$vs <- as.factor(df2$vs)
df2$am <- as.factor(df2$am)
df2$gear <- as.factor(df2$gear)
df2$carb <- as.factor(df2$carb)
# Should get error if I try to use clusters because df2 contains factors with
# more than two levels
testthat::expect_error(processClusterLassoInputs(X=df2, y=stats::rnorm(nrow(df2)),
clusters=1:3, nlambda=10), "When stats::model.matrix converted the provided data.frame X to a matrix, the number of columns changed (probably because the provided data.frame contained a factor variable with at least three levels). Please convert X to a matrix yourself using model.matrix and provide cluster assignments according to the columns of the new matrix.",
fixed=TRUE)
# Should be fine with no clusters
res <- processClusterLassoInputs(X=df2, y=stats::rnorm(nrow(df2)),
clusters=list(), nlambda=10)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("x", "clusters", "prototypes",
"var_names"))
X_df_model <- stats::model.matrix(~ ., df2)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
# X
testthat::expect_true(is.matrix(res$x))
testthat::expect_true(all(!is.na(res$x)))
testthat::expect_true(is.numeric(res$x))
testthat::expect_equal(ncol(res$x), ncol(X_df_model))
testthat::expect_equal(nrow(res$x), nrow(X_df))
testthat::expect_true(all(abs(res$x - X_df_model) < 10^(-9)))
# var_names
testthat::expect_equal(length(res$var_names), ncol(X_df_model))
testthat::expect_true(is.character(res$var_names))
testthat::expect_identical(res$var_names, colnames(X_df_model))
# X as a matrix with column names
x2 <- x
colnames(x2) <- LETTERS[1:11]
ret <- processClusterLassoInputs(X=x2, y=y, clusters=good_clusters, nlambda=10)
testthat::expect_true(is.list(ret))
testthat::expect_identical(names(ret), c("x", "clusters", "prototypes",
"var_names"))
# X
testthat::expect_true(is.matrix(ret$x))
testthat::expect_true(all(!is.na(ret$x)))
testthat::expect_true(is.numeric(ret$x))
testthat::expect_equal(ncol(ret$x), 11)
testthat::expect_equal(nrow(ret$x), 15)
testthat::expect_true(all(abs(ret$x - x) < 10^(-9)))
# var_names
testthat::expect_equal(length(ret$var_names), ncol(x2))
testthat::expect_true(is.character(ret$var_names))
testthat::expect_identical(ret$var_names, LETTERS[1:11])
# Bad inputs
testthat::expect_error(processClusterLassoInputs(X="x", y=y[1:10],
clusters=good_clusters,
nlambda=10),
"is.matrix(X) | is.data.frame(X) is not TRUE",
fixed=TRUE)
testthat::expect_error(processClusterLassoInputs(X=x, y=y[1:10],
clusters=good_clusters,
nlambda=10),
"n == length(y) is not TRUE",
fixed=TRUE)
testthat::expect_error(processClusterLassoInputs(X=x, y=y,
clusters=list(1:4, 4:6),
nlambda=10),
"Overlapping clusters detected; clusters must be non-overlapping. Overlapping clusters: 1, 2.",
fixed=TRUE)
# Duplicate clusters are now silently removed (#156): identical to passing the
# cluster once (processClusterLassoInputs is deterministic here).
testthat::expect_identical(processClusterLassoInputs(X=x, y=y,
clusters=list(2:3, 2:3),
nlambda=10),
processClusterLassoInputs(X=x, y=y,
clusters=list(2:3),
nlambda=10))
testthat::expect_error(processClusterLassoInputs(X=x, y=y,
clusters=list(1:4,
as.integer(NA)),
nlambda=10),
"!is.na(clusters) are not all TRUE",
fixed=TRUE)
testthat::expect_error(processClusterLassoInputs(X=x, y=y,
clusters=list(2:3,
c(4, 4, 5)),
nlambda=10),
"length(clusters[[i]]) == length(unique(clusters[[i]])) is not TRUE",
fixed=TRUE)
testthat::expect_error(processClusterLassoInputs(X=x, y=y,
clusters=good_clusters,
nlambda=1),
"nlambda >= 2 is not TRUE", fixed=TRUE)
testthat::expect_error(processClusterLassoInputs(X=x, y=y,
clusters=good_clusters,
nlambda=x),
"length(nlambda) == 1 is not TRUE", fixed=TRUE)
testthat::expect_error(processClusterLassoInputs(X=x, y=y,
clusters=good_clusters,
nlambda="nlambda"),
"is.numeric(nlambda) | is.integer(nlambda) is not TRUE",
fixed=TRUE)
testthat::expect_error(processClusterLassoInputs(X=x, y=y,
clusters=good_clusters,
nlambda=10.5),
"nlambda == round(nlambda) is not TRUE",
fixed=TRUE)
})## Test passed with 99 successes.
Tests for checkGetXglmnetInputs():
testthat::test_that("checkGetXglmnetInputs works", {
set.seed(82612)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
process <- processClusterLassoInputs(X=x, y=y, clusters=good_clusters,
nlambda=10)
checkGetXglmnetInputs(x=process$x, clusters=process$clusters,
type="protolasso", prototypes=process$prototypes)
checkGetXglmnetInputs(x=process$x, clusters=process$clusters,
type="clusterRepLasso",
prototypes=process$prototypes)
# X as a data.frame
X_df <- datasets::mtcars
res <- processClusterLassoInputs(X=X_df, y=stats::rnorm(nrow(X_df)),
clusters=1:3, nlambda=10)
checkGetXglmnetInputs(x=res$x, clusters=res$clusters, type="clusterRepLasso",
prototypes=res$prototypes)
# X as a dataframe with factors (number of columns of final design matrix
# after one-hot encoding factors won't match number of columns of df2)
# cyl, gear, and carb are factors with more than 2 levels
df2 <- X_df
df2$cyl <- as.factor(df2$cyl)
df2$vs <- as.factor(df2$vs)
df2$am <- as.factor(df2$am)
df2$gear <- as.factor(df2$gear)
df2$carb <- as.factor(df2$carb)
# Should get an error if clusters are provided since df2 contains factors
# with more than two levels
testthat::expect_error(processClusterLassoInputs(X=df2, y=stats::rnorm(nrow(df2)),
clusters=1:3, nlambda=10),
"When stats::model.matrix converted the provided data.frame X to a matrix, the number of columns changed (probably because the provided data.frame contained a factor variable with at least three levels). Please convert X to a matrix yourself using model.matrix and provide cluster assignments according to the columns of the new matrix.", fixed=TRUE)
# Should be fine if no clusters are provided
res <- processClusterLassoInputs(X=df2, y=stats::rnorm(nrow(df2)),
clusters=list(), nlambda=10)
checkGetXglmnetInputs(x=res$x, clusters=res$clusters, type="protolasso",
prototypes=res$prototypes)
# X as a matrix with column names
x2 <- x
colnames(x2) <- LETTERS[1:11]
ret <- processClusterLassoInputs(X=x2, y=y, clusters=good_clusters, nlambda=10)
checkGetXglmnetInputs(x=ret$x, clusters=ret$clusters, type="clusterRepLasso",
prototypes=ret$prototypes)
# Bad prototype inputs
# Error has quotation marks
testthat::expect_error(checkGetXglmnetInputs(x=process$x,
clusters=process$clusters,
type="clsterRepLasso",
prototypes=process$prototypes))
testthat::expect_error(checkGetXglmnetInputs(x=process$x,
clusters=process$clusters,
type=c("clusterRepLasso",
"protolasso"),
prototypes=process$prototypes),
"length(type) == 1 is not TRUE",
fixed=TRUE)
testthat::expect_error(checkGetXglmnetInputs(x=process$x,
clusters=process$clusters,
type=2,
prototypes=process$prototypes),
"is.character(type) is not TRUE",
fixed=TRUE)
testthat::expect_error(checkGetXglmnetInputs(x=process$x,
clusters=process$clusters,
type=as.character(NA),
prototypes=process$prototypes),
"!is.na(type) is not TRUE",
fixed=TRUE)
})## Test passed with 5 successes.
Tests for getXglmnet():
testthat::test_that("getXglmnet works", {
set.seed(82612)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
process <- processClusterLassoInputs(X=x, y=y, clusters=good_clusters,
nlambda=10)
res <- getXglmnet(x=process$x, clusters=process$clusters,
type="protolasso", prototypes=process$prototypes)
testthat::expect_true(is.matrix(res))
testthat::expect_true(is.numeric(res))
testthat::expect_identical(colnames(res), names(process$clusters))
testthat::expect_true(nrow(res) == 15)
# Each column of res should be one of the prototypes. Features 9 - 11 are
# in clusters by themselves and are therefore their own prototypes.
testthat::expect_true(ncol(res) == 5)
for(i in 1:length(good_clusters)){
proto_i_found <- FALSE
cluster_i <- good_clusters[[i]]
for(j in 1:length(cluster_i)){
proto_i_found <- proto_i_found | all(abs(res[, i] - x[, cluster_i[j]]) <
10^(-9))
}
testthat::expect_true(proto_i_found)
}
testthat::expect_true(all(abs(res[, 3] - x[, 9]) < 10^(-9)))
testthat::expect_true(all(abs(res[, 4] - x[, 10]) < 10^(-9)))
testthat::expect_true(all(abs(res[, 5] - x[, 11]) < 10^(-9)))
res <- getXglmnet(x=process$x, clusters=process$clusters,
type="clusterRepLasso", prototypes=process$prototypes)
testthat::expect_true(is.matrix(res))
testthat::expect_true(is.numeric(res))
testthat::expect_identical(colnames(res), names(process$clusters))
testthat::expect_true(nrow(res) == 15)
# Each column of res should be one of the cluster representatives. Features 9
# - 11 are in clusters by themselves and are therefore their own cluster
# representatives.
testthat::expect_true(ncol(res) == 5)
for(i in 1:length(good_clusters)){
cluster_i <- good_clusters[[i]]
clus_rep_i <- rowMeans(x[, cluster_i])
testthat::expect_true(all(abs(res[, i] - clus_rep_i) <
10^(-9)))
}
testthat::expect_true(all(abs(res[, 3] - x[, 9]) < 10^(-9)))
testthat::expect_true(all(abs(res[, 4] - x[, 10]) < 10^(-9)))
testthat::expect_true(all(abs(res[, 5] - x[, 11]) < 10^(-9)))
# X as a data.frame
X_df <- datasets::mtcars
res <- processClusterLassoInputs(X=X_df, y=stats::rnorm(nrow(X_df)),
clusters=1:3, nlambda=10)
ret_df <- getXglmnet(x=res$x, clusters=res$clusters, type="protolasso",
prototypes=res$prototypes)
X_df_model <- stats::model.matrix(~ ., X_df)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
testthat::expect_true(is.matrix(ret_df))
testthat::expect_true(is.numeric(ret_df))
testthat::expect_identical(colnames(ret_df), names(res$clusters))
testthat::expect_true(nrow(ret_df) == nrow(X_df))
# Each column of ret_df should be one of the prototypes.
testthat::expect_true(ncol(ret_df) == ncol(X_df_model) - 3 + 1)
proto_found <- FALSE
for(j in 1:3){
proto_found <- proto_found | all(abs(ret_df[, 1] - X_df_model[, j]) < 10^(-9))
}
testthat::expect_true(proto_found)
for(j in 4:ncol(X_df_model)){
testthat::expect_true(all(abs(ret_df[, j - 2] - X_df_model[, j]) < 10^(-9)))
}
ret_df <- getXglmnet(x=res$x, clusters=res$clusters, type="clusterRepLasso",
prototypes=res$prototypes)
testthat::expect_true(is.matrix(ret_df))
testthat::expect_true(is.numeric(ret_df))
testthat::expect_identical(colnames(ret_df), names(res$clusters))
testthat::expect_true(nrow(ret_df) == nrow(X_df))
# Each column of ret_df should be one of the prototypes.
testthat::expect_true(ncol(ret_df) == ncol(X_df_model) - 3 + 1)
proto_found <- FALSE
clus_rep <- rowMeans(X_df_model[, 1:3])
testthat::expect_true(all(abs(ret_df[, 1] - clus_rep) < 10^(-9)))
for(j in 4:ncol(X_df_model)){
testthat::expect_true(all(abs(ret_df[, j - 2] - X_df_model[, j]) < 10^(-9)))
}
# X as a dataframe with factors (number of columns of final design matrix
# after one-hot encoding factors won't match number of columns of df2)
# cyl, gear, and carb are factors with more than 2 levels
df2 <- X_df
df2$cyl <- as.factor(df2$cyl)
df2$vs <- as.factor(df2$vs)
df2$am <- as.factor(df2$am)
df2$gear <- as.factor(df2$gear)
df2$carb <- as.factor(df2$carb)
res <- processClusterLassoInputs(X=df2, y=stats::rnorm(nrow(df2)),
clusters=list(), nlambda=10)
ret_df <- getXglmnet(x=res$x, clusters=res$clusters, type="protolasso",
prototypes=res$prototypes)
X_df_model <- stats::model.matrix(~ ., df2)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
testthat::expect_true(is.matrix(ret_df))
testthat::expect_true(is.numeric(ret_df))
testthat::expect_identical(colnames(ret_df), names(res$clusters))
testthat::expect_true(nrow(ret_df) == nrow(X_df))
# Each column of ret_df should be one of the prototypes.
testthat::expect_true(ncol(ret_df) == ncol(X_df_model))
for(j in 1:ncol(X_df_model)){
testthat::expect_true(all(abs(ret_df[, j] - X_df_model[, j]) < 10^(-9)))
}
ret_df <- getXglmnet(x=res$x, clusters=res$clusters, type="clusterRepLasso",
prototypes=res$prototypes)
testthat::expect_true(is.matrix(ret_df))
testthat::expect_true(is.numeric(ret_df))
testthat::expect_identical(colnames(ret_df), names(res$clusters))
testthat::expect_true(nrow(ret_df) == nrow(X_df))
# Each column of ret_df should be one of the prototypes.
testthat::expect_true(ncol(ret_df) == ncol(X_df_model))
for(j in 1:ncol(X_df_model)){
testthat::expect_true(all(abs(ret_df[, j] - X_df_model[, j]) < 10^(-9)))
}
# X as a matrix with column names (returned X shouldn't have column names)
x2 <- x
colnames(x2) <- LETTERS[1:11]
process <- processClusterLassoInputs(X=x2, y=y, clusters=good_clusters,
nlambda=10)
res <- getXglmnet(x=process$x, clusters=process$clusters,
type="protolasso", prototypes=process$prototypes)
testthat::expect_true(is.matrix(res))
testthat::expect_true(is.numeric(res))
testthat::expect_identical(colnames(res), names(process$clusters))
testthat::expect_true(nrow(res) == 15)
# Each column of res should be one of the prototypes. Features 9 - 11 are
# in clusters by themselves and are therefore their own prototypes.
testthat::expect_true(ncol(res) == 5)
for(i in 1:length(good_clusters)){
proto_i_found <- FALSE
cluster_i <- good_clusters[[i]]
for(j in 1:length(cluster_i)){
proto_i_found <- proto_i_found | all(abs(res[, i] - x[, cluster_i[j]]) <
10^(-9))
}
testthat::expect_true(proto_i_found)
}
testthat::expect_true(all(abs(res[, 3] - x[, 9]) < 10^(-9)))
testthat::expect_true(all(abs(res[, 4] - x[, 10]) < 10^(-9)))
testthat::expect_true(all(abs(res[, 5] - x[, 11]) < 10^(-9)))
res <- getXglmnet(x=process$x, clusters=process$clusters,
type="clusterRepLasso", prototypes=process$prototypes)
testthat::expect_true(is.matrix(res))
testthat::expect_true(is.numeric(res))
testthat::expect_identical(colnames(res), names(process$clusters))
testthat::expect_true(nrow(res) == 15)
# Each column of res should be one of the cluster representatives. Features 9
# - 11 are in clusters by themselves and are therefore their own cluster
# representatives.
testthat::expect_true(ncol(res) == 5)
for(i in 1:length(good_clusters)){
cluster_i <- good_clusters[[i]]
clus_rep_i <- rowMeans(x[, cluster_i])
testthat::expect_true(all(abs(res[, i] - clus_rep_i) <
10^(-9)))
}
testthat::expect_true(all(abs(res[, 3] - x[, 9]) < 10^(-9)))
testthat::expect_true(all(abs(res[, 4] - x[, 10]) < 10^(-9)))
testthat::expect_true(all(abs(res[, 5] - x[, 11]) < 10^(-9)))
# Bad prototype inputs
# Error has quotation marks
testthat::expect_error(getXglmnet(x=process$x, clusters=process$clusters,
type="clsterRepLasso",
prototypes=process$prototypes))
testthat::expect_error(getXglmnet(x=process$x, clusters=process$clusters,
type=c("clusterRepLasso", "protolasso"),
prototypes=process$prototypes),
"length(type) == 1 is not TRUE",
fixed=TRUE)
testthat::expect_error(getXglmnet(x=process$x, clusters=process$clusters,
type=2, prototypes=process$prototypes),
"is.character(type) is not TRUE",
fixed=TRUE)
testthat::expect_error(getXglmnet(x=process$x, clusters=process$clusters,
type=as.character(NA),
prototypes=process$prototypes),
"!is.na(type) is not TRUE",
fixed=TRUE)
# do.call(cbind) preserves integer storage of an integer x (#58)
x_int <- matrix(1:12, nrow = 4, ncol = 3)
int_clusters <- list(c1 = 1L, c2 = 2L, c3 = 3L)
res_int <- getXglmnet(x_int, int_clusters, type = "protolasso",
prototypes = c(1L, 2L, 3L))
testthat::expect_true(is.integer(res_int))
})## Test passed with 117 successes.
Tests for getSelectedSets():
testthat::test_that("getSelectedSets works", {
set.seed(82612)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
process <- processClusterLassoInputs(X=x, y=y, clusters=good_clusters,
nlambda=100)
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="protolasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=y, family="gaussian", nlambda=100)
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
# Pick an arbitrary lasso set
lasso_set <- lasso_sets[[5]]
res <- getSelectedSets(lasso_set, process$clusters, process$prototypes,
process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_set",
"selected_clusts_list"))
# selected_set
testthat::expect_true(is.integer(res$selected_set))
testthat::expect_true(all(!is.na(res$selected_set)))
testthat::expect_true(all(res$selected_set %in% process$prototypes))
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
testthat::expect_equal(length(res$selected_set),
length(res$selected_clusts_list))
sel_feats <- unlist(res$selected_clusts_list)
testthat::expect_true(all(sel_feats %in% 1:11))
n_clusts <- length(res$selected_clusts_list)
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i, process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
# Try again with cluster representative lasso
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="clusterRepLasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=y, family="gaussian", nlambda=100)
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
# Pick an arbitrary lasso set
lasso_set <- lasso_sets[[5]]
res <- getSelectedSets(lasso_set, process$clusters, process$prototypes,
process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_set",
"selected_clusts_list"))
# selected_set
testthat::expect_true(is.integer(res$selected_set))
testthat::expect_true(all(!is.na(res$selected_set)))
testthat::expect_true(all(res$selected_set %in% process$prototypes))
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
testthat::expect_equal(length(res$selected_set),
length(res$selected_clusts_list))
sel_feats <- unlist(res$selected_clusts_list)
testthat::expect_true(all(sel_feats %in% 1:11))
n_clusts <- length(res$selected_clusts_list)
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i, process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
# X as a data.frame
X_df <- datasets::mtcars
X_df_model <- stats::model.matrix(~ ., X_df)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
process <- processClusterLassoInputs(X=X_df, y=rnorm(nrow(X_df)),
clusters=1:3, nlambda=100)
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="protolasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=rnorm(nrow(X_df)), family="gaussian",
nlambda=100)
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
# Pick an arbitrary lasso set
lasso_set <- lasso_sets[[min(length(lasso_sets), 3)]]
res <- getSelectedSets(lasso_set, process$clusters, process$prototypes,
process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_set",
"selected_clusts_list"))
# selected_set
testthat::expect_true(is.integer(res$selected_set))
testthat::expect_true(all(!is.na(res$selected_set)))
testthat::expect_true(all(res$selected_set %in% process$prototypes))
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
testthat::expect_equal(length(res$selected_set),
length(res$selected_clusts_list))
sel_feats <- unlist(res$selected_clusts_list)
testthat::expect_true(all(sel_feats %in% 1:ncol(X_df_model)))
n_clusts <- length(res$selected_clusts_list)
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i, process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
# X as a dataframe with factors (number of columns of final design matrix
# after one-hot encoding factors won't match number of columns of df2)
# cyl, gear, and carb are factors with more than 2 levels
df2 <- X_df
df2$cyl <- as.factor(df2$cyl)
df2$vs <- as.factor(df2$vs)
df2$am <- as.factor(df2$am)
df2$gear <- as.factor(df2$gear)
df2$carb <- as.factor(df2$carb)
X_df_model <- stats::model.matrix(~ ., df2)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
# Should throw an error if we assign clusters because df2 contains factors
# with more than two levels
testthat::expect_error(processClusterLassoInputs(X=df2, y=rnorm(nrow(df2)),
clusters=1:3, nlambda=100),
"When stats::model.matrix converted the provided data.frame X to a matrix, the number of columns changed (probably because the provided data.frame contained a factor variable with at least three levels). Please convert X to a matrix yourself using model.matrix and provide cluster assignments according to the columns of the new matrix.", fixed=TRUE)
# Should be fine if no clusters are provided
process <- processClusterLassoInputs(X=df2, y=rnorm(nrow(df2)),
clusters=list(), nlambda=100)
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="clusterRepLasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=rnorm(nrow(df2)), family="gaussian",
nlambda=100)
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
# Pick an arbitrary lasso set
lasso_set <- lasso_sets[[min(length(lasso_sets), 3)]]
res <- getSelectedSets(lasso_set, process$clusters, process$prototypes,
process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_set",
"selected_clusts_list"))
# selected_set
testthat::expect_true(is.integer(res$selected_set))
testthat::expect_true(all(!is.na(res$selected_set)))
testthat::expect_true(all(res$selected_set %in% process$prototypes))
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
testthat::expect_equal(length(res$selected_set),
length(res$selected_clusts_list))
sel_feats <- unlist(res$selected_clusts_list)
testthat::expect_true(all(sel_feats %in% 1:ncol(X_df_model)))
n_clusts <- length(res$selected_clusts_list)
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i, process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
# X as a dataframe with factors (number of columns of final design matrix
# after one-hot encoding factors won't match number of columns of df2)
# cyl, gear, and carb are factors with more than 2 levels
df2 <- X_df
df2$cyl <- as.factor(df2$cyl)
df2$vs <- as.factor(df2$vs)
df2$am <- as.factor(df2$am)
df2$gear <- as.factor(df2$gear)
df2$carb <- as.factor(df2$carb)
X_df_model <- stats::model.matrix(~ ., df2)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
# Should throw an error if we assign clusters because df2 contains factors
# with more than two levels
testthat::expect_error(processClusterLassoInputs(X=df2, y=rnorm(nrow(df2)),
clusters=1:3, nlambda=100),
"When stats::model.matrix converted the provided data.frame X to a matrix, the number of columns changed (probably because the provided data.frame contained a factor variable with at least three levels). Please convert X to a matrix yourself using model.matrix and provide cluster assignments according to the columns of the new matrix.", fixed=TRUE)
# Should be fine if no clusters are provided
process <- processClusterLassoInputs(X=df2, y=rnorm(nrow(df2)),
clusters=list(), nlambda=100)
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="clusterRepLasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=rnorm(nrow(df2)), family="gaussian",
nlambda=100)
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
# Pick an arbitrary lasso set
lasso_set <- lasso_sets[[min(length(lasso_sets), 3)]]
res <- getSelectedSets(lasso_set, process$clusters, process$prototypes,
process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_set",
"selected_clusts_list"))
# selected_set
testthat::expect_true(is.integer(res$selected_set))
testthat::expect_true(all(!is.na(res$selected_set)))
testthat::expect_true(all(res$selected_set %in% process$prototypes))
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
testthat::expect_equal(length(res$selected_set),
length(res$selected_clusts_list))
sel_feats <- unlist(res$selected_clusts_list)
testthat::expect_true(all(sel_feats %in% 1:ncol(X_df_model)))
n_clusts <- length(res$selected_clusts_list)
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i, process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
# X as a matrix with column names
x2 <- x
colnames(x2) <- LETTERS[1:11]
process <- processClusterLassoInputs(X=x2, y=y,
clusters=good_clusters, nlambda=100)
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="protolasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=y, family="gaussian",
nlambda=100)
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
# Pick an arbitrary lasso set
lasso_set <- lasso_sets[[min(length(lasso_sets), 3)]]
res <- getSelectedSets(lasso_set, process$clusters, process$prototypes,
process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_set",
"selected_clusts_list"))
# selected_set
testthat::expect_true(is.integer(res$selected_set))
testthat::expect_true(all(!is.na(res$selected_set)))
testthat::expect_true(all(res$selected_set %in% process$prototypes))
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
testthat::expect_equal(length(res$selected_set),
length(res$selected_clusts_list))
sel_feats <- unlist(res$selected_clusts_list)
testthat::expect_true(all(sel_feats %in% 1:11))
n_clusts <- length(res$selected_clusts_list)
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i, process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
})## Test passed with 80 successes.
Tests for getClusterSelsFromGlmnet():
testthat::test_that("getClusterSelsFromGlmnet works", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
process <- processClusterLassoInputs(X=x, y=y, clusters=good_clusters,
nlambda=100)
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="protolasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=y, family="gaussian", nlambda=100)
# Deliberately the raw predict.glmnet idiom rather than clusterLassoCore()'s
# normalisation (#190). These fixtures exercise the CONSUMER against the
# installed glmnet, bypassing the boundary on purpose, which is what makes
# them complementary to the mocked shape pins further below; copying the
# normalisation in here would defeat that.
#
# The two assertions beside each such fixture -- here and in the
# "getSelectedSets works" block above -- are the canary for that choice. On a
# glmnet that returned a data.frame, the fixtures in THIS block would stop at
# the consumer's new guard, but that block's would not: it indexes a single
# element out and calls getSelectedSets(), which this PR does not guard, and
# a data.frame's [[k]] is column k -- a well-formed integer vector whose
# assertions pass silently. The canary makes both blocks fail the same,
# legible way instead.
#
# BOTH assertions are needed, for the same reason the consumer needs two
# guards: a data.frame IS a list, so expect_true(is.list(...)) alone passes
# on the very container this is watching for. The pair mirrors
# getClusterSelsFromGlmnet()'s own stopifnot() pair exactly.
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
res <- getClusterSelsFromGlmnet(lasso_sets, process$clusters,
process$prototypes, process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% process$prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:11))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i,
process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# Try again with cluster representative lasso
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="clusterRepLasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=y, family="gaussian", nlambda=100)
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
res <- getClusterSelsFromGlmnet(lasso_sets, process$clusters,
process$prototypes, process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% process$prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:11))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i,
process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# X as a data.frame
X_df <- datasets::mtcars
X_df_model <- stats::model.matrix(~ ., X_df)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
process <- processClusterLassoInputs(X=X_df, y=rnorm(nrow(X_df)),
clusters=1:3, nlambda=100)
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="protolasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=rnorm(nrow(X_df)), family="gaussian",
nlambda=100)
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
res <- getClusterSelsFromGlmnet(lasso_sets, process$clusters,
process$prototypes, process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% process$prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:ncol(X_df_model)))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i,
process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# X as a dataframe with factors (number of columns of final design matrix
# after one-hot encoding factors won't match number of columns of df2)
# cyl, gear, and carb are factors with more than 2 levels
df2 <- X_df
df2$cyl <- as.factor(df2$cyl)
df2$vs <- as.factor(df2$vs)
df2$am <- as.factor(df2$am)
df2$gear <- as.factor(df2$gear)
df2$carb <- as.factor(df2$carb)
X_df_model <- stats::model.matrix(~ ., df2)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
process <- processClusterLassoInputs(X=df2, y=rnorm(nrow(df2)),
clusters=list(), nlambda=100)
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="clusterRepLasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=rnorm(nrow(df2)), family="gaussian",
nlambda=100)
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
res <- getClusterSelsFromGlmnet(lasso_sets, process$clusters,
process$prototypes, process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% process$prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:ncol(X_df_model)))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i,
process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# X as a matrix with column names
x2 <- x
colnames(x2) <- LETTERS[1:11]
process <- processClusterLassoInputs(X=x2, y=y,
clusters=good_clusters, nlambda=100)
X_glmnet <- getXglmnet(x=process$x, clusters=process$clusters,
type="protolasso", prototypes=process$prototypes)
fit <- glmnet::glmnet(x=X_glmnet, y=y, family="gaussian",
nlambda=100)
lasso_sets <- unique(glmnet::predict.glmnet(fit, type="nonzero"))
testthat::expect_true(is.list(lasso_sets))
testthat::expect_false(is.data.frame(lasso_sets))
res <- getClusterSelsFromGlmnet(lasso_sets, process$clusters,
process$prototypes, process$var_names)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% process$prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:11))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(process$clusters)){
clust_i_found <- clust_i_found | identical(clust_i,
process$clusters[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# An all-empty lasso path (max_length == 0) must return an empty selection,
# not crash with "model_size > 0 is not TRUE" (#157).
out_empty <- getClusterSelsFromGlmnet(list(integer(0)), clusters = list(1L),
prototypes = 1L, feat_names = NA)
testthat::expect_length(out_empty$selected_sets, 0)
testthat::expect_length(out_empty$selected_clusts_list, 0)
})## Test passed with 594 successes.
predict.glmnet(type = "nonzero") does not return a stable container across glmnet
versions here either, and unlike cssLasso() this path must preserve one slot per
penalty rather than flatten them. clusterLassoCore() therefore normalises the result to
a list before unique() sees it, and getClusterSelsFromGlmnet() refuses anything else
(#190). These blocks mock glmnet::predict.glmnet to hand protolasso() and
clusterRepLasso() each container shape directly, so they pin the contract on any machine
regardless of which glmnet is installed – a test exercising only the installed version
pins nothing about the other one.
glmnet() itself is not mocked, so fit is a real fit whose path length is whatever the
installed glmnet’s early-stopping heuristics produce (nlambda is a request, not a
promise: measured on the protolasso design these blocks build, nlambda = 100 yields 66
penalties, and 61 on the clusterRepLasso() design the multi-column block uses). The mocks
that need a whole-path fixture therefore size it from ncol(object$beta) rather than
hard-coding a slot count, which would be a stopifnot() error rather than a legible test
failure. The two blocks that pin a specific geometry – the square one and the
neither-orientation one – hard-code theirs instead, and assert or exploit the path length
rather than deriving it.
Only the single-column data.frame block is a red-green regression test for the container
bug proper. The multi-column, list and square blocks are pins that pass on the pre-fix source
too. The blocks named requires one entry per penalty, rejects a data.frame and rejects a
non-list pin the boundary’s entry-count assertion and the consumer’s two container guards,
each of which is otherwise observed by nothing.
The boundary’s own is.list() / !is.data.frame() pair is deliberately unpinned: both are
post-conditions on a total normalisation, so nothing short of mocking a container nonzeroCoef()
cannot emit would reach them. They stand as defence-in-depth against a future glmnet, and
deleting either leaves every block below green – the expected result, not a coverage gap.
The inner nrow() check in the row-major branch is a different case and is pinned, by the
block named rejects a data.frame of neither orientation. It is that branch’s own disambiguation predicate rather than a post-condition, and
its unreachability is empirical – no glmnet in play emits a data.frame whose ncol and nrow
both differ from the penalty count – rather than structural, so it is worth one assertion.
testthat::test_that("clusterLassoCore normalises a single-column data.frame (#190)", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
# glmnet 4.x's shape when every penalty selected exactly ONE feature: apply()
# collapses to a vector, so data.frame() gives a single column with one ROW
# per penalty and the column<->penalty convention inverts. Read as a list
# this is one model of size n_pen; read correctly it is n_pen models of size
# 1. The values must cycle through more than one index, or the two readings
# coincide and this block cannot go red.
#
# The cycle needs n_pen >= 3, so assert it rather than documenting it: at
# n_pen == 1 the fixture is a 1 x 1 data.frame, both readings agree, and this
# block would pass pre-fix -- a green red-green test, which is worse than no
# test at all.
testthat::local_mocked_bindings(
predict.glmnet = function(object, ...){
testthat::expect_gte(ncol(object$beta), 3L)
data.frame(which=rep_len(c(1L, 2L, 3L), ncol(object$beta)))
}, .package="glmnet")
res <- protolasso(x, y, good_clusters, nlambda=100)
# Pre-fix, unique() dedupes the data.frame's ROWS and lengths() then counts
# rows per column, so this fixture is read as max_length = 3: the block sees
# selected_sets of length 3, holding a fabricated size-3 model c(1L, 7L, 9L)
# that glmnet never selected, and drops the genuine size-1 one. Both
# assertions below move.
testthat::expect_length(res$selected_sets, 1)
testthat::expect_identical(res$selected_sets[[1]], 1L)
})
testthat::test_that("clusterLassoCore normalises a multi-column data.frame (#190)", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
# glmnet 4.x's other data.frame layout: every penalty selected the same
# NUMBER k >= 2 of features -- the sets may differ, {1,2} and {2,3} qualify
# -- so apply() returns a k x n_pen matrix and data.frame() lays out one
# COLUMN per penalty. This one degenerates to the right answer
# pre-fix -- each column is strictly increasing down the rows, so no two rows
# can be equal and unique() is a no-op on it -- which makes this a pin rather
# than a red-green test. It holds the column-major branch of the
# normalisation against a later refactor, and it goes red if the two branches
# are transposed or the reshaping is deleted.
#
# Driven through clusterRepLasso() so the shared clusterLassoCore() body is
# covered from both exported entry points.
testthat::local_mocked_bindings(
predict.glmnet = function(object, ...){
n_pen <- ncol(object$beta)
as.data.frame(matrix(rep_len(c(1L, 2L, 1L, 3L), 2L*n_pen), nrow=2L,
ncol=n_pen))
}, .package="glmnet")
res <- clusterRepLasso(x, y, good_clusters, nlambda=100)
# Design indices 1 and 2 are red_cluster and green_cluster, whose prototypes
# are features 1 and 7; getSelectedSets() maps the fixture's indices back
# through prototypes, so these are not the fixture's own integers.
testthat::expect_length(res$selected_sets, 2)
testthat::expect_null(res$selected_sets[[1]])
testthat::expect_identical(res$selected_sets[[2]], c(1L, 7L))
})
testthat::test_that("clusterLassoCore leaves the list shape alone (#190)", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
# The branch glmnet 5.0 always takes, and the one 4.x takes here today.
# Mixed sizes and a NULL slot, so the fixture is not degenerate.
#
# This is a pin, not a red-green test, and it is deliberately kept despite
# being green pre-fix: measured green under deleting the reshaping and under
# replacing it with a bare as.list(), because both leave a list untouched.
# What it does catch is applying the reshaping UNCONDITIONALLY -- dropping
# the is.data.frame() test on the grounds that "the reshape is harmless on a
# list" is the likeliest future edit to that block -- under which it errors
# with "'list' object cannot be coerced to type 'integer'". Apart from the
# entry-count block below, which trips a different assertion incidentally,
# this block catches that. It is not the only one -- an unconditional
# reshape reaches as.integer() on a list from every caller, so the
# end-to-end protolasso/clusterRepLasso blocks error too -- but it is the
# only one of the mocked shape pins that does.
testthat::local_mocked_bindings(
predict.glmnet = function(object, ...){
testthat::expect_gte(ncol(object$beta), 3L)
rep_len(list(NULL, 1L, c(1L, 2L)), ncol(object$beta))
}, .package="glmnet")
res <- protolasso(x, y, good_clusters, nlambda=100)
testthat::expect_length(res$selected_sets, 2)
testthat::expect_identical(res$selected_sets[[1]], 1L)
testthat::expect_identical(res$selected_sets[[2]], c(1L, 7L))
})
testthat::test_that("getClusterSelsFromGlmnet rejects a data.frame (#190)", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
process <- processClusterLassoInputs(X=x, y=y, clusters=good_clusters,
nlambda=100)
# The message pin separates the NEW guard from the downstream
# all(lasso_set <= length(clusters)) check in getSelectedSets(), which fires
# only when the fixture's indices exceed length(clusters). Measured on
# 2f66a14 with this five-cluster fixture: the call returns cleanly
# (selected_sets of length 2), so a bare expect_error() is red-green here as
# well -- but it would go green again on any later edit that made this call
# error for some unrelated reason, which is why the pin stays.
#
# fixed=TRUE is mandatory: the message contains "!", "(" and ".", so it does
# not match itself as a regex.
testthat::expect_error(
getClusterSelsFromGlmnet(data.frame(which=c(1L, 2L)), process$clusters,
process$prototypes, process$var_names),
"!is.data.frame(lasso_sets) is not TRUE", fixed=TRUE)
})
testthat::test_that("clusterLassoCore requires one entry per penalty (#190)", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
# The entry-count assertion is a new bound on the default code path: before
# #190 clusterLassoCore() accepted whatever predict.glmnet handed it,
# afterwards it refuses anything whose entry count differs from
# ncol(fit$beta). Nothing else observes it, so without this block a later
# weakening of it would go unnoticed. On glmnet 4.1.9 the bound excludes
# nothing (400/400 agreement over random fits); CI's R-CMD-check matrix,
# which installs glmnet 5.0, is its cross-version test.
testthat::local_mocked_bindings(
predict.glmnet = function(object, ...){
rep_len(list(1L), ncol(object$beta) + 1L)
}, .package="glmnet")
testthat::expect_error(protolasso(x, y, good_clusters, nlambda=100),
"identical(length(nonzero), n_pen) is not TRUE",
fixed=TRUE)
})
testthat::test_that("getClusterSelsFromGlmnet rejects a non-list (#190)", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
process <- processClusterLassoInputs(X=x, y=y, clusters=good_clusters,
nlambda=100)
# The data.frame block above can never reach the is.list() guard, because a
# data.frame IS a list and its fixture always falls through to the second
# guard; this block is the only thing that exercises the first. Measured on
# 2f66a14: this call returns cleanly (selected_sets of length 1), so the red
# side is a clean "no error was thrown".
testthat::expect_error(
getClusterSelsFromGlmnet(1:3, process$clusters, process$prototypes,
process$var_names),
"is.list(lasso_sets) is not TRUE", fixed=TRUE)
})
testthat::test_that("clusterLassoCore reads a square data.frame column-major (#190)", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
# The one shape on which BOTH branch predicates are true: k == n_pen, so
# ncol(nonzero_mat) == n_pen and nrow(nonzero_mat) == n_pen alike. The
# normalisation resolves it by testing ncol first, which is correct --
# apply() simplifies to a matrix only when it has one column per penalty --
# but nothing else in this file pins that ORDER, because no other fixture is
# square (the single-column one is n_pen x 1, the multi-column one 2 x n_pen).
#
# Swapping the predicate and the two readings together is wrong on exactly
# this shape and correct everywhere else, so without this block that refactor
# passes the whole suite while reintroducing #190's failure mode: confidently
# wrong model sizes. Read row-major, slot 1 is ROW 1 -- c(1L, 1L, 1L) --
# which getSelectedSets() rejects for having duplicates.
#
# nlambda is honoured exactly at small values (measured: 2 -> 2, 3 -> 3), so
# a square fixture is directly constructible; assert it rather than trusting
# it, since a shortened path would make this block vacuous.
testthat::local_mocked_bindings(
predict.glmnet = function(object, ...){
n_pen <- ncol(object$beta)
testthat::expect_identical(n_pen, 3L)
as.data.frame(matrix(c(1L, 2L, 3L, 1L, 2L, 4L, 1L, 3L, 4L), nrow=3L,
ncol=3L))
}, .package="glmnet")
res <- protolasso(x, y, good_clusters, nlambda=3)
# Prototypes are c(1, 7, 9, 10, 11), so the fixture's first column c(1, 2, 3)
# maps to features 1, 7 and 9.
testthat::expect_length(res$selected_sets, 3)
testthat::expect_null(res$selected_sets[[1]])
testthat::expect_null(res$selected_sets[[2]])
testthat::expect_identical(res$selected_sets[[3]], c(1L, 7L, 9L))
})## Test passed with 3 successes.
## Test passed with 3 successes.
## Test passed with 4 successes.
## Test passed with 1 success.
## Test passed with 1 success.
## Test passed with 1 success.
## Test passed with 5 successes.
The last of the boundary’s assertions, the row-major branch’s own nrow() check. Unlike the
is.list() / !is.data.frame() pair it guards a reachable-in-principle shape – a
data.frame whose ncol and nrow both differ from the penalty count – so it costs one
assertion rather than being unpinnable.
testthat::test_that("clusterLassoCore rejects a data.frame of neither orientation (#190)", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
# Neither ncol nor nrow matches n_pen (66 on this design at nlambda = 100),
# so the reshaping cannot tell which orientation it is looking at and stops
# instead of guessing. No glmnet in play emits this, which is why the guard
# is otherwise unobserved.
testthat::local_mocked_bindings(
predict.glmnet = function(...) data.frame(a=1:2, b=3:4),
.package="glmnet")
testthat::expect_error(protolasso(x, y, good_clusters, nlambda=100),
"nrow(nonzero_mat) == n_pen is not TRUE", fixed=TRUE)
})## Test passed with 1 success.
Finally, tests for protolasso():
testthat::test_that("protolasso works", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
# Get properly formatted clusters and prototypes for testing
format_clust_res <- formatClusters(clusters=good_clusters, p=11,
clust_names=names(good_clusters),
get_prototypes=TRUE, x=x, y=y)
prototypes <- format_clust_res$prototypes
clus_formatted <- format_clust_res$clusters
res <- protolasso(x, y, good_clusters, nlambda=60)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list", "beta"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
testthat::expect_true(is.null(names(res$selected_sets[[i]])))
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:11))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(clus_formatted)){
clust_i_found <- clust_i_found | identical(clust_i,
clus_formatted[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# beta
testthat::expect_true(grepl("dgCMatrix", class(res$beta)))
testthat::expect_true(nrow(res$beta) == 11 - 8 + 2)
testthat::expect_true(ncol(res$beta) <= 60)
# X as a data.frame
X_df <- datasets::mtcars
X_df_model <- stats::model.matrix(~ ., X_df)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
y_df <- rnorm(nrow(X_df))
# Get properly formatted clusters and prototypes for testing
format_clust_res <- formatClusters(clusters=1:3, p=ncol(X_df_model),
get_prototypes=TRUE, x=X_df_model, y=y_df)
prototypes <- format_clust_res$prototypes
clus_formatted <- format_clust_res$clusters
res <- protolasso(X_df, y_df, 1:3, nlambda=80)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list", "beta"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
testthat::expect_true(all(names(res$selected_sets[[i]]) %in%
colnames(X_df_model)))
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:ncol(X_df_model)))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(clus_formatted)){
clust_i_found <- clust_i_found | identical(clust_i,
clus_formatted[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# beta
testthat::expect_true(grepl("dgCMatrix", class(res$beta)))
testthat::expect_true(nrow(res$beta) == ncol(X_df_model) - 3 + 1)
testthat::expect_true(ncol(res$beta) <= 80)
# X as a dataframe with factors (number of columns of final design matrix
# after one-hot encoding factors won't match number of columns of df2)
# cyl, gear, and carb are factors with more than 2 levels
df2 <- X_df
df2$cyl <- as.factor(df2$cyl)
df2$vs <- as.factor(df2$vs)
df2$am <- as.factor(df2$am)
df2$gear <- as.factor(df2$gear)
df2$carb <- as.factor(df2$carb)
X_df_model <- stats::model.matrix(~ ., df2)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
# Should get an error if we try to call protolasso on df2 with clusters
# because df2 contains factors with more than two levels
testthat::expect_error(protolasso(df2, y_df, 4:6, nlambda=70),
"When stats::model.matrix converted the provided data.frame X to a matrix, the number of columns changed (probably because the provided data.frame contained a factor variable with at least three levels). Please convert X to a matrix yourself using model.matrix and provide cluster assignments according to the columns of the new matrix.", fixed=TRUE)
# Should be fine if no clusters are provided
res <- protolasso(df2, y_df, nlambda=70)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list", "beta"))
# Get properly formatted clusters and prototypes for testing
format_clust_res <- formatClusters(clusters=4:6, p=ncol(X_df_model),
get_prototypes=TRUE, x=X_df_model, y=y_df)
prototypes <- format_clust_res$prototypes
clus_formatted <- format_clust_res$clusters
res <- protolasso(X_df_model, y_df, 4:6, nlambda=70)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list", "beta"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
testthat::expect_true(all(names(res$selected_sets[[i]]) %in%
colnames(X_df_model)))
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:ncol(X_df_model)))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(clus_formatted)){
clust_i_found <- clust_i_found | identical(clust_i,
clus_formatted[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# beta
testthat::expect_true(grepl("dgCMatrix", class(res$beta)))
testthat::expect_true(nrow(res$beta) == ncol(X_df_model) - 3 + 1)
testthat::expect_true(ncol(res$beta) <= 70)
# X as a matrix with column names
x2 <- x
colnames(x2) <- LETTERS[1:11]
# Get properly formatted clusters and prototypes for testing
format_clust_res <- formatClusters(clusters=good_clusters, p=11,
clust_names=names(good_clusters),
get_prototypes=TRUE, x=x2, y=y)
prototypes <- format_clust_res$prototypes
clus_formatted <- format_clust_res$clusters
res <- protolasso(x2, y, good_clusters, nlambda=50)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list", "beta"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
testthat::expect_true(all(names(res$selected_sets[[i]]) %in%
LETTERS[1:11]))
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:11))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(clus_formatted)){
clust_i_found <- clust_i_found | identical(clust_i,
clus_formatted[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# beta
testthat::expect_true(grepl("dgCMatrix", class(res$beta)))
testthat::expect_true(nrow(res$beta) == 11 - 8 + 2)
testthat::expect_true(ncol(res$beta) <= 50)
# Bad inputs
testthat::expect_error(protolasso(X="x", y=y[1:10], clusters=good_clusters,
nlambda=10),
"is.matrix(X) | is.data.frame(X) is not TRUE",
fixed=TRUE)
testthat::expect_error(protolasso(X=x, y=y[1:10], clusters=good_clusters,
nlambda=10),
"n == length(y) is not TRUE", fixed=TRUE)
testthat::expect_error(protolasso(X=x, y=y, clusters=list(1:4, 4:6),
nlambda=10),
"Overlapping clusters detected; clusters must be non-overlapping. Overlapping clusters: 1, 2.", fixed=TRUE)
# Duplicate clusters are now silently removed (#156); the fitted result for a
# duplicated cluster is identical to passing the cluster once (seed both).
set.seed(921)
dup_pl <- protolasso(X=x, y=y, clusters=list(2:3, 2:3), nlambda=10)
set.seed(921)
dedup_pl <- protolasso(X=x, y=y, clusters=list(2:3), nlambda=10)
testthat::expect_identical(dup_pl, dedup_pl)
testthat::expect_error(protolasso(X=x, y=y,
clusters=list(1:4, as.integer(NA)),
nlambda=10),
"!is.na(clusters) are not all TRUE", fixed=TRUE)
testthat::expect_error(protolasso(X=x, y=y, clusters=list(2:3, c(4, 4, 5)),
nlambda=10),
"length(clusters[[i]]) == length(unique(clusters[[i]])) is not TRUE",
fixed=TRUE)
testthat::expect_error(protolasso(X=x, y=y, clusters=good_clusters,
nlambda=1), "nlambda >= 2 is not TRUE",
fixed=TRUE)
testthat::expect_error(protolasso(X=x, y=y, clusters=good_clusters,
nlambda=x),
"length(nlambda) == 1 is not TRUE", fixed=TRUE)
testthat::expect_error(protolasso(X=x, y=y, clusters=good_clusters,
nlambda="nlambda"),
"is.numeric(nlambda) | is.integer(nlambda) is not TRUE",
fixed=TRUE)
testthat::expect_error(protolasso(X=x, y=y, clusters=good_clusters,
nlambda=10.5),
"nlambda == round(nlambda) is not TRUE",
fixed=TRUE)
# (#127) A single all-encompassing cluster (or a genuine p < 2 input) leaves
# glmnet with a 1-column design; clusterLassoCore now errors up front with a
# cssr message naming both causes, instead of glmnet's "2 or more columns".
testthat::expect_error(
protolasso(X=x, y=y, clusters=list(all=1:11)),
"need at least 2 cluster representatives to fit the lasso", fixed=TRUE)
})## Test passed with 575 successes.
Tests for clusterRepLasso():
testthat::test_that("clusterRepLasso works", {
set.seed(61282)
x <- matrix(stats::rnorm(15*11), nrow=15, ncol=11)
y <- stats::rnorm(15)
good_clusters <- list(red_cluster=1L:4L, green_cluster=5L:8L)
# Get properly formatted clusters and prototypes for testing
format_clust_res <- formatClusters(clusters=good_clusters, p=11,
clust_names=names(good_clusters),
get_prototypes=TRUE, x=x, y=y)
prototypes <- format_clust_res$prototypes
clus_formatted <- format_clust_res$clusters
res <- clusterRepLasso(x, y, good_clusters, nlambda=60)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list", "beta"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
testthat::expect_true(is.null(names(res$selected_sets[[i]])))
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:11))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(clus_formatted)){
clust_i_found <- clust_i_found | identical(clust_i,
clus_formatted[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# beta
testthat::expect_true(grepl("dgCMatrix", class(res$beta)))
testthat::expect_true(nrow(res$beta) == 11 - 8 + 2)
testthat::expect_true(ncol(res$beta) <= 60)
# X as a data.frame
X_df <- datasets::mtcars
X_df_model <- stats::model.matrix(~ ., X_df)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
y_df <- rnorm(nrow(X_df))
# Get properly formatted clusters and prototypes for testing
format_clust_res <- formatClusters(clusters=1:3, p=ncol(X_df_model),
get_prototypes=TRUE, x=X_df_model, y=y_df)
prototypes <- format_clust_res$prototypes
clus_formatted <- format_clust_res$clusters
res <- clusterRepLasso(X_df, y_df, 1:3, nlambda=80)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list", "beta"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
testthat::expect_true(all(names(res$selected_sets[[i]]) %in%
colnames(X_df_model)))
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:ncol(X_df_model)))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(clus_formatted)){
clust_i_found <- clust_i_found | identical(clust_i,
clus_formatted[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# beta
testthat::expect_true(grepl("dgCMatrix", class(res$beta)))
testthat::expect_true(nrow(res$beta) == ncol(X_df_model) - 3 + 1)
testthat::expect_true(ncol(res$beta) <= 80)
# X as a dataframe with factors (number of columns of final design matrix
# after one-hot encoding factors won't match number of columns of df2)
# cyl, gear, and carb are factors with more than 2 levels
df2 <- X_df
df2$cyl <- as.factor(df2$cyl)
df2$vs <- as.factor(df2$vs)
df2$am <- as.factor(df2$am)
df2$gear <- as.factor(df2$gear)
df2$carb <- as.factor(df2$carb)
# Should get an error if we try to call clusterRepLasso on df2 with clusters
# because df2 contains factors with more than two levels
testthat::expect_error(clusterRepLasso(df2, y_df, 4:6, nlambda=70),
"When stats::model.matrix converted the provided data.frame X to a matrix, the number of columns changed (probably because the provided data.frame contained a factor variable with at least three levels). Please convert X to a matrix yourself using model.matrix and provide cluster assignments according to the columns of the new matrix.", fixed=TRUE)
# Should be fine if no clusters are provided
res <- clusterRepLasso(df2, y_df, nlambda=70)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list", "beta"))
X_df_model <- stats::model.matrix(~ ., df2)
X_df_model <- X_df_model[, colnames(X_df_model) != "(Intercept)"]
# Get properly formatted clusters and prototypes for testing
format_clust_res <- formatClusters(clusters=4:6, p=ncol(X_df_model),
get_prototypes=TRUE, x=X_df_model, y=y_df)
prototypes <- format_clust_res$prototypes
clus_formatted <- format_clust_res$clusters
res <- clusterRepLasso(X_df_model, y_df, 4:6, nlambda=70)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list", "beta"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
testthat::expect_true(all(names(res$selected_sets[[i]]) %in%
colnames(X_df_model)))
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:ncol(X_df_model)))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(clus_formatted)){
clust_i_found <- clust_i_found | identical(clust_i,
clus_formatted[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# beta
testthat::expect_true(grepl("dgCMatrix", class(res$beta)))
testthat::expect_true(nrow(res$beta) == ncol(X_df_model) - 3 + 1)
testthat::expect_true(ncol(res$beta) <= 70)
# X as a matrix with column names
x2 <- x
colnames(x2) <- LETTERS[1:11]
# Get properly formatted clusters and prototypes for testing
format_clust_res <- formatClusters(clusters=good_clusters, p=11,
clust_names=names(good_clusters),
get_prototypes=TRUE, x=x2, y=y)
prototypes <- format_clust_res$prototypes
clus_formatted <- format_clust_res$clusters
res <- clusterRepLasso(x2, y, good_clusters, nlambda=50)
testthat::expect_true(is.list(res))
testthat::expect_identical(names(res), c("selected_sets",
"selected_clusts_list", "beta"))
# selected_sets
testthat::expect_true(is.list(res$selected_sets))
# Selected models should have one of each size without repetition
lengths <- lengths(res$selected_sets)
lengths <- lengths[lengths != 0]
testthat::expect_identical(lengths, unique(lengths))
for(i in 1:length(res$selected_sets)){
if(!is.null(res$selected_sets[[i]])){
testthat::expect_true(is.integer(res$selected_sets[[i]]))
testthat::expect_true(all(!is.na(res$selected_sets[[i]])))
testthat::expect_true(all(res$selected_sets[[i]] %in% prototypes))
testthat::expect_equal(length(res$selected_sets[[i]]), i)
testthat::expect_true(all(names(res$selected_sets[[i]]) %in%
LETTERS[1:11]))
} else{
testthat::expect_true(is.null(res$selected_sets[[i]]))
}
}
# selected_clusts_list
testthat::expect_true(is.list(res$selected_clusts_list))
# Selected models should have one of each size without repetition
clust_lengths <- lengths(res$selected_clusts_list)
clust_lengths <- clust_lengths[clust_lengths != 0]
testthat::expect_identical(clust_lengths, unique(clust_lengths))
for(k in 1:length(res$selected_clusts_list)){
if(!is.null(res$selected_clusts_list[[k]])){
testthat::expect_true(is.list(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_sets[[k]]),
length(res$selected_clusts_list[[k]]))
testthat::expect_equal(length(res$selected_clusts_list[[k]]), k)
sel_feats <- unlist(res$selected_clusts_list[[k]])
testthat::expect_true(all(sel_feats %in% 1:11))
testthat::expect_equal(length(sel_feats), length(unique(sel_feats)))
n_clusts <- k
for(i in 1:n_clusts){
clust_i_found <- FALSE
clust_i <- res$selected_clusts_list[[k]][[i]]
for(j in 1:length(clus_formatted)){
clust_i_found <- clust_i_found | identical(clust_i,
clus_formatted[[j]])
}
testthat::expect_true(clust_i_found)
}
} else{
testthat::expect_true(is.null(res$selected_clusts_list[[k]]))
}
}
# beta
testthat::expect_true(grepl("dgCMatrix", class(res$beta)))
testthat::expect_true(nrow(res$beta) == 11 - 8 + 2)
testthat::expect_true(ncol(res$beta) <= 50)
# Bad inputs
testthat::expect_error(clusterRepLasso(X="x", y=y[1:10], clusters=good_clusters,
nlambda=10),
"is.matrix(X) | is.data.frame(X) is not TRUE",
fixed=TRUE)
testthat::expect_error(clusterRepLasso(X=x, y=y[1:10], clusters=good_clusters,
nlambda=10),
"n == length(y) is not TRUE", fixed=TRUE)
testthat::expect_error(clusterRepLasso(X=x, y=y, clusters=list(1:4, 4:6),
nlambda=10),
"Overlapping clusters detected; clusters must be non-overlapping. Overlapping clusters: 1, 2.", fixed=TRUE)
# Duplicate clusters are now silently removed (#156); identical to passing the
# cluster once (seed both).
set.seed(921)
dup_crl <- clusterRepLasso(X=x, y=y, clusters=list(2:3, 2:3), nlambda=10)
set.seed(921)
dedup_crl <- clusterRepLasso(X=x, y=y, clusters=list(2:3), nlambda=10)
testthat::expect_identical(dup_crl, dedup_crl)
testthat::expect_error(clusterRepLasso(X=x, y=y,
clusters=list(1:4, as.integer(NA)),
nlambda=10),
"!is.na(clusters) are not all TRUE", fixed=TRUE)
testthat::expect_error(clusterRepLasso(X=x, y=y, clusters=list(2:3, c(4, 4, 5)),
nlambda=10),
"length(clusters[[i]]) == length(unique(clusters[[i]])) is not TRUE",
fixed=TRUE)
testthat::expect_error(clusterRepLasso(X=x, y=y, clusters=good_clusters,
nlambda=1), "nlambda >= 2 is not TRUE",
fixed=TRUE)
testthat::expect_error(clusterRepLasso(X=x, y=y, clusters=good_clusters,
nlambda=x),
"length(nlambda) == 1 is not TRUE", fixed=TRUE)
testthat::expect_error(clusterRepLasso(X=x, y=y, clusters=good_clusters,
nlambda="nlambda"),
"is.numeric(nlambda) | is.integer(nlambda) is not TRUE",
fixed=TRUE)
testthat::expect_error(clusterRepLasso(X=x, y=y, clusters=good_clusters,
nlambda=10.5),
"nlambda == round(nlambda) is not TRUE",
fixed=TRUE)
# (#127) Same degenerate-design guard as protolasso: a single all-encompassing
# cluster yields a 1-column design, now rejected with a clear cssr message
# rather than glmnet's "2 or more columns".
testthat::expect_error(
clusterRepLasso(X=x, y=y, clusters=list(all=1:11)),
"need at least 2 cluster representatives to fit the lasso", fixed=TRUE)
})## Test passed with 527 successes.
Regression test for the empty-lasso-path edge case (#157):
testthat::test_that("protolasso/clusterRepLasso return an empty selection when the lasso path selects nothing (#157)", {
# X'y must be EXACTLY zero to empty the whole glmnet path (see plan note:
# floating-point orthogonality drives lambda_max->0 and selects everything).
# Paired +1/-1 integer rows give crossprod(X, y) == 0 exactly.
y <- as.double(c(1, 1, 1, 1, -1, -1, -1, -1))
X <- cbind(c(1,-1,0,0,0,0,0,0), c(0,0,1,-1,0,0,0,0),
c(0,0,0,0,1,-1,0,0), c(0,0,0,0,0,0,1,-1))
storage.mode(X) <- "double"
testthat::expect_identical(max(abs(crossprod(X, y))), 0) # exactly orthogonal
# Was: crash "model_size > 0 is not TRUE". Now: empty selection, no error.
res_p <- protolasso(X, y)
testthat::expect_length(res_p$selected_sets, 0)
testthat::expect_length(res_p$selected_clusts_list, 0)
res_c <- clusterRepLasso(X, y)
testthat::expect_length(res_c$selected_sets, 0)
testthat::expect_length(res_c$selected_clusts_list, 0)
# A normal signal case still selects features (guards against over-fixing).
set.seed(2)
Xs <- matrix(stats::rnorm(24 * 4), 24, 4)
ys <- Xs[, 1] * 2 + stats::rnorm(24, sd = 0.1)
testthat::expect_gt(length(protolasso(Xs, ys)$selected_sets), 0)
})## Test passed with 6 successes.
Regression tests for the named output contract of protolasso()/clusterRepLasso(): selected_clusts_list[[k]] is a named list of the selected clusters (#158a).
testthat::test_that("protolasso and clusterRepLasso name selected_clusts_list with the selected clusters' names (#158a)", {
set.seed(158)
n <- 40L
p <- 6L
X <- matrix(stats::rnorm(n * p), nrow = n, ncol = p)
y <- as.numeric(X %*% c(1, 0, 1, 0, 1, 0) + stats::rnorm(n))
clusters <- list(clustA = c(2L, 5L), clustB = c(4L, 6L))
# formatClusters reorders to clustA, clustB, then one singleton per unclustered
# feature in ascending order (c3 = feature 1, c4 = feature 3).
expected_names <- c("clustA", "clustB", "c3", "c4")
for (fun in list(protolasso, clusterRepLasso)) {
res <- fun(X, y, clusters)
testthat::expect_true(any(lengths(res$selected_clusts_list) > 0))
for (k in seq_along(res$selected_clusts_list)) {
sk <- res$selected_clusts_list[[k]]
if (length(sk) > 0) {
testthat::expect_false(is.null(names(sk)))
testthat::expect_equal(length(names(sk)), k)
testthat::expect_true(all(names(sk) %in% expected_names))
# Name-based indexing returns the same cluster as position-based.
testthat::expect_identical(sk[[names(sk)[1]]], sk[[1]])
}
}
}
})## Test passed with 26 successes.
The rows of beta carry the reordered cluster names, so they cross-reference selected_clusts_list (#158b).
testthat::test_that("protolasso and clusterRepLasso beta carries cluster names as rownames (#158b)", {
set.seed(158)
n <- 40L
p <- 6L
X <- matrix(stats::rnorm(n * p), nrow = n, ncol = p)
y <- as.numeric(X %*% c(1, 0, 1, 0, 1, 0) + stats::rnorm(n))
clusters <- list(clustA = c(2L, 5L), clustB = c(4L, 6L))
expected_names <- c("clustA", "clustB", "c3", "c4")
for (fun in list(protolasso, clusterRepLasso)) {
res <- fun(X, y, clusters)
# GUARANTEED equality: one beta row per reordered cluster, in order.
testthat::expect_identical(rownames(res$beta), expected_names)
# SUBSET cross-check: each selected cluster's name is among beta's rownames
# (a never-selected cluster is still a beta row, so this is not equality).
for (k in seq_along(res$selected_clusts_list)) {
sk <- res$selected_clusts_list[[k]]
if (length(sk) > 0) {
testthat::expect_true(all(names(sk) %in% rownames(res$beta)))
}
}
}
})## Test passed with 8 successes.
processClusterLassoInputs() rejects an X whose columns are partly named and partly NA-named, with the corrected remedy message (#158d).
testthat::test_that("processClusterLassoInputs rejects X with mixed named and NA-named columns (#158d)", {
set.seed(158)
n <- 20L
p <- 4L
X <- matrix(stats::rnorm(n * p), nrow = n, ncol = p)
colnames(X) <- c("g1", NA, "g3", NA)
y <- stats::rnorm(n)
testthat::expect_error(
processClusterLassoInputs(X = X, y = y, clusters = list(), nlambda = 10),
"please either name all features in X or remove the names altogether",
fixed = TRUE)
})## Test passed with 1 success.