Package: PathwaySpace 1.0.3.1
In this tutorial, we demonstrate how to model signal decay functions targeted at specific nodes. Using a simple lattice graph, we describe the steps to create and apply different decay functions for PathwaySpace projections. Nodes in this example represent spots, providing an intuitive starting point for understanding how these functions can be used to capture behaviors in larger or more complex graphs, such as from spatial transcriptomics data.
# Check required packages for this vignette
if (!require("remotes", quietly = TRUE)){
install.packages("remotes")
}
if (!require("RGraphSpace", quietly = TRUE)){
remotes::install_github("sysbiolab/RGraphSpace")
}
if (!require("PathwaySpace", quietly = TRUE)){
remotes::install_github("sysbiolab/PathwaySpace")
}
if (!require("SpotSpace", quietly = TRUE)){
remotes::install_github("sysbiolab/SpotSpace")
}
# Check versions
if (packageVersion("RGraphSpace") < "1.1.0"){
message("Need to update 'RGraphSpace' for this vignette")
remotes::install_github("sysbiolab/RGraphSpace")
}
if (packageVersion("PathwaySpace") < "1.0.3.1"){
message("Need to update 'PathwaySpace' for this vignette")
remotes::install_github("sysbiolab/PathwaySpace")
}
if (packageVersion("SpotSpace") < "0.0.2"){
message("Need to update 'SpotSpace' for this vignette")
remotes::install_github("sysbiolab/SpotSpace")
}
# Load packages
library(igraph)
library(ggplot2)
library(RGraphSpace)
library(PathwaySpace)
library(SpotSpace)
library(patchwork)
# Create a lattice graph with igraph
g <- make_lattice(c(9, 9), directed = FALSE)
V(g)$name <- paste0("n",1:vcount(g))
gs <- GraphSpace(g, layout = layout_on_grid(g))
plotGraphSpace(gs, add.labels = TRUE)

# Build a PathwaySpace object
ps <- buildPathwaySpace(gs)
In the context of PathwaySpace, a decay function describes
how a signal decreases as a function of distance, modeling the gradual
loss of intensity. These functions are used to attenuate signals over
graph-based domains, so contributions from distant vertices are weighted
less than those nearby. PathwaySpace provides three built-in
decay function constructors, weibullDecay(),
expDecay(), and linearDecay(), all sharing a
consistent argument structure for practical use and comparison:
\[ \begin{gather} \small y = signal \times \text{decay}^{\left(\tfrac{x}{\text{pdist}}\right)^{\text{shape}}} && \scriptsize{(\text{Weibull})} \end{gather} \] \[ \begin{gather} \small y = signal \times \text{decay}^{\left(\tfrac{x}{\text{pdist}}\right)} && \scriptsize{(\text{Exponential})} \end{gather} \]
\[ \begin{gather} \small y = signal \times \Bigl(1 - (1 - \text{decay}) \times \tfrac{x}{\text{pdist}}\Bigr) \ & \scriptsize{(\text{Linear*})} \\ \end{gather} \\[10pt] \scriptstyle\text{*output clipped to prevent flipping sign} \]
where \(\textbf{signal}\) represents
the initial intensity, \(\textbf{decay}\) controls the rate of
attenuation, \(\textbf{x}\) is a vector
of normalized distances, \(\textbf{shape}\) adjusts the curvature of
the decay, \(\textbf{pdist}\) is a
normalization term, and \(\textbf{y}\)
is the resulting signal decay values. Next, the main arguments are
illustrated using the weibullDecay() constructor, which
returns a decay function with customized parameters.
weibullDecay(decay = 0.25, shape = 2, pdist = 0.75)
## function (x, signal)
## {
## y <- signal * 0.25^((x/0.75)^2)
## return(y)
## }
## <environment: 0x5868b31f8200>
## attr(,"name")
## [1] "weibullDecay"
… and to visualize how different parameters affect the signal
attenuation, rerun the weibullDecay() constructor with
plot = TRUE.
# Run Weibull constructor with decay = 0.5, shape = 1, and pdist = 0.25
p1 <- weibullDecay(decay = 0.4, shape = 1, pdist = 0.2, plot = TRUE)
# Run Weibull constructor with decay = 0.5, shape = 2, and pdist = 0.50
p2 <- weibullDecay(decay = 0.4, shape = 2, pdist = 0.4, plot = TRUE)
# Run Weibull constructor with decay = 0.25, shape = 3, and pdist = 0.75
p3 <- weibullDecay(decay = 0.2, shape = 4, pdist = 0.8, plot = TRUE)
p1 + p2 + p3

Note that the normalization term \(\textbf{pdist}\) anchors the decay to a reference distance where the initial signal \(S_0\) decreases to \(S_0 * \textbf{decay}\), controlling the extent over which the signal is projected along the x-axis. The \(\textbf{shape}\) parameter, on the other hand, controls the curvature of the decay function: When \(\textbf{shape} = 1\), the function follows an exponential decay; and for \(\textbf{shape} > 1\), the curve transitions from convex to concave, increasingly sigmoidal.
Next, we demonstrate the expDecay() and
linearDecay() constructors:
# Run the exp expDecay constructor with decay = 0.4 and pdist = 0.2
p1 <- expDecay(decay = 0.4, pdist = 0.2, plot = TRUE)
# Run the linearDecay constructor with decay = 0.5,and pdist = 0.25
p2 <- linearDecay(decay = 0.4, pdist = 0.3, plot = TRUE)
p1 + p2

