├── .github ├── .gitignore └── workflows │ ├── pkgdown.yaml │ ├── test-coverage.yaml │ ├── R-CMD-check.yaml │ └── pr-commands.yaml ├── src ├── .gitignore ├── Makevars ├── Makevars.win ├── CoordDescTypes.h ├── XrnetUtils.h ├── XrnetUtils.cpp ├── DataFunctions.h ├── GaussianSolver.h └── Xrnet.h ├── revdep ├── failures.md ├── problems.md ├── .gitignore ├── email.yml ├── cran.md └── README.md ├── .gitattributes ├── docs ├── logo.png ├── favicon.ico ├── favicon-16x16.png ├── favicon-32x32.png ├── apple-touch-icon.png ├── reference │ ├── Rplot001.png │ ├── Rplot002.png │ ├── figures │ │ ├── eqn1.gif │ │ ├── eqn2.gif │ │ ├── eqn3.gif │ │ ├── eqn4.gif │ │ ├── logo.png │ │ ├── cv_results-1.png │ │ └── plot-error-curve-1.png │ ├── tune_xrnet-1.png │ ├── plot.tune_xrnet-1.png │ ├── plot.tune_xrnet-2.png │ ├── y_linear.html │ ├── ext_linear.html │ └── x_linear.html ├── apple-touch-icon-60x60.png ├── apple-touch-icon-76x76.png ├── apple-touch-icon-120x120.png ├── apple-touch-icon-152x152.png ├── apple-touch-icon-180x180.png ├── pkgdown.yml ├── link.svg ├── bootstrap-toc.css ├── docsearch.js ├── pkgdown.js ├── articles │ └── index.html ├── bootstrap-toc.js ├── 404.html ├── cran_comments.html ├── authors.html ├── CODE_OF_CONDUCT.html ├── news │ └── index.html └── pkgdown.css ├── .gitignore ├── man ├── figures │ ├── eqn1.gif │ ├── eqn2.gif │ ├── eqn3.gif │ ├── eqn4.gif │ ├── logo.png │ ├── cv_results-1.png │ └── plot-error-curve-1.png ├── y_linear.Rd ├── ext_linear.Rd ├── x_linear.Rd ├── define_ridge.Rd ├── define_lasso.Rd ├── define_enet.Rd ├── xrnet_control.Rd ├── coef.xrnet.Rd ├── plot.tune_xrnet.Rd ├── coef.tune_xrnet.Rd ├── predict.xrnet.Rd ├── predict.tune_xrnet.Rd ├── define_penalty.Rd ├── tune_xrnet.Rd └── xrnet.Rd ├── _pkgdown.yml ├── data └── GaussianExample.rda ├── pkgdown └── favicon │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── apple-touch-icon.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ └── apple-touch-icon-180x180.png ├── tests ├── testthat.R └── testthat │ ├── testdata │ ├── path_en.rds │ ├── xtest.rds │ ├── ytest.rds │ ├── ztest.rds │ ├── b0_glmnet.rds │ ├── betas_glmnet.rds │ ├── path_lasso.rds │ ├── path_ridge.rds │ ├── alphas_cvx_mat.rds │ ├── betas_binomial.rds │ ├── betas_cvx_auto.rds │ ├── betas_cvx_mat.rds │ ├── xtest_binomial.rds │ ├── ytest_binomial.rds │ ├── alphas_cvx_auto.rds │ ├── cv_checks │ │ ├── cv_mean.rds │ │ ├── foldid.rds │ │ ├── cv_auc_glmnet.rds │ │ ├── cv_mae_glmnet.rds │ │ ├── foldid_binomial.rds │ │ └── cv_deviance_glmnet.rds │ └── lambda_binomial.rds │ ├── helper-soln-glmnet-binomial.R │ ├── helper-soln-match-CVX.R │ ├── helper-soln-match-cvx-auto.R │ ├── helper-CV-errors.R │ ├── helper-soln-glmnet-noext.R │ ├── helper.R │ ├── test-CV-errors.R │ ├── test-predict-function.R │ └── test-user-input.R ├── CRAN-SUBMISSION ├── cran-comments.md ├── codecov.yml ├── .Rbuildignore ├── R ├── data.R ├── RcppExports.R ├── coef_xrnet.R ├── coef_tune_xrnet.R ├── predict_tune_xrnet.R ├── plot_tune_xrnet.R └── predict_xrnet.R ├── xrnet.Rproj ├── NAMESPACE ├── NEWS.md ├── DESCRIPTION ├── CODE_OF_CONDUCT.md └── paper ├── paper.bib └── paper.md /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.dll 4 | -------------------------------------------------------------------------------- /revdep/failures.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /src/Makevars: -------------------------------------------------------------------------------- 1 | PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) 2 | -------------------------------------------------------------------------------- /src/Makevars.win: -------------------------------------------------------------------------------- 1 | PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | data/* binary 3 | src/* text=lf 4 | R/* text=lf 5 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | inst/doc 6 | docs 7 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /man/figures/eqn1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/man/figures/eqn1.gif -------------------------------------------------------------------------------- /man/figures/eqn2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/man/figures/eqn2.gif -------------------------------------------------------------------------------- /man/figures/eqn3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/man/figures/eqn3.gif -------------------------------------------------------------------------------- /man/figures/eqn4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/man/figures/eqn4.gif -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/man/figures/logo.png -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://uscbiostats.github.io/xrnet/ 2 | template: 3 | bootstrap: 5 4 | 5 | -------------------------------------------------------------------------------- /docs/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/favicon-16x16.png -------------------------------------------------------------------------------- /docs/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/favicon-32x32.png -------------------------------------------------------------------------------- /data/GaussianExample.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/data/GaussianExample.rda -------------------------------------------------------------------------------- /docs/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/reference/Rplot001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/Rplot001.png -------------------------------------------------------------------------------- /docs/reference/Rplot002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/Rplot002.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /man/figures/cv_results-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/man/figures/cv_results-1.png -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | Sys.setenv("R_TESTS" = "") 2 | library(testthat) 3 | library(xrnet) 4 | test_check("xrnet") 5 | -------------------------------------------------------------------------------- /CRAN-SUBMISSION: -------------------------------------------------------------------------------- 1 | Version: 1.0.1 2 | Date: 2025-12-17 08:44:03 UTC 3 | SHA: 340e03d9e054138c08944f7c137903f7b9782ac0 4 | -------------------------------------------------------------------------------- /docs/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /docs/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /docs/reference/figures/eqn1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/figures/eqn1.gif -------------------------------------------------------------------------------- /docs/reference/figures/eqn2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/figures/eqn2.gif -------------------------------------------------------------------------------- /docs/reference/figures/eqn3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/figures/eqn3.gif -------------------------------------------------------------------------------- /docs/reference/figures/eqn4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/figures/eqn4.gif -------------------------------------------------------------------------------- /docs/reference/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/figures/logo.png -------------------------------------------------------------------------------- /docs/reference/tune_xrnet-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/tune_xrnet-1.png -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Submission 2 | 3 | This is a patch version change to update the primary author's email address. 4 | -------------------------------------------------------------------------------- /docs/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /docs/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /docs/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /man/figures/plot-error-curve-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/man/figures/plot-error-curve-1.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /tests/testthat/testdata/path_en.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/path_en.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/xtest.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/xtest.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/ytest.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/ytest.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/ztest.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/ztest.rds -------------------------------------------------------------------------------- /docs/reference/plot.tune_xrnet-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/plot.tune_xrnet-1.png -------------------------------------------------------------------------------- /docs/reference/plot.tune_xrnet-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/plot.tune_xrnet-2.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | checks 2 | library 3 | checks.noindex 4 | library.noindex 5 | data.sqlite 6 | *.html 7 | cloud.noindex 8 | -------------------------------------------------------------------------------- /tests/testthat/testdata/b0_glmnet.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/b0_glmnet.rds -------------------------------------------------------------------------------- /docs/reference/figures/cv_results-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/figures/cv_results-1.png -------------------------------------------------------------------------------- /tests/testthat/testdata/betas_glmnet.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/betas_glmnet.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/path_lasso.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/path_lasso.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/path_ridge.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/path_ridge.rds -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /revdep/email.yml: -------------------------------------------------------------------------------- 1 | release_date: ??? 2 | rel_release_date: ??? 3 | my_news_url: ??? 4 | release_version: ??? 5 | release_details: ??? 6 | -------------------------------------------------------------------------------- /tests/testthat/testdata/alphas_cvx_mat.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/alphas_cvx_mat.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/betas_binomial.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/betas_binomial.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/betas_cvx_auto.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/betas_cvx_auto.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/betas_cvx_mat.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/betas_cvx_mat.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/xtest_binomial.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/xtest_binomial.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/ytest_binomial.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/ytest_binomial.rds -------------------------------------------------------------------------------- /docs/reference/figures/plot-error-curve-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/docs/reference/figures/plot-error-curve-1.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /tests/testthat/testdata/alphas_cvx_auto.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/alphas_cvx_auto.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/cv_checks/cv_mean.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/cv_checks/cv_mean.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/cv_checks/foldid.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/cv_checks/foldid.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/lambda_binomial.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/lambda_binomial.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/cv_checks/cv_auc_glmnet.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/cv_checks/cv_auc_glmnet.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/cv_checks/cv_mae_glmnet.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/cv_checks/cv_mae_glmnet.rds -------------------------------------------------------------------------------- /tests/testthat/testdata/cv_checks/foldid_binomial.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/cv_checks/foldid_binomial.rds -------------------------------------------------------------------------------- /tests/testthat/helper-soln-glmnet-binomial.R: -------------------------------------------------------------------------------- 1 | betas_binomial <- readRDS("testdata/betas_binomial.rds") 2 | lambda_binomial <- readRDS("testdata/lambda_binomial.rds") 3 | -------------------------------------------------------------------------------- /tests/testthat/testdata/cv_checks/cv_deviance_glmnet.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/USCbiostats/xrnet/HEAD/tests/testthat/testdata/cv_checks/cv_deviance_glmnet.rds -------------------------------------------------------------------------------- /tests/testthat/helper-soln-match-CVX.R: -------------------------------------------------------------------------------- 1 | alphas_cvx_mat <- readRDS(file = "testdata/alphas_cvx_mat.rds") 2 | betas_cvx_mat <- readRDS(file = "testdata/betas_cvx_mat.rds") 3 | -------------------------------------------------------------------------------- /tests/testthat/helper-soln-match-cvx-auto.R: -------------------------------------------------------------------------------- 1 | alphas_cvx_auto <- readRDS(file = "testdata/alphas_cvx_auto.rds") 2 | betas_cvx_auto <- readRDS(file = "testdata/betas_cvx_auto.rds") 3 | -------------------------------------------------------------------------------- /revdep/cran.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 0 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 0 new problems 6 | * We failed to check 0 packages 7 | 8 | -------------------------------------------------------------------------------- /docs/pkgdown.yml: -------------------------------------------------------------------------------- 1 | pandoc: 3.1.11 2 | pkgdown: 2.0.9 3 | pkgdown_sha: ~ 4 | articles: {} 5 | last_built: 2024-07-09T03:47Z 6 | urls: 7 | reference: https://uscbiostats.github.io/xrnet/reference 8 | article: https://uscbiostats.github.io/xrnet/articles 9 | 10 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /src/CoordDescTypes.h: -------------------------------------------------------------------------------- 1 | #ifndef COORD_DESC_TYPES_H 2 | #define COORD_DESC_TYPES_H 3 | 4 | #include 5 | 6 | typedef Eigen::Map MapMat; 7 | typedef Eigen::MappedSparseMatrix MapSpMat; 8 | typedef Eigen::Map MapVec; 9 | 10 | #endif // COORD_DESC_TYPES_H 11 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^\.travis\.yml$ 4 | ^README\.Rmd$ 5 | ^README-.*\.png$ 6 | ^appveyor\.yml$ 7 | docs 8 | paper 9 | CODE_OF_CONDUCT.md 10 | cran-comments.md 11 | ^revdep$ 12 | ^CRAN-RELEASE$ 13 | ^\.github$ 14 | ^_pkgdown\.yml$ 15 | ^docs$ 16 | ^pkgdown$ 17 | ^codecov\.yml$ 18 | ^cran-comments\.md$ 19 | ^CRAN-SUBMISSION$ 20 | -------------------------------------------------------------------------------- /man/y_linear.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{y_linear} 5 | \alias{y_linear} 6 | \title{Simulated outcome data} 7 | \format{ 8 | A vector with 100 elements 9 | } 10 | \usage{ 11 | y_linear 12 | } 13 | \description{ 14 | Simulated outcome data 15 | } 16 | \keyword{datasets} 17 | -------------------------------------------------------------------------------- /R/data.R: -------------------------------------------------------------------------------- 1 | #' Simulated example data for hierarchical regularized linear regression 2 | #' 3 | #' @format A matrix with 100 rows and 50 variables 4 | "x_linear" 5 | 6 | #' Simulated outcome data 7 | #' 8 | #' @format A vector with 100 elements 9 | "y_linear" 10 | 11 | #' Simulated external data 12 | #' 13 | #' @format A matrix with 50 rows and 4 columns 14 | "ext_linear" 15 | -------------------------------------------------------------------------------- /man/ext_linear.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{ext_linear} 5 | \alias{ext_linear} 6 | \title{Simulated external data} 7 | \format{ 8 | A matrix with 50 rows and 4 columns 9 | } 10 | \usage{ 11 | ext_linear 12 | } 13 | \description{ 14 | Simulated external data 15 | } 16 | \keyword{datasets} 17 | -------------------------------------------------------------------------------- /tests/testthat/helper-CV-errors.R: -------------------------------------------------------------------------------- 1 | cv_mean <- readRDS(file = "testdata/cv_checks/cv_mean.rds") 2 | foldid <- readRDS(file = "testdata/cv_checks/foldid.rds") 3 | cv_mae <- readRDS(file = "testdata/cv_checks/cv_mae_glmnet.rds") 4 | foldid_binomial <- readRDS("testdata/cv_checks/foldid_binomial.rds") 5 | cv_auc <- readRDS(file = "testdata/cv_checks/cv_auc_glmnet.rds") 6 | cv_deviance <- readRDS(file = "testdata/cv_checks/cv_deviance_glmnet.rds") 7 | -------------------------------------------------------------------------------- /man/x_linear.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{x_linear} 5 | \alias{x_linear} 6 | \title{Simulated example data for hierarchical regularized linear regression} 7 | \format{ 8 | A matrix with 100 rows and 50 variables 9 | } 10 | \usage{ 11 | x_linear 12 | } 13 | \description{ 14 | Simulated example data for hierarchical regularized linear regression 15 | } 16 | \keyword{datasets} 17 | -------------------------------------------------------------------------------- /xrnet.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 4 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageCheckArgs: --no-multiarch 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /tests/testthat/helper-soln-glmnet-noext.R: -------------------------------------------------------------------------------- 1 | xtest <- readRDS(file = "testdata/xtest.rds") 2 | ytest <- readRDS(file = "testdata/ytest.rds") 3 | b0_glmnet <- readRDS(file = "testdata/b0_glmnet.rds") 4 | betas_glmnet <- readRDS(file = "testdata/betas_glmnet.rds") 5 | n <- length(ytest) 6 | sd_y <- sqrt(var(ytest) * (n - 1) / n) 7 | ytest_scaled <- ytest / sd_y 8 | test_control <- list(tolerance = 1e-15) 9 | path_ridge <- readRDS(file = "testdata/path_ridge.rds") 10 | path_lasso <- readRDS(file = "testdata/path_lasso.rds") 11 | path_en <- readRDS(file = "testdata/path_en.rds") 12 | -------------------------------------------------------------------------------- /tests/testthat/helper.R: -------------------------------------------------------------------------------- 1 | # linear data 2 | xtest <- readRDS(file = "testdata/xtest.rds") 3 | ytest <- readRDS(file = "testdata/ytest.rds") 4 | ztest <- readRDS(file = "testdata/ztest.rds") 5 | 6 | # scaled y 7 | n <- length(ytest) 8 | sd_y <- sqrt(var(ytest) * (n - 1) / n) 9 | ytest_scaled <- ytest / sd_y 10 | 11 | # sparse versions of features / external data 12 | xsparse <- Matrix::Matrix(xtest, sparse = TRUE) 13 | zsparse <- Matrix::Matrix(ztest, sparse = TRUE) 14 | 15 | # binary data 16 | xtest_binomial <- readRDS(file = "testdata/xtest_binomial.rds") 17 | ytest_binomial <- readRDS(file = "testdata/ytest_binomial.rds") 18 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Platform 2 | 3 | |field |value | 4 | |:--------|:----------------------------| 5 | |version |R version 4.4.1 (2024-06-14) | 6 | |os |macOS Sonoma 14.5 | 7 | |system |aarch64, darwin20 | 8 | |ui |X11 | 9 | |language |(EN) | 10 | |collate |en_US.UTF-8 | 11 | |ctype |en_US.UTF-8 | 12 | |tz |America/Los_Angeles | 13 | |date |2024-07-14 | 14 | |pandoc |NA | 15 | 16 | # Dependencies 17 | 18 | |package |old |new |Δ | 19 | |:-------|:---|:---|:--| 20 | 21 | # Revdeps 22 | 23 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(coef,tune_xrnet) 4 | S3method(coef,xrnet) 5 | S3method(plot,tune_xrnet) 6 | S3method(predict,tune_xrnet) 7 | S3method(predict,xrnet) 8 | export(define_enet) 9 | export(define_lasso) 10 | export(define_penalty) 11 | export(define_ridge) 12 | export(tune_xrnet) 13 | export(xrnet) 14 | export(xrnet_control) 15 | importFrom(Rcpp,sourceCpp) 16 | importFrom(bigmemory,attach.big.matrix) 17 | importFrom(bigmemory,describe) 18 | importFrom(bigmemory,is.big.matrix) 19 | importFrom(foreach,"%dopar%") 20 | importFrom(foreach,foreach) 21 | importFrom(grDevices,colorRampPalette) 22 | importFrom(graphics,axis) 23 | importFrom(graphics,filled.contour) 24 | importFrom(graphics,points) 25 | importFrom(methods,is) 26 | importFrom(stats,predict) 27 | useDynLib(xrnet, .registration = TRUE) 28 | -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # xrnet 1.0.1 2 | 3 | * Updates author metadata. 4 | 5 | # xrnet 1.0.0 6 | 7 | * Changes minimum supported R version 4.0. 8 | 9 | * Removes C++11 in SystemRequirements since it is guaranteed that R >= 4.0 that C++11 is minimum supported compiler. This also enables removing CXX_STD=CXX11 in Makevars and Makevars.win. 10 | 11 | * Fixes gcc-UBSAN errors detected in latest Rdevel checks. Note that these errors do not impact solutions. They occur when no external data matrix is provided and the underlying C++ code attempts to created a mapped matrix with a pointer to an empty matrix. Since the external data matrix is never used in this case, the resulting standard regularized regression with no external data still produces a correct solution. 12 | 13 | # xrnet 0.1.7 14 | 15 | * Patched release to fix tests on Solaris OS and removed test dependency on glmnet 16 | 17 | # xrnet 0.1.6 18 | 19 | * First release to CRAN 20 | 21 | * Initial release supports linear and logistic hierarchical regularized regression 22 | -------------------------------------------------------------------------------- /man/define_ridge.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/define_penalty.R 3 | \name{define_ridge} 4 | \alias{define_ridge} 5 | \title{Define ridge regularization object for predictor and external data} 6 | \usage{ 7 | define_ridge( 8 | num_penalty = 20, 9 | penalty_ratio = NULL, 10 | user_penalty = NULL, 11 | custom_multiplier = NULL 12 | ) 13 | } 14 | \arguments{ 15 | \item{num_penalty}{number of penalty values to fit in grid. Default is 20.} 16 | 17 | \item{penalty_ratio}{ratio between minimum and maximum penalty for x. 18 | Default is 1e-04 if \eqn{n > p} and 0.01 if \eqn{n <= p}.} 19 | 20 | \item{user_penalty}{user-defined vector of penalty values to use in penalty 21 | path.} 22 | 23 | \item{custom_multiplier}{variable-specific penalty multipliers to apply to 24 | overall penalty. Default is 1 for all variables. 0 is no penalization.} 25 | } 26 | \value{ 27 | A list object with regularization settings that are used to define 28 | the regularization for predictors or external data in \code{\link{xrnet}} and 29 | \code{\link{tune_xrnet}}. The list elements will match those returned by 30 | \code{\link{define_penalty}}, but with the penalty_type automatically set 31 | to 0. 32 | } 33 | \description{ 34 | Helper function to define a ridge penalty regularization object. 35 | See \code{define_penalty} for more details. 36 | } 37 | -------------------------------------------------------------------------------- /man/define_lasso.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/define_penalty.R 3 | \name{define_lasso} 4 | \alias{define_lasso} 5 | \title{Define lasso regularization object for predictor and external data} 6 | \usage{ 7 | define_lasso( 8 | num_penalty = 20, 9 | penalty_ratio = NULL, 10 | user_penalty = NULL, 11 | custom_multiplier = NULL 12 | ) 13 | } 14 | \arguments{ 15 | \item{num_penalty}{number of penalty values to fit in grid. Default is 20.} 16 | 17 | \item{penalty_ratio}{ratio between minimum and maximum penalty for x. 18 | Default is 1e-04 if \eqn{n > p} and 0.01 if \eqn{n <= p}.} 19 | 20 | \item{user_penalty}{user-defined vector of penalty values to use in penalty 21 | path.} 22 | 23 | \item{custom_multiplier}{variable-specific penalty multipliers to apply to 24 | overall penalty. 25 | Default is 1 for all variables. 0 is no penalization.} 26 | } 27 | \value{ 28 | A list object with regularization settings that are used to define 29 | the regularization 30 | for predictors or external data in \code{\link{xrnet}} and 31 | \code{\link{tune_xrnet}}. The list 32 | elements will match those returned by \code{\link{define_penalty}}, 33 | but with the penalty_type automatically set to 1. 34 | } 35 | \description{ 36 | Helper function to define a lasso penalty regularization object. 37 | See \code{define_penalty} for more details. 38 | } 39 | -------------------------------------------------------------------------------- /R/RcppExports.R: -------------------------------------------------------------------------------- 1 | # Generated by using Rcpp::compileAttributes() -> do not edit by hand 2 | # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | computeResponseRcpp <- function(X, mattype_x, Fixed, beta0, betas, gammas, response_type, family) { 5 | .Call(`_xrnet_computeResponseRcpp`, X, mattype_x, Fixed, beta0, betas, gammas, response_type, family) 6 | } 7 | 8 | fitModelCVRcpp <- function(x, mattype_x, y, ext, is_sparse_ext, fixed, weights_user, intr, stnd, penalty_type, cmult, quantiles, num_penalty, penalty_ratio, penalty_user, penalty_user_ext, lower_cl, upper_cl, family, user_loss, test_idx, thresh, maxit, ne, nx) { 9 | .Call(`_xrnet_fitModelCVRcpp`, x, mattype_x, y, ext, is_sparse_ext, fixed, weights_user, intr, stnd, penalty_type, cmult, quantiles, num_penalty, penalty_ratio, penalty_user, penalty_user_ext, lower_cl, upper_cl, family, user_loss, test_idx, thresh, maxit, ne, nx) 10 | } 11 | 12 | fitModelRcpp <- function(x, mattype_x, y, ext, is_sparse_ext, fixed, weights_user, intr, stnd, penalty_type, cmult, quantiles, num_penalty, penalty_ratio, penalty_user, penalty_user_ext, lower_cl, upper_cl, family, thresh, maxit, ne, nx) { 13 | .Call(`_xrnet_fitModelRcpp`, x, mattype_x, y, ext, is_sparse_ext, fixed, weights_user, intr, stnd, penalty_type, cmult, quantiles, num_penalty, penalty_ratio, penalty_user, penalty_user_ext, lower_cl, upper_cl, family, thresh, maxit, ne, nx) 14 | } 15 | 16 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: xrnet 2 | Type: Package 3 | Title: Hierarchical Regularized Regression 4 | Version: 1.0.1 5 | Authors@R: c( 6 | person("Garrett", "Weaver", email = "gweaverdev@icloud.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-9918-8386")), 7 | person("Dixin", "Shen", email = "dixinshe@usc.edu", role = c("aut")), 8 | person("Juan Pablo", "Lewinger", email = "lewinger@usc.edu", role = c("ctb", "ths"))) 9 | URL: https://github.com/USCbiostats/xrnet, https://uscbiostats.github.io/xrnet/ 10 | Description: Fits hierarchical regularized regression models 11 | to incorporate potentially informative external data, Weaver and Lewinger (2019) . 12 | Utilizes coordinate descent to efficiently fit regularized regression models both with and without 13 | external information with the most common penalties used in practice (i.e. ridge, lasso, elastic net). 14 | Support for standard R matrices, sparse matrices and big.matrix objects. 15 | License: GPL-2 16 | Encoding: UTF-8 17 | LazyData: true 18 | RoxygenNote: 7.3.1 19 | Suggests: 20 | knitr, 21 | rmarkdown, 22 | testthat, 23 | Matrix, 24 | doParallel 25 | LinkingTo: 26 | Rcpp, 27 | RcppEigen, 28 | BH, 29 | bigmemory 30 | Imports: 31 | Rcpp (>= 0.12.19), 32 | foreach, 33 | bigmemory, 34 | methods 35 | Depends: 36 | R (>= 4.0) 37 | BugReports: https://github.com/USCbiostats/xrnet/issues 38 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who 4 | contribute through reporting issues, posting feature requests, updating documentation, 5 | submitting pull requests or patches, and other activities. 6 | 7 | We are committed to making participation in this project a harassment-free experience for 8 | everyone, regardless of level of experience, gender, gender identity and expression, 9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 10 | 11 | Examples of unacceptable behavior by participants include the use of sexual language or 12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment, 13 | insults, or other unprofessional conduct. 14 | 15 | Project maintainers have the right and responsibility to remove, edit, or reject comments, 16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this 17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed 18 | from the project team. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 21 | opening an issue or contacting one or more of the project maintainers. 22 | 23 | This Code of Conduct is adapted from the Contributor Covenant 24 | (http://contributor-covenant.org), version 1.0.0, available at 25 | http://contributor-covenant.org/version/1/0/0/ 26 | -------------------------------------------------------------------------------- /man/define_enet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/define_penalty.R 3 | \name{define_enet} 4 | \alias{define_enet} 5 | \title{Define elastic net regularization object for predictor and external data} 6 | \usage{ 7 | define_enet( 8 | en_param = 0.5, 9 | num_penalty = 20, 10 | penalty_ratio = NULL, 11 | user_penalty = NULL, 12 | custom_multiplier = NULL 13 | ) 14 | } 15 | \arguments{ 16 | \item{en_param}{elastic net parameter, between 0 and 1} 17 | 18 | \item{num_penalty}{number of penalty values to fit in grid. Default is 20.} 19 | 20 | \item{penalty_ratio}{ratio between minimum and maximum penalty for x. 21 | Default is 1e-04 if \eqn{n > p} and 0.01 if \eqn{n <= p}.} 22 | 23 | \item{user_penalty}{user-defined vector of penalty values to use in penalty 24 | path.} 25 | 26 | \item{custom_multiplier}{variable-specific penalty multipliers to apply to 27 | overall penalty. Default is 1 for all variables. 0 is no penalization.} 28 | } 29 | \value{ 30 | A list object with regularization settings that are used to define 31 | the regularization for predictors or external data in \code{\link{xrnet}} and 32 | \code{\link{tune_xrnet}}. The list elements will match those returned by 33 | \code{\link{define_penalty}}, but with the penalty_type set to match the 34 | value of \code{en_param}. 35 | } 36 | \description{ 37 | Helper function to define a elastic net penalty regularization 38 | object. See \code{define_penalty} for more details. 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | permissions: read-all 15 | 16 | jobs: 17 | pkgdown: 18 | runs-on: ubuntu-latest 19 | # Only restrict concurrency for non-PR jobs 20 | concurrency: 21 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 22 | env: 23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 24 | permissions: 25 | contents: write 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: any::pkgdown, local::. 38 | needs: website 39 | 40 | - name: Build site 41 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 42 | shell: Rscript {0} 43 | 44 | - name: Deploy to GitHub pages 🚀 45 | if: github.event_name != 'pull_request' 46 | uses: JamesIves/github-pages-deploy-action@v4.5.0 47 | with: 48 | clean: false 49 | branch: gh-pages 50 | folder: docs 51 | -------------------------------------------------------------------------------- /man/xrnet_control.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/xrnet.R 3 | \name{xrnet_control} 4 | \alias{xrnet_control} 5 | \title{Control function for xrnet fitting} 6 | \usage{ 7 | xrnet_control( 8 | tolerance = 1e-08, 9 | max_iterations = 1e+05, 10 | dfmax = NULL, 11 | pmax = NULL, 12 | lower_limits = NULL, 13 | upper_limits = NULL 14 | ) 15 | } 16 | \arguments{ 17 | \item{tolerance}{positive convergence criterion. Default is 1e-08.} 18 | 19 | \item{max_iterations}{maximum number of iterations to run coordinate 20 | gradient descent across all penalties before returning an error. 21 | Default is 1e+05.} 22 | 23 | \item{dfmax}{maximum number of variables allowed in model. Default is 24 | \eqn{ncol(x) + ncol(unpen) + ncol(external) + intercept[1] + intercept[2]}.} 25 | 26 | \item{pmax}{maximum number of variables with nonzero coefficient estimate. 27 | Default is \eqn{min(2 * dfmax + 20, ncol(x) + ncol(unpen) + ncol(external) 28 | + intercept[2])}.} 29 | 30 | \item{lower_limits}{vector of lower limits for each coefficient. Default is 31 | -Inf for all variables.} 32 | 33 | \item{upper_limits}{vector of upper limits for each coefficient. Default is 34 | Inf for all variables.} 35 | } 36 | \value{ 37 | A list object with the following components: 38 | \item{tolerance}{The coordinate descent stopping criterion.} 39 | \item{dfmax}{The maximum number of variables that will be allowed in the 40 | model.} 41 | \item{pmax}{The maximum number of variables with nonzero coefficient 42 | estimate.} 43 | \item{lower_limits}{Feature-specific numeric vector of lower bounds for 44 | coefficient estimates} 45 | \item{upper_limits}{Feature-specific numeric vector of upper bounds for 46 | coefficient estimates} 47 | } 48 | \description{ 49 | Control function for \code{\link{xrnet}} fitting. 50 | } 51 | -------------------------------------------------------------------------------- /man/coef.xrnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/coef_xrnet.R 3 | \name{coef.xrnet} 4 | \alias{coef.xrnet} 5 | \title{Get coefficient estimates from "xrnet" model object.} 6 | \usage{ 7 | \method{coef}{xrnet}(object, p = NULL, pext = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{object}{A \code{\link{xrnet}} object.} 11 | 12 | \item{p}{vector of penalty values to apply to predictor variables.} 13 | 14 | \item{pext}{vector of penalty values to apply to external data variables.} 15 | 16 | \item{...}{pass other arguments to xrnet function (if needed).} 17 | } 18 | \value{ 19 | A list with coefficient estimates at each of the requested penalty 20 | combinations. 21 | \item{beta0}{matrix of first-level intercepts indexed by penalty values, NULL 22 | if no first-level intercept in original model fit.} 23 | \item{betas}{3-dimensional array of first-level penalized coefficients 24 | indexed by penalty values.} 25 | \item{gammas}{3-dimensional array of first-level non-penalized coefficients 26 | indexed by penalty values, NULL if unpen NULL in original model fit.} 27 | \item{alpha0}{matrix of second-level intercepts indexed by penalty values, 28 | NULL if no second-level intercept in original model fit.} 29 | \item{alphas}{3-dimensional array of second-level external data coefficients 30 | indexed by penalty values, NULL if external NULL in original model fit.} 31 | } 32 | \description{ 33 | Returns coefficients from 'xrnet' model. Note that we currently 34 | only support returning coefficient estimates that are in the original 35 | path(s). 36 | } 37 | \examples{ 38 | data(GaussianExample) 39 | 40 | fit_xrnet <- xrnet( 41 | x = x_linear, 42 | y = y_linear, 43 | external = ext_linear, 44 | family = "gaussian" 45 | ) 46 | 47 | lambda1 <- fit_xrnet$penalty[10] 48 | lambda2 <- fit_xrnet$penalty_ext[10] 49 | 50 | coef_xrnet <- coef( 51 | fit_xrnet, 52 | p = lambda1, 53 | pext = lambda2, 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /src/XrnetUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef XRNET_UTILS_H 2 | #define XRNET_UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | void compute_penalty(Eigen::Ref path, 8 | const Eigen::Ref & penalty_user, 9 | const double & penalty_type, 10 | const double & penalty_ratio, 11 | const Eigen::Ref & gradient, 12 | const Eigen::Ref & cmult, 13 | const int & begin, 14 | const int & end, 15 | const double & ys); 16 | 17 | double logit_inv(double x); 18 | 19 | Eigen::Map createEigenMapFromRcppNumericMatrix(const Rcpp::NumericMatrix& mat); 20 | 21 | template 22 | Eigen::MatrixXd computeResponse(const TX & X, 23 | const Eigen::Ref & Fixed, 24 | const Eigen::Ref & beta0, 25 | const Eigen::Ref & betas, 26 | const Eigen::Ref & gammas, 27 | const std::string & response_type, 28 | const std::string & family) { 29 | 30 | Eigen::MatrixXd pred(X.rows(), betas.cols()); 31 | if (gammas.cols() > 0) 32 | pred = Eigen::VectorXd::Constant(X.rows(), 1.0) * beta0.transpose() + X * betas + Fixed * gammas; 33 | else 34 | pred = Eigen::VectorXd::Constant(X.rows(), 1.0) * beta0.transpose() + X * betas; 35 | 36 | if (response_type == "response") { 37 | if (family == "binomial") { 38 | pred = pred.unaryExpr(&logit_inv); 39 | } 40 | } 41 | return pred; 42 | } 43 | 44 | template int sgn(T val) { 45 | return (T(0) < val) - (val < T(0)); 46 | } 47 | 48 | #endif // XRNET_UTILS_H 49 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: any::covr, any::xml2 29 | needs: coverage 30 | 31 | - name: Test coverage 32 | run: | 33 | cov <- covr::package_coverage( 34 | quiet = FALSE, 35 | clean = FALSE, 36 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 37 | ) 38 | covr::to_cobertura(cov) 39 | shell: Rscript {0} 40 | 41 | - uses: codecov/codecov-action@v4 42 | with: 43 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} 44 | file: ./cobertura.xml 45 | plugin: noop 46 | disable_search: true 47 | token: ${{ secrets.CODECOV_TOKEN }} 48 | 49 | - name: Show testthat output 50 | if: always() 51 | run: | 52 | ## -------------------------------------------------------------------- 53 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 54 | shell: bash 55 | 56 | - name: Upload test results 57 | if: failure() 58 | uses: actions/upload-artifact@v4 59 | with: 60 | name: coverage-test-failures 61 | path: ${{ runner.temp }}/package 62 | -------------------------------------------------------------------------------- /man/plot.tune_xrnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_tune_xrnet.R 3 | \name{plot.tune_xrnet} 4 | \alias{plot.tune_xrnet} 5 | \title{Plot k-fold cross-validation error grid} 6 | \usage{ 7 | \method{plot}{tune_xrnet}(x, p = NULL, pext = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A tune_xrnet class object} 11 | 12 | \item{p}{(optional) penalty value for x (for generating an error curve across 13 | external penalties). Use value "opt" to use the optimal penalty value.} 14 | 15 | \item{pext}{(optional) penalty value for external (for generating an error 16 | curve across primary penalties). Use value "opt" to use the optimal penalty 17 | value.} 18 | 19 | \item{...}{Additional graphics parameters} 20 | } 21 | \value{ 22 | None 23 | } 24 | \description{ 25 | Generates plots to visualize the mean cross-validation error. 26 | If no external data was used in the model fit, a plot of the cross-validated 27 | error with standard error bars is generated for all penalty values. If 28 | external data was used in the model fit, a contour plot of the 29 | cross-validated errors is created. Error curves can also be generated for a 30 | fixed value of the primary penalty on x (p) or the external penalty (pext) 31 | when external data is used. 32 | } 33 | \details{ 34 | The parameter values p and pext can be used to generate profiled 35 | error curves by fixing either the penalty on x or the penalty on external to 36 | a fixed value. You cannot specify both at the same time as this would only 37 | return a single point. 38 | } 39 | \examples{ 40 | 41 | ## load example data 42 | data(GaussianExample) 43 | 44 | ## 5-fold cross validation 45 | cv_xrnet <- tune_xrnet( 46 | x = x_linear, 47 | y = y_linear, 48 | external = ext_linear, 49 | family = "gaussian", 50 | control = xrnet_control(tolerance = 1e-6) 51 | ) 52 | 53 | ## contour plot of cross-validated error 54 | plot(cv_xrnet) 55 | 56 | ## error curve of external penalties at optimal penalty value 57 | plot(cv_xrnet, p = "opt") 58 | } 59 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | 6 | /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */ 7 | 8 | /* All levels of nav */ 9 | nav[data-toggle='toc'] .nav > li > a { 10 | display: block; 11 | padding: 4px 20px; 12 | font-size: 13px; 13 | font-weight: 500; 14 | color: #767676; 15 | } 16 | nav[data-toggle='toc'] .nav > li > a:hover, 17 | nav[data-toggle='toc'] .nav > li > a:focus { 18 | padding-left: 19px; 19 | color: #563d7c; 20 | text-decoration: none; 21 | background-color: transparent; 22 | border-left: 1px solid #563d7c; 23 | } 24 | nav[data-toggle='toc'] .nav > .active > a, 25 | nav[data-toggle='toc'] .nav > .active:hover > a, 26 | nav[data-toggle='toc'] .nav > .active:focus > a { 27 | padding-left: 18px; 28 | font-weight: bold; 29 | color: #563d7c; 30 | background-color: transparent; 31 | border-left: 2px solid #563d7c; 32 | } 33 | 34 | /* Nav: second level (shown on .active) */ 35 | nav[data-toggle='toc'] .nav .nav { 36 | display: none; /* Hide by default, but at >768px, show it */ 37 | padding-bottom: 10px; 38 | } 39 | nav[data-toggle='toc'] .nav .nav > li > a { 40 | padding-top: 1px; 41 | padding-bottom: 1px; 42 | padding-left: 30px; 43 | font-size: 12px; 44 | font-weight: normal; 45 | } 46 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 47 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 48 | padding-left: 29px; 49 | } 50 | nav[data-toggle='toc'] .nav .nav > .active > a, 51 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 52 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 53 | padding-left: 28px; 54 | font-weight: 500; 55 | } 56 | 57 | /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ 58 | nav[data-toggle='toc'] .nav > .active > ul { 59 | display: block; 60 | } 61 | -------------------------------------------------------------------------------- /R/coef_xrnet.R: -------------------------------------------------------------------------------- 1 | #' Get coefficient estimates from "xrnet" model object. 2 | #' 3 | #' @description Returns coefficients from 'xrnet' model. Note that we currently 4 | #' only support returning coefficient estimates that are in the original 5 | #' path(s). 6 | #' 7 | #' @param object A \code{\link{xrnet}} object. 8 | #' @param p vector of penalty values to apply to predictor variables. 9 | #' @param pext vector of penalty values to apply to external data variables. 10 | #' @param ... pass other arguments to xrnet function (if needed). 11 | #' 12 | #' @return A list with coefficient estimates at each of the requested penalty 13 | #' combinations. 14 | #' \item{beta0}{matrix of first-level intercepts indexed by penalty values, NULL 15 | #' if no first-level intercept in original model fit.} 16 | #' \item{betas}{3-dimensional array of first-level penalized coefficients 17 | #' indexed by penalty values.} 18 | #' \item{gammas}{3-dimensional array of first-level non-penalized coefficients 19 | #' indexed by penalty values, NULL if unpen NULL in original model fit.} 20 | #' \item{alpha0}{matrix of second-level intercepts indexed by penalty values, 21 | #' NULL if no second-level intercept in original model fit.} 22 | #' \item{alphas}{3-dimensional array of second-level external data coefficients 23 | #' indexed by penalty values, NULL if external NULL in original model fit.} 24 | #' 25 | #' @examples 26 | #' data(GaussianExample) 27 | #' 28 | #' fit_xrnet <- xrnet( 29 | #' x = x_linear, 30 | #' y = y_linear, 31 | #' external = ext_linear, 32 | #' family = "gaussian" 33 | #' ) 34 | #' 35 | #' lambda1 <- fit_xrnet$penalty[10] 36 | #' lambda2 <- fit_xrnet$penalty_ext[10] 37 | #' 38 | #' coef_xrnet <- coef( 39 | #' fit_xrnet, 40 | #' p = lambda1, 41 | #' pext = lambda2, 42 | #' ) 43 | #' @export 44 | coef.xrnet <- function(object, 45 | p = NULL, 46 | pext = NULL, 47 | ...) { 48 | predict( 49 | object, 50 | newdata = NULL, 51 | newdata_fixed = NULL, 52 | p = p, 53 | pext = pext, 54 | type = "coefficients", 55 | ... 56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | branches: [main, master] 12 | 13 | name: R-CMD-check 14 | 15 | permissions: read-all 16 | 17 | jobs: 18 | R-CMD-check: 19 | runs-on: ${{ matrix.config.os }} 20 | 21 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | config: 27 | - {os: macos-latest, r: 'release'} 28 | 29 | - {os: windows-latest, r: 'release'} 30 | # use 4.1 to check with rtools40's older compiler 31 | - {os: windows-latest, r: '4.1'} 32 | 33 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 34 | - {os: ubuntu-latest, r: 'release'} 35 | - {os: ubuntu-latest, r: 'oldrel-1'} 36 | - {os: ubuntu-latest, r: 'oldrel-2'} 37 | - {os: ubuntu-latest, r: 'oldrel-3'} 38 | - {os: ubuntu-latest, r: 'oldrel-4'} 39 | 40 | env: 41 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 42 | R_KEEP_PKG_SOURCE: yes 43 | 44 | steps: 45 | - uses: actions/checkout@v4 46 | 47 | - uses: r-lib/actions/setup-pandoc@v2 48 | 49 | - uses: r-lib/actions/setup-r@v2 50 | with: 51 | r-version: ${{ matrix.config.r }} 52 | http-user-agent: ${{ matrix.config.http-user-agent }} 53 | use-public-rspm: true 54 | 55 | - uses: r-lib/actions/setup-r-dependencies@v2 56 | with: 57 | extra-packages: any::rcmdcheck 58 | needs: check 59 | 60 | - uses: r-lib/actions/check-r-package@v2 61 | with: 62 | upload-snapshots: true 63 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 64 | -------------------------------------------------------------------------------- /man/coef.tune_xrnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/coef_tune_xrnet.R 3 | \name{coef.tune_xrnet} 4 | \alias{coef.tune_xrnet} 5 | \title{Get coefficient estimates from "tune_xrnet" model object.} 6 | \usage{ 7 | \method{coef}{tune_xrnet}(object, p = "opt", pext = "opt", ...) 8 | } 9 | \arguments{ 10 | \item{object}{A \code{\link{tune_xrnet}} object.} 11 | 12 | \item{p}{vector of penalty values to apply to predictor variables. 13 | Default is optimal value in tune_xrnet object.} 14 | 15 | \item{pext}{vector of penalty values to apply to external data variables. 16 | Default is optimal value in tune_xrnet object.} 17 | 18 | \item{...}{pass other arguments to xrnet function (if needed).} 19 | } 20 | \value{ 21 | A list with coefficient estimates at each of the requested penalty 22 | combinations. 23 | \item{beta0}{matrix of first-level intercepts indexed by penalty values, NULL 24 | if no first-level intercept in original model fit.} 25 | \item{betas}{3-dimensional array of first-level penalized coefficients 26 | indexed by penalty values.} 27 | \item{gammas}{3-dimensional array of first-level non-penalized coefficients 28 | indexed by penalty values, NULL if unpen NULL in original model fit.} 29 | \item{alpha0}{matrix of second-level intercepts indexed by penalty values, 30 | NULL if no second-level intercept in original model fit.} 31 | \item{alphas}{3-dimensional array of second-level external data coefficients 32 | indexed by penalty values, NULL if external NULL in original model fit.} 33 | } 34 | \description{ 35 | Returns coefficients from 'xrnet' model. Note that we currently 36 | only support returning coefficient estimates that are in the original 37 | path(s). 38 | } 39 | \examples{ 40 | ## Cross validation of hierarchical linear regression model 41 | data(GaussianExample) 42 | 43 | ## 5-fold cross validation 44 | cv_xrnet <- tune_xrnet( 45 | x = x_linear, 46 | y = y_linear, 47 | external = ext_linear, 48 | family = "gaussian", 49 | control = xrnet_control(tolerance = 1e-6) 50 | ) 51 | 52 | ## Get coefficient estimates at optimal penalty combination 53 | coef_opt <- coef(cv_xrnet) 54 | } 55 | -------------------------------------------------------------------------------- /man/predict.xrnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/predict_xrnet.R 3 | \name{predict.xrnet} 4 | \alias{predict.xrnet} 5 | \title{Predict function for "xrnet" object} 6 | \usage{ 7 | \method{predict}{xrnet}( 8 | object, 9 | newdata = NULL, 10 | newdata_fixed = NULL, 11 | p = NULL, 12 | pext = NULL, 13 | type = c("response", "link", "coefficients"), 14 | ... 15 | ) 16 | } 17 | \arguments{ 18 | \item{object}{A \code{\link{xrnet}} object} 19 | 20 | \item{newdata}{matrix with new values for penalized variables} 21 | 22 | \item{newdata_fixed}{matrix with new values for unpenalized variables} 23 | 24 | \item{p}{vector of penalty values to apply to predictor variables} 25 | 26 | \item{pext}{vector of penalty values to apply to external data variables} 27 | 28 | \item{type}{type of prediction to make using the xrnet model, options 29 | include: 30 | \itemize{ 31 | \item response 32 | \item link (linear predictor) 33 | \item coefficients 34 | }} 35 | 36 | \item{...}{pass other arguments to xrnet function (if needed)} 37 | } 38 | \value{ 39 | The object returned is based on the value of type as follows: 40 | \itemize{ 41 | \item response: An array with the response predictions based on the data 42 | for each penalty combination 43 | \item link: An array with linear predictions based on the data for each 44 | penalty combination 45 | \item coefficients: A list with the coefficient estimates for each 46 | penalty combination. See \code{\link{coef.xrnet}}. 47 | } 48 | } 49 | \description{ 50 | Extract coefficients or predict response in new data using 51 | fitted model from an \code{\link{xrnet}} object. Note that we currently only 52 | support returning coefficient estimates that are in the original path(s). 53 | } 54 | \examples{ 55 | data(GaussianExample) 56 | 57 | fit_xrnet <- xrnet( 58 | x = x_linear, 59 | y = y_linear, 60 | external = ext_linear, 61 | family = "gaussian" 62 | ) 63 | 64 | lambda1 <- fit_xrnet$penalty[10] 65 | lambda2 <- fit_xrnet$penalty_ext[10] 66 | 67 | coef_xrnet <- predict( 68 | fit_xrnet, 69 | p = lambda1, 70 | pext = lambda2, 71 | type = "coefficients" 72 | ) 73 | 74 | pred_xrnet <- predict( 75 | fit_xrnet, 76 | p = lambda1, 77 | pext = lambda2, 78 | newdata = x_linear, 79 | type = "response" 80 | ) 81 | } 82 | -------------------------------------------------------------------------------- /docs/docsearch.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // register a handler to move the focus to the search bar 4 | // upon pressing shift + "/" (i.e. "?") 5 | $(document).on('keydown', function(e) { 6 | if (e.shiftKey && e.keyCode == 191) { 7 | e.preventDefault(); 8 | $("#search-input").focus(); 9 | } 10 | }); 11 | 12 | $(document).ready(function() { 13 | // do keyword highlighting 14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */ 15 | var mark = function() { 16 | 17 | var referrer = document.URL ; 18 | var paramKey = "q" ; 19 | 20 | if (referrer.indexOf("?") !== -1) { 21 | var qs = referrer.substr(referrer.indexOf('?') + 1); 22 | var qs_noanchor = qs.split('#')[0]; 23 | var qsa = qs_noanchor.split('&'); 24 | var keyword = ""; 25 | 26 | for (var i = 0; i < qsa.length; i++) { 27 | var currentParam = qsa[i].split('='); 28 | 29 | if (currentParam.length !== 2) { 30 | continue; 31 | } 32 | 33 | if (currentParam[0] == paramKey) { 34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20")); 35 | } 36 | } 37 | 38 | if (keyword !== "") { 39 | $(".contents").unmark({ 40 | done: function() { 41 | $(".contents").mark(keyword); 42 | } 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | mark(); 49 | }); 50 | }); 51 | 52 | /* Search term highlighting ------------------------------*/ 53 | 54 | function matchedWords(hit) { 55 | var words = []; 56 | 57 | var hierarchy = hit._highlightResult.hierarchy; 58 | // loop to fetch from lvl0, lvl1, etc. 59 | for (var idx in hierarchy) { 60 | words = words.concat(hierarchy[idx].matchedWords); 61 | } 62 | 63 | var content = hit._highlightResult.content; 64 | if (content) { 65 | words = words.concat(content.matchedWords); 66 | } 67 | 68 | // return unique words 69 | var words_uniq = [...new Set(words)]; 70 | return words_uniq; 71 | } 72 | 73 | function updateHitURL(hit) { 74 | 75 | var words = matchedWords(hit); 76 | var url = ""; 77 | 78 | if (hit.anchor) { 79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor; 80 | } else { 81 | url = hit.url + '?q=' + escape(words.join(" ")); 82 | } 83 | 84 | return url; 85 | } 86 | -------------------------------------------------------------------------------- /man/predict.tune_xrnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/predict_tune_xrnet.R 3 | \name{predict.tune_xrnet} 4 | \alias{predict.tune_xrnet} 5 | \title{Predict function for "tune_xrnet" object} 6 | \usage{ 7 | \method{predict}{tune_xrnet}( 8 | object, 9 | newdata = NULL, 10 | newdata_fixed = NULL, 11 | p = "opt", 12 | pext = "opt", 13 | type = c("response", "link", "coefficients"), 14 | ... 15 | ) 16 | } 17 | \arguments{ 18 | \item{object}{A \code{\link{tune_xrnet}} object} 19 | 20 | \item{newdata}{matrix with new values for penalized variables} 21 | 22 | \item{newdata_fixed}{matrix with new values for unpenalized variables} 23 | 24 | \item{p}{vector of penalty values to apply to predictor variables. 25 | Default is optimal value in tune_xrnet object.} 26 | 27 | \item{pext}{vector of penalty values to apply to external data variables. 28 | Default is optimal value in tune_xrnet object.} 29 | 30 | \item{type}{type of prediction to make using the xrnet model, options 31 | include: 32 | \itemize{ 33 | \item response 34 | \item link (linear predictor) 35 | \item coefficients 36 | }} 37 | 38 | \item{...}{pass other arguments to xrnet function (if needed)} 39 | } 40 | \value{ 41 | The object returned is based on the value of type as follows: 42 | \itemize{ 43 | \item response: An array with the response predictions based on the data 44 | for each penalty combination 45 | \item link: An array with linear predictions based on the data for each 46 | penalty combination 47 | \item coefficients: A list with the coefficient estimates for each 48 | penalty combination. See \code{\link{coef.xrnet}}. 49 | } 50 | } 51 | \description{ 52 | Extract coefficients or predict response in new data using 53 | fitted model from a \code{\link{tune_xrnet}} object. Note that we currently 54 | only support returning results that are in the original path(s). 55 | } 56 | \examples{ 57 | data(GaussianExample) 58 | 59 | ## 5-fold cross validation 60 | cv_xrnet <- tune_xrnet( 61 | x = x_linear, 62 | y = y_linear, 63 | external = ext_linear, 64 | family = "gaussian", 65 | control = xrnet_control(tolerance = 1e-6) 66 | ) 67 | 68 | ## Get coefficients and predictions at optimal penalty combination 69 | coef_xrnet <- predict(cv_xrnet, type = "coefficients") 70 | pred_xrnet <- predict(cv_xrnet, newdata = x_linear, type = "response") 71 | } 72 | -------------------------------------------------------------------------------- /R/coef_tune_xrnet.R: -------------------------------------------------------------------------------- 1 | #' Get coefficient estimates from "tune_xrnet" model object. 2 | #' 3 | #' @description Returns coefficients from 'xrnet' model. Note that we currently 4 | #' only support returning coefficient estimates that are in the original 5 | #' path(s). 6 | #' 7 | #' @param object A \code{\link{tune_xrnet}} object. 8 | #' @param p vector of penalty values to apply to predictor variables. 9 | #' Default is optimal value in tune_xrnet object. 10 | #' @param pext vector of penalty values to apply to external data variables. 11 | #' Default is optimal value in tune_xrnet object. 12 | #' @param ... pass other arguments to xrnet function (if needed). 13 | #' 14 | #' @return A list with coefficient estimates at each of the requested penalty 15 | #' combinations. 16 | #' \item{beta0}{matrix of first-level intercepts indexed by penalty values, NULL 17 | #' if no first-level intercept in original model fit.} 18 | #' \item{betas}{3-dimensional array of first-level penalized coefficients 19 | #' indexed by penalty values.} 20 | #' \item{gammas}{3-dimensional array of first-level non-penalized coefficients 21 | #' indexed by penalty values, NULL if unpen NULL in original model fit.} 22 | #' \item{alpha0}{matrix of second-level intercepts indexed by penalty values, 23 | #' NULL if no second-level intercept in original model fit.} 24 | #' \item{alphas}{3-dimensional array of second-level external data coefficients 25 | #' indexed by penalty values, NULL if external NULL in original model fit.} 26 | #' 27 | #' @examples 28 | #' ## Cross validation of hierarchical linear regression model 29 | #' data(GaussianExample) 30 | #' 31 | #' ## 5-fold cross validation 32 | #' cv_xrnet <- tune_xrnet( 33 | #' x = x_linear, 34 | #' y = y_linear, 35 | #' external = ext_linear, 36 | #' family = "gaussian", 37 | #' control = xrnet_control(tolerance = 1e-6) 38 | #' ) 39 | #' 40 | #' ## Get coefficient estimates at optimal penalty combination 41 | #' coef_opt <- coef(cv_xrnet) 42 | #' @export 43 | coef.tune_xrnet <- function(object, 44 | p = "opt", 45 | pext = "opt", 46 | ...) { 47 | if (p == "opt") { 48 | p <- object$opt_penalty 49 | } 50 | if (pext == "opt") { 51 | pext <- object$opt_penalty_ext 52 | } 53 | 54 | predict( 55 | object$fitted_model, 56 | newdata = NULL, 57 | newdata_fixed = NULL, 58 | p = p, 59 | pext = pext, 60 | type = "coefficients", 61 | ... 62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /man/define_penalty.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/define_penalty.R 3 | \name{define_penalty} 4 | \alias{define_penalty} 5 | \title{Define regularization object for predictor and external data.} 6 | \usage{ 7 | define_penalty( 8 | penalty_type = 1, 9 | quantile = 0.5, 10 | num_penalty = 20, 11 | penalty_ratio = NULL, 12 | user_penalty = NULL, 13 | custom_multiplier = NULL 14 | ) 15 | } 16 | \arguments{ 17 | \item{penalty_type}{type of regularization. Default is 1 (Lasso). 18 | Can supply either a scalar value or vector with length equal to the number of 19 | variables the matrix. 20 | \itemize{ 21 | \item 0 = Ridge 22 | \item (0,1) = Elastic-Net 23 | \item 1 = Lasso / Quantile 24 | }} 25 | 26 | \item{quantile}{specifies quantile for quantile penalty. Default of 0.5 27 | reduces to lasso (currently not implemented).} 28 | 29 | \item{num_penalty}{number of penalty values to fit in grid. Default is 20.} 30 | 31 | \item{penalty_ratio}{ratio between minimum and maximum penalty for x. 32 | Default is 1e-04 if \eqn{n > p} and 0.01 if \eqn{n <= p}.} 33 | 34 | \item{user_penalty}{user-defined vector of penalty values to use in penalty 35 | path.} 36 | 37 | \item{custom_multiplier}{variable-specific penalty multipliers to apply to 38 | overall penalty. Default is 1 for all variables. 0 is no penalization.} 39 | } 40 | \value{ 41 | A list object with regularization settings that are used to define 42 | the regularization for predictors or external data in \code{\link{xrnet}} and 43 | \code{\link{tune_xrnet}}: 44 | \item{penalty_type}{The penalty type, scalar with value in range [0, 1].} 45 | \item{quantile}{Quantile for quantile penalty, 0.5 defaults to lasso 46 | (not currently implemented).} 47 | \item{num_penalty}{The number of penalty values in the penalty path.} 48 | \item{penalty_ratio}{The ratio of the minimum penalty value compared to the 49 | maximum penalty value.} 50 | \item{user_penalty}{User-defined numeric vector of penalty values, NULL if 51 | not provided by user.} 52 | \item{custom_multiplier}{User-defined feature-specific penalty multipliers, 53 | NULL if not provided by user.} 54 | } 55 | \description{ 56 | Defines regularization for predictors and external data 57 | variables in \code{\link{xrnet}} fitting. Use helper functions define_lasso, 58 | define_ridge, or define_enet to specify a common penalty on x or external. 59 | } 60 | \examples{ 61 | 62 | # define ridge penalty with penalty grid split into 30 values 63 | my_penalty <- define_penalty(penalty_type = 0, num_penalty = 30) 64 | 65 | # define elastic net (0.5) penalty with user-defined penalty 66 | my_custom_penalty <- define_penalty( 67 | penalty_type = 0.5, user_penalty = c(100, 50, 10, 1, 0.1) 68 | ) 69 | } 70 | -------------------------------------------------------------------------------- /R/predict_tune_xrnet.R: -------------------------------------------------------------------------------- 1 | #' Predict function for "tune_xrnet" object 2 | #' 3 | #' @description Extract coefficients or predict response in new data using 4 | #' fitted model from a \code{\link{tune_xrnet}} object. Note that we currently 5 | #' only support returning results that are in the original path(s). 6 | #' 7 | #' @param object A \code{\link{tune_xrnet}} object 8 | #' @param newdata matrix with new values for penalized variables 9 | #' @param newdata_fixed matrix with new values for unpenalized variables 10 | #' @param p vector of penalty values to apply to predictor variables. 11 | #' Default is optimal value in tune_xrnet object. 12 | #' @param pext vector of penalty values to apply to external data variables. 13 | #' Default is optimal value in tune_xrnet object. 14 | #' @param type type of prediction to make using the xrnet model, options 15 | #' include: 16 | #' \itemize{ 17 | #' \item response 18 | #' \item link (linear predictor) 19 | #' \item coefficients 20 | #' } 21 | #' @param ... pass other arguments to xrnet function (if needed) 22 | #' 23 | #' @return The object returned is based on the value of type as follows: 24 | #' \itemize{ 25 | #' \item response: An array with the response predictions based on the data 26 | #' for each penalty combination 27 | #' \item link: An array with linear predictions based on the data for each 28 | #' penalty combination 29 | #' \item coefficients: A list with the coefficient estimates for each 30 | #' penalty combination. See \code{\link{coef.xrnet}}. 31 | #' } 32 | #' 33 | #' @examples 34 | #' data(GaussianExample) 35 | #' 36 | #' ## 5-fold cross validation 37 | #' cv_xrnet <- tune_xrnet( 38 | #' x = x_linear, 39 | #' y = y_linear, 40 | #' external = ext_linear, 41 | #' family = "gaussian", 42 | #' control = xrnet_control(tolerance = 1e-6) 43 | #' ) 44 | #' 45 | #' ## Get coefficients and predictions at optimal penalty combination 46 | #' coef_xrnet <- predict(cv_xrnet, type = "coefficients") 47 | #' pred_xrnet <- predict(cv_xrnet, newdata = x_linear, type = "response") 48 | #' @export 49 | predict.tune_xrnet <- function(object, 50 | newdata = NULL, 51 | newdata_fixed = NULL, 52 | p = "opt", 53 | pext = "opt", 54 | type = c("response", "link", "coefficients"), 55 | ...) { 56 | if (p == "opt") { 57 | p <- object$opt_penalty 58 | } 59 | if (pext == "opt") { 60 | pext <- object$opt_penalty_ext 61 | } 62 | 63 | predict(object$fitted_model, 64 | newdata = newdata, 65 | newdata_fixed = newdata_fixed, 66 | p = p, 67 | pext = pext, 68 | type = type, 69 | ... 70 | ) 71 | } 72 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: Commands 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | document: 13 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 14 | name: document 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - uses: r-lib/actions/pr-fetch@v2 22 | with: 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | 25 | - uses: r-lib/actions/setup-r@v2 26 | with: 27 | use-public-rspm: true 28 | 29 | - uses: r-lib/actions/setup-r-dependencies@v2 30 | with: 31 | extra-packages: any::roxygen2 32 | needs: pr-document 33 | 34 | - name: Document 35 | run: roxygen2::roxygenise() 36 | shell: Rscript {0} 37 | 38 | - name: commit 39 | run: | 40 | git config --local user.name "$GITHUB_ACTOR" 41 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 42 | git add man/\* NAMESPACE 43 | git commit -m 'Document' 44 | 45 | - uses: r-lib/actions/pr-push@v2 46 | with: 47 | repo-token: ${{ secrets.GITHUB_TOKEN }} 48 | 49 | style: 50 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 51 | name: style 52 | runs-on: ubuntu-latest 53 | env: 54 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 55 | steps: 56 | - uses: actions/checkout@v4 57 | 58 | - uses: r-lib/actions/pr-fetch@v2 59 | with: 60 | repo-token: ${{ secrets.GITHUB_TOKEN }} 61 | 62 | - uses: r-lib/actions/setup-r@v2 63 | 64 | - name: Install dependencies 65 | run: install.packages("styler") 66 | shell: Rscript {0} 67 | 68 | - name: Style 69 | run: styler::style_pkg() 70 | shell: Rscript {0} 71 | 72 | - name: commit 73 | run: | 74 | git config --local user.name "$GITHUB_ACTOR" 75 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 76 | git add \*.R 77 | git commit -m 'Style' 78 | 79 | - uses: r-lib/actions/pr-push@v2 80 | with: 81 | repo-token: ${{ secrets.GITHUB_TOKEN }} 82 | -------------------------------------------------------------------------------- /src/XrnetUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "XrnetUtils.h" 2 | #include 3 | 4 | typedef Eigen::Map MapMat; 5 | typedef Eigen::MappedSparseMatrix MapSpMat; 6 | 7 | void compute_penalty(Eigen::Ref path, 8 | const Eigen::Ref & penalty_user, 9 | const double & penalty_type, 10 | const double & penalty_ratio, 11 | const Eigen::Ref & gradient, 12 | const Eigen::Ref & cmult, 13 | const int & begin, 14 | const int & end, 15 | const double & ys) { 16 | const double bigNum = 9.9e35; 17 | const double minPenaltyRatio = 1e-6; 18 | const double maxInflationFactor = 1e-3; 19 | const int npenalty = path.size(); 20 | if (penalty_user[0] == 0.0) { 21 | path[0] = bigNum; 22 | double max_penalty = 0.0; 23 | for (int k = begin; k < end; ++k) { 24 | if (cmult[k] > 0.0) { 25 | max_penalty = std::max(max_penalty, std::abs(gradient[k] / cmult[k])); 26 | } 27 | } 28 | double eqs = std::max(minPenaltyRatio, penalty_ratio); 29 | double alf = pow(eqs, 1.0 / (npenalty - 1)); 30 | path[1] = alf * (max_penalty / std::max(penalty_type, maxInflationFactor)); 31 | for (int l = 2; l < npenalty; ++l) { 32 | path[l] = alf * path[l - 1]; 33 | } 34 | } 35 | else { 36 | path = penalty_user / ys; 37 | } 38 | } 39 | 40 | // [[Rcpp::export]] 41 | Eigen::MatrixXd computeResponseRcpp(SEXP X, 42 | const int & mattype_x, 43 | const Eigen::Map Fixed, 44 | const Eigen::Map beta0, 45 | const Eigen::Map betas, 46 | const Eigen::Map gammas, 47 | const std::string & response_type, 48 | const std::string & family) { 49 | 50 | if (mattype_x == 1) { 51 | Rcpp::NumericMatrix x_mat(X); 52 | MapMat xmap((const double *) &x_mat[0], x_mat.rows(), x_mat.cols()); 53 | return computeResponse(xmap, Fixed, beta0, betas, gammas, response_type, family); 54 | } else if (mattype_x == 2) { 55 | Rcpp::S4 x_info(X); 56 | Rcpp::XPtr xptr((SEXP) x_info.slot("address")); 57 | MapMat xmap((const double *)xptr->matrix(), xptr->nrow(), xptr->ncol()); 58 | return computeResponse(xmap, Fixed, beta0, betas, gammas, response_type, family); 59 | } else { 60 | return computeResponse(Rcpp::as(X), Fixed, beta0, betas, gammas, response_type, family); 61 | } 62 | } 63 | 64 | double logit_inv(double x) { 65 | return 1 / (1 + std::exp(-x)); 66 | } 67 | 68 | Eigen::Map createEigenMapFromRcppNumericMatrix(const Rcpp::NumericMatrix& mat) { 69 | if (mat.rows() == 0 || mat.cols() == 0) { 70 | Eigen::MatrixXd emptyMat; 71 | return Eigen::Map(emptyMat.data(), emptyMat.rows(), emptyMat.cols()); 72 | } 73 | return Eigen::Map(mat.begin(), mat.rows(), mat.cols()); 74 | } 75 | -------------------------------------------------------------------------------- /paper/paper.bib: -------------------------------------------------------------------------------- 1 | @Manual{R, 2 | title = {R: A Language and Environment for Statistical Computing}, 3 | author = {{R Core Team}}, 4 | organization = {R Foundation for Statistical Computing}, 5 | address = {Vienna, Austria}, 6 | year = {2019}, 7 | url = {https://www.R-project.org/} 8 | } 9 | 10 | @Article{friedman2010, 11 | title = {Regularization Paths for Generalized Linear Models via Coordinate Descent}, 12 | author = {Jerome Friedman and Trevor Hastie and Robert Tibshirani}, 13 | journal = {Journal of Statistical Software}, 14 | year = {2010}, 15 | volume = {33}, 16 | number = {1}, 17 | pages = {1--22}, 18 | url = {http://www.jstatsoft.org/v33/i01/}, 19 | doi = {10.18637/jss.v033.i01} 20 | } 21 | 22 | @Article{zeng2017, 23 | author = {Yaohui Zeng and Patrick Breheny}, 24 | title = {The biglasso Package: A Memory- and Computation-Efficient 25 | Solver for Lasso Model Fitting with Big Data in {R}}, 26 | journal = {ArXiv e-prints}, 27 | eprint = {1701.05936}, 28 | year = {2017}, 29 | url = {https://arxiv.org/abs/1701.05936} 30 | } 31 | 32 | @Article{breheny2011, 33 | author = {Patrick Breheny and Jian Huang}, 34 | title = {Coordinate descent algorithms for nonconvex penalized 35 | regression, with applications to biological feature selection}, 36 | journal = {Annals of Applied Statistics}, 37 | year = {2011}, 38 | volume = {5}, 39 | pages = {232--253}, 40 | number = {1}, 41 | doi = {doi:10.1214/10-AOAS388} 42 | } 43 | 44 | @Article{kane2013, 45 | title = {Scalable Strategies for Computing with Massive Data}, 46 | author = {Michael J. Kane and John Emerson and Stephen Weston}, 47 | journal = {Journal of Statistical Software}, 48 | year = {2013}, 49 | volume = {55}, 50 | number = {14}, 51 | pages = {1--19}, 52 | url = {http://www.jstatsoft.org/v55/i14/}, 53 | doi = {10.18637/jss.v055.i14} 54 | } 55 | 56 | @Article{fan2001, 57 | title = {Variable Selection via Nonconcave Penalized Likelihood and its Oracle Properties}, 58 | author = {Jianqing Fan and Runze Li}, 59 | journal = {Journal of the American Statistical Association}, 60 | year = {2001}, 61 | volume = {96}, 62 | number = {456}, 63 | pages = {1348--1360}, 64 | doi = {10.1198/016214501753382273} 65 | } 66 | 67 | @Article{zhang2010, 68 | title = {Nearly unbiased variable selection under minimax concave penalty}, 69 | author = {Cun-Hui Zhang}, 70 | journal = {The Annals of Statistics}, 71 | year = {2010}, 72 | volume = {38}, 73 | number = {2}, 74 | pages = {894--942}, 75 | doi = {10.1214/09-AOS729} 76 | } 77 | 78 | @MISC{eigenweb, 79 | author = {Ga\"{e}l Guennebaud and Beno\^{i}t Jacob and others}, 80 | title = {Eigen v3}, 81 | howpublished = {http://eigen.tuxfamily.org}, 82 | year = {2010} 83 | } 84 | 85 | @Article{rcppEigen, 86 | title = {Fast and Elegant Numerical Linear Algebra Using the {RcppEigen} Package}, 87 | author = {Douglas Bates and Dirk Eddelbuettel}, 88 | journal = {Journal of Statistical Software}, 89 | year = {2013}, 90 | volume = {52}, 91 | number = {5}, 92 | pages = {1--24}, 93 | url = {http://www.jstatsoft.org/v52/i05/}, 94 | doi = {10.18637/jss.v052.i05} 95 | } 96 | 97 | @Article{rcpp, 98 | title = {{Rcpp}: Seamless {R} and {C++} Integration}, 99 | author = {Dirk Eddelbuettel and Romain Fran\c{c}ois}, 100 | journal = {Journal of Statistical Software}, 101 | year = {2011}, 102 | volume = {40}, 103 | number = {8}, 104 | pages = {1--18}, 105 | url = {http://www.jstatsoft.org/v40/i08/}, 106 | doi = {10.18637/jss.v040.i08}, 107 | } 108 | -------------------------------------------------------------------------------- /src/DataFunctions.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_FUNCTIONS_H 2 | #define DATA_FUNCTIONS_H 3 | 4 | #include 5 | 6 | template 7 | void compute_moments(const matType & X, 8 | const Eigen::Ref & wgts_user, 9 | Eigen::Ref xm, 10 | Eigen::Ref cent, 11 | Eigen::Ref xv, 12 | Eigen::Ref xs, 13 | const bool & centered, 14 | const bool & scaled, 15 | int idx) { 16 | if (centered) { 17 | if (scaled) { 18 | for (int j = 0; j < X.cols(); ++j, ++idx) { 19 | auto xj = X.col(j); 20 | xm[idx] = xj.cwiseProduct(wgts_user).sum(); 21 | cent[idx] = xm[idx]; 22 | xs[idx] = 1 / std::sqrt(xj.cwiseProduct(xj.cwiseProduct(wgts_user)).sum() - xm[idx] * xm[idx]); 23 | } 24 | } else { 25 | for (int j = 0; j < X.cols(); ++j, ++idx) { 26 | auto xj = X.col(j); 27 | xm[idx] = xj.cwiseProduct(wgts_user).sum(); 28 | cent[idx] = xm[idx]; 29 | xv[idx] = xj.cwiseProduct(xj.cwiseProduct(wgts_user)).sum() - xm[idx] * xm[idx]; 30 | } 31 | } 32 | } 33 | else { 34 | if (scaled) { 35 | for (int j = 0; j < X.cols(); ++j, ++idx) { 36 | auto xj = X.col(j); 37 | xm[idx] = xj.cwiseProduct(wgts_user).sum(); 38 | double vc = xj.cwiseProduct(xj.cwiseProduct(wgts_user)).sum() - xm[idx] * xm[idx]; 39 | xs[idx] = 1 / std::sqrt(vc); 40 | xv[idx] = 1.0 + xm[idx] * xm[idx] / vc; 41 | } 42 | } else { 43 | for (int j = 0; j < X.cols(); ++j, ++idx) { 44 | auto xj = X.col(j); 45 | xm[idx] = xj.cwiseProduct(wgts_user).sum(); 46 | xv[idx] = xj.cwiseProduct(xj.cwiseProduct(wgts_user)).sum(); 47 | } 48 | } 49 | } 50 | } 51 | 52 | template 53 | Eigen::MatrixXd create_XZ(const matA & X, 54 | const matB & Z, 55 | Eigen::Ref xm, 56 | const Eigen::Ref & cent, 57 | const Eigen::Ref & wgts_user, 58 | Eigen::Ref xv, 59 | Eigen::Ref xs, 60 | const bool & intr_ext, 61 | const bool & scale_z, 62 | int idx) { 63 | 64 | // initialize XZ matrix 65 | Eigen::MatrixXd XZ(0, 0); 66 | 67 | if (Z.size() == 0) 68 | return XZ; 69 | else 70 | XZ.resize(X.rows(), intr_ext + Z.cols()); 71 | 72 | // map means and sds of X 73 | auto cent_x = cent.head(X.cols()); 74 | auto xs_x = xs.head(X.cols()); 75 | 76 | int col_xz = 0; 77 | 78 | // add intercept 79 | if (intr_ext) { 80 | auto xzj = XZ.col(col_xz); 81 | xzj = (X * xs_x).array() - xs_x.cwiseProduct(cent_x).sum(); 82 | double xzj_mean = xzj.cwiseProduct(wgts_user).sum(); 83 | xv[idx] = xzj.cwiseProduct(xzj.cwiseProduct(wgts_user)).sum() - xzj_mean * xzj_mean; 84 | ++idx; 85 | ++col_xz; 86 | } 87 | 88 | // fill in columns of XZ 89 | for (int j = 0; j < Z.cols(); ++j, ++col_xz, ++idx) { 90 | auto zj = Z.col(j); 91 | auto xzj = XZ.col(col_xz); 92 | xm[idx] = zj.sum() / zj.size(); 93 | if (scale_z) { 94 | xs[idx] = 1 / std::sqrt(zj.cwiseProduct(zj / zj.size()).sum() - xm[idx] * xm[idx]); 95 | } 96 | xzj = xs[idx] * ((X * zj.cwiseProduct(xs_x)) - xs_x.cwiseProduct(cent_x.cwiseProduct(zj)).sum() * Eigen::VectorXd::Ones(X.rows())); 97 | double xzj_mean = xzj.cwiseProduct(wgts_user).sum(); 98 | xv[idx] = xzj.cwiseProduct(xzj.cwiseProduct(wgts_user)).sum() - xzj_mean * xzj_mean; 99 | xzj /= xs[idx]; 100 | 101 | } 102 | return XZ; 103 | } 104 | 105 | #endif // DATA_FUNCTIONS_H 106 | -------------------------------------------------------------------------------- /tests/testthat/test-CV-errors.R: -------------------------------------------------------------------------------- 1 | library(doParallel) 2 | library(bigmemory) 3 | 4 | context("check computation of CV fold errors") 5 | 6 | test_that("gaussian, mse (sequential)", { 7 | main_penalty <- define_penalty(0, num_penalty = 20) 8 | external_penalty <- define_penalty(1, num_penalty = 20) 9 | 10 | fit_xrnet <- tune_xrnet( 11 | x = xtest, 12 | y = ytest, 13 | external = ztest, 14 | family = "gaussian", 15 | penalty_main = main_penalty, 16 | penalty_external = external_penalty, 17 | control = list(tolerance = 1e-10), 18 | loss = "mse", 19 | foldid = foldid 20 | ) 21 | 22 | expect_equal( 23 | which.min(cv_mean), 24 | which.min(fit_xrnet$cv_mean), 25 | check.attribute = FALSE 26 | ) 27 | 28 | fit_xrnet <- tune_xrnet( 29 | x = xsparse, 30 | y = ytest, 31 | external = ztest, 32 | family = "gaussian", 33 | penalty_main = main_penalty, 34 | penalty_external = external_penalty, 35 | control = list(tolerance = 1e-10), 36 | loss = "mse", 37 | foldid = foldid 38 | ) 39 | 40 | expect_equal( 41 | which.min(cv_mean), 42 | which.min(fit_xrnet$cv_mean), 43 | check.attribute = FALSE 44 | ) 45 | 46 | fit_xrnet <- tune_xrnet( 47 | x = xtest, 48 | y = ytest, 49 | external = zsparse, 50 | family = "gaussian", 51 | penalty_main = main_penalty, 52 | penalty_external = external_penalty, 53 | control = list(tolerance = 1e-10), 54 | loss = "mse", 55 | foldid = foldid 56 | ) 57 | 58 | expect_equal( 59 | which.min(cv_mean), 60 | which.min(fit_xrnet$cv_mean), 61 | check.attribute = FALSE 62 | ) 63 | }) 64 | 65 | test_that("gaussian, mse (parallel)", { 66 | main_penalty <- define_penalty(0, num_penalty = 20) 67 | external_penalty <- define_penalty(1, num_penalty = 20) 68 | 69 | cl <- makeCluster(2, type = "PSOCK") 70 | registerDoParallel(cl) 71 | 72 | fit_xrnet <- tune_xrnet( 73 | x = xtest, 74 | y = ytest, 75 | external = ztest, 76 | family = "gaussian", 77 | penalty_main = main_penalty, 78 | penalty_external = external_penalty, 79 | control = list(tolerance = 1e-10), 80 | loss = "mse", 81 | foldid = foldid, 82 | parallel = TRUE 83 | ) 84 | 85 | expect_equal( 86 | which.min(cv_mean), 87 | which.min(fit_xrnet$cv_mean), 88 | check.attribute = FALSE 89 | ) 90 | 91 | fit_xrnet <- tune_xrnet( 92 | x = as.big.matrix(xtest), 93 | y = ytest, 94 | external = ztest, 95 | family = "gaussian", 96 | penalty_main = main_penalty, 97 | penalty_external = external_penalty, 98 | control = list(tolerance = 1e-10), 99 | loss = "mse", 100 | foldid = foldid, 101 | parallel = TRUE 102 | ) 103 | 104 | expect_equal( 105 | which.min(cv_mean), 106 | which.min(fit_xrnet$cv_mean), 107 | check.attribute = FALSE 108 | ) 109 | }) 110 | 111 | test_that("gaussian, mae (sequential)", { 112 | main_penalty <- define_penalty(0, num_penalty = 20) 113 | 114 | fit_xrnet <- tune_xrnet( 115 | x = xtest, 116 | y = ytest, 117 | family = "gaussian", 118 | penalty_main = main_penalty, 119 | control = list(tolerance = 1e-12), 120 | loss = "mae", 121 | foldid = foldid 122 | ) 123 | 124 | expect_equal( 125 | cv_mae, 126 | drop(fit_xrnet$cv_mean), 127 | check.attribute = FALSE, 128 | tolerance = 1e-5 129 | ) 130 | }) 131 | 132 | test_that("binomial, auc (sequential)", { 133 | main_penalty <- define_penalty( 134 | penalty_type = 0, 135 | num_penalty = 20, 136 | penalty_ratio = 0.001 137 | ) 138 | 139 | fit_xrnet <- tune_xrnet( 140 | x = xtest_binomial, 141 | y = ytest_binomial, 142 | family = "binomial", 143 | penalty_main = main_penalty, 144 | control = list(tolerance = 1e-10), 145 | loss = "auc", 146 | foldid = foldid_binomial 147 | ) 148 | 149 | expect_equal( 150 | cv_auc, 151 | drop(fit_xrnet$cv_mean), 152 | check.attribute = FALSE, 153 | tolerance = 1e-5 154 | ) 155 | }) 156 | 157 | test_that("binomial, deviance (sequential)", { 158 | main_penalty <- define_penalty( 159 | penalty_type = 0, 160 | num_penalty = 20, 161 | penalty_ratio = 0.001 162 | ) 163 | 164 | fit_xrnet <- tune_xrnet( 165 | x = xtest_binomial, 166 | y = ytest_binomial, 167 | family = "binomial", 168 | penalty_main = main_penalty, 169 | control = list(tolerance = 1e-10), 170 | loss = "deviance", 171 | foldid = foldid_binomial 172 | ) 173 | 174 | expect_equal( 175 | cv_deviance, 176 | drop(fit_xrnet$cv_mean), 177 | check.attribute = FALSE 178 | ) 179 | }) 180 | -------------------------------------------------------------------------------- /R/plot_tune_xrnet.R: -------------------------------------------------------------------------------- 1 | #' Plot k-fold cross-validation error grid 2 | #' 3 | #' @description Generates plots to visualize the mean cross-validation error. 4 | #' If no external data was used in the model fit, a plot of the cross-validated 5 | #' error with standard error bars is generated for all penalty values. If 6 | #' external data was used in the model fit, a contour plot of the 7 | #' cross-validated errors is created. Error curves can also be generated for a 8 | #' fixed value of the primary penalty on x (p) or the external penalty (pext) 9 | #' when external data is used. 10 | #' 11 | #' @param x A tune_xrnet class object 12 | #' @param p (optional) penalty value for x (for generating an error curve across 13 | #' external penalties). Use value "opt" to use the optimal penalty value. 14 | #' @param pext (optional) penalty value for external (for generating an error 15 | #' curve across primary penalties). Use value "opt" to use the optimal penalty 16 | #' value. 17 | #' @param ... Additional graphics parameters 18 | #' 19 | #' @return None 20 | #' 21 | #' @details The parameter values p and pext can be used to generate profiled 22 | #' error curves by fixing either the penalty on x or the penalty on external to 23 | #' a fixed value. You cannot specify both at the same time as this would only 24 | #' return a single point. 25 | #' 26 | #' @examples 27 | #' 28 | #' ## load example data 29 | #' data(GaussianExample) 30 | #' 31 | #' ## 5-fold cross validation 32 | #' cv_xrnet <- tune_xrnet( 33 | #' x = x_linear, 34 | #' y = y_linear, 35 | #' external = ext_linear, 36 | #' family = "gaussian", 37 | #' control = xrnet_control(tolerance = 1e-6) 38 | #' ) 39 | #' 40 | #' ## contour plot of cross-validated error 41 | #' plot(cv_xrnet) 42 | #' 43 | #' ## error curve of external penalties at optimal penalty value 44 | #' plot(cv_xrnet, p = "opt") 45 | #' @export 46 | #' @importFrom graphics filled.contour axis points 47 | #' @importFrom grDevices colorRampPalette 48 | 49 | plot.tune_xrnet <- function(x, p = NULL, pext = NULL, ...) { 50 | if (is.null(x$fitted_model$alphas) || !is.null(p) || !is.null(pext)) { 51 | if (is.null(x$fitted_model$alphas)) { 52 | xval <- log(as.numeric(rownames(x$cv_mean))) 53 | cverr <- x$cv_mean[, 1] 54 | cvsd <- x$cv_sd[, 1] 55 | xlab <- "log(Penalty)" 56 | xopt_val <- log(x$opt_penalty) 57 | } else { 58 | if (!is.null(p) && !is.null(pext)) { 59 | stop( 60 | "Please only specify either penalty or penalty_ext, 61 | cannot specify both at the same time" 62 | ) 63 | } else if (!is.null(p)) { 64 | if (p == "opt") { 65 | p <- x$opt_penalty 66 | } 67 | p_idx <- match(p, x$fitted_model$penalty) 68 | if (is.na(p_idx)) { 69 | stop("The penalty value 'p' is not in the fitted model") 70 | } 71 | xval <- log(as.numeric(colnames(x$cv_mean))) 72 | cverr <- x$cv_mean[p_idx, ] 73 | cvsd <- x$cv_sd[p_idx, ] 74 | xlab <- "log(External Penalty)" 75 | xopt_val <- log(x$opt_penalty_ext) 76 | } else { 77 | if (pext == "opt") { 78 | pext <- x$opt_penalty_ext 79 | } 80 | pext_idx <- match(pext, x$fitted_model$penalty_ext) 81 | if (is.na(pext_idx)) { 82 | stop("The penalty value 'p' is not in the fitted model") 83 | } 84 | xval <- log(as.numeric(rownames(x$cv_mean))) 85 | cverr <- x$cv_mean[, pext_idx] 86 | cvsd <- x$cv_sd[, pext_idx] 87 | xlab <- "log(Penalty)" 88 | xopt_val <- log(x$opt_penalty) 89 | } 90 | } 91 | graphics::plot( 92 | x = xval, 93 | y = cverr, 94 | ylab = paste0("Mean CV Error (", x$loss, ")"), 95 | xlab = xlab, 96 | ylim = range(c(cverr - cvsd, cverr + cvsd)), 97 | type = "n" 98 | ) 99 | graphics::arrows( 100 | xval, 101 | cverr - cvsd, 102 | xval, 103 | cverr + cvsd, 104 | length = 0.025, 105 | angle = 90, 106 | code = 3, 107 | col = "lightgray" 108 | ) 109 | graphics::points( 110 | x = xval, 111 | y = cverr, 112 | col = "dodgerblue4", 113 | pch = 16, 114 | ) 115 | graphics::abline(v = xopt_val, col = "firebrick") 116 | } else { 117 | cvgrid <- x$cv_mean 118 | cvgrid <- cvgrid[rev(seq_len(nrow(cvgrid))), ] 119 | cvgrid <- cvgrid[, rev(seq_len(ncol(cvgrid)))] 120 | minx <- log(x$opt_penalty_ext) 121 | miny <- log(x$opt_penalty) 122 | 123 | contour_colors <- c( 124 | "#014636", "#016C59", "#02818A", "#3690C0", 125 | "#67A9CF", "#A6BDDB", "#D0D1E6", "#ECE2F0", "#FFF7FB" 126 | ) 127 | 128 | graphics::filled.contour( 129 | x = log(as.numeric(colnames(cvgrid))), 130 | y = log(as.numeric(rownames(cvgrid))), 131 | z = t(cvgrid), 132 | col = colorRampPalette(contour_colors)(25), 133 | xlab = "log(External Penalty)", 134 | ylab = "log(Penalty)", 135 | plot.axes = { 136 | axis(1) 137 | axis(2) 138 | points(minx, miny, col = "red", pch = 16) 139 | } 140 | ) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /docs/pkgdown.js: -------------------------------------------------------------------------------- 1 | /* http://gregfranko.com/blog/jquery-best-practices/ */ 2 | (function($) { 3 | $(function() { 4 | 5 | $('nav.navbar').headroom(); 6 | 7 | Toc.init({ 8 | $nav: $("#toc"), 9 | $scope: $("main h2, main h3, main h4, main h5, main h6") 10 | }); 11 | 12 | if ($('#toc').length) { 13 | $('body').scrollspy({ 14 | target: '#toc', 15 | offset: $("nav.navbar").outerHeight() + 1 16 | }); 17 | } 18 | 19 | // Activate popovers 20 | $('[data-bs-toggle="popover"]').popover({ 21 | container: 'body', 22 | html: true, 23 | trigger: 'focus', 24 | placement: "top", 25 | sanitize: false, 26 | }); 27 | 28 | $('[data-bs-toggle="tooltip"]').tooltip(); 29 | 30 | /* Clipboard --------------------------*/ 31 | 32 | function changeTooltipMessage(element, msg) { 33 | var tooltipOriginalTitle=element.getAttribute('data-bs-original-title'); 34 | element.setAttribute('data-bs-original-title', msg); 35 | $(element).tooltip('show'); 36 | element.setAttribute('data-bs-original-title', tooltipOriginalTitle); 37 | } 38 | 39 | if(ClipboardJS.isSupported()) { 40 | $(document).ready(function() { 41 | var copyButton = ""; 42 | 43 | $("div.sourceCode").addClass("hasCopyButton"); 44 | 45 | // Insert copy buttons: 46 | $(copyButton).prependTo(".hasCopyButton"); 47 | 48 | // Initialize tooltips: 49 | $('.btn-copy-ex').tooltip({container: 'body'}); 50 | 51 | // Initialize clipboard: 52 | var clipboard = new ClipboardJS('[data-clipboard-copy]', { 53 | text: function(trigger) { 54 | return trigger.parentNode.textContent.replace(/\n#>[^\n]*/g, ""); 55 | } 56 | }); 57 | 58 | clipboard.on('success', function(e) { 59 | changeTooltipMessage(e.trigger, 'Copied!'); 60 | e.clearSelection(); 61 | }); 62 | 63 | clipboard.on('error', function(e) { 64 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); 65 | }); 66 | 67 | }); 68 | } 69 | 70 | /* Search marking --------------------------*/ 71 | var url = new URL(window.location.href); 72 | var toMark = url.searchParams.get("q"); 73 | var mark = new Mark("main#main"); 74 | if (toMark) { 75 | mark.mark(toMark, { 76 | accuracy: { 77 | value: "complementary", 78 | limiters: [",", ".", ":", "/"], 79 | } 80 | }); 81 | } 82 | 83 | /* Search --------------------------*/ 84 | /* Adapted from https://github.com/rstudio/bookdown/blob/2d692ba4b61f1e466c92e78fd712b0ab08c11d31/inst/resources/bs4_book/bs4_book.js#L25 */ 85 | // Initialise search index on focus 86 | var fuse; 87 | $("#search-input").focus(async function(e) { 88 | if (fuse) { 89 | return; 90 | } 91 | 92 | $(e.target).addClass("loading"); 93 | var response = await fetch($("#search-input").data("search-index")); 94 | var data = await response.json(); 95 | 96 | var options = { 97 | keys: ["what", "text", "code"], 98 | ignoreLocation: true, 99 | threshold: 0.1, 100 | includeMatches: true, 101 | includeScore: true, 102 | }; 103 | fuse = new Fuse(data, options); 104 | 105 | $(e.target).removeClass("loading"); 106 | }); 107 | 108 | // Use algolia autocomplete 109 | var options = { 110 | autoselect: true, 111 | debug: true, 112 | hint: false, 113 | minLength: 2, 114 | }; 115 | var q; 116 | async function searchFuse(query, callback) { 117 | await fuse; 118 | 119 | var items; 120 | if (!fuse) { 121 | items = []; 122 | } else { 123 | q = query; 124 | var results = fuse.search(query, { limit: 20 }); 125 | items = results 126 | .filter((x) => x.score <= 0.75) 127 | .map((x) => x.item); 128 | if (items.length === 0) { 129 | items = [{dir:"Sorry 😿",previous_headings:"",title:"No results found.",what:"No results found.",path:window.location.href}]; 130 | } 131 | } 132 | callback(items); 133 | } 134 | $("#search-input").autocomplete(options, [ 135 | { 136 | name: "content", 137 | source: searchFuse, 138 | templates: { 139 | suggestion: (s) => { 140 | if (s.title == s.what) { 141 | return `${s.dir} >
${s.title}
`; 142 | } else if (s.previous_headings == "") { 143 | return `${s.dir} >
${s.title}
> ${s.what}`; 144 | } else { 145 | return `${s.dir} >
${s.title}
> ${s.previous_headings} > ${s.what}`; 146 | } 147 | }, 148 | }, 149 | }, 150 | ]).on('autocomplete:selected', function(event, s) { 151 | window.location.href = s.path + "?q=" + q + "#" + s.id; 152 | }); 153 | }); 154 | })(window.jQuery || window.$) 155 | 156 | 157 | -------------------------------------------------------------------------------- /docs/articles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Articles • xrnet 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 45 | 46 | 47 | 48 | 49 | 50 |
51 |
52 | 104 | 105 | 106 |
107 | 108 |
109 |
110 | 113 | 114 |
115 |

All vignettes

116 |

117 | 118 | 121 |
122 |
123 |
124 | 125 |
126 | 129 | 130 |
131 |

Site built with pkgdown 1.3.0.

132 |
133 |
134 |
135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/GaussianSolver.h: -------------------------------------------------------------------------------- 1 | #ifndef GAUSSIAN_SOLVER_H 2 | #define GAUSSIAN_SOLVER_H 3 | 4 | #include "CoordSolver.h" 5 | 6 | template 7 | class GaussianSolver : public CoordSolver { 8 | 9 | typedef Eigen::VectorXd VecXd; 10 | typedef Eigen::VectorXi VecXi; 11 | typedef Eigen::Map MapMat; 12 | typedef Eigen::MappedSparseMatrix MapSpMat; 13 | typedef Eigen::Map MapVec; 14 | 15 | private: 16 | using CoordSolver::wgts; 17 | using CoordSolver::wgts_user; 18 | using CoordSolver::y; 19 | using CoordSolver::wgts_sum; 20 | using CoordSolver::X; 21 | using CoordSolver::xs; 22 | using CoordSolver::xm; 23 | using CoordSolver::Fixed; 24 | using CoordSolver::XZ; 25 | using CoordSolver::gradient; 26 | using CoordSolver::residuals; 27 | using CoordSolver::intercept; 28 | using CoordSolver::ym; 29 | using CoordSolver::ys; 30 | 31 | public: 32 | // constructor (dense X matrix) 33 | GaussianSolver(const Eigen::Ref & y_, 34 | const Eigen::Ref & X_, 35 | const Eigen::Ref & Fixed_, 36 | const Eigen::Ref & XZ_, 37 | const double * xmptr, 38 | double * xvptr, 39 | const double * xsptr, 40 | VecXd wgts_user_, 41 | bool intercept_, 42 | const double * penalty_type_, 43 | const double * cmult_, 44 | VecXd quantiles_, 45 | const double * ucl_, 46 | const double * lcl_, 47 | int ne_, 48 | int nx_, 49 | double tolerance_, 50 | int max_iterations_) : 51 | CoordSolver(y_, 52 | X_, 53 | Fixed_, 54 | XZ_, 55 | xmptr, 56 | xvptr, 57 | xsptr, 58 | wgts_user_, 59 | intercept_, 60 | penalty_type_, 61 | cmult_, 62 | quantiles_, 63 | ucl_, 64 | lcl_, 65 | ne_, 66 | nx_, 67 | tolerance_, 68 | max_iterations_) 69 | { 70 | init(); 71 | }; 72 | 73 | // constructor (sparse X matrix) 74 | GaussianSolver(const Eigen::Ref & y_, 75 | const MapSpMat X_, 76 | const Eigen::Ref & Fixed_, 77 | const Eigen::Ref & XZ_, 78 | const double * xmptr, 79 | double * xvptr, 80 | const double * xsptr, 81 | VecXd wgts_user_, 82 | bool intercept_, 83 | const double * penalty_type_, 84 | const double * cmult_, 85 | VecXd quantiles_, 86 | const double * ucl_, 87 | const double * lcl_, 88 | int ne_, 89 | int nx_, 90 | double tolerance_, 91 | int max_iterations_) : 92 | CoordSolver(y_, 93 | X_, 94 | Fixed_, 95 | XZ_, 96 | xmptr, 97 | xvptr, 98 | xsptr, 99 | wgts_user_, 100 | intercept_, 101 | penalty_type_, 102 | cmult_, 103 | quantiles_, 104 | ucl_, 105 | lcl_, 106 | ne_, 107 | nx_, 108 | tolerance_, 109 | max_iterations_) 110 | { 111 | init(); 112 | }; 113 | 114 | // destructor 115 | virtual ~GaussianSolver() {} 116 | 117 | // initialize function 118 | void init() { 119 | wgts = wgts_user; 120 | wgts_sum = wgts.sum(); 121 | ym = y.col(0).cwiseProduct(wgts_user).sum(); 122 | ys = std::sqrt(y.col(0).cwiseProduct(y.col(0).cwiseProduct(wgts_user)).sum() - ym * ym); 123 | if (!intercept) {ym = 0.0;} 124 | residuals.array() = wgts.array() * (y.col(0).array() - ym) / ys; 125 | double resids_sum = residuals.sum(); 126 | 127 | int idx = 0; 128 | for (int k = 0; k < X.cols(); ++k, ++idx) { 129 | gradient[idx] = xs[idx] * (X.col(k).dot(residuals) - xm[idx] * resids_sum); 130 | } 131 | idx += Fixed.cols(); 132 | for (int k = 0; k < XZ.cols(); ++k, ++idx) { 133 | gradient[idx] = xs[idx] * (XZ.col(k).dot(residuals) - xm[idx] * resids_sum); 134 | } 135 | } 136 | }; 137 | 138 | #endif // GAUSSIAN_SOLVER_H 139 | -------------------------------------------------------------------------------- /man/tune_xrnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tune_xrnet.R 3 | \name{tune_xrnet} 4 | \alias{tune_xrnet} 5 | \title{k-fold cross-validation for hierarchical regularized regression} 6 | \usage{ 7 | tune_xrnet( 8 | x, 9 | y, 10 | external = NULL, 11 | unpen = NULL, 12 | family = c("gaussian", "binomial"), 13 | penalty_main = define_penalty(), 14 | penalty_external = define_penalty(), 15 | weights = NULL, 16 | standardize = c(TRUE, TRUE), 17 | intercept = c(TRUE, FALSE), 18 | loss = c("deviance", "mse", "mae", "auc"), 19 | nfolds = 5, 20 | foldid = NULL, 21 | parallel = FALSE, 22 | control = list() 23 | ) 24 | } 25 | \arguments{ 26 | \item{x}{predictor design matrix of dimension \eqn{n x p}, matrix options 27 | include: 28 | \itemize{ 29 | \item matrix 30 | \item big.matrix 31 | \item filebacked.big.matrix 32 | \item sparse matrix (dgCMatrix) 33 | }} 34 | 35 | \item{y}{outcome vector of length \eqn{n}} 36 | 37 | \item{external}{(optional) external data design matrix of dimension 38 | \eqn{p x q}, matrix options include: 39 | \itemize{ 40 | \item matrix 41 | \item sparse matrix (dgCMatrix) 42 | }} 43 | 44 | \item{unpen}{(optional) unpenalized predictor design matrix, matrix options 45 | include: 46 | \itemize{ 47 | \item matrix 48 | }} 49 | 50 | \item{family}{error distribution for outcome variable, options include: 51 | \itemize{ 52 | \item "gaussian" 53 | \item "binomial" 54 | }} 55 | 56 | \item{penalty_main}{specifies regularization object for x. See 57 | \code{\link{define_penalty}} for more details.} 58 | 59 | \item{penalty_external}{specifies regularization object for external. See 60 | \code{\link{define_penalty}} for more details. 61 | See \code{\link{define_penalty}} for more details.} 62 | 63 | \item{weights}{optional vector of observation-specific weights. 64 | Default is 1 for all observations.} 65 | 66 | \item{standardize}{indicates whether x and/or external should be 67 | standardized. Default is c(TRUE, TRUE).} 68 | 69 | \item{intercept}{indicates whether an intercept term is included for x and/or 70 | external. Default is c(TRUE, FALSE).} 71 | 72 | \item{loss}{loss function for cross-validation. Options include: 73 | \itemize{ 74 | \item "deviance" 75 | \item "mse" (Mean Squared Error) 76 | \item "mae" (Mean Absolute Error) 77 | \item "auc" (Area under the curve) 78 | }} 79 | 80 | \item{nfolds}{number of folds for cross-validation. Default is 5.} 81 | 82 | \item{foldid}{(optional) vector that identifies user-specified fold for each 83 | observation. If NULL, folds are automatically generated.} 84 | 85 | \item{parallel}{use \code{foreach} function to fit folds in parallel if TRUE, 86 | must register cluster (\code{doParallel}) before using.} 87 | 88 | \item{control}{specifies xrnet control object. See 89 | \code{\link{xrnet_control}} for more details.} 90 | } 91 | \value{ 92 | A list of class \code{tune_xrnet} with components 93 | \item{cv_mean}{mean cross-validated error for each penalty combination. 94 | Object returned is a vector if there is no external data (external = NULL) 95 | and matrix if there is external data.} 96 | \item{cv_sd}{estimated standard deviation for cross-validated errors. 97 | Object returned is a vector if there is no external data (external = NULL) 98 | and matrix if there is external data.} 99 | \item{loss}{loss function used to compute cross-validation error} 100 | \item{opt_loss}{the value of the loss function for the optimal 101 | cross-validated error} 102 | \item{opt_penalty}{first-level penalty value that achieves the optimal loss} 103 | \item{opt_penalty_ext}{second-level penalty value that achieves the optimal 104 | loss (if external data is present)} 105 | \item{fitted_model}{fitted xrnet object using all data, see 106 | \code{\link{xrnet}} for details of object} 107 | } 108 | \description{ 109 | k-fold cross-validation for hierarchical regularized 110 | regression \code{\link{xrnet}} 111 | } 112 | \details{ 113 | k-fold cross-validation is used to determine the 'optimal' 114 | combination of hyperparameter values, where optimal is based on the optimal 115 | value obtained for the user-selected loss function across the k folds. To 116 | efficiently traverse all possible combinations of the hyperparameter values, 117 | 'warm-starts' are used to traverse the penalty from largest to smallest 118 | penalty value(s). Note that the penalty grid for the folds is generated 119 | by fitting the model on the entire training data. Parallelization is enabled 120 | through the \code{foreach} and \code{doParallel} R packages. To use 121 | parallelization, \code{parallel = TRUE}, you must first create the cluster 122 | \code{makeCluster} and then register the cluster \code{registerDoParallel}. 123 | See the \code{parallel}, \code{foreach}, and/or \code{doParallel} R packages 124 | for more details on how to setup parallelization. 125 | } 126 | \examples{ 127 | ## cross validation of hierarchical linear regression model 128 | data(GaussianExample) 129 | 130 | ## 5-fold cross validation 131 | cv_xrnet <- tune_xrnet( 132 | x = x_linear, 133 | y = y_linear, 134 | external = ext_linear, 135 | family = "gaussian", 136 | control = xrnet_control(tolerance = 1e-6) 137 | ) 138 | 139 | ## contour plot of cross-validated error 140 | plot(cv_xrnet) 141 | } 142 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | (function() { 6 | 'use strict'; 7 | 8 | window.Toc = { 9 | helpers: { 10 | // return all matching elements in the set, or their descendants 11 | findOrFilter: function($el, selector) { 12 | // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/ 13 | // http://stackoverflow.com/a/12731439/358804 14 | var $descendants = $el.find(selector); 15 | return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])'); 16 | }, 17 | 18 | generateUniqueIdBase: function(el) { 19 | var text = $(el).text(); 20 | var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-'); 21 | return anchor || el.tagName.toLowerCase(); 22 | }, 23 | 24 | generateUniqueId: function(el) { 25 | var anchorBase = this.generateUniqueIdBase(el); 26 | for (var i = 0; ; i++) { 27 | var anchor = anchorBase; 28 | if (i > 0) { 29 | // add suffix 30 | anchor += '-' + i; 31 | } 32 | // check if ID already exists 33 | if (!document.getElementById(anchor)) { 34 | return anchor; 35 | } 36 | } 37 | }, 38 | 39 | generateAnchor: function(el) { 40 | if (el.id) { 41 | return el.id; 42 | } else { 43 | var anchor = this.generateUniqueId(el); 44 | el.id = anchor; 45 | return anchor; 46 | } 47 | }, 48 | 49 | createNavList: function() { 50 | return $(''); 51 | }, 52 | 53 | createChildNavList: function($parent) { 54 | var $childList = this.createNavList(); 55 | $parent.append($childList); 56 | return $childList; 57 | }, 58 | 59 | generateNavEl: function(anchor, text) { 60 | var $a = $(''); 61 | $a.attr('href', '#' + anchor); 62 | $a.text(text); 63 | var $li = $('
  • '); 64 | $li.append($a); 65 | return $li; 66 | }, 67 | 68 | generateNavItem: function(headingEl) { 69 | var anchor = this.generateAnchor(headingEl); 70 | var $heading = $(headingEl); 71 | var text = $heading.data('toc-text') || $heading.text(); 72 | return this.generateNavEl(anchor, text); 73 | }, 74 | 75 | // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). 76 | getTopLevel: function($scope) { 77 | for (var i = 1; i <= 6; i++) { 78 | var $headings = this.findOrFilter($scope, 'h' + i); 79 | if ($headings.length > 1) { 80 | return i; 81 | } 82 | } 83 | 84 | return 1; 85 | }, 86 | 87 | // returns the elements for the top level, and the next below it 88 | getHeadings: function($scope, topLevel) { 89 | var topSelector = 'h' + topLevel; 90 | 91 | var secondaryLevel = topLevel + 1; 92 | var secondarySelector = 'h' + secondaryLevel; 93 | 94 | return this.findOrFilter($scope, topSelector + ',' + secondarySelector); 95 | }, 96 | 97 | getNavLevel: function(el) { 98 | return parseInt(el.tagName.charAt(1), 10); 99 | }, 100 | 101 | populateNav: function($topContext, topLevel, $headings) { 102 | var $context = $topContext; 103 | var $prevNav; 104 | 105 | var helpers = this; 106 | $headings.each(function(i, el) { 107 | var $newNav = helpers.generateNavItem(el); 108 | var navLevel = helpers.getNavLevel(el); 109 | 110 | // determine the proper $context 111 | if (navLevel === topLevel) { 112 | // use top level 113 | $context = $topContext; 114 | } else if ($prevNav && $context === $topContext) { 115 | // create a new level of the tree and switch to it 116 | $context = helpers.createChildNavList($prevNav); 117 | } // else use the current $context 118 | 119 | $context.append($newNav); 120 | 121 | $prevNav = $newNav; 122 | }); 123 | }, 124 | 125 | parseOps: function(arg) { 126 | var opts; 127 | if (arg.jquery) { 128 | opts = { 129 | $nav: arg 130 | }; 131 | } else { 132 | opts = arg; 133 | } 134 | opts.$scope = opts.$scope || $(document.body); 135 | return opts; 136 | } 137 | }, 138 | 139 | // accepts a jQuery object, or an options object 140 | init: function(opts) { 141 | opts = this.helpers.parseOps(opts); 142 | 143 | // ensure that the data attribute is in place for styling 144 | opts.$nav.attr('data-toggle', 'toc'); 145 | 146 | var $topContext = this.helpers.createChildNavList(opts.$nav); 147 | var topLevel = this.helpers.getTopLevel(opts.$scope); 148 | var $headings = this.helpers.getHeadings(opts.$scope, topLevel); 149 | this.helpers.populateNav($topContext, topLevel, $headings); 150 | } 151 | }; 152 | 153 | $(function() { 154 | $('nav[data-toggle="toc"]').each(function(i, el) { 155 | var $nav = $(el); 156 | Toc.init($nav); 157 | }); 158 | }); 159 | })(); 160 | -------------------------------------------------------------------------------- /tests/testthat/test-predict-function.R: -------------------------------------------------------------------------------- 1 | library(bigmemory) 2 | 3 | context("test predict function works correctly") 4 | 5 | test_that("predict returns estimates for penalties already fit by xrnet object", { 6 | main_penalty <- define_penalty(0, user_penalty = c(2, 1, 0.05)) 7 | external_penalty <- define_penalty(1, user_penalty = c(0.2, 0.1, 0.05)) 8 | 9 | test_control <- xrnet_control(tolerance = 1e-15) 10 | 11 | xrnet_object <- xrnet( 12 | x = xtest, 13 | y = ytest, 14 | external = ztest, 15 | family = "gaussian", 16 | penalty_main = main_penalty, 17 | penalty_external = external_penalty, 18 | control = test_control 19 | ) 20 | 21 | test_pred <- predict(xrnet_object, p = 1, pext = 0.05, type = "coefficients") 22 | test_coef <- coef(xrnet_object, p = 1, pext = 0.05) 23 | 24 | expect_identical(drop(test_pred$betas), xrnet_object$betas[, 2, 3]) 25 | expect_identical(drop(test_coef$betas), xrnet_object$betas[, 2, 3]) 26 | expect_identical(drop(test_pred$beta0), xrnet_object$beta0[2, 3]) 27 | expect_identical(drop(test_pred$alphas), xrnet_object$alphas[, 2, 3]) 28 | expect_identical(drop(test_pred$alpha0), xrnet_object$alpha0[2, 3]) 29 | }) 30 | 31 | test_that("predict returns right predictions for penalties already fit by xrnet object", { 32 | main_penalty <- define_penalty(0, user_penalty = c(2, 1, 0.05)) 33 | external_penalty <- define_penalty(1, user_penalty = c(0.2, 0.1, 0.05)) 34 | 35 | test_control <- xrnet_control(tolerance = 1e-15) 36 | 37 | xrnet_object <- xrnet( 38 | x = xtest, 39 | y = ytest, 40 | external = ztest, 41 | family = "gaussian", 42 | penalty_main = main_penalty, 43 | penalty_external = external_penalty, 44 | control = test_control 45 | ) 46 | 47 | xtest_big <- as.big.matrix(xtest) 48 | 49 | predy <- cbind(1, xtest) %*% c(xrnet_object$beta0[2, 2], xrnet_object$betas[, 2, 2]) 50 | pred_xrnet <- predict(xrnet_object, p = 1, pext = 0.1, newdata = xtest) 51 | pred_xrnet_big <- predict(xrnet_object, p = 1, pext = 0.1, newdata = xtest_big) 52 | pred_xrnet_sparse <- predict(xrnet_object, p = 1, pext = 0.1, newdata = xsparse) 53 | expect_equivalent(pred_xrnet, predy) 54 | expect_equivalent(pred_xrnet_big, predy) 55 | expect_equivalent(pred_xrnet_sparse, predy) 56 | 57 | predy <- cbind(1, xtest) %*% c(xrnet_object$beta0[2, 3], xrnet_object$betas[, 2, 3]) 58 | pred_xrnet <- predict(xrnet_object, p = 1, pext = 0.05, newdata = xtest) 59 | pred_xrnet_big <- predict(xrnet_object, p = 1, pext = 0.05, newdata = xtest_big) 60 | pred_xrnet_sparse <- predict(xrnet_object, p = 1, pext = 0.05, newdata = xsparse) 61 | expect_equivalent(pred_xrnet, predy) 62 | expect_equivalent(pred_xrnet_big, predy) 63 | expect_equivalent(pred_xrnet_sparse, predy) 64 | 65 | predy <- cbind(1, xtest) %*% c(xrnet_object$beta0[1, 3], xrnet_object$betas[, 1, 3]) 66 | pred_xrnet <- predict(xrnet_object, p = 2, pext = 0.05, newdata = xtest) 67 | pred_xrnet_big <- predict(xrnet_object, p = 2, pext = 0.05, newdata = xtest_big) 68 | pred_xrnet_sparse <- predict(xrnet_object, p = 2, pext = 0.05, newdata = xsparse) 69 | expect_equivalent(pred_xrnet, predy) 70 | expect_equivalent(pred_xrnet_big, predy) 71 | expect_equivalent(pred_xrnet_sparse, predy) 72 | }) 73 | 74 | test_that("predict returns right predictions for penalties already fit by xrnet object, no external data", { 75 | main_penalty <- define_penalty(penalty_type = 0, user_penalty = c(2, 1, 0.05)) 76 | 77 | test_control <- xrnet_control(tolerance = 1e-15) 78 | 79 | xrnet_object <- xrnet( 80 | x = xtest, 81 | y = ytest, 82 | family = "gaussian", 83 | intercept = c(T, F), 84 | penalty_main = main_penalty, 85 | control = test_control 86 | ) 87 | 88 | predy1 <- cbind(1, xtest) %*% c(xrnet_object$beta0[1, 1], xrnet_object$betas[, 1, 1]) 89 | predy2 <- cbind(1, xtest) %*% c(xrnet_object$beta0[2, 1], xrnet_object$betas[, 2, 1]) 90 | predy3 <- cbind(1, xtest) %*% c(xrnet_object$beta0[3, 1], xrnet_object$betas[, 3, 1]) 91 | pred_xrnet <- predict(xrnet_object, p = c(0.05, 1, 2), newdata = xtest) 92 | expect_equivalent(pred_xrnet, cbind(predy1, predy2, predy3)) 93 | }) 94 | 95 | test_that("predict returns right estimates for penalties already fit by tune_xrnet object", { 96 | main_penalty <- define_penalty(0, user_penalty = c(2, 1, 0.05)) 97 | external_penalty <- define_penalty(1, user_penalty = c(0.2, 0.1, 0.05)) 98 | 99 | test_control <- xrnet_control(tolerance = 1e-15) 100 | 101 | xrnet_object <- tune_xrnet( 102 | x = xtest, 103 | y = ytest, 104 | external = ztest, 105 | family = "gaussian", 106 | penalty_main = main_penalty, 107 | penalty_external = external_penalty, 108 | control = test_control 109 | ) 110 | 111 | test_pred <- predict(xrnet_object, p = "opt", pext = "opt", type = "coefficients") 112 | test_coef <- coef(xrnet_object, p = "opt", pext = "opt") 113 | 114 | optl1 <- which(xrnet_object$fitted_model$penalty == xrnet_object$opt_penalty) 115 | optl2 <- which(xrnet_object$fitted_model$penalty_ext == xrnet_object$opt_penalty_ext) 116 | 117 | expect_identical(drop(test_pred$betas), xrnet_object$fitted_model$betas[, optl1, optl2]) 118 | expect_identical(drop(test_coef$betas), xrnet_object$fitted_model$betas[, optl1, optl2]) 119 | expect_identical(drop(test_pred$beta0), xrnet_object$fitted_model$beta0[optl1, optl2]) 120 | expect_identical(drop(test_pred$alphas), xrnet_object$fitted_model$alphas[, optl1, optl2]) 121 | expect_identical(drop(test_pred$alpha0), xrnet_object$fitted_model$alpha0[optl1, optl2]) 122 | }) 123 | -------------------------------------------------------------------------------- /R/predict_xrnet.R: -------------------------------------------------------------------------------- 1 | #' Predict function for "xrnet" object 2 | #' 3 | #' @description Extract coefficients or predict response in new data using 4 | #' fitted model from an \code{\link{xrnet}} object. Note that we currently only 5 | #' support returning coefficient estimates that are in the original path(s). 6 | #' 7 | #' @param object A \code{\link{xrnet}} object 8 | #' @param newdata matrix with new values for penalized variables 9 | #' @param newdata_fixed matrix with new values for unpenalized variables 10 | #' @param p vector of penalty values to apply to predictor variables 11 | #' @param pext vector of penalty values to apply to external data variables 12 | #' @param type type of prediction to make using the xrnet model, options 13 | #' include: 14 | #' \itemize{ 15 | #' \item response 16 | #' \item link (linear predictor) 17 | #' \item coefficients 18 | #' } 19 | #' @param ... pass other arguments to xrnet function (if needed) 20 | #' 21 | #' @return The object returned is based on the value of type as follows: 22 | #' \itemize{ 23 | #' \item response: An array with the response predictions based on the data 24 | #' for each penalty combination 25 | #' \item link: An array with linear predictions based on the data for each 26 | #' penalty combination 27 | #' \item coefficients: A list with the coefficient estimates for each 28 | #' penalty combination. See \code{\link{coef.xrnet}}. 29 | #' } 30 | #' @examples 31 | #' data(GaussianExample) 32 | #' 33 | #' fit_xrnet <- xrnet( 34 | #' x = x_linear, 35 | #' y = y_linear, 36 | #' external = ext_linear, 37 | #' family = "gaussian" 38 | #' ) 39 | #' 40 | #' lambda1 <- fit_xrnet$penalty[10] 41 | #' lambda2 <- fit_xrnet$penalty_ext[10] 42 | #' 43 | #' coef_xrnet <- predict( 44 | #' fit_xrnet, 45 | #' p = lambda1, 46 | #' pext = lambda2, 47 | #' type = "coefficients" 48 | #' ) 49 | #' 50 | #' pred_xrnet <- predict( 51 | #' fit_xrnet, 52 | #' p = lambda1, 53 | #' pext = lambda2, 54 | #' newdata = x_linear, 55 | #' type = "response" 56 | #' ) 57 | #' @export 58 | predict.xrnet <- function(object, 59 | newdata = NULL, 60 | newdata_fixed = NULL, 61 | p = NULL, 62 | pext = NULL, 63 | type = c("response", "link", "coefficients"), 64 | ...) { 65 | if (missing(type)) { 66 | type <- "response" 67 | } else { 68 | type <- match.arg(type) 69 | } 70 | 71 | if (missing(newdata) && !match(type, c("coefficients"), FALSE)) { 72 | stop("newdata needs to be specified") 73 | } 74 | 75 | if (is.null(p)) { 76 | stop("p not specified") 77 | } 78 | 79 | if (!is.null(object$penalty_ext) && is.null(pext)) { 80 | stop("pext not specified") 81 | } 82 | 83 | if (!(all(p %in% object$penalty)) || !(all(pext %in% object$penalty_ext))) { 84 | stop( 85 | "Not all penalty values in path(s), 86 | please refit xrnet() model with desired penalty values" 87 | ) 88 | } 89 | 90 | p <- rev(sort(p)) 91 | idxl1 <- which(object$penalty %in% p) 92 | if (!is.null(object$penalty_ext)) { 93 | pext <- rev(sort(pext)) 94 | idxl2 <- which(object$penalty_ext %in% pext) 95 | } else { 96 | idxl2 <- 1 97 | } 98 | 99 | beta0 <- object$beta0[idxl1, idxl2, drop = F] 100 | betas <- object$betas[, idxl1, idxl2, drop = F] 101 | gammas <- object$gammas[, idxl1, idxl2, drop = F] 102 | alpha0 <- object$alpha0[idxl1, idxl2, drop = F] 103 | alphas <- object$alphas[, idxl1, idxl2, drop = F] 104 | 105 | if (type == "coefficients") { 106 | return(list( 107 | beta0 = beta0, 108 | betas = betas, 109 | gammas = gammas, 110 | alpha0 = alpha0, 111 | alphas = alphas, 112 | penalty = p, 113 | penalty_ext = pext 114 | )) 115 | } 116 | 117 | if (type %in% c("link", "response")) { 118 | if (is(newdata, "matrix")) { 119 | if (typeof(newdata) != "double") { 120 | stop("newdata must be of type double") 121 | } 122 | mattype_x <- 1 123 | } 124 | else if (is.big.matrix(newdata)) { 125 | if (bigmemory::describe(newdata)@description$type != "double") { 126 | stop("newdata must be of type double") 127 | } 128 | mattype_x <- 2 129 | } else if ("dgCMatrix" %in% class(newdata)) { 130 | if (typeof(newdata@x) != "double") { 131 | stop("newdata must be of type double") 132 | } 133 | mattype_x <- 3 134 | } else { 135 | stop( 136 | "newdata must be a matrix, big.matrix, 137 | filebacked.big.matrix, or dgCMatrix" 138 | ) 139 | } 140 | 141 | beta0 <- as.vector(beta0) 142 | betas <- `dim<-`( 143 | aperm(betas, c(1, 3, 2)), 144 | c(dim(betas)[1], dim(betas)[2] * dim(betas)[3]) 145 | ) 146 | if (!is.null(gammas)) { 147 | gammas <- `dim<-`( 148 | aperm(gammas, c(1, 3, 2)), 149 | c(dim(gammas)[1], dim(gammas)[2] * dim(gammas)[3]) 150 | ) 151 | } else { 152 | gammas <- matrix(vector("numeric", 0), 0, 0) 153 | newdata_fixed <- matrix(vector("numeric", 0), 0, 0) 154 | } 155 | 156 | result <- computeResponseRcpp( 157 | newdata, 158 | mattype_x, 159 | newdata_fixed, 160 | beta0, 161 | betas, 162 | gammas, 163 | type, 164 | object$family 165 | ) 166 | 167 | if (length(pext) > 1) { 168 | dim(result) <- c(NROW(result), length(pext), length(p)) 169 | result <- aperm(result, c(1, 3, 2)) 170 | } 171 | return(drop(result)) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /man/xrnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/xrnet.R 3 | \name{xrnet} 4 | \alias{xrnet} 5 | \title{Fit hierarchical regularized regression model} 6 | \usage{ 7 | xrnet( 8 | x, 9 | y, 10 | external = NULL, 11 | unpen = NULL, 12 | family = c("gaussian", "binomial"), 13 | penalty_main = define_penalty(), 14 | penalty_external = define_penalty(), 15 | weights = NULL, 16 | standardize = c(TRUE, TRUE), 17 | intercept = c(TRUE, FALSE), 18 | control = list() 19 | ) 20 | } 21 | \arguments{ 22 | \item{x}{predictor design matrix of dimension \eqn{n x p}, matrix options 23 | include: 24 | \itemize{ 25 | \item matrix 26 | \item big.matrix 27 | \item filebacked.big.matrix 28 | \item sparse matrix (dgCMatrix) 29 | }} 30 | 31 | \item{y}{outcome vector of length \eqn{n}} 32 | 33 | \item{external}{(optional) external data design matrix of dimension 34 | \eqn{p x q}, 35 | matrix options include: 36 | \itemize{ 37 | \item matrix 38 | \item sparse matrix (dgCMatrix) 39 | }} 40 | 41 | \item{unpen}{(optional) unpenalized predictor design matrix, matrix options 42 | include: 43 | \itemize{ 44 | \item matrix 45 | }} 46 | 47 | \item{family}{error distribution for outcome variable, options include: 48 | \itemize{ 49 | \item "gaussian" 50 | \item "binomial" 51 | }} 52 | 53 | \item{penalty_main}{specifies regularization object for x. See 54 | \code{\link{define_penalty}} for more details.} 55 | 56 | \item{penalty_external}{specifies regularization object for external. See 57 | \code{\link{define_penalty}} for more details.} 58 | 59 | \item{weights}{optional vector of observation-specific weights. Default is 1 60 | for all observations.} 61 | 62 | \item{standardize}{indicates whether x and/or external should be 63 | standardized. Default is c(TRUE, TRUE).} 64 | 65 | \item{intercept}{indicates whether an intercept term is included for x 66 | and/or external. Default is c(TRUE, FALSE).} 67 | 68 | \item{control}{specifies xrnet control object. See 69 | \code{\link{xrnet_control}} for more details.} 70 | } 71 | \value{ 72 | A list of class \code{xrnet} with components: 73 | \item{beta0}{matrix of first-level intercepts indexed by penalty values} 74 | \item{betas}{3-dimensional array of first-level penalized coefficients 75 | indexed by penalty values} 76 | \item{gammas}{3-dimensional array of first-level non-penalized coefficients 77 | indexed by penalty values} 78 | \item{alpha0}{matrix of second-level intercepts indexed by penalty values} 79 | \item{alphas}{3-dimensional array of second-level external data coefficients 80 | indexed by penalty values} 81 | \item{penalty}{vector of first-level penalty values} 82 | \item{penalty_ext}{vector of second-level penalty values} 83 | \item{family}{error distribution for outcome variable} 84 | \item{num_passes}{total number of passes over the data in the coordinate 85 | descent algorithm} 86 | \item{status}{error status for xrnet fitting} 87 | \itemize{ 88 | \item 0 = OK 89 | \item 1 = Error/Warning 90 | } 91 | \item{error_msg}{description of error} 92 | } 93 | \description{ 94 | Fits hierarchical regularized regression model that enables the 95 | incorporation of external data for predictor variables. Both the predictor 96 | variables and external data can be regularized by the most common penalties 97 | (lasso, ridge, elastic net). Solutions are computed across a two-dimensional 98 | grid of penalties (a separate penalty path is computed for the predictors and 99 | external variables). Currently support regularized linear and logistic 100 | regression, future extensions to other outcomes (i.e. Cox regression) will be 101 | implemented in the next major update. 102 | } 103 | \details{ 104 | This function extends the coordinate descent algorithm of the 105 | R package \code{glmnet} to allow the type of regularization (i.e. ridge, 106 | lasso) to be feature-specific. This extension is used to enable fitting 107 | hierarchical regularized regression models, where external information for 108 | the predictors can be included in the \code{external=} argument. In addition, 109 | elements of the R package \code{biglasso} are utilized to enable the use of 110 | standard R matrices, memory-mapped matrices from the \code{bigmemory} 111 | package, or sparse matrices from the \code{Matrix} package. 112 | } 113 | \examples{ 114 | ### hierarchical regularized linear regression ### 115 | data(GaussianExample) 116 | 117 | ## define penalty for predictors and external variables 118 | ## default is ridge for predictors and lasso for external 119 | ## see define_penalty() function for more details 120 | 121 | penMain <- define_penalty(0, num_penalty = 20) 122 | penExt <- define_penalty(1, num_penalty = 20) 123 | 124 | ## fit model with defined regularization 125 | fit_xrnet <- xrnet( 126 | x = x_linear, 127 | y = y_linear, 128 | external = ext_linear, 129 | family = "gaussian", 130 | penalty_main = penMain, 131 | penalty_external = penExt 132 | ) 133 | } 134 | \references{ 135 | Jerome Friedman, Trevor Hastie, Robert Tibshirani (2010). 136 | Regularization Paths for Generalized Linear Models via Coordinate Descent. 137 | Journal of Statistical Software, 33(1), 1-22. URL 138 | http://www.jstatsoft.org/v33/i01/. 139 | 140 | Zeng, Y., and Breheny, P. (2017). 141 | The biglasso Package: A Memory- and Computation-Efficient Solver for Lasso 142 | Model Fitting with Big Data in R. arXiv preprint arXiv:1701.05936. URL 143 | https://arxiv.org/abs/1701.05936. 144 | 145 | Michael J. Kane, John Emerson, Stephen Weston (2013). 146 | Scalable Strategies for Computing with Massive Data. 147 | Journal of Statistical Software, 55(14), 1-19. URL 148 | http://www.jstatsoft.org/v55/i14/. 149 | } 150 | -------------------------------------------------------------------------------- /src/Xrnet.h: -------------------------------------------------------------------------------- 1 | #ifndef XRNET_H 2 | #define XRNET_H 3 | 4 | #include 5 | #include "CoordSolver.h" 6 | 7 | template 8 | class Xrnet { 9 | 10 | typedef Eigen::VectorXd VecXd; 11 | typedef Eigen::MatrixXd MatXd; 12 | typedef Eigen::Map MapVec; 13 | typedef Eigen::Map MapMat; 14 | 15 | protected: 16 | const int n; 17 | const int nv_x; 18 | const int nv_fixed; 19 | const int nv_ext; 20 | const bool intr; 21 | const bool intr_ext; 22 | TZ ext; 23 | MapVec xm; 24 | MapVec cent; 25 | MapVec xs; 26 | const double ym; 27 | const double ys; 28 | VecXd beta0; 29 | MatXd betas; 30 | MatXd gammas; 31 | VecXd alpha0; 32 | MatXd alphas; 33 | VecXd strong_sum; 34 | 35 | public: 36 | // constructor (dense external) 37 | Xrnet(const int & n_, 38 | const int & nv_x_, 39 | const int & nv_fixed_, 40 | const int & nv_ext_, 41 | const int & nv_total_, 42 | const bool & intr_, 43 | const bool & intr_ext_, 44 | const Eigen::Ref & ext_, 45 | const double * xmptr, 46 | const double * centptr, 47 | const double * xsptr, 48 | const double & ym_, 49 | const double & ys_, 50 | const int & num_penalty_) : 51 | n(n_), 52 | nv_x(nv_x_), 53 | nv_fixed(nv_fixed_), 54 | nv_ext(nv_ext_), 55 | intr(intr_), 56 | intr_ext(intr_ext_), 57 | ext(ext_.data(), nv_x_, nv_ext_), 58 | xm(xmptr, nv_total_), 59 | cent(centptr, nv_total_), 60 | xs(xsptr, nv_total_), 61 | ym(ym_), 62 | ys(ys_) 63 | { 64 | beta0 = Eigen::VectorXd::Zero(num_penalty_); 65 | betas = Eigen::MatrixXd::Zero(nv_x_, num_penalty_); 66 | gammas = Eigen::MatrixXd::Zero(nv_fixed_, num_penalty_); 67 | alpha0 = Eigen::VectorXd::Zero(num_penalty_); 68 | alphas = Eigen::MatrixXd::Zero(nv_ext_, num_penalty_); 69 | strong_sum = Eigen::VectorXd::Zero(num_penalty_); 70 | }; 71 | 72 | // constructor (sparse external) 73 | Xrnet(const int & n_, 74 | const int & nv_x_, 75 | const int & nv_fixed_, 76 | const int & nv_ext_, 77 | const int & nv_total_, 78 | const bool & intr_, 79 | const bool & intr_ext_, 80 | const Eigen::MappedSparseMatrix & ext_, 81 | const double * xmptr, 82 | const double * centptr, 83 | const double * xsptr, 84 | const double & ym_, 85 | const double & ys_, 86 | const int & num_penalty_) : 87 | n(n_), 88 | nv_x(nv_x_), 89 | nv_fixed(nv_fixed_), 90 | nv_ext(nv_ext_), 91 | intr(intr_), 92 | intr_ext(intr_ext_), 93 | ext(ext_), 94 | xm(xmptr, nv_total_), 95 | cent(centptr, nv_total_), 96 | xs(xsptr, nv_total_), 97 | ym(ym_), 98 | ys(ys_) 99 | { 100 | beta0 = Eigen::VectorXd::Zero(num_penalty_); 101 | betas = Eigen::MatrixXd::Zero(nv_x_, num_penalty_); 102 | gammas = Eigen::MatrixXd::Zero(nv_fixed_, num_penalty_); 103 | alpha0 = Eigen::VectorXd::Zero(num_penalty_); 104 | alphas = Eigen::MatrixXd::Zero(nv_ext_, num_penalty_); 105 | strong_sum = Eigen::VectorXd::Zero(num_penalty_); 106 | }; 107 | 108 | // destructor 109 | virtual ~Xrnet(){}; 110 | 111 | // getters 112 | VecXd getXm(){return xm;}; 113 | VecXd getBeta0(){return beta0;}; 114 | double getBeta0(const int & idx){return beta0[idx];}; 115 | MatXd getBetas(){return betas;}; 116 | VecXd getBetas(const int & idx){return betas.col(idx);}; 117 | MatXd getGammas(){return gammas;}; 118 | VecXd getAlpha0(){return alpha0;}; 119 | MatXd getAlphas(){return alphas;}; 120 | 121 | // save results for single penalty 122 | virtual void add_results(double b0, VecXd coef, const int & idx) { 123 | 124 | // unstandardize variables by sd of y (if continuous) 125 | coef = ys * coef.cwiseProduct(xs); 126 | b0 *= ys; 127 | 128 | // get external coefficients 129 | if (nv_ext > 0) { 130 | alphas.col(idx) = coef.tail(nv_ext); 131 | } 132 | 133 | // unstandardize predictors w/ external data (x) 134 | if (nv_ext + intr_ext > 0) { 135 | VecXd z_alpha = Eigen::VectorXd::Zero(nv_x); 136 | if (intr_ext) { 137 | z_alpha.array() += coef[nv_x + nv_fixed]; 138 | } 139 | if (nv_ext > 0) { 140 | z_alpha += ext * coef.tail(nv_ext); 141 | } 142 | betas.col(idx) = z_alpha.cwiseProduct(xs.head(nv_x)) + coef.head(nv_x); 143 | } 144 | else { 145 | betas.col(idx) = coef.head(nv_x); 146 | } 147 | 148 | // unstandardize predictors w/o external data (fixed) 149 | if (nv_fixed > 0) { 150 | gammas.col(idx) = coef.segment(nv_x, nv_fixed); 151 | } 152 | 153 | // compute 2nd level intercepts 154 | if (intr_ext) { 155 | if (nv_ext > 0) { 156 | alpha0[idx] = betas.col(idx).mean() - xm.tail(nv_ext).dot(alphas.col(idx)); 157 | } 158 | else { 159 | alpha0[idx] = betas.col(idx).mean(); 160 | } 161 | } 162 | 163 | // compute 1st level intercepts 164 | if (intr) { 165 | beta0[idx] = (ym + b0) - cent.head(nv_x).dot(betas.col(idx)); 166 | if (nv_fixed > 0) { 167 | beta0[idx] -= cent.segment(nv_x, nv_fixed).dot(gammas.col(idx)); 168 | } 169 | } 170 | } 171 | }; 172 | 173 | #endif // XRNET_H 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Page not found (404) • xrnet 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 | 26 | 27 | Skip to contents 28 | 29 | 30 |
    67 |
    68 |
    72 | 73 | Content not found. Please use links in the navbar. 74 | 75 |
    76 |
    77 | 78 | 79 |
    82 | 83 | 86 | 87 |
    88 |
    89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /docs/reference/y_linear.html: -------------------------------------------------------------------------------- 1 | 2 | Simulated outcome data — y_linear • xrnet 6 | Skip to contents 7 | 8 | 9 |
    41 |
    42 |
    47 | 48 |
    49 |

    Simulated outcome data

    50 |
    51 | 52 |
    53 |

    Usage

    54 |
    y_linear
    55 |
    56 | 57 |
    58 |

    Format

    59 |

    A vector with 100 elements

    60 |
    61 | 62 |
    64 | 65 | 66 |
    69 | 70 | 73 | 74 |
    75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/reference/ext_linear.html: -------------------------------------------------------------------------------- 1 | 2 | Simulated external data — ext_linear • xrnet 6 | Skip to contents 7 | 8 | 9 |
    41 |
    42 |
    47 | 48 |
    49 |

    Simulated external data

    50 |
    51 | 52 |
    53 |

    Usage

    54 |
    ext_linear
    55 |
    56 | 57 |
    58 |

    Format

    59 |

    A matrix with 50 rows and 4 columns

    60 |
    61 | 62 |
    64 | 65 | 66 |
    69 | 70 | 73 | 74 |
    75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/cran_comments.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | NA • xrnet 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
    67 |
    68 | 110 | 111 | 112 | 113 |
    114 | 115 |
    116 |
    117 | 120 | 121 | 122 |
    123 |

    124 | Test environments

    125 |
      126 |
    • local Windows 10 x64 install, R 3.6.2
    • 127 |
    • ubuntu 14.04.5 LTS (travis ci), R 3.6.2 / 4.0.0
    • 128 |
    • OS X (travis ci), R 3.6.2
    • 129 |
    • windows i386/x64 (appveyor), R 3.6.2 / 4.0.0
    • 130 |
    • win-builder, R 3.6.2 / 4.0.0
    • 131 |
    132 |
    133 |
    134 |

    135 | R CMD check results

    136 |

    There were no ERRORs or WARNINGs

    137 |

    There was 1 NOTE for ubuntu (3.6.2 / 4.0.0):

    138 |
      139 |
    • 140 |

      checking installed package size … NOTE installed size is 27.7Mb sub-directories of 1Mb or more: libs 27.2Mb

      141 |

      The size appears to be due to the use of Rcpp/RcppEigen.

      142 |
    • 143 |
    144 |
    145 |
    146 |

    147 | BH (boost) dependency

    148 |

    Note that when compiling BH package, the following warning appears multiple times: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

    149 |

    This appears to be specific to the Boost header files in the BH package and cannot be altered.

    150 |
    151 | 152 | 153 |
    154 | 155 |
    156 | 157 | 158 | 159 |
    160 | 163 | 164 |
    165 |

    Site built with pkgdown 1.4.1.

    166 |
    167 | 168 |
    169 |
    170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /tests/testthat/test-user-input.R: -------------------------------------------------------------------------------- 1 | library(bigmemory) 2 | library(Matrix) 3 | 4 | context("check user input errors") 5 | 6 | ######################### xrnet() errors ######################### 7 | 8 | test_that("throw error when matching family not found", { 9 | x <- matrix(runif(10), nrow = 5) 10 | y <- 1:5 11 | external <- matrix(runif(10), nrow = 2) 12 | expect_error(xrnet(x, y, external, family = "badfamily")) 13 | }) 14 | 15 | test_that("throw error when dimensions of x and y do not match", { 16 | x <- matrix(runif(10), ncol = 2) 17 | y <- 1:10 18 | external <- matrix(runif(10), nrow = 2) 19 | expect_error( 20 | xrnet(x, y, external, "gaussian"), 21 | "Length of y (10) not equal to the number of rows of x (5)", 22 | fixed = TRUE 23 | ) 24 | }) 25 | 26 | test_that("throw error when dimensions of unpen and y do not match", { 27 | x <- matrix(runif(20), ncol = 2) 28 | unpen <- matrix(runif(10), ncol = 2) 29 | y <- 1:10 30 | external <- matrix(runif(10), nrow = 2) 31 | expect_error( 32 | xrnet(x, y, external, unpen = unpen, family = "gaussian"), 33 | "Length of y (10) not equal to the number of rows of unpen (5)", 34 | fixed = TRUE 35 | ) 36 | }) 37 | 38 | test_that("throw error when ncol(x) not equal to nrow(external)", { 39 | x <- matrix(runif(10), ncol = 2) 40 | y <- 1:5 41 | external <- matrix(runif(10), nrow = 5) 42 | expect_error( 43 | xrnet(x, y, external, "gaussian"), 44 | "Number of columns in x (2) not equal to the number of rows in external (5)", 45 | fixed = TRUE 46 | ) 47 | }) 48 | 49 | test_that("throw error if x is not of type double", { 50 | x <- matrix(1L:10L, nrow = 5) 51 | y <- 1:5 52 | expect_error(xrnet(x, y, family = "gaussian")) 53 | expect_error(xrnet(as.big.matrix(x), y, family = "gaussian")) 54 | }) 55 | 56 | test_that("throw error if x not one of accepted types", { 57 | x <- list(1:10) 58 | y <- 1:5 59 | expect_error(xrnet(x, y, family = "gaussian")) 60 | expect_error(xrnet(x, y, family = "gaussian")) 61 | }) 62 | 63 | test_that("throw error if external is not of type double", { 64 | x <- matrix(runif(10), nrow = 5) 65 | y <- 1:5 66 | external <- matrix(1L:10L, nrow = 2) 67 | expect_error(xrnet(x, y, external, family = "gaussian")) 68 | }) 69 | 70 | test_that("throw error when dimensions of weights and y do not match", { 71 | x <- matrix(runif(10), ncol = 2) 72 | y <- 1:5 73 | wgts <- rep(1:3) 74 | expect_error( 75 | xrnet(x, y, weights = wgts, family = "gaussian"), 76 | "Length of weights (3) not equal to length of y (5)", 77 | fixed = TRUE 78 | ) 79 | }) 80 | 81 | test_that("throw error when weights negative", { 82 | x <- matrix(runif(10), ncol = 2) 83 | y <- 1:5 84 | wgts <- c(rep(1, 4), -1) 85 | expect_error( 86 | xrnet(x, y, weights = wgts, family = "gaussian"), 87 | fixed = TRUE 88 | ) 89 | }) 90 | 91 | 92 | 93 | ######################### xrnet_control() errors ######################### 94 | 95 | test_that("throw error when tolerance non-positive", { 96 | expect_error(xrnet_control(tolerance = 0)) 97 | expect_error(xrnet_control(tolerance = -1)) 98 | }) 99 | 100 | test_that("throw error when max iterations non-positive or not an integer", { 101 | expect_error(xrnet_control(max_iterations = 0)) 102 | expect_error(xrnet_control(max_iterations = -1)) 103 | expect_error(xrnet_control(max_iterations = 2.5)) 104 | }) 105 | 106 | ######################### initialize_penalty() errors ######################### 107 | 108 | test_that("throw error when length of penalty_type != ncol(x)", { 109 | x <- matrix(runif(20), ncol = 5) 110 | y <- 1:4 111 | p <- define_penalty(penalty_type = rep(1, 4)) 112 | expect_error(xrnet(x, y, family = "gaussian", penalty_main = p)) 113 | }) 114 | 115 | test_that("throw error when num_penalty < 3", { 116 | x <- matrix(runif(20), ncol = 5) 117 | y <- 1:4 118 | p <- define_penalty(num_penalty = 2) 119 | expect_error(xrnet(x, y, family = "gaussian", penalty_main = p)) 120 | }) 121 | 122 | test_that("throw error when length of custom_multiplier != ncol(x)", { 123 | x <- matrix(runif(20), ncol = 5) 124 | y <- 1:4 125 | p <- define_penalty(custom_multiplier = rep(10, 4)) 126 | expect_error(xrnet(x, y, family = "gaussian", penalty_main = p)) 127 | }) 128 | 129 | test_that("throw error when length of penalty_type_ext != ncol(external)", { 130 | x <- matrix(runif(20), ncol = 5) 131 | y <- 1:4 132 | external <- matrix(runif(20), nrow = 5) 133 | p <- define_penalty(penalty_type = rep(0, 2)) 134 | expect_error(xrnet(x, y, external, family = "gaussian", penalty_external = p)) 135 | }) 136 | 137 | test_that("throw error when num_penalty_ext < 3", { 138 | x <- matrix(runif(20), ncol = 5) 139 | y <- 1:4 140 | external <- matrix(runif(20), nrow = 5) 141 | p <- define_penalty(num_penalty = 2) 142 | expect_error(xrnet(x, y, external, family = "gaussian", penalty_external = p)) 143 | }) 144 | 145 | test_that("throw error when length of custom_multiplier_ext != ncol(external)", { 146 | x <- matrix(runif(20), ncol = 5) 147 | y <- 1:4 148 | external <- matrix(runif(20), nrow = 5) 149 | p <- define_penalty(custom_multiplier = rep(10, 2)) 150 | expect_error(xrnet(x, y, external, family = "gaussian", penalty_external = p)) 151 | }) 152 | 153 | ######################### initialize_control() errors ######################### 154 | 155 | test_that("throw error when dfmax non-positive or not an integer", { 156 | x <- matrix(runif(10), nrow = 5) 157 | y <- 1:5 158 | 159 | expect_error( 160 | xrnet( 161 | x = x, 162 | y = y, 163 | family = "gaussian", 164 | control = xrnet_control(dfmax = 0) 165 | ) 166 | ) 167 | 168 | expect_error( 169 | xrnet( 170 | x = x, 171 | y = y, 172 | family = "gaussian", 173 | control = xrnet_control(dfmax = -1) 174 | ) 175 | ) 176 | 177 | expect_error( 178 | xrnet( 179 | x = x, 180 | y = y, 181 | family = "gaussian", 182 | control = xrnet_control(dfmax = 2.5) 183 | ) 184 | ) 185 | }) 186 | 187 | 188 | test_that("throw error when pmax non-positive or not an integer", { 189 | x <- matrix(runif(10), nrow = 5) 190 | y <- 1:5 191 | 192 | expect_error( 193 | xrnet( 194 | x = x, 195 | y = y, 196 | family = "gaussian", 197 | control = xrnet_control(pmax = 0) 198 | ) 199 | ) 200 | 201 | expect_error( 202 | xrnet( 203 | x = x, 204 | y = y, 205 | family = "gaussian", 206 | control = xrnet_control(pmax = -1) 207 | ) 208 | ) 209 | 210 | expect_error( 211 | xrnet( 212 | x = x, 213 | y = y, 214 | family = "gaussian", 215 | control = xrnet_control(pmax = 2.5) 216 | ) 217 | ) 218 | }) 219 | 220 | test_that("throw error when length lower_limits or upper_limits does not match total number of variables", { 221 | x <- matrix(runif(20), ncol = 5) 222 | y <- 1:5 223 | 224 | expect_error( 225 | xrnet( 226 | x = x, 227 | y = y, 228 | family = "gaussian", 229 | control = xrnet_control(lower_limits = rep(0, 2)) 230 | ) 231 | ) 232 | 233 | expect_error( 234 | xrnet( 235 | x = x, 236 | y = y, 237 | family = "gaussian", 238 | control = xrnet_control(upper_limits = rep(0, 2)) 239 | ) 240 | ) 241 | }) 242 | -------------------------------------------------------------------------------- /docs/reference/x_linear.html: -------------------------------------------------------------------------------- 1 | 2 | Simulated example data for hierarchical regularized linear regression — x_linear • xrnet 6 | Skip to contents 7 | 8 | 9 |
    41 |
    42 |
    47 | 48 |
    49 |

    Simulated example data for hierarchical regularized linear regression

    50 |
    51 | 52 |
    53 |

    Usage

    54 |
    x_linear
    55 |
    56 | 57 |
    58 |

    Format

    59 |

    A matrix with 100 rows and 50 variables

    60 |
    61 | 62 |
    64 | 65 | 66 |
    69 | 70 | 73 | 74 |
    75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /paper/paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'xrnet: Hierarchical Regularized Regression to Incorporate External Data' 3 | authors: 4 | - affiliation: 1 5 | name: Garrett M Weaver 6 | orcid: 0000-0002-9918-8386 7 | - affiliation: 1 8 | name: Juan Pablo Lewinger 9 | date: "07 September 2019" 10 | output: 11 | pdf_document: default 12 | html_document: 13 | df_print: paged 14 | bibliography: paper.bib 15 | tags: 16 | - regularized regression 17 | - lasso regression 18 | - ridge regression 19 | - elastic net regression 20 | - hierarchical regression 21 | affiliations: 22 | - index: 1 23 | name: Department of Preventive Medicine, University of Southern California 24 | --- 25 | 26 | # Summary 27 | 28 | Regularized regression is an essential tool for both feature selection and prediction with high-dimensional data. A number of R [@R] packages have been developed to fit regularized regression models, including ``glmnet`` [@friedman2010], ``biglasso`` [@zeng2017], and ``ncvreg`` [@breheny2011]. These packages can fit multiple model types including linear, multivariate linear, logistic, and Cox regression with different regularization penalties. The penalties control the complexity of the models by shrinking the coefficients toward zero, with the degree of shrinkage controlled by a tuning parameter that is typically selected by cross-validation. In addition to shrinkage, penalties like the lasso, elastic-net, SCAD [@fan2001], and MCP [@zhang2010] also perform feature selection by shrinking some coefficients to exactly zero. 29 | 30 | In statistical genetics and bioinformatics, there has been an increased interest in extending regularized regression methods to integrate external data that may be informative for the association of high-dimensional genomic features (i.e., gene expression, methylation, genotypes) with a health-related outcome (i.e., cancer recurrence). Potential sources of external information within these domains include genomic annotations that describe the underlying functions of a genomic region, and summary statistics derived from external data sources or previous studies. The primary interest is to exploit the external data to both improve the estimation of the regression coefficients and increase the overall predictive performance of the fitted model. 31 | 32 | The ``xrnet`` R package implements a novel extension of regularized regression that enables the integration of meta-features, a particular type of external data. Meta-features, also known as meta-variables or co-data, refer to characteristics of the predictor variables. For example, meta-features of a gene expression variable can be the known function/s of the particular gene. Meta-features of a single nucleotide polymorphism (SNP) genotype variable could be information about whether the SNP is within a regulatory region. Let $y$ be an $n$-dimensional outcome vector, $X$ be a set of $p$ potential predictors measured for the $n$ observations, and $Z$ be a set of $q$ meta-features available for the $p$ predictors. Our model is related to a standard two-level hierarchical regression model, where the mean effects of the predictors, $\beta$, on a outcome are assumed to be dependent on the set of meta-features, $Z$, through a second set of regression coefficients, $\alpha$. 33 | 34 | $$y = X\beta + \epsilon$$ 35 | $$\beta = \alpha_0 1_p + Z\alpha + \gamma$$ 36 | 37 | where $\epsilon$ and $\gamma$ are error terms and $1_p$ denotes a vector of ones of dimension $p$. As a concrete example, assume that $X$ is a set of gene expression features measured on $n$ subjects and that $Z$ is a single meta-feature, $Z_1$, consisting of a 0-1 indicator variable for whether each gene in $X$ has function "A". In this simple case, the hierarchical model assumes that the mean effect of expression on the outcome among genes with function "A" is $\alpha_0 +\alpha_1$ and the effect among genes that do not have function "A" is $\alpha_0$. 38 | 39 | The general form of our model extends this two level hierarchy to a high-dimensional setting by jointly modeling $X$ and $Z$ in a regularized regression framework that accounts for the hierarchical nature of the data. In the case of a continuous outcome, the model can be expressed by the following convex optimization problem. 40 | 41 | $$ \displaystyle\min_{\beta, \alpha_0, \alpha}\ \dfrac{1}{2}||y - X\beta||_2^2 + \dfrac{\lambda_1}{r}\sum_{j=1}^p|\beta_j - \alpha_0 - Z^T_j\alpha|^r + \dfrac{\lambda_2}{s}\sum_{k=1}^q|\alpha_k|^s$$ 42 | 43 | In the joint minimization above, $\lambda_1$ and $\lambda_2$ are hyperparameters and $r, s = 1 (lasso), 2(ridge)$ determine the type of regularization at each level. ``xrnet`` can also penalize either level with an elastic-net penalty as well. Unlike standard regularized regression, the predictor coefficients, $\beta$, are not shrunk towards zero, but rather towards $\alpha_01_p + Z\alpha$ as $\lambda_1$ increases. The third term in the model allows for variable selection of the 'meta-features' and can shrink $\alpha$ towards zero. To efficiently solve this convex optimization problem for various hyperparameter combinations, the variable substitution $\gamma = \beta - \alpha_0I_p - Z\alpha$ is used to re-express the problem. The objective function is then a standard regularized regression where the type of regularization and hyperparameter values are variable-specific. 44 | 45 | $$\displaystyle\min_{\gamma, \alpha_0, \alpha}\ \dfrac{1}{2}||y - X\gamma - \alpha_0 X 1_p + XZ\alpha)||_2^2 + \dfrac{\lambda_1}{r}\sum_{j=1}^p|\gamma_j|^r + \dfrac{\lambda_2}{s}\sum_{k=1}^q|\alpha_k|^s$$ 46 | 47 | This package extends the coordinate descent algorithm of Friedman [@friedman2010] to allow for this variable-specific penalization in order to fit the model described above. 48 | 49 | Along with this extension, ``xrnet`` can fit standard regularized regression models and integrates popular features from the R packages ``glmnet`` and ``biglasso``. Below is a comparison of features that are available in ``xrnet``, ``glmnet``, and ``biglasso``. In addition to continuous and binary outcomes, there is active development to extend ``xrnet`` to survival outcomes, including Cox regression and accelerated failure time models. 50 | 51 | | Feature | xrnet | glmnet | biglasso | 52 | |---------|-------|--------|----------| 53 | | Matrix types supported | Dense (In-Memory), Sparse (In-Memory), Memory-mapped | Dense (In-Memory), Sparse (In-Memory) | Memory-mapped | 54 | | Outcome types supported | Gaussian, Binomial | Gaussian, Multiresponse Gaussian, Binomial, Poisson, Cox | Gaussian, Binomial | 55 | | Feature-specific penalty scaling | yes | yes | yes | 56 | | Feature-specific penalty types | yes | no | no | 57 | | User controls feature standardization | yes | yes | no | 58 | | User controls inclusion of intercept | yes | yes | no | 59 | | Box (upper/lower constrains on) estimates | yes | yes | no | 60 | | Enhanced feature screening | no | no | yes | 61 | | Integration of external data | yes | no | no | 62 | 63 | The core functionality of the package is written in C++ with integration to R by using the Rcpp R package [@rcpp]. The Eigen linear algebra library [@eigenweb] and RcppEigen [@rcppEigen] R package are utilized to handle the dense and sparse data structures. Overall, this R package aims to provide a set of functions to fit and tune hierarchical regularized regression models and unifies some of the best features from currently available R packages for regularized regression into a single easy to use interface. 64 | 65 | # Funding and Support 66 | 67 | This work is supported by the National Institute of Health (NIH) Grant P01CA196569. 68 | 69 | # References 70 | -------------------------------------------------------------------------------- /docs/authors.html: -------------------------------------------------------------------------------- 1 | 2 | Authors and Citation • xrnet 6 | Skip to contents 7 | 8 | 9 |
    41 |
    42 |
    45 | 46 |
    47 |

    Authors

    48 | 49 |
    • 50 |

      Garrett Weaver. Author, maintainer. 51 |

      52 |
    • 53 |
    • 54 |

      Dixin Shen. Author. 55 |

      56 |
    • 57 |
    • 58 |

      Juan Pablo Lewinger. Contributor, thesis advisor. 59 |

      60 |
    • 61 |
    62 | 63 |
    64 |

    Citation

    65 |

    Source: DESCRIPTION

    66 | 67 |

    Weaver G, Shen D (2024). 68 | xrnet: Hierarchical Regularized Regression. 69 | R package version 0.1.7, https://uscbiostats.github.io/xrnet/, https://github.com/USCbiostats/xrnet. 70 |

    71 |
    @Manual{,
    72 |   title = {xrnet: Hierarchical Regularized Regression},
    73 |   author = {Garrett Weaver and Dixin Shen},
    74 |   year = {2024},
    75 |   note = {R package version 0.1.7, https://uscbiostats.github.io/xrnet/},
    76 |   url = {https://github.com/USCbiostats/xrnet},
    77 | }
    78 |
    79 |
    81 | 82 | 83 |
    86 | 87 | 90 | 91 |
    92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.html: -------------------------------------------------------------------------------- 1 | 2 | Contributor Code of Conduct • xrnet 6 | Skip to contents 7 | 8 | 9 |
    41 |
    42 |
    46 | 47 |
    48 | 49 |

    As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.

    50 |

    We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.

    51 |

    Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.

    52 |

    Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.

    53 |

    Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.

    54 |

    This Code of Conduct is adapted from the Contributor Covenant (http://contributor-covenant.org), version 1.0.0, available at http://contributor-covenant.org/version/1/0/0/

    55 |
    56 | 57 |
    58 | 59 | 60 |
    63 | 64 | 67 | 68 |
    69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /docs/news/index.html: -------------------------------------------------------------------------------- 1 | 2 | Changelog • xrnet 6 | Skip to contents 7 | 8 | 9 |
    41 |
    42 |
    46 | 47 |
    48 |

    xrnet 1.0.0

    49 |
    • Changes minimum supported R version 4.0.

    • 50 |
    • Removes C++11 in SystemRequirements since it is guaranteed that R >= 4.0 that C++11 is minimum supported compiler. This also enables removing CXX_STD=CXX11 in Makevars and Makevars.win.

    • 51 |
    • Fixes gcc-UBSAN errors detected in latest Rdevel checks. Note that these errors do not impact solutions. They occur when no external data matrix is provided and the underlying C++ code attempts to created a mapped matrix with a pointer to an empty matrix. Since the external data matrix is never used in this case, the resulting standard regularized regression with no external data still produces a correct solution.

    • 52 |
    53 |
    54 |

    xrnet 0.1.7

    CRAN release: 2020-03-01

    55 |
    • Patched release to fix tests on Solaris OS and removed test dependency on glmnet
    • 56 |
    57 |
    58 |

    xrnet 0.1.6

    CRAN release: 2020-02-28

    59 |
    • First release to CRAN

    • 60 |
    • Initial release supports linear and logistic hierarchical regularized regression

    • 61 |
    62 |
    64 | 65 | 66 |
    75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/pkgdown.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer */ 2 | 3 | /** 4 | * Basic idea: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/ 5 | * Details: https://github.com/philipwalton/solved-by-flexbox/blob/master/assets/css/components/site.css 6 | * 7 | * .Site -> body > .container 8 | * .Site-content -> body > .container .row 9 | * .footer -> footer 10 | * 11 | * Key idea seems to be to ensure that .container and __all its parents__ 12 | * have height set to 100% 13 | * 14 | */ 15 | 16 | html, body { 17 | height: 100%; 18 | } 19 | 20 | body { 21 | position: relative; 22 | } 23 | 24 | body > .container { 25 | display: flex; 26 | height: 100%; 27 | flex-direction: column; 28 | } 29 | 30 | body > .container .row { 31 | flex: 1 0 auto; 32 | } 33 | 34 | footer { 35 | margin-top: 45px; 36 | padding: 35px 0 36px; 37 | border-top: 1px solid #e5e5e5; 38 | color: #666; 39 | display: flex; 40 | flex-shrink: 0; 41 | } 42 | footer p { 43 | margin-bottom: 0; 44 | } 45 | footer div { 46 | flex: 1; 47 | } 48 | footer .pkgdown { 49 | text-align: right; 50 | } 51 | footer p { 52 | margin-bottom: 0; 53 | } 54 | 55 | img.icon { 56 | float: right; 57 | } 58 | 59 | img { 60 | max-width: 100%; 61 | } 62 | 63 | /* Fix bug in bootstrap (only seen in firefox) */ 64 | summary { 65 | display: list-item; 66 | } 67 | 68 | /* Typographic tweaking ---------------------------------*/ 69 | 70 | .contents .page-header { 71 | margin-top: calc(-60px + 1em); 72 | } 73 | 74 | dd { 75 | margin-left: 3em; 76 | } 77 | 78 | /* Section anchors ---------------------------------*/ 79 | 80 | a.anchor { 81 | margin-left: -30px; 82 | display:inline-block; 83 | width: 30px; 84 | height: 30px; 85 | visibility: hidden; 86 | 87 | background-image: url(./link.svg); 88 | background-repeat: no-repeat; 89 | background-size: 20px 20px; 90 | background-position: center center; 91 | } 92 | 93 | .hasAnchor:hover a.anchor { 94 | visibility: visible; 95 | } 96 | 97 | @media (max-width: 767px) { 98 | .hasAnchor:hover a.anchor { 99 | visibility: hidden; 100 | } 101 | } 102 | 103 | 104 | /* Fixes for fixed navbar --------------------------*/ 105 | 106 | .contents h1, .contents h2, .contents h3, .contents h4 { 107 | padding-top: 60px; 108 | margin-top: -40px; 109 | } 110 | 111 | /* Navbar submenu --------------------------*/ 112 | 113 | .dropdown-submenu { 114 | position: relative; 115 | } 116 | 117 | .dropdown-submenu>.dropdown-menu { 118 | top: 0; 119 | left: 100%; 120 | margin-top: -6px; 121 | margin-left: -1px; 122 | border-radius: 0 6px 6px 6px; 123 | } 124 | 125 | .dropdown-submenu:hover>.dropdown-menu { 126 | display: block; 127 | } 128 | 129 | .dropdown-submenu>a:after { 130 | display: block; 131 | content: " "; 132 | float: right; 133 | width: 0; 134 | height: 0; 135 | border-color: transparent; 136 | border-style: solid; 137 | border-width: 5px 0 5px 5px; 138 | border-left-color: #cccccc; 139 | margin-top: 5px; 140 | margin-right: -10px; 141 | } 142 | 143 | .dropdown-submenu:hover>a:after { 144 | border-left-color: #ffffff; 145 | } 146 | 147 | .dropdown-submenu.pull-left { 148 | float: none; 149 | } 150 | 151 | .dropdown-submenu.pull-left>.dropdown-menu { 152 | left: -100%; 153 | margin-left: 10px; 154 | border-radius: 6px 0 6px 6px; 155 | } 156 | 157 | /* Sidebar --------------------------*/ 158 | 159 | #pkgdown-sidebar { 160 | margin-top: 30px; 161 | position: -webkit-sticky; 162 | position: sticky; 163 | top: 70px; 164 | } 165 | 166 | #pkgdown-sidebar h2 { 167 | font-size: 1.5em; 168 | margin-top: 1em; 169 | } 170 | 171 | #pkgdown-sidebar h2:first-child { 172 | margin-top: 0; 173 | } 174 | 175 | #pkgdown-sidebar .list-unstyled li { 176 | margin-bottom: 0.5em; 177 | } 178 | 179 | /* bootstrap-toc tweaks ------------------------------------------------------*/ 180 | 181 | /* All levels of nav */ 182 | 183 | nav[data-toggle='toc'] .nav > li > a { 184 | padding: 4px 20px 4px 6px; 185 | font-size: 1.5rem; 186 | font-weight: 400; 187 | color: inherit; 188 | } 189 | 190 | nav[data-toggle='toc'] .nav > li > a:hover, 191 | nav[data-toggle='toc'] .nav > li > a:focus { 192 | padding-left: 5px; 193 | color: inherit; 194 | border-left: 1px solid #878787; 195 | } 196 | 197 | nav[data-toggle='toc'] .nav > .active > a, 198 | nav[data-toggle='toc'] .nav > .active:hover > a, 199 | nav[data-toggle='toc'] .nav > .active:focus > a { 200 | padding-left: 5px; 201 | font-size: 1.5rem; 202 | font-weight: 400; 203 | color: inherit; 204 | border-left: 2px solid #878787; 205 | } 206 | 207 | /* Nav: second level (shown on .active) */ 208 | 209 | nav[data-toggle='toc'] .nav .nav { 210 | display: none; /* Hide by default, but at >768px, show it */ 211 | padding-bottom: 10px; 212 | } 213 | 214 | nav[data-toggle='toc'] .nav .nav > li > a { 215 | padding-left: 16px; 216 | font-size: 1.35rem; 217 | } 218 | 219 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 220 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 221 | padding-left: 15px; 222 | } 223 | 224 | nav[data-toggle='toc'] .nav .nav > .active > a, 225 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 226 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 227 | padding-left: 15px; 228 | font-weight: 500; 229 | font-size: 1.35rem; 230 | } 231 | 232 | /* orcid ------------------------------------------------------------------- */ 233 | 234 | .orcid { 235 | font-size: 16px; 236 | color: #A6CE39; 237 | /* margins are required by official ORCID trademark and display guidelines */ 238 | margin-left:4px; 239 | margin-right:4px; 240 | vertical-align: middle; 241 | } 242 | 243 | /* Reference index & topics ----------------------------------------------- */ 244 | 245 | .ref-index th {font-weight: normal;} 246 | 247 | .ref-index td {vertical-align: top; min-width: 100px} 248 | .ref-index .icon {width: 40px;} 249 | .ref-index .alias {width: 40%;} 250 | .ref-index-icons .alias {width: calc(40% - 40px);} 251 | .ref-index .title {width: 60%;} 252 | 253 | .ref-arguments th {text-align: right; padding-right: 10px;} 254 | .ref-arguments th, .ref-arguments td {vertical-align: top; min-width: 100px} 255 | .ref-arguments .name {width: 20%;} 256 | .ref-arguments .desc {width: 80%;} 257 | 258 | /* Nice scrolling for wide elements --------------------------------------- */ 259 | 260 | table { 261 | display: block; 262 | overflow: auto; 263 | } 264 | 265 | /* Syntax highlighting ---------------------------------------------------- */ 266 | 267 | pre { 268 | word-wrap: normal; 269 | word-break: normal; 270 | border: 1px solid #eee; 271 | } 272 | 273 | pre, code { 274 | background-color: #f8f8f8; 275 | color: #333; 276 | } 277 | 278 | pre code { 279 | overflow: auto; 280 | word-wrap: normal; 281 | white-space: pre; 282 | } 283 | 284 | pre .img { 285 | margin: 5px 0; 286 | } 287 | 288 | pre .img img { 289 | background-color: #fff; 290 | display: block; 291 | height: auto; 292 | } 293 | 294 | code a, pre a { 295 | color: #375f84; 296 | } 297 | 298 | a.sourceLine:hover { 299 | text-decoration: none; 300 | } 301 | 302 | .fl {color: #1514b5;} 303 | .fu {color: #000000;} /* function */ 304 | .ch,.st {color: #036a07;} /* string */ 305 | .kw {color: #264D66;} /* keyword */ 306 | .co {color: #888888;} /* comment */ 307 | 308 | .message { color: black; font-weight: bolder;} 309 | .error { color: orange; font-weight: bolder;} 310 | .warning { color: #6A0366; font-weight: bolder;} 311 | 312 | /* Clipboard --------------------------*/ 313 | 314 | .hasCopyButton { 315 | position: relative; 316 | } 317 | 318 | .btn-copy-ex { 319 | position: absolute; 320 | right: 0; 321 | top: 0; 322 | visibility: hidden; 323 | } 324 | 325 | .hasCopyButton:hover button.btn-copy-ex { 326 | visibility: visible; 327 | } 328 | 329 | /* headroom.js ------------------------ */ 330 | 331 | .headroom { 332 | will-change: transform; 333 | transition: transform 200ms linear; 334 | } 335 | .headroom--pinned { 336 | transform: translateY(0%); 337 | } 338 | .headroom--unpinned { 339 | transform: translateY(-100%); 340 | } 341 | 342 | /* mark.js ----------------------------*/ 343 | 344 | mark { 345 | background-color: rgba(255, 255, 51, 0.5); 346 | border-bottom: 2px solid rgba(255, 153, 51, 0.3); 347 | padding: 1px; 348 | } 349 | 350 | /* vertical spacing after htmlwidgets */ 351 | .html-widget { 352 | margin-bottom: 10px; 353 | } 354 | 355 | /* fontawesome ------------------------ */ 356 | 357 | .fab { 358 | font-family: "Font Awesome 5 Brands" !important; 359 | } 360 | 361 | /* don't display links in code chunks when printing */ 362 | /* source: https://stackoverflow.com/a/10781533 */ 363 | @media print { 364 | code a:link:after, code a:visited:after { 365 | content: ""; 366 | } 367 | } 368 | --------------------------------------------------------------------------------