FSHybridPLS fits Hybrid Penalized Partial Least Squares
models when predictors include both functional curves and scalar
covariates. Functional and scalar parts are treated jointly in a hybrid
Hilbert space, with roughness penalties on functional coefficient
directions.
The method is described in Mun and Jang (2026), doi:10.48550/arXiv.2601.16364.
A predictor_hybrid object stores scalar covariates,
functional fd objects, and precomputed Gram / penalty
matrices used by the algorithm.
library(FSHybridPLS)
set.seed(1)
sim <- simulate_hybrid_data(
n = 50,
n_functional = 1,
n_scalar = 3,
n_basis = 6
)
sim$W
#> $Z
#> Z1 Z2 Z3
#> [1,] 0.893673702 1.473881181 1.07444096
#> [2,] -1.047298149 0.677268492 1.89565477
#> [3,] 1.971337386 0.379962687 -0.60299730
#> [4,] -0.383632106 -0.192798426 -0.39086782
#> [5,] 1.654145302 1.577891795 -0.41622203
#> [6,] 1.512212694 0.596234109 -0.37565742
#> [7,] 0.082965734 -1.173576941 -0.36663095
#> [8,] 0.567220915 -0.155642535 -0.29567745
#> [9,] -1.024548480 -1.918909820 1.44182041
#> [10,] 0.323006503 -0.195258846 -0.69753829
#> [11,] 1.043612458 -2.592327670 -0.38816751
#> [12,] 0.099078487 1.314002167 0.65253645
#> [13,] -0.454136909 -0.635543001 1.12477245
#> [14,] -0.655781852 -0.429978839 -0.77211080
#> [15,] -0.035922423 -0.169318332 -0.50808622
#> [16,] 1.069161461 0.612218174 0.52362059
#> [17,] -0.483974930 0.678340177 1.01775423
#> [18,] -0.121010111 0.567951972 -0.25116459
#> [19,] -1.294140004 -0.572542604 -1.42999345
#> [20,] 0.494312836 -1.363291256 1.70912103
#> [21,] 1.307901520 -0.388722244 1.43506957
#> [22,] 1.497041009 0.277914132 -0.71037115
#> [23,] 0.814702731 -0.823081122 -0.06506757
#> [24,] -1.869788790 -0.068840934 -1.75946874
#> [25,] 0.482029504 -1.167662326 0.56972297
#> [26,] 0.456135603 -0.008309014 1.61234680
#> [27,] -0.353400286 0.128855402 -1.63728065
#> [28,] 0.170489471 -0.145875628 -0.77956851
#> [29,] -0.864035954 -0.163910957 -0.64117693
#> [30,] 0.679230774 1.763552003 -0.68113139
#> [31,] -0.327101015 0.762586512 -2.03328560
#> [32,] -1.569082185 1.111431081 0.50096356
#> [33,] -0.367450756 -0.923206953 -1.53179814
#> [34,] 1.364434929 0.164341838 -0.02499764
#> [35,] -0.334281365 1.154825187 0.59298472
#> [36,] 0.732750042 -0.056521425 -0.19819542
#> [37,] 0.946585640 -2.129360648 0.89200839
#> [38,] 0.004398704 0.344845762 -0.02571507
#> [39,] -0.352322306 -1.904955446 -0.64766045
#> [40,] -0.529695509 -0.811170153 0.64635942
#> [41,] 0.739589226 1.324004321 -0.43383274
#> [42,] -1.063457415 0.615636849 1.77261118
#> [43,] 0.246210844 1.091668956 -0.01825971
#> [44,] -0.289499367 0.306604862 0.85281499
#> [45,] -2.264889356 -0.110158762 0.20516290
#> [46,] -1.408850456 -0.924312773 -3.00804860
#> [47,] 0.916019329 1.592913754 -1.36611193
#> [48,] -0.191278951 0.045010598 -0.42410226
#> [49,] 0.803283216 -0.715128401 0.23680366
#> [50,] 1.887474463 0.865223100 -2.34272312
#>
#> $functional_list
#> $functional_list[[1]]
#> $coefs
#> reps 1 reps 2 reps 3 reps 4 reps 5 reps 6
#> [1,] -0.6264538 0.4874291 -0.62124058 0.82122120 0.61982575 1.35867955
#> [2,] 0.1836433 0.7383247 -2.21469989 0.59390132 -0.05612874 -0.10278773
#> [3,] -0.8356286 0.5757814 1.12493092 0.91897737 -0.15579551 0.38767161
#> [4,] 1.5952808 -0.3053884 -0.04493361 0.78213630 -1.47075238 -0.05380504
#> [5,] 0.3295078 1.5117812 -0.01619026 0.07456498 -0.47815006 -1.37705956
#> [6,] -0.8204684 0.3898432 0.94383621 -1.98935170 0.41794156 -0.41499456
#> reps 7 reps 8 reps 9 reps 10 reps 11 reps 12
#> [1,] -0.3942900 0.6969634 -0.1123462 1.4330237 2.40161776 -1.8049586
#> [2,] -0.0593134 0.5566632 0.8811077 1.9803999 -0.03924000 1.4655549
#> [3,] 1.1000254 -0.6887557 0.3981059 -0.3672215 0.68973936 0.1532533
#> [4,] 0.7631757 -0.7074952 -0.6120264 -1.0441346 0.02800216 2.1726117
#> [5,] -0.1645236 0.3645820 0.3411197 0.5697196 -0.74327321 0.4755095
#> [6,] -0.2533617 0.7685329 -1.1293631 -0.1350546 0.18879230 -0.7099464
#> reps 13 reps 14 reps 15 reps 16 reps 17 reps 18
#> [1,] 0.610726353 0.07434132 0.5939462 -0.5425200 -1.27659221 -0.9109216
#> [2,] -0.934097632 -0.58952095 0.3329504 1.2078678 -0.57326541 0.1580288
#> [3,] -1.253633400 -0.56866873 1.0630998 1.1604026 -1.22461261 -0.6545846
#> [4,] 0.291446236 -0.13517862 -0.3041839 0.7002136 -0.47340064 1.7672873
#> [5,] -0.443291873 1.17808700 0.3700188 1.5868335 -0.62036668 0.7167075
#> [6,] 0.001105352 -1.52356680 0.2670988 0.5584864 0.04211587 0.9101742
#> reps 19 reps 20 reps 21 reps 22 reps 23 reps 24
#> [1,] 0.3841854 -0.2073807 -0.5059575 -0.07356440 0.5314962 -0.65209478
#> [2,] 1.6821761 -0.3928079 1.3430388 -0.03763417 -1.5183941 -0.05689678
#> [3,] -0.6357365 -0.3199929 -0.2145794 -0.68166048 0.3065579 -1.91435943
#> [4,] -0.4616447 -0.2791133 -0.1795565 -0.32427027 -1.5364498 1.17658331
#> [5,] 1.4322822 0.4941883 -0.1001907 0.06016044 -0.3009761 -1.66497244
#> [6,] -0.6506964 -0.1773305 0.7126663 -0.58889449 -0.5282799 -0.46353040
#> reps 25 reps 26 reps 27 reps 28 reps 29 reps 30
#> [1,] -1.11592011 0.45018710 1.0000288 1.0584830 -0.14439960 -0.33400084
#> [2,] -0.75081900 -0.01855983 -0.6212667 0.8864227 0.20753834 -0.03472603
#> [3,] 2.08716655 -0.31806837 -1.3844268 -0.6192430 2.30797840 0.78763961
#> [4,] 0.01739562 -0.92936215 1.8692906 2.2061025 0.10580237 2.07524501
#> [5,] -1.28630053 -1.48746031 0.4251004 -0.2550270 0.45699881 1.02739244
#> [6,] -1.64060553 -1.07519230 -0.2386471 -1.4244947 -0.07715294 1.20790840
#> reps 31 reps 32 reps 33 reps 34 reps 35 reps 36
#> [1,] -1.2313234 1.4645873 -0.7317482 0.4119747 -2.28523554 -0.1643758
#> [2,] 0.9838956 -0.7660820 0.8303732 -0.3810761 2.49766159 0.4206946
#> [3,] 0.2199248 -0.4302118 -1.2080828 0.4094018 0.66706617 -0.4002467
#> [4,] -1.4672500 -0.9261095 -1.0479844 1.6888733 0.54132734 -1.3702079
#> [5,] 0.5210227 -0.1771040 1.4411577 1.5865884 -0.01339952 0.9878383
#> [6,] -0.1587546 0.4020118 -1.0158475 -0.3309078 0.51010842 1.5197450
#> reps 37 reps 38 reps 39 reps 40 reps 41 reps 42
#> [1,] -0.30874057 -0.6303003 0.1971934 -0.059723276 0.7073107 -0.5447907
#> [2,] -1.25328976 -0.3409686 0.2631756 -0.098178744 1.0341077 -0.2556707
#> [3,] 0.64224131 -1.1565724 -0.9858267 0.560820729 0.2234804 -0.1661210
#> [4,] -0.04470914 1.8031419 -2.8889207 -1.186458639 -0.8787076 1.0204639
#> [5,] -1.73321841 -0.3311320 -0.6404817 1.096777044 1.1629646 0.1362219
#> [6,] 0.00213186 -1.6055134 0.5705076 -0.005344028 -2.0001649 0.4071676
#> reps 43 reps 44 reps 45 reps 46 reps 47 reps 48
#> [1,] -0.06965481 0.3747244 1.7196273 -0.2589326 -2.2891240 1.3242586
#> [2,] -0.24766434 -0.4252677 0.2700549 0.3943792 0.7410012 -0.7012317
#> [3,] 0.69555081 0.9510128 -0.4221840 -0.8518571 -1.3162452 -0.5806143
#> [4,] 1.14622836 -0.3892372 -1.1891133 2.6491669 0.9198037 -1.0010722
#> [5,] -2.40309621 -0.2843307 -0.3310330 0.1560117 0.3981302 -0.6681786
#> [6,] 0.57273956 0.8574098 -0.9398293 1.1302073 -0.4075286 0.9451850
#> reps 49 reps 50
#> [1,] 0.4337021 1.7784293
#> [2,] 1.0051592 0.1344477
#> [3,] -0.3901187 0.7655990
#> [4,] 0.3763703 0.9551367
#> [5,] 0.2441649 -0.0505657
#> [6,] -1.4262573 -0.3058154
#>
#> $basis
#> $call
#> basisfd(type = type, rangeval = rangeval, nbasis = nbasis, params = params,
#> dropind = dropind, quadvals = quadvals, values = values,
#> basisvalues = basisvalues)
#>
#> $type
#> [1] "bspline"
#>
#> $rangeval
#> [1] 0 1
#>
#> $nbasis
#> [1] 6
#>
#> $params
#> [1] 0.3333333 0.6666667
#>
#> $dropind
#> NULL
#>
#> $quadvals
#> NULL
#>
#> $values
#> list()
#>
#> $basisvalues
#> list()
#>
#> $names
#> [1] "bspl4.1" "bspl4.2" "bspl4.3" "bspl4.4" "bspl4.5" "bspl4.6"
#>
#> attr(,"class")
#> [1] "basisfd"
#>
#> $fdnames
#> $fdnames$args
#> [1] "time"
#>
#> $fdnames$reps
#> [1] "reps 1" "reps 2" "reps 3" "reps 4" "reps 5" "reps 6" "reps 7"
#> [8] "reps 8" "reps 9" "reps 10" "reps 11" "reps 12" "reps 13" "reps 14"
#> [15] "reps 15" "reps 16" "reps 17" "reps 18" "reps 19" "reps 20" "reps 21"
#> [22] "reps 22" "reps 23" "reps 24" "reps 25" "reps 26" "reps 27" "reps 28"
#> [29] "reps 29" "reps 30" "reps 31" "reps 32" "reps 33" "reps 34" "reps 35"
#> [36] "reps 36" "reps 37" "reps 38" "reps 39" "reps 40" "reps 41" "reps 42"
#> [43] "reps 43" "reps 44" "reps 45" "reps 46" "reps 47" "reps 48" "reps 49"
#> [50] "reps 50"
#>
#> $fdnames$funs
#> [1] "values"
#>
#>
#> attr(,"class")
#> [1] "fd"
#>
#>
#> $gram_list
#> $gram_list[[1]]
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.0476190476 0.0291666667 0.0061507937 0.0003968254 0.0000000000
#> [2,] 0.0291666667 0.0738095238 0.0520833333 0.0114583333 0.0001488095
#> [3,] 0.0061507937 0.0520833333 0.1089285714 0.0709821429 0.0114583333
#> [4,] 0.0003968254 0.0114583333 0.0709821429 0.1089285714 0.0520833333
#> [5,] 0.0000000000 0.0001488095 0.0114583333 0.0520833333 0.0738095238
#> [6,] 0.0000000000 0.0000000000 0.0003968254 0.0061507937 0.0291666667
#> [,6]
#> [1,] 0.0000000000
#> [2,] 0.0000000000
#> [3,] 0.0003968254
#> [4,] 0.0061507937
#> [5,] 0.0291666667
#> [6,] 0.0476190476
#>
#>
#> $gram_deriv_list
#> $gram_deriv_list[[1]]
#> [,1] [,2] [,3] [,4] [,5] [,6]
#> [1,] 324.0 -445.500 94.500 27.000 0.000 0.0
#> [2,] -445.5 648.000 -182.250 -30.375 10.125 0.0
#> [3,] 94.5 -182.250 121.500 -30.375 -30.375 27.0
#> [4,] 27.0 -30.375 -30.375 121.500 -182.250 94.5
#> [5,] 0.0 10.125 -30.375 -182.250 648.000 -445.5
#> [6,] 0.0 0.000 27.000 94.500 -445.500 324.0
#>
#>
#> $eval_point
#> [1] 0.00 0.02 0.04 0.06 0.08 0.10 0.12 0.14 0.16 0.18 0.20 0.22 0.24 0.26 0.28
#> [16] 0.30 0.32 0.34 0.36 0.38 0.40 0.42 0.44 0.46 0.48 0.50 0.52 0.54 0.56 0.58
#> [31] 0.60 0.62 0.64 0.66 0.68 0.70 0.72 0.74 0.76 0.78 0.80 0.82 0.84 0.86 0.88
#> [46] 0.90 0.92 0.94 0.96 0.98 1.00
#>
#> $n_basis_list
#> [1] 6
#>
#> $n_sample
#> [1] 50
#>
#> $n_functional
#> [1] 1
#>
#> $n_scalar
#> [1] 3
#>
#> attr(,"class")
#> [1] "predictor_hybrid"
length(sim$y)
#> [1] 50split_and_normalize_all() performs a train/test split,
within-modality standardization, between-modality variance balancing,
and response standardization using training statistics.
fit <- fit_hybridPLS(
prep$predictor_train,
prep$response_train,
n_iter = 3,
lambda = 1e-3,
validation_data = list(
W_test = prep$predictor_test,
y_test = prep$response_test
)
)
fit
#> Hybrid Penalized PLS model
#> Components (n_iter): 3
#> Functional predictors: 1
#> Lambda: 0.001
#> Validation RMSE by component:
#> 0.5779, 0.3936, 0.3822
preds <- predict(fit, prep$predictor_test)
rmse <- sqrt(mean((prep$response_test - preds)^2))
rmse
#> [1] 0.3822361sessionInfo()
#> R version 4.5.0 (2025-04-11 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 26200)
#>
#> Matrix products: default
#> LAPACK version 3.12.1
#>
#> locale:
#> [1] LC_COLLATE=C
#> [2] LC_CTYPE=English_United States.utf8
#> [3] LC_MONETARY=English_United States.utf8
#> [4] LC_NUMERIC=C
#> [5] LC_TIME=English_United States.utf8
#>
#> time zone: America/Los_Angeles
#> tzcode source: internal
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] FSHybridPLS_0.1.0
#>
#> loaded via a namespace (and not attached):
#> [1] cli_3.6.5 knitr_1.50 rlang_1.1.6 xfun_0.52
#> [5] KernSmooth_2.23-26 mclust_6.1.1 fds_1.8 deSolve_1.40
#> [9] jsonlite_2.0.0 hdrcde_3.4 RCurl_1.98-1.17 colorspace_2.1-1
#> [13] htmltools_0.5.8.1 pracma_2.4.4 sass_0.4.10 rmarkdown_2.29
#> [17] grid_4.5.0 evaluate_1.0.4 jquerylib_0.1.4 bitops_1.0-9
#> [21] MASS_7.3-65 ks_1.15.1 fastmap_1.2.0 yaml_2.3.10
#> [25] mvtnorm_1.3-3 lifecycle_1.0.5 rainbow_3.8 cluster_2.1.8.1
#> [29] compiler_4.5.0 fda_6.3.0 lattice_0.22-6 digest_0.6.37
#> [33] R6_2.6.1 splines_4.5.0 pcaPP_2.0-5 bslib_0.9.0
#> [37] Matrix_1.7-3 tools_4.5.0 cachem_1.1.0