Skip to content

Commit c9f11c3

Browse files
committed
functions pen, deriv; add penalty atan, exp, lsp, lq; update vignette and readme
1 parent 0de1677 commit c9f11c3

18 files changed

Lines changed: 847 additions & 120 deletions

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ LinkingTo:
4141
RoxygenNote: 7.3.3
4242
RdMacros: Rdpack
4343
Suggests:
44+
ggforce,
4445
knitr,
4546
MASS,
4647
rmarkdown

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# Generated by roxygen2: do not edit by hand
22

33
S3method(plot,grasps)
4+
export(deriv)
45
export(gen_prec_sbm)
56
export(grasps)
7+
export(pen)
68
export(sparsify_block_banded)
79
import(ggplot2)
810
importFrom(Rcpp,evalCpp)

R/deriv.R

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#' Derivative Computation
2+
#'
3+
#' @description
4+
#' Compute the derivative of the penalty function.
5+
#'
6+
#' @param omega A numeric scalar or vector at which the penalty is evaluated.
7+
#'
8+
#' @param penalty A character vector specifying one or more penalty types.
9+
#' Available options include:
10+
#' \enumerate{
11+
#' \item "lasso": Least absolute shrinkage and selection operator
12+
#' \insertCite{tibshirani1996regression,friedman2008sparse}{grasps}.
13+
#' \item "atan": Arctangent type penalty \insertCite{wang2016variable}{grasps}.
14+
#' \item "exp": Exponential type penalty \insertCite{wang2018variable}{grasps}.
15+
#' \item "lq": Lq penalty \insertCite{frank1993statistical,fu1998penalized,fan2001variable}{grasps}.
16+
#' \item "lsp": Log-sum penalty \insertCite{candes2008enhancing}{grasps}.
17+
#' \item "mcp": Minimax concave penalty \insertCite{zhang2010nearly}{grasps}.
18+
#' \item "scad": Smoothly clipped absolute deviation \insertCite{fan2001variable,fan2009network}{grasps}.
19+
#' }
20+
#'
21+
#' @param lambda A non-negative scalar or vector of the same length as
22+
#' \code{penalty} specifying the regularization parameter.
23+
#'
24+
#' @param gamma A scalar or vector of the same length as \code{penalty}
25+
#' specifying the additional parameter for the penalty function.
26+
#' The defaults are:
27+
#' \enumerate{
28+
#' \item "atan": 0.005
29+
#' \item "exp": 0.01
30+
#' \item "lq": 0.5
31+
#' \item "lsp": 0.1
32+
#' \item "mcp": 3
33+
#' \item "scad": 3.7
34+
#' }
35+
#'
36+
#' @return
37+
#' A data frame containing:
38+
#' \describe{
39+
#' \item{omega}{The input \code{omega} values.}
40+
#' \item{penalty}{The penalty type for each row.}
41+
#' \item{lambda}{The regularization parameter used.}
42+
#' \item{gamma}{The additional penalty parameter used.}
43+
#' \item{value}{The computed derivative value.}
44+
#' }
45+
#'
46+
#' @references
47+
#' \insertAllCited{}
48+
#'
49+
#' @export
50+
51+
deriv <- function(omega, penalty, lambda, gamma = NULL) {
52+
53+
n <- length(penalty)
54+
if (length(lambda) == 1) {
55+
lambda <- rep(lambda, n)
56+
}
57+
if (length(gamma) == 1) {
58+
gamma <- rep(gamma, n)
59+
}
60+
61+
res <- do.call(rbind, lapply(seq_len(n), function(k) {
62+
deriv_internal(omega = omega, penalty = penalty[k], lambda = lambda[k], gamma = gamma[k])
63+
}))
64+
res <- as.data.frame(res)
65+
return(res)
66+
}
67+
68+
69+
#' @noRd
70+
71+
deriv_internal <- function(omega, penalty, lambda, gamma) {
72+
73+
if (!(penalty %in% c("lasso", "atan", "exp", "lq", "lsp", "mcp", "scad"))) {
74+
stop('Error in `penalty`!\nAvailable options: "lasso", "atan", "exp", "lq", "lsp", "mcp", "scad".')
75+
}
76+
77+
if (lambda < 0) {
78+
stop('The parameter `lambda` must be non-negative!')
79+
}
80+
81+
## default gamma by penalty
82+
if (missing(gamma) || is.null(gamma)) {
83+
gamma <- switch(penalty,
84+
"atan" = 0.005, "exp" = 0.01, "lq" = 0.5,
85+
"lsp" = 0.1, "mcp" = 3, "scad" = 3.7, NA)
86+
}
87+
88+
a <- abs(omega)
89+
90+
if (penalty == "atan") {
91+
if (gamma <= 0) {
92+
warning(sprintf('For "%s", typically `gamma` > 0.', penalty), call. = FALSE)
93+
}
94+
res <- lambda * gamma * (gamma + 2/pi) / (gamma^2 + a^2)
95+
96+
} else if (penalty == "exp") {
97+
if (gamma <= 0) {
98+
warning(sprintf('For "%s", typically `gamma` > 0.', penalty), call. = FALSE)
99+
}
100+
res <- (lambda / gamma) * exp(-a / gamma)
101+
102+
} else if (penalty == "lasso") {
103+
res <- lambda
104+
105+
} else if (penalty == "lq") {
106+
if (gamma <= 0 || gamma >= 1) {
107+
warning(sprintf('For "%s", typically 0 < `gamma` < 1.', penalty), call. = FALSE)
108+
}
109+
epsilon <- 1e-10
110+
res <- lambda * gamma * ((a + epsilon)^(gamma - 1))
111+
# res <- lambda * gamma * (pmax(a, epsilon)^(gamma - 1))
112+
# res <- lambda * gamma * (a^(gamma - 1)); res[a == 0] <- 0
113+
114+
} else if (penalty == "lsp") {
115+
if (gamma <= 0) {
116+
warning(sprintf('For "%s", typically `gamma` > 0.', penalty), call. = FALSE)
117+
}
118+
res <- lambda / (gamma + a)
119+
120+
} else if (penalty == "mcp") {
121+
if (gamma <= 1) {
122+
warning(sprintf('For "%s", typically `gamma` > 1.', penalty), call. = FALSE)
123+
}
124+
res <- (lambda - a/gamma) * (a <= gamma*lambda)
125+
126+
} else if (penalty == "scad") {
127+
if (gamma <= 2) {
128+
warning(sprintf('For "%s", typically `gamma` > 2.', penalty), call. = FALSE)
129+
}
130+
res <- lambda * (a <= lambda) +
131+
pmax(gamma*lambda - a, 0) / (gamma - 1) * (a > lambda)
132+
}
133+
134+
return(data.frame(omega = omega, penalty = penalty, lambda = lambda, gamma = gamma, value = res))
135+
}

