The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Multiblock basics: one projector, many tables

1. Why multiblock?

Many studies collect several tables on the same samples – e.g. transcriptomics + metabolomics, or multiple sensor blocks. Most single-table reductions (PCA, ICA, NMF, …) ignore that structure. multiblock_projector is a thin wrapper that keeps track of which original columns belong to which block, so you can

We demonstrate with a minimal two-block toy-set.

set.seed(1)
n  <- 100
pA <- 7; pB <- 5                    # two blocks, different widths

XA <- matrix(rnorm(n * pA), n, pA)
XB <- matrix(rnorm(n * pB), n, pB)
X  <- cbind(XA, XB)                 # global data matrix
blk_idx <- list(A = 1:pA, B = (pA + 1):(pA + pB)) # Named list is good practice

2. Wrap a single PCA as a multiblock projector

# 2-component centred PCA (using base SVD for brevity)
preproc_fitted <- fit(center(), X)
Xc        <- transform(preproc_fitted, X)          # Centered data
svd_res   <- svd(Xc, nu = 0, nv = 2)               # only V (loadings)
mb        <- multiblock_projector(
  v             = svd_res$v,                       # p × k loadings
  preproc       = preproc_fitted,                  # remembers centering
  block_indices = blk_idx
)

print(mb)
#> Projector object:
#>   Input dimension: 12
#>   Output dimension: 2
#>   With pre-processing:
#> A finalized pre-processing pipeline:
#>  Step  1 : center

2.1 Project the whole data

scores_all <- project(mb, X)                       # n × 2
head(round(scores_all, 3))
#>        [,1]   [,2]
#> [1,] -0.815 -1.159
#> [2,]  1.075 -3.326
#> [3,] -0.068  1.124
#> [4,] -0.055 -0.788
#> [5,] -0.554  1.005
#> [6,] -0.942  1.565

2.2 Project one block only

# Project using only data from block A (requires original columns)
scores_A <- project_block(mb, XA, block = 1)       
# Project using only data from block B
scores_B <- project_block(mb, XB, block = 2)       

cor(scores_all[,1], scores_A[,1])                  # high (they coincide)
#> [1] 0.7228449

Because the global PCA treats all columns jointly, projecting only block A gives exactly the same latent coordinates as when the whole matrix is available – useful when a block is missing at prediction time.

2.3 Partial feature projection

Need to use just three variables from block B?

# Get the global indices for the first 3 columns of block B
sel_cols_global <- blk_idx[["B"]][1:3]
# Extract the corresponding data columns from the full matrix or block B
part_XB_data  <- X[, sel_cols_global, drop = FALSE] # Data must match global indices

scores_part <- partial_project(mb, part_XB_data,
                               colind = sel_cols_global)  # Use global indices
head(round(scores_part, 3))
#>        [,1]   [,2]
#> [1,] -2.546 -0.723
#> [2,]  1.594 -3.584
#> [3,]  0.815  0.912
#> [4,] -0.329 -0.648
#> [5,] -2.223  1.394
#> [6,] -2.628  0.972

3. Adding scores → multiblock_biprojector

If you also keep the sample scores (from the original fit) you get two-way functionality: re-construct data, measure error, run permutation tests, etc. That is one extra line when creating the object:

bi <- multiblock_biprojector(
  v             = svd_res$v,
  s             = Xc %*% svd_res$v,    # Calculate scores: Xc %*% V
  sdev          = svd_res$d[1:2] / sqrt(n-1), # SVD d are related to sdev
  preproc       = preproc_fitted,
  block_indices = blk_idx
)
print(bi)
#> Multiblock Bi-Projector object:
#>   Projection matrix dimensions:  12 x 2 
#>   Block indices:
#>     Block 1: 1,2,3,4,5,6,7
#>     Block 2: 8,9,10,11,12

Now you can, for instance, test whether component-wise consensus between blocks is stronger than by chance.

# Quick permutation test (use more permutations for real analyses)
# use_rspectra=FALSE needed for this 2-block example; larger problems can use TRUE
perm_res <- perm_test(bi, Xlist = list(A = XA, B = XB), nperm = 99, use_rspectra = FALSE)
print(perm_res$component_results)
#>   comp observed pval lower_ci upper_ci
#> 1    1 84.25129  0.1 78.70594 88.96802

The perm_test method for multiblock_biprojector uses an eigen-based score consensus statistic to assess whether blocks share more variance than expected by chance.

4. Take-aways

Verb What it does in multiblock context
project() whole-matrix projection (uses preprocessing)
project_block() scores based on one block’s data
partial_project() scores from an arbitrary subset of global columns
coef(..., block=) retrieve loadings for a specific block
perm_test() permutation test for block consensus (biprojector)

This light infrastructure lets you prototype block-aware analyses quickly, while still tapping into the entire multiblock toolkit (cross-validation, reconstruction metrics, composition with compose_projector, etc.).

sessionInfo()
#> R version 4.5.1 (2025-06-13)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Sonoma 14.3
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
#> 
#> locale:
#> [1] C/en_CA.UTF-8/en_CA.UTF-8/C/en_CA.UTF-8/en_CA.UTF-8
#> 
#> time zone: America/Toronto
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] glmnet_4.1-10      Matrix_1.7-3       knitr_1.51         tibble_3.3.1      
#> [5] dplyr_1.1.4        ggplot2_4.0.1      multivarious_0.3.1
#> 
#> loaded via a namespace (and not attached):
#>  [1] GPArotation_2025.3-1 utf8_1.2.6           sass_0.4.10         
#>  [4] future_1.68.0        generics_0.1.4       shape_1.4.6.1       
#>  [7] lattice_0.22-7       listenv_0.10.0       digest_0.6.39       
#> [10] magrittr_2.0.4       evaluate_1.0.5       grid_4.5.1          
#> [13] RColorBrewer_1.1-3   iterators_1.0.14     fastmap_1.2.0       
#> [16] foreach_1.5.2        jsonlite_2.0.0       ggrepel_0.9.6       
#> [19] RSpectra_0.16-2      survival_3.8-3       scales_1.4.0        
#> [22] pls_2.8-5            codetools_0.2-20     jquerylib_0.1.4     
#> [25] cli_3.6.5            crayon_1.5.3         rlang_1.1.7         
#> [28] chk_0.10.0           parallelly_1.45.1    future.apply_1.20.0 
#> [31] splines_4.5.1        withr_3.0.2          cachem_1.1.0        
#> [34] yaml_2.3.12          otel_0.2.0           tools_4.5.1         
#> [37] parallel_4.5.1       corpcor_1.6.10       globals_0.18.0      
#> [40] rsvd_1.0.5           assertthat_0.2.1     vctrs_0.7.0         
#> [43] R6_2.6.1             matrixStats_1.5.0    proxy_0.4-27        
#> [46] lifecycle_1.0.5      MASS_7.3-65          irlba_2.3.5.1       
#> [49] pkgconfig_2.0.3      pillar_1.11.1        bslib_0.9.0         
#> [52] geigen_2.3           gtable_0.3.6         glue_1.8.0          
#> [55] Rcpp_1.1.1           xfun_0.55            tidyselect_1.2.1    
#> [58] svd_0.5.8            farver_2.1.2         htmltools_0.5.9     
#> [61] labeling_0.4.3       rmarkdown_2.30       compiler_4.5.1      
#> [64] S7_0.2.1

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.