The exponential model follows an asymptotic decay, while the linear model decreases proportionally with distance and is clipped at zero to prevent flipping sign. Together, these decay models produce distinct geometric profiles when projecting signals onto the 2D coordinate space, ranging from cone-like projections in the linear model (see the conceptual representation in Figure 1A) to radially symmetric peaks in the exponential model, with the Weibull model transitioning between them to form dome-like surfaces depending on its \(\textbf{shape}\) parameter.
Returning to our lattice example, next we assign a linear decay model
to the PathwaySpace object, setting \(\textbf{pdist}\) as the average
center-to-center distance between vertices. For more details, refer to
the documentation of the vertexDecay() accessor.
# Get distance to the nearest vertex
near_df <- getNearestNode(ps)
pdist <- mean(near_df$dist)
# 'pdist' set as the average center-to-center distance between vertices
pdist
## [1] 0.1
# Setting a linear decay model for all vertices
vertexDecay(ps) <- linearDecay(pdist = pdist)
Now we project a random binary signal using the
circularProjection() function.
# Add a random binary signal
set.seed(10)
vertexSignal(ps) <- sample(c(0,1), gs_vcount(ps), replace = TRUE)
# Running and plotting projections
ps <- circularProjection(ps, k = 1)
plotPathwaySpace(ps, marks = "n34")

By assigning a new decay model to specific vertices, we can explore
how signals propagate across the network under different rules. To
illustrate, we assign a Weibull decay function to vertex
n34.
# Changing decay model of vertex 'n34'
vertexDecay(ps)[["n34"]] <- weibullDecay(shape = 2, pdist = pdist*10)
# Running and plotting projections
ps <- circularProjection(ps, k = 1)
plotPathwaySpace(ps, marks = "n34")

## R version 4.5.1 (2025-06-13)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.3 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: America/Sao_Paulo
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] patchwork_1.3.2 igraph_2.2.0 SpotSpace_0.0.2
## [4] PathwaySpace_1.0.3.1 RGraphSpace_1.1.0 ggplot2_4.0.0.9000
## [7] remotes_2.5.0 bs4cards_0.1.1
##
## loaded via a namespace (and not attached):
## [1] deldir_2.0-4 pbapply_1.7-4 gridExtra_2.3
## [4] rlang_1.1.6 magrittr_2.0.4 RcppAnnoy_0.0.22
## [7] spatstat.geom_3.6-0 matrixStats_1.5.0 ggridges_0.5.7
## [10] compiler_4.5.1 png_0.1-8 vctrs_0.6.5
## [13] reshape2_1.4.4 stringr_1.5.2 pkgconfig_2.0.3
## [16] fastmap_1.2.0 fontawesome_0.5.3 promises_1.3.3
## [19] rmarkdown_2.30 purrr_1.1.0 xfun_0.53
## [22] cachem_1.1.0 jsonlite_2.0.0 goftest_1.2-3
## [25] later_1.4.4 spatstat.utils_3.2-0 irlba_2.3.5.1
## [28] parallel_4.5.1 cluster_2.1.8.1 R6_2.6.1
## [31] ica_1.0-3 spatstat.data_3.1-8 stringi_1.8.7
## [34] bslib_0.9.0 RColorBrewer_1.1-3 reticulate_1.43.0
## [37] spatstat.univar_3.1-4 parallelly_1.45.1 lmtest_0.9-40
## [40] jquerylib_0.1.4 scattermore_1.2 Rcpp_1.1.0
## [43] knitr_1.50 tensor_1.5.1 future.apply_1.20.0
## [46] zoo_1.8-14 sctransform_0.4.2 httpuv_1.6.16
## [49] Matrix_1.7-4 splines_4.5.1 tidyselect_1.2.1
## [52] abind_1.4-8 rstudioapi_0.17.1 yaml_2.3.10
## [55] spatstat.random_3.4-2 spatstat.explore_3.5-3 codetools_0.2-20
## [58] miniUI_0.1.2 listenv_0.9.1 lattice_0.22-5
## [61] tibble_3.3.0 plyr_1.8.9 shiny_1.11.1
## [64] withr_3.0.2 S7_0.2.0 ROCR_1.0-11
## [67] evaluate_1.0.5 Rtsne_0.17 future_1.67.0
## [70] fastDummies_1.7.5 survival_3.8-3 polyclip_1.10-7
## [73] fitdistrplus_1.2-4 pillar_1.11.1 Seurat_5.3.1.9999
## [76] KernSmooth_2.23-26 plotly_4.11.0 generics_0.1.4
## [79] RcppHNSW_0.6.0 sp_2.2-0 scales_1.4.0
## [82] globals_0.18.0 xtable_1.8-4 glue_1.8.0
## [85] lazyeval_0.2.2 tools_4.5.1 data.table_1.17.8
## [88] RSpectra_0.16-2 RANN_2.6.2 fs_1.6.6
## [91] dotCall64_1.2 cowplot_1.2.0 grid_4.5.1
## [94] tidyr_1.3.1 nlme_3.1-168 cli_3.6.5
## [97] spatstat.sparse_3.1-0 spam_2.11-1 viridisLite_0.4.2
## [100] dplyr_1.1.4 uwot_0.2.3 gtable_0.3.6
## [103] sass_0.4.10 digest_0.6.37 progressr_0.17.0
## [106] ggrepel_0.9.6 htmlwidgets_1.6.4 SeuratObject_5.2.0
## [109] farver_2.1.2 htmltools_0.5.8.1 lifecycle_1.0.4
## [112] httr_1.4.7 mime_0.13 MASS_7.3-65