R/grasps.R

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@
1818
#'
1919
#' @param penalty A character string specifying the penalty for estimating
2020
#' precision matrix. Available options include: \enumerate{
21-
#' \item "adapt": adaptive lasso \insertCite{zou2006adaptive,fan2009network}{grasps}.
22-
#' \item "lasso": lasso \insertCite{tibshirani1996regression,friedman2008sparse}{grasps}.
23-
#' \item "mcp": minimax concave penalty \insertCite{zhang2010nearly}{grasps}.
24-
#' \item "scad": smoothly clipped absolute deviation \insertCite{fan2001variable,fan2009network}{grasps}.
21+
#' \item "lasso": Least absolute shrinkage and selection operator
22+
#' \insertCite{tibshirani1996regression,friedman2008sparse}{grasps}.
23+
#' \item "adapt": Adaptive lasso \insertCite{zou2006adaptive,fan2009network}{grasps}.
24+
#' \item "atan": Arctangent type penalty \insertCite{wang2016variable}{grasps}.
25+
#' \item "exp": Exponential type penalty \insertCite{wang2018variable}{grasps}.
26+
#' \item "lq": Lq penalty \insertCite{frank1993statistical,fu1998penalized,fan2001variable}{grasps}.
27+
#' \item "lsp": Log-sum penalty \insertCite{candes2008enhancing}{grasps}.
28+
#' \item "mcp": Minimax concave penalty \insertCite{zhang2010nearly}{grasps}.
29+
#' \item "scad": Smoothly clipped absolute deviation \insertCite{fan2001variable,fan2009network}{grasps}.
2530
#' }
2631
#'
2732
#' @param diag.ind A boolean (default = TRUE) specifying whether to penalize
@@ -44,9 +49,13 @@
4449
#' corresponds to the group penalty only. The default values is a sequence from
4550
#' 0.05 to 0.95 with increments of 0.05.
4651
#'
47-
#' @param gamma A scalar specifying the hyperparameter for the chosen
52+
#' @param gamma A scalar specifying the additional parameter for the chosen
4853
#' \code{penalty}. Default values: \enumerate{
4954
#' \item "adapt": 0.5
55+
#' \item "atan": 0.005
56+
#' \item "exp": 0.01
57+
#' \item "lq": 0.5
58+
#' \item "lsp": 0.1
5059
#' \item "mcp": 3
5160
#' \item "scad": 3.7
5261
#' }
@@ -162,33 +171,45 @@ grasps <- function(X, n = nrow(X), membership, penalty,
162171
if (length(membership) != d) {
163172
stop('The length of `membership` must equal the column dimension of `X`!')
164173
}
165-
if (!penalty %in% c("lasso", "adapt", "mcp", "scad")) {
166-
stop('Error in `penalty`!\nAvailable options: "lasso", "adapt", "mcp", "scad".')
174+
175+
if (!(penalty %in% c("lasso", "adapt", "atan", "exp", "lq", "lsp", "mcp", "scad"))) {
176+
stop('Error in `penalty`!
177+
Available options: "lasso", "adapt", "atan", "exp", "lq", "lsp", "mcp", "scad".')
167178
}
168-
if (!crit %in% c("AIC", "BIC", "EBIC", "HBIC", "CV")) {
169-
stop('Error in `crit`!\nAvailable options: "AIC", "BIC", "EBIC", "HBIC", "CV".')
179+
180+
if (!(crit %in% c("AIC", "BIC", "EBIC", "HBIC", "CV"))) {
181+
stop('Error in `crit`!
182+
Available options: "AIC", "BIC", "EBIC", "HBIC", "CV".')
170183
}
184+
171185
if (!all(lambda > 0)) {
172186
stop('The parameter `lambda` must be positive!')
173187
}
188+
174189
if (!all(alpha >= 0 & alpha <= 1)) {
175190
stop('The parameter `alpha` must be in [0,1]!')
176191
}
192+
177193
if (rho <= 0) {
178194
stop('The parameter `rho` must be positive!')
179195
}
196+
180197
if (tau.incr <= 1) {
181198
stop('The parameter `tau.incr` must be greater than 1!')
182199
}
200+
183201
if (tau.decr <= 1) {
184202
stop('The parameter `tau.decr` must be greater than 1!')
185203
}
204+
186205
if (nu <= 1) {
187206
stop('The parameter `nu` must be greater than 1!')
188207
}
208+
189209
if (tol.abs <= 0) {
190210
stop('The parameter `tol.abs` must be positive!')
191211
}
212+
192213
if (tol.rel <= 0) {
193214
stop('The parameter `tol.rel` must be positive!')
194215
}
@@ -252,16 +273,11 @@ grasps <- function(X, n = nrow(X), membership, penalty,
252273
parameter <- data.frame(alpha = alpha, lambda = lambda)
253274
}
254275

255-
if (is.null(gamma)) {
256-
if (penalty == "adapt") {
257-
gamma <- 0.5
258-
} else if (penalty == "mcp") {
259-
gamma <- 3
260-
} else if (penalty == "scad") {
261-
gamma <- 3.7
262-
} else {
263-
gamma <- NA
264-
}
276+
## default gamma by penalty
277+
if (missing(gamma) || is.null(gamma)) {
278+
gamma <- switch(penalty,
279+
"adapt" = 0.5, "atan" = 0.005, "exp" = 0.01, "lq" = 0.5,
280+
"lsp" = 0.1, "mcp" = 3, "scad" = 3.7, NA)
265281
}
266282

267283
if (nrow(parameter) > 1) {

R/pen.R

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#' Penalty Function Computation
2+
#'
3+
#' @description
4+
#' Compute the penalty function.
5+
#'
6+
#' @param omega A numeric scalar or vector at which the penalty is evaluated.
7+
#'
8+
#' @param penalty A character vector specifying one or more penalty types.
9+
#' Available options include:
10+
#' \enumerate{
11+
#' \item "lasso": Least absolute shrinkage and selection operator
12+
#' \insertCite{tibshirani1996regression,friedman2008sparse}{grasps}.
13+
#' \item "atan": Arctangent type penalty \insertCite{wang2016variable}{grasps}.
14+
#' \item "exp": Exponential type penalty \insertCite{wang2018variable}{grasps}.
15+
#' \item "lq": Lq penalty \insertCite{frank1993statistical,fu1998penalized,fan2001variable}{grasps}.
16+
#' \item "lsp": Log-sum penalty \insertCite{candes2008enhancing}{grasps}.
17+
#' \item "mcp": Minimax concave penalty \insertCite{zhang2010nearly}{grasps}.
18+
#' \item "scad": Smoothly clipped absolute deviation \insertCite{fan2001variable,fan2009network}{grasps}.
19+
#' }
20+
#'
21+
#' @param lambda A non-negative scalar or vector of the same length as
22+
#' \code{penalty} specifying the regularization parameter.
23+
#'
24+
#' @param gamma A scalar or vector of the same length as \code{penalty}
25+
#' specifying the additional parameter for the penalty function.
26+
#' The defaults are:
27+
#' \enumerate{
28+
#' \item "atan": 0.005
29+
#' \item "exp": 0.01
30+
#' \item "lq": 0.5
31+
#' \item "lsp": 0.1
32+
#' \item "mcp": 3
33+
#' \item "scad": 3.7
34+
#' }
35+
#'
36+
#' @return
37+
#' A data frame containing:
38+
#' \describe{
39+
#' \item{omega}{The input \code{omega} values.}
40+
#' \item{penalty}{The penalty type for each row.}
41+
#' \item{lambda}{The regularization parameter used.}
42+
#' \item{gamma}{The additional penalty parameter used.}
43+
#' \item{value}{The computed penalty value.}
44+
#' }
45+
#'
46+
#' @references
47+
#' \insertAllCited{}
48+
#'
49+
#' @export
50+
51+
pen <- function(omega, penalty, lambda, gamma = NULL) {
52+
53+
n <- length(penalty)
54+
if (length(lambda) == 1) {
55+
lambda <- rep(lambda, n)
56+
}
57+
if (length(gamma) == 1) {
58+
gamma <- rep(gamma, n)
59+
}
60+
61+
res <- do.call(rbind, lapply(seq_len(n), function(k) {
62+
pen_internal(omega = omega, penalty = penalty[k], lambda = lambda[k], gamma = gamma[k])
63+
}))
64+
res <- as.data.frame(res)
65+
return(res)
66+
}
67+
68+
69+
#' @noRd
70+
71+
pen_internal <- function(omega, penalty, lambda, gamma) {
72+
73+
if (!(penalty %in% c("lasso", "atan", "exp", "lq", "lsp", "mcp", "scad"))) {
74+
stop('Error in `penalty`!\nAvailable options: "lasso", "atan", "exp", "lq", "lsp", "mcp", "scad".')
75+
}
76+
77+
if (lambda < 0) {
78+
stop('The parameter `lambda` must be non-negative!')
79+
}
80+
81+
## default gamma by penalty
82+
if (missing(gamma) || is.null(gamma)) {
83+
gamma <- switch(penalty,
84+
"atan" = 0.005, "exp" = 0.01, "lq" = 0.5,
85+
"lsp" = 0.1, "mcp" = 3, "scad" = 3.7, NA)
86+
}
87+
88+
a <- abs(omega)
89+
90+
if (penalty == "atan") {
91+
if (gamma <= 0) {
92+
warning(sprintf('For "%s", typically `gamma` > 0.', penalty), call. = FALSE)
93+
}
94+
res <- lambda * (gamma + 2/pi) * atan(a / gamma)
95+
96+
} else if (penalty == "exp") {
97+
if (gamma <= 0) {
98+
warning(sprintf('For "%s", typically `gamma` > 0.', penalty), call. = FALSE)
99+
}
100+
res <- lambda * (1 - exp(-a / gamma))
101+
102+
} else if (penalty == "lasso") {
103+
res <- lambda * a
104+
105+
} else if (penalty == "lq") {
106+
if (gamma <= 0 || gamma >= 1) {
107+
warning(sprintf('For "%s", typically 0 < `gamma` < 1.', penalty), call. = FALSE)
108+
}
109+
epsilon <- 1e-10
110+
res <- lambda * ((a + epsilon)^gamma)
111+
# res <- lambda * (pmax(a, epsilon)^gamma)
112+
# res <- lambda * (a^gamma)
113+
114+
} else if (penalty == "lsp") {
115+
if (gamma <= 0) {
116+
warning(sprintf('For "%s", typically `gamma` > 0.', penalty), call. = FALSE)
117+
}
118+
res <- lambda * log1p(a / gamma)
119+
120+
} else if (penalty == "mcp") {
121+
if (gamma <= 1) {
122+
warning(sprintf('For "%s", typically `gamma` > 1.', penalty), call. = FALSE)
123+
}
124+
res <- (lambda * a - a^2/(2*gamma)) * (a <= gamma * lambda) +
125+
(0.5 * gamma * lambda^2) * (a > gamma * lambda)
126+
127+
} else if (penalty == "scad") {
128+
if (gamma <= 2) {
129+
warning(sprintf('For "%s", typically `gamma` > 2.', penalty), call. = FALSE)
130+
}
131+
res <- lambda * a * (a <= lambda) +
132+
(2 * gamma * lambda * a - a^2 - lambda^2) / (2 * (gamma-1)) * (lambda < a & a <= gamma * lambda) +
133+
lambda^2 * (gamma+1) / 2 * (a > gamma*lambda)
134+
}
135+
136+
return(data.frame(omega = omega, penalty = penalty, lambda = lambda, gamma = gamma, value = res))
137+
}

0 commit comments

Comments
 (0)