SecAct (Secreted protein Activity inference) is a computational framework for inferring the signaling activity of over 1,000 secreted proteins from gene expression profiles. This vignette provides a detailed explanation of the mathematical algorithm underlying SecAct.
Given: - Y: Gene expression matrix (genes × samples) - X: Signature matrix (genes × secreted proteins)
We aim to infer the activity matrix β (secreted proteins × samples) that best explains the observed gene expression changes.
SecAct employs ridge regression to solve this inverse problem:
\[\hat{\beta} = (X^TX + \lambda I)^{-1} X^T Y\]
Where: - \(\hat{\beta}\): Estimated activity coefficients - \(\lambda\): Regularization parameter (default: 5×10⁵) - \(I\): Identity matrix
The ridge penalty \(\lambda I\) provides: 1. Numerical stability when \(X^TX\) is ill-conditioned 2. Regularization to prevent overfitting 3. Unique solution even when features > samples
For computational efficiency, SecAct uses Cholesky decomposition:
A = X'X + λI (symmetric positive definite)
R = chol(A) (A = R'R)
β = R⁻¹ (R')⁻¹ X' Y
This approach is: - Numerically stable: Exploits the SPD structure - Computationally efficient: O(n³/3) vs O(n³) for general inverse - Memory efficient: Only stores upper triangular R
SecAct assesses statistical significance through permutation testing:
Permutation testing procedure
The z-score quantifies how many standard deviations the observed coefficient deviates from the null:
\[z = \frac{\hat{\beta} - \mu_{null}}{\sigma_{null}}\]
Where: - \(\mu_{null}\): Mean of permutation coefficients - \(\sigma_{null}\): Standard deviation of permutation coefficients
The empirical p-value is calculated as:
\[p = \frac{\sum_{i=1}^{n_{rand}} I(|\beta_i^{rand}| \geq |\hat{\beta}|) + 1}{n_{rand} + 1}\]
The “+1” correction ensures p-values are never exactly zero.
The SecAct signature matrix contains: - 1,170 secreted proteins - 7,919 downstream target genes - Curated from published literature and databases
library(SecAct)
# Download signature matrix (first time only)
SecAct.download.data()
# Load signature matrix
sig_path <- SecAct.get.sigmat.path()
sig_mat <- read.table(gzfile(sig_path), sep = "\t", header = TRUE, row.names = 1, nrows = 100)
cat("Signature matrix dimensions:\n")
cat(" Genes:", nrow(sig_mat), "(showing first 100)\n")
cat(" Secreted proteins:", ncol(sig_mat), "\n")To reduce multicollinearity, SecAct groups highly correlated signatures:
# Demonstrate signature grouping concept
set.seed(123)
# Simulate correlation matrix for 20 signatures
n_sig <- 20
cor_mat <- matrix(runif(n_sig^2, 0.2, 0.9), n_sig, n_sig)
cor_mat <- (cor_mat + t(cor_mat)) / 2
diag(cor_mat) <- 1
rownames(cor_mat) <- colnames(cor_mat) <- paste0("SP", 1:n_sig)
# Hierarchical clustering
hc <- hclust(as.dist(1 - cor_mat), method = "complete")
plot(hc, main = "Signature Grouping by Correlation", xlab = "", sub = "")
abline(h = 0.1, col = "red", lty = 2)
text(15, 0.15, "Correlation threshold = 0.9", col = "red")Hierarchical clustering of signature correlation
SecAct provides two implementations:
| Feature | SecAct.inference.r |
SecAct.inference.gsl |
|---|---|---|
| Language | Pure R | C with GSL |
| Speed | Moderate | Fast |
| Platform | All | Unix/macOS |
| Precision | 64-bit | 64-bit |
# Compare R and GSL implementations
data_path <- system.file("extdata/GSE100093.IFNG.expr.gz", package = "SecAct")
Y <- read.table(gzfile(data_path), sep = "\t", header = TRUE, row.names = 1)
# Download signature matrix (first time only)
SecAct.download.data()
# Run both implementations
set.seed(42)
result_r <- SecAct.inference.r(Y[, 1:3], lambda = 5e5, nrand = 100)
set.seed(42)
result_gsl <- SecAct.inference.gsl(Y[, 1:3], lambda = 5e5, nrand = 100)
# Compare results
cor_beta <- cor(as.vector(result_r$beta), as.vector(result_gsl$beta))
cor_zscore <- cor(as.vector(result_r$zscore), as.vector(result_gsl$zscore))
cat("Implementation Consistency:\n")
cat(" Beta correlation:", round(cor_beta, 4), "\n")
cat(" Z-score correlation:", round(cor_zscore, 4), "\n")The regularization parameter λ controls the bias-variance tradeoff:
# Demonstrate lambda effect
lambdas <- c(1e3, 1e4, 1e5, 5e5, 1e6, 1e7)
Y_small <- Y[, 1:2]
results <- lapply(lambdas, function(l) {
SecAct.inference.r(Y_small, lambda = l, nrand = 50)
})
# Plot coefficient ranges
coef_ranges <- sapply(results, function(r) range(r$beta))
par(mar = c(4, 4, 2, 1))
plot(log10(lambdas), coef_ranges[2,], type = "b", pch = 19, col = "blue",
ylim = range(coef_ranges), xlab = "log10(lambda)", ylab = "Coefficient range",
main = "Lambda Effect on Coefficients")
lines(log10(lambdas), coef_ranges[1,], type = "b", pch = 19, col = "red")
legend("topright", c("Max", "Min"), col = c("blue", "red"), pch = 19)
abline(v = log10(5e5), lty = 2, col = "gray")
text(log10(5e5), mean(coef_ranges), "Default", pos = 4)More permutations yield more precise p-values:
| n_rand | P-value precision | Computation time |
|---|---|---|
| 100 | 0.01 | Fast |
| 1000 | 0.001 | Moderate |
| 10000 | 0.0001 | Slow |
Recommendation: Use 1000 permutations for publication-quality results.
SecAct combines:
For questions or issues, please contact:
sessionInfo()
#> R version 4.6.0 (2026-04-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.4 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] SecAct_1.0.1 rmarkdown_2.31
#>
#> loaded via a namespace (and not attached):
#> [1] digest_0.6.39 R6_2.6.1 fastmap_1.2.0 xfun_0.59
#> [5] maketools_1.3.2 cachem_1.1.0 knitr_1.51 htmltools_0.5.9
#> [9] buildtools_1.0.0 lifecycle_1.0.5 cli_3.6.6 sass_0.4.10
#> [13] jquerylib_0.1.4 compiler_4.6.0 sys_3.4.3 tools_4.6.0
#> [17] evaluate_1.0.5 bslib_0.11.0 yaml_2.3.12 otel_0.2.0
#> [21] jsonlite_2.0.0 rlang_1.2.0