| uvarpro | R Documentation |
Performs unsupervised variable selection using the Variable Priority
framework. uvarpro constructs forest rules and region-release
contrasts from unlabeled data. get.beta.entropy uses local lasso
regressions to summarize which variables distinguish these contrasts.
sdependent displays the resulting importance matrix as a
variable-dependence graph and identifies candidate signal variables.
uvarpro(data,
method = c("auto", "unsupv", "rnd"),
ntree = 200, nodesize = NULL,
max.rules.tree = 20, max.tree = 200,
verbose = FALSE, seed = NULL,
...)
get.beta.entropy(o,
second.stage = FALSE,
pre.filter = TRUE,
papply = mclapply,
vimp.min = 0,
nfolds = 10,
maxit = 2500,
thresh = 1e-3,
parallel = FALSE,
use.cv = TRUE,
lambda.sel = c("lambda.1se", "lambda.min"),
nlambda = NULL,
lambda.min.ratio = NULL,
nlambda.lasso = nlambda,
lambda.min.ratio.lasso = lambda.min.ratio)
sdependent(I,
threshold = .25,
layout = "grid",
q.signal = .75,
directed = TRUE,
min.degree = NULL,
title = "s-Dependent Variable Detection",
plot = TRUE)
data |
Data frame containing the features to be analyzed, without an outcome or class-label column. Rows with missing values are omitted, unused factor levels are dropped, and categorical features are hot-encoded. See Details. |
method |
Forest construction method. |
ntree |
Number of trees to grow. |
nodesize |
Minimum terminal node size. If |
max.rules.tree |
Maximum number of rules sampled per tree for the region-release analysis. |
max.tree |
Maximum number of trees used to extract rules. |
verbose |
Currently unused. |
seed |
Seed argument for |
... |
Additional named forest arguments recognized by
|
o |
An object returned by |
second.stage |
If |
pre.filter |
Should preliminary importance from |
papply |
An |
vimp.min |
Strict lower cutoff for preliminary importance when
|
nfolds |
Number of cross-validation folds for each local
logistic-lasso analysis. At least three folds are required.
Ignored when |
maxit |
Maximum number of iterations passed to |
thresh |
Convergence tolerance passed to |
parallel |
Should |
use.cv |
Should the logistic-lasso penalty be chosen by
cross-validation? If |
lambda.sel |
Cross-validation penalty choice for the logistic
stage. |
nlambda |
Number of penalty values requested for the logistic
stage. |
lambda.min.ratio |
Smallest requested logistic-stage penalty
relative to the largest penalty on the path. |
nlambda.lasso, lambda.min.ratio.lasso |
Corresponding path
controls for the optional second-stage linear lasso. They default
to the logistic-stage settings and are used only when
|
I |
A finite, nonnegative numeric importance matrix, usually
obtained from |
threshold |
Positive edge cutoff on the scale of |
layout |
Graph layout. Supported names are |
q.signal |
Quantile of the global importance scores among nonisolated vertices used for signal designation. Values range from zero to one; the default is the upper quartile. A signal must meet both this cutoff and the degree condition. |
directed |
Should the graph be directed? In the directed graph, an edge goes from the released variable to the predictor used to distinguish its region-release contrasts. For an undirected display, supply a symmetric importance matrix; see Note. |
min.degree |
Minimum degree for candidate signal designation. With directed graphs this is the out-degree. The default is 1 for directed graphs and 2 for undirected graphs. |
title |
Title for the graph. |
plot |
Should the graph be plotted? If |
UVarPro studies dependence among features in unlabeled data. The population framework of Zhou et al. (2026) seeks a signal set that accounts for dependence among the remaining variables. Redundant or interchangeable variables can give rise to multiple signal sets.
The analysis has three steps: uvarpro constructs local
region-release comparisons, get.beta.entropy estimates
a lasso coefficient importance matrix, and sdependent
displays its dependence graph.
For method = "auto", copies of the processed features
serve as the multivariate response and the original features
serve as predictors in rfsrc. Regressing the feature vector
on itself produces a forest autoencoder that partitions feature
space.
For method = "unsupv", the forest uses unsupervised
splitting with internally selected pseudo-responses. The default
ytry is min(ceiling(sqrt(p)), p - 1), where p
is the number of processed features. For method = "rnd",
an independent Gaussian response is generated and
splitrule = "random" is used. All three methods use the
same subsequent region-release and lasso calculations.
Complete observations are hot-encoded and saved as o$x.
Membership indices refer to these observations; lasso rows and
columns and graph vertices refer to the processed features.
Each encoded column is analyzed separately.
A tree rule defines a region R through restrictions on
feature values. Releasing variable s removes its restrictions
while retaining the others. Write the enlarged region as
R^{(s)} and its complementary, or near-miss, region as
C_s = R^{(s)} \setminus R.
The complementary region contains observations admitted by
releasing s that still satisfy the other rule conditions.
uvarpro extracts original-region out-of-bag membership
and complementary-region membership. Comparisons are retained
when both sets are nonempty. The default o$entropy stores
these index pairs by released variable, with comp first
and oob second.
For each comparison, get.beta.entropy combines the
complementary observations (class 0) and original observations
(class 1). It predicts this membership label using the eligible
features other than the released variable. Excluding the released
variable prevents the classifier from simply recovering the rule
restriction that separates the groups. The remaining predictors
identify features that distinguish the local contrast. Repeating
this analysis across rules allows different predictors to
contribute in different parts of feature space.
The default entropy function computes a dispersion ratio. Let
d(M) be the average column standard deviation of a feature
matrix M. For the original and complementary feature matrices
X_O and X_C, the stored rule score is
H = \frac{d(X_C) + d(X_O)}{2 d(X_C \cup X_O)}.
Here the union denotes row concatenation. The ratio compares average within-group dispersion with pooled dispersion. Before scoring, features are divided by their full-data standard deviations without centering; near-zero standard deviations are replaced by one. All features, including the released variable, enter this calculation.
importance(o) summarizes the rule scores. By default,
get.beta.entropy uses preliminary importance to screen
predictors and release variables before the local regressions.
Set pre.filter = FALSE to include all processed features.
Each local classification problem uses binomial lasso logistic
regression from glmnet, with penalty parameter
\alpha = 1. With use.cv = TRUE,
cv.glmnet selects a penalty separately for each comparison
using binomial deviance; the default choice is lambda.1se.
With use.cv = FALSE, the last computed penalty on the
regularization path is used.
For released variable s and retained comparison r,
let \widehat\beta_{srj} denote the
coefficient of predictor j. The entries of the importance
matrix are mean absolute coefficients across retained comparisons.
The intercept is discarded; unselected predictors and the released
coordinate receive zero. Writing \mathcal K_s for the
retained comparisons, the default matrix has entries
I_{sj} = \frac{1}{|\mathcal K_s|}
\sum_{r \in \mathcal K_s} |\widehat\beta_{srj}|,
\qquad j \ne s, \qquad I_{ss} = 0.
Rows identify released variables and columns identify predictors.
Thus I[s, j] measures predictor j's contribution
when variable s is released. The matrix is generally
asymmetric. Column sums aggregate each predictor's contributions
across release tasks; column means give the same ranking.
A comparison is retained when fitting and coefficient extraction
succeed and at least one finite, nonzero predictor coefficient
remains. Failed fits and intercept-only solutions are excluded.
Retained comparisons receive equal weight, including zeros for
unselected predictors. A release row is omitted when it has no
retained comparisons, so predictor columns can outnumber rows.
The result is NULL when no rows remain.
Before each logistic fit, scale(X, center = FALSE) divides
each predictor column by
\sqrt{\sum_i X_{ij}^2/(m-1)},
where m is the local sample size. glmnet also applies
its default internal standardization and returns coefficients on
the scale of the matrix supplied to it.
With second.stage = TRUE, predictors selected by the
logistic lasso enter a local Gaussian lasso with the released
variable as its response. This regression uses the combined
complementary and original observations, the same predictor
scaling, and the response from o$x. Coefficients are taken
at the last computed penalty on the linear-lasso path.
Absolute second-stage coefficients are averaged over the
comparisons retained by the logistic stage, with zeros for
unselected predictors. A second-stage fit that fails or selects
no predictors contributes zeros to this average. Row s is
then divided by the full-data standard deviation of the released
variable:
I^{(2)}_{sj} =
\frac{1}{\widehat\sigma_s |\mathcal K_s|}
\sum_{r \in \mathcal K_s} |\widehat\gamma_{srj}|.
Here \widehat\gamma_{srj} is a
second-stage coefficient. A zero or nonfinite response standard
deviation produces NA in that row.
get.beta.entropy returns this matrix when
second.stage = TRUE. Choose the graph cutoff on the
coefficient scale of the selected stage.
In UVarPro, s-dependence concerns which variables remain
informative about the local contrast induced by releasing
s. The graph summarizes their estimated contributions.
The theoretical motivation distinguishes releasing a noise variable, whose contrast is explained by associated signal variables, from releasing a signal variable, whose contrast can also involve associated noisy variables. This asymmetry motivates aggregating contributions across release tasks.
sdependent aligns rows to column names, adds zero rows
for missing release variables, and then clears the diagonal.
Column order determines vertex order before isolated vertices
are removed. It calculates the column scores
G_j = \sum_s I_{sj} and constructs
the binary adjacency matrix
A_{sj} = \mathbf{1}\{I_{sj} \ge t\}, \quad s \ne j,
\qquad A_{ss} = 0,
where t is threshold. Each retained connection is
one edge, with its plotted width determined by the corresponding
importance entry.
With directed = TRUE, A_{sj} = 1
creates the arrow s \longrightarrow j: predictor
j contributes to distinguishing the comparisons formed by
releasing s. Arrows describe predictive contributions.
Reciprocal arrows and cycles are allowed.
The out-degree of s counts thresholded entries in its row,
giving the number of predictors connected to its release tasks.
The in-degree of j counts thresholded entries in its column,
giving the number of release variables connected to that predictor.
Global importance G_j sums the unthresholded magnitudes.
Isolated vertices are removed. Global scores are calculated before thresholding and removal, so they include subthreshold contributions and contributions from rows whose vertices are later removed. The signal-designation quantile uses the remaining vertices.
A retained vertex enters signal.vars when its degree is
at least min.degree and its global importance is at least
the q.signal quantile of retained scores. For directed
graphs, these conditions use out-degree and column-sum importance.
The defaults require at least one outgoing edge and importance
at or above the upper quartile of nonisolated vertices, including
ties.
Signal vertices are blue; other retained vertices are gray.
A directed edge is blue when its source is designated as signal
and gray otherwise. Vertex size is
6 + 2 * log1p(imp.score). An edge with importance w
has width 1 + 3w/w_{\max}, where
w_{\max} is the largest plotted edge value. Widths
are rescaled for each plot.
threshold controls the displayed connections;
q.signal and min.degree control vertex highlighting.
Changing the edge cutoff can also change the quantile reference
set through vertex removal. Signal designations are exploratory.
The simulation in Examples contains four independent standard
normal variables w, x, y, and z, each
accompanied by five noisy copies with independent Gaussian errors
of variance 0.1. Two further features are
h_1 = w + x + \epsilon_{21} and
h_2 = y + z + \epsilon_{22}, with
independent Gaussian errors of variance 0.4. All errors are
independent of the four source variables. All 26 features,
including the sources, are supplied to uvarpro.
The signal set is \{w, x, y, z\}:
conditioning on these four variables makes the other 22 features
mutually independent. Releasing any of wi.1, ...,
wi.5 has w as its s-dependent signal;
the xi, yi, and zi blocks similarly point
to x, y, and z. Releasing h1 has
s-dependent signals w and x, while releasing
h2 has y and z.
When a source such as w is released, its noisy copies and
h1 can help predict local membership; x can also
contribute by accounting for its component of h1. The
graph can therefore contain connections in both directions.
Repeated contributions across the noisy-feature releases motivate
the column-sum ranking of the four signals.
The example uses pre.filter = FALSE to keep all 26 features
eligible as predictors and release variables. It compares the
estimated importance entries and graph connections with this known
dependence structure.
A custom entropy function accepts xC, xO,
and .... The first two arguments contain the scaled
complementary and original features, with the released feature
first. Additional information includes compMembership,
oobMembership, the processed data, and
xvar.names.
Return a numeric rule score, or a list containing the score first
and auxiliary information second. For subsequent use of
get.beta.entropy, supply
list(comp = compMembership, oob = oobMembership) as the
second element, preserving that order. See Examples.
uvarpro returns an object of class "uvarpro". Its
principal components are:
The forest used to generate rules.
Rule-level information with columns tree,
branch, variable, n.oob, and imp.
The variable index refers to xvar.names. Unevaluated
comparisons have NA importance.
The complete-case, hot-encoded feature data, before the scaling used in scoring or local regressions.
Processed and original feature names, respectively.
With the default scoring function, membership pairs
grouped by released variable. Each pair contains comp
and oob observation indices into x. May be
NULL when no usable memberships are available. Custom
callbacks can change the auxiliary information stored here.
Rule extraction limits.
The value "unsupv", irrespective of the
forest construction method.
get.beta.entropy returns a named numeric matrix, with released
variables as rows and eligible predictors as columns. Entries are
mean absolute logistic-lasso coefficients when
second.stage = FALSE, and response-scaled mean absolute
linear-lasso coefficients when second.stage = TRUE. Rows
with no retained comparisons are omitted. Returns NULL if
no usable release rows remain.
sdependent invisibly returns a list with components:
Names of vertices meeting both the degree and importance-quantile conditions. Can be empty even when the graph is nonempty.
Named global column-sum scores for nonisolated vertices, sorted in increasing order.
Named degrees for nonisolated vertices, in graph
vertex order. These are out-degrees when directed = TRUE.
If no edges remain, sdependent returns a character diagnostic.
See Note.
For reproducible serial lasso calculations, call set.seed
immediately before get.beta.entropy(o, papply = lapply) to
control its randomized cross-validation folds. Seed forest
construction separately. Small local classes, degenerate predictors,
and excessive pre-filtering can leave no usable comparisons.
Individual lasso errors are caught and warnings suppressed.
For an undirected display, supply a symmetric importance matrix.
For square input with matching row and column order,
pmax(I, t(I)) retains either direction. Symmetrization can
change global scores and signal designations. Undirected edge
colors use the first endpoint in the internal edge list.
To admit weaker positive edges in an empty graph, decrease
threshold. Keep the cutoff positive, since >= admits
zero-valued entries at a zero cutoff. Check for NULL lasso
output before graph construction.
Min Lu and Hemant Ishwaran
Tang F. and Ishwaran H. (2017). Random forest missing data algorithms. Statistical Analysis and Data Mining, 10:363-377.
Zhou L., Lu M. and Ishwaran H. (2026). Variable priority for unsupervised variable selection. Pattern Recognition, 172:112727. \Sexpr[results=rd]{tools:::Rd_expr_doi("10.1016/j.patcog.2025.112727")}.
varpro, importance,
cv.glmnet,
glmnet,
scale,
graph_from_adjacency_matrix
## Small forest for a quick check
set.seed(1)
o <- uvarpro(mtcars, ntree = 1)
## ------------------------------------------------------------
## Latent-variable model: known signals and s-dependence
## ------------------------------------------------------------
set.seed(21)
n <- 1000
w <- rnorm(n)
x <- rnorm(n)
y <- rnorm(n)
z <- rnorm(n)
ei <- matrix(rnorm(n * 20, sd = sqrt(.1)), ncol = 20)
e21 <- rnorm(n, sd = sqrt(.4))
e22 <- rnorm(n, sd = sqrt(.4))
wi <- w + ei[, 1:5]
xi <- x + ei[, 6:10]
yi <- y + ei[, 11:15]
zi <- z + ei[, 16:20]
h1 <- w + x + e21
h2 <- y + z + e22
dta <- data.frame(w = w, wi = wi, x = x, xi = xi,
y = y, yi = yi, z = z, zi = zi, h1 = h1, h2 = h2)
signal <- c("w", "x", "y", "z")
## All 26 columns are observed features. The remaining 22 are
## mutually independent conditional on the four signal variables.
o <- uvarpro(dta)
print(importance(o))
## Retain every feature for the local lasso analysis, including
## the noisy-copy and mixed-variable releases that reveal signals.
set.seed(22)
beta <- get.beta.entropy(o, pre.filter = FALSE,
papply = lapply, nfolds = 5)
if (!is.null(beta)) {
## Compare the column-sum ranking with the known signal set.
score <- sort(colSums(beta), decreasing = TRUE)
print(data.frame(variable = names(score),
importance = unname(score),
signal = names(score) %in% signal))
## Inspect one copy from each block and the two mixed variables.
## Population targets: each copy points to its source; h1 points
## to w and x, and h2 to y and z. See Details for source releases.
release <- c("wi.1", "xi.1", "yi.1", "zi.1", "h1", "h2")
release <- intersect(release, rownames(beta))
print(round(beta[release, signal, drop = FALSE], 2))
## Inspect the corresponding adjacency block.
## A[s, j] = 1 gives the arrow s -> j.
threshold <- 0.25
A <- 1L * (beta[release, signal, drop = FALSE] >= threshold)
print(A)
if (all(is.finite(beta)) &&
requireNamespace("igraph", quietly = TRUE)) {
set.seed(23)
## Pass the lasso matrix directly; row alignment is automatic.
graph.info <- sdependent(beta, threshold = threshold, layout = "fr",
title = "Latent model: s-dependence")
if (is.list(graph.info)) {
print(graph.info$signal.vars)
print(graph.info$degree)
} else {
print(graph.info)
}
## Return graph summaries without drawing the figure:
## graph.info <- sdependent(beta, threshold = threshold, plot = FALSE)
}
}
## Optional second-stage lasso, keeping the same feature set.
## set.seed(22)
## beta.second <- get.beta.entropy(o, pre.filter = FALSE,
## second.stage = TRUE,
## papply = lapply, nfolds = 5)
## ------------------------------------------------------------
## Boston housing: alternative forest and categorical features
## ------------------------------------------------------------
if (requireNamespace("mlbench", quietly = TRUE)) {
data(BostonHousing, package = "mlbench")
## Every column is treated as a feature; no response is specified.
o.unsupv <- uvarpro(BostonHousing, method = "unsupv")
print(importance(o.unsupv))
## Random splitting uses the same subsequent analysis.
## o.random <- uvarpro(BostonHousing, method = "rnd")
Boston <- BostonHousing
Boston$zn <- factor(Boston$zn)
Boston$chas <- factor(Boston$chas)
Boston$lstat <- factor(round(0.2 * Boston$lstat))
Boston$nox <- factor(round(20 * Boston$nox))
Boston$rm <- factor(round(Boston$rm))
o.factor <- uvarpro(Boston)
print(importance(o.factor))
print(get.orgvimp(o.factor))
print(get.orgvimp(o.factor, pretty = FALSE))
}
## ------------------------------------------------------------
## Iowa housing: larger lasso analysis
## ------------------------------------------------------------
data(housing, package = "randomForestSRC")
iowa <- roughfix(housing)
## Numeric coding below is only a speed-oriented illustration;
## retain factors to use the usual hot-encoding instead.
iowa <- data.frame(data.matrix(iowa))
o.iowa <- uvarpro(iowa, ntree = 50, max.tree = 50,
max.rules.tree = 5)
set.seed(22)
beta.iowa <- get.beta.entropy(o.iowa, papply = lapply, nfolds = 5)
if (!is.null(beta.iowa)) {
print(sort(colMeans(beta.iowa), decreasing = TRUE))
## sdependent(beta.iowa)
}
## ------------------------------------------------------------
## Custom scoring callback: reproduces the default dispersion ratio
## ------------------------------------------------------------
my.entropy <- function(xC, xO, ...) {
mean.sd <- function(x) mean(apply(x, 2, sd, na.rm = TRUE))
imp <- 0.5 * (mean.sd(xC) + mean.sd(xO)) /
mean.sd(rbind(xC, xO))
dots <- list(...)
list(imp = imp,
membership = list(comp = dots$compMembership,
oob = dots$oobMembership))
}
if (requireNamespace("mlbench", quietly = TRUE)) {
data(BostonHousing, package = "mlbench")
o.custom <- uvarpro(BostonHousing, entropy = my.entropy,
ntree = 50, max.tree = 50, max.rules.tree = 5)
print(importance(o.custom))
## you can still run the lasso if you want
## beta.custom <- get.beta.entropy(o.custom, papply = lapply)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.