1 Overview

RedeR is an R-based package combined with a Java application for network analysis and visualization. RedeR is designed to deal with three key challenges in network analysis. Firstly, biological networks are modular and hierarchical, so network visualization needs to take advantage of such structural features. Secondly, network analysis relies on statistical methods, many of which are already available in resources like CRAN or Bioconductor. Thirdly, in larger networks user input is needed to focus the view of the network on the biologically relevant parts, rather than relying on an automatic layout function. The graphical user interface is depicted in Figure 1.

RedeR's graphical user interface. The **startRedeR()** function initiates the R-to-Java interface. RedeR ($\geq$ 3) will need the Java Runtime Environment (JRE) version 11 (Java $\geq$ 11). To verify the Java version on your system, please refer to the *system requirements* section.

Figure 1: RedeR's graphical user interface
The startRedeR() function initiates the R-to-Java interface. RedeR (\(\geq\) 3) will need the Java Runtime Environment (JRE) version 11 (Java \(\geq\) 11). To verify the Java version on your system, please refer to the system requirements section.

2 Quick start

#--- Load required packages
library("RedeR")
library("igraph")

2.1 Initializing the interface

The RedeR/R package sets all details to initialize the R-to-Java interface. Next, the startRedeR() function will launch the RedeR application (Figure 1), initializing the R-to-Java interface:

#--- Initialize the R-to-Java interface
startRedeR()

Note that RedeR >=2.0.0 is designed to run on Java>=11 (see system requirements).

2.2 Displaying graphs

The addGraphToRedeR() function displays igraph objects in the RedeR application. The following snippet will display the gtoy1 graph in RedeR, using the layout_with_kk() function to set the network layout (Figure 2):

#--- Add a graph
gtoy1 <- graph.lattice(c(3,3,3))
addGraphToRedeR(g=gtoy1, layout=layout_with_kk(gtoy1))
A toy example generated by the **addGraphToRedeR()** function.

Figure 2: A toy example generated by the addGraphToRedeR() function


In the reverse process, graphs are transferred from the application to R using the getGraphFromRedeR() function:

#--- Get a graph
gtoy2 <- getGraphFromRedeR()
summary(gtoy2)
## IGRAPH 32f572d U--- 0 0 --
# IGRAPH fc08ebf UN-- 27 54 -- 
# + attr: zoom (g/n), name (v/c), x (v/n), y (v/n)
#...
#--- Reset the application:
resetRedeR()

2.3 Working with containers

The addGraphToRedeR() method is also used to nest graphs into containers when setting isNested = TRUE. Next, the graphs g1 and g2 are nested into containers and then displayed in the RedeR application.

#--- Generate two scale-free graphs
g1 <- sample_pa(7)
g2 <- sample_pa(5)

#--- Set names to graph vertices
V(g1)$name <- paste("m",1:7,sep="")
V(g2)$name <- paste("n",1:5,sep="")

#--- Nest graphs into containers
addGraphToRedeR(g=g1, isNested=TRUE, gcoord=c(30,30), gscale=50)
addGraphToRedeR(g=g2, isNested=TRUE, gcoord=c(70,70), gscale=40)
Nested graphs in *RedeR* using the **addGraphToRedeR()** function.

Figure 3: Nested graphs in RedeR using the addGraphToRedeR() function


In this case, g1 and g2 are nested into containers N1 and N2, respectively (Figure 3). Each subgraph will retain the network structure, allowing access to individual nodes and edges in subsequent jobs. For example, the following snippet selects all nodes in container N2 and then retrieves the corresponding subgraph.

#--- Select nodes within a container 
selectNodes(nodes="N2")

#--- Get nodes from 'N2'
getGraphFromRedeR(status="selected")
## IGRAPH 2e43e29 U--- 0 0 -- 
## + edges from 2e43e29:
#--- Reset the application:
resetRedeR()

2.4 Interactive layout

The following snippet generates a scale-free graph using igraph's sample_pa() function and then displays the network in RedeR.

#--- Generate a larger scale-free graph
g1 <- sample_pa(100, directed=FALSE)

#--- Set names to igraph vertices
V(g1)$name <- paste0("V",1:vcount(g1))

#--- Check attributes in the 'g1' graph
summary(g1)
## IGRAPH 7278de3 UN-- 100 99 -- Barabasi graph
## + attr: name (g/c), power (g/n), m (g/n), zero.appeal (g/n), algorithm
## | (g/c), name (v/c)
#--- Send the 'g1' graph to RedeR
addGraphToRedeR(g=g1, zoom=50)

Next, the relaxRedeR() function starts RedeR's interactive force-directed layout algorithm, which will arrange the network as in Figure 4A. The layout algorithm has 9 parameters (p1 to p9), set either in the relaxRedeR() function or in the interactive application. These parameters control the layout algorithm by adjusting the relaxing process to the hierarchical levels of the network.

#--- Run RedeR's interactive layout
relaxRedeR(p1=20, p2=150, p3=20, p4=100, p5=10)
Graph layouts set by *RedeR*'s interactive force-directed algorithm. **A**) Section of a scale-free graph generated by igraph's **sample_pa()** function. **B**) Section a demo network available in the *RedeR* application.

Figure 4: Graph layouts set by RedeR's interactive force-directed algorithm
A) Section of a scale-free graph generated by igraph's sample_pa() function. B) Section a demo network available in the RedeR application.

#--- Reset the application:
resetRedeR()


3 Command-line attributes

RedeR attributes can be set either using the graphical user interface or the command-line interface. When using command-line attributes, these must follow igraph syntax rules and valid RedeR's attribute names. Graph attributes are set directly on a graph using igraph shortcuts, while node and edge attributes are set using igraph's V() and E() functions, respectivelly. For example:

#--- Set a new graph attribute in 'g1'
g1$bgcolor <- "white"

#--- Set new node attributes in 'g1'
V(g1)$nodeLineColor <- "skyblue"
V(g1)$nodeSize <- 50

#--- Set new edge attributes in 'g1'
E(g1)$edgeLineColor <- "skyblue"
E(g1)$edgeLineWidth <- 10
#--- Check the new attributes in 'g1'
summary(g1)
## IGRAPH 7278de3 UN-- 100 99 -- Barabasi graph
## + attr: name (g/c), power (g/n), m (g/n), zero.appeal (g/n), algorithm
## | (g/c), bgcolor (g/c), name (v/c), nodeLineColor (v/c), nodeSize
## | (v/n), edgeLineColor (e/c), edgeLineWidth (e/n)


Tables 1, 2, and 3 list all command-line attributes available for the current version (RedeR 2.99.3), including usage examples.


Table 1: Graph attributes. Examples of how to set RedeR's graph attributes using igraph shortcuts.
RedeR attribute Description Value Usage example
bgcolor Background color of the app panel Single color, hexadecimal or name g$bgcolor <- 'white'
gscale Graph expansion factor in the app panel Single number in [0, 100] g$gscale <- 75
zoom Zoom scale applied to the app panel Single number in [0, 100] g$zoom <- 100
nestShape Container shapes 'ELLIPSE', 'RECTANGLE', 'ROUNDED_RECTANGLE', 'TRIANGLE', 'DIAMOND' g$nestShape <- 'ELLIPSE'
nestSize Container size Single number >=0 g$nestSize <- 500
nestColor Container color Single color, hexadecimal or name g$nestColor <- 'grey'
nestLineType Container line types 'SOLID', 'DOTTED', 'DASHED', 'LONG_DASH' g$nestLineType <- 'SOLID'
nestLineWidth Container line width Single number >=0 g$nestLineWidth <- 2
nestLineColor Container line color Single color, hexadecimal or name g$nestLineColor <- 'grey'
nestLabel Label of the container Single string g$nestLabel <- 'a_name'
nestLabelSize Label size Single number >=0 g$nestLabelSize <- 24
nestLabelColor Label color Single color, hexadecimal or name g$nestLabelColor <- 'grey20'
nestLabelCoords Label xy-coord, relative to container Numeric vector with two numbers g$nestLabelCoords <- c(x=0, y=0)


Table 2: Node attributes. Examples of how to set RedeR's node attributes using igraph shortcuts.
RedeR attribute Description Value Usage example
name Node name Character vector (unique IDs) V(g)$name <- paste0('Node',1:vcount(g))
x Node x-coordinate Numeric vector in (-Inf, Inf) V(g)$x <- runif(vcount(g))
y Node y-coordinate Numeric vector in (-Inf, Inf) V(g)$y <- runif(vcount(g))
nodeShape Node shapes 'ELLIPSE', 'RECTANGLE', 'ROUNDED_RECTANGLE', 'TRIANGLE', 'DIAMOND' V(g)$nodeShape <- 'ELLIPSE'
nodeSize Node size Numeric vector >=0 V(g)$nodeSize <- 20
nodeColor Node color Hexadecimal or color name V(g)$nodeColor <- 'white'
nodeLineWidth Line width Numeric vector >=0 V(g)$nodeLineWidth <- 1
nodeLineColor Line color Hexadecimal or color name V(g)$nodeLineColor <- 'grey'
nodeLabel Node label Character vector V(g)$nodeLabel <- V(g)$name
nodeLabelSize Label size Numeric vector >=0 V(g)$nodeLabelSize <- 12
nodeLabelColor Label color Hexadecimal or color name V(g)$nodeLabelColor <- 'black'
nodeBend Node bend Numeric vector in [0,100] V(g)$nodeBend <- 50
nodeWeight Node weight (not implemented) Numeric vector >=0 V(g)$nodeWeight <- 0


Table 3: Edge attributes. Examples of how to set RedeR's edge attributes using igraph shortcuts.
RedeR attribute Description Value Usage example
edgeLineType Line types 'SOLID', 'DOTTED', 'DASHED', 'LONG_DASH' E(g)$edgeLineType <- 'SOLID'
edgeLineWidth Line width Numeric vector >=0 E(g)$edgeLineWidth <- 1
edgeLineColor Line color Hexadecimal or color name E(g)$edgeLineColor <- 'grey'
arrowType Arrows in directed graphs Integer vector: -1, 0, 1 E(g)$arrowType <- 1
arrowType Arrows in undirected graphs Integer vector: 0 (A-B), 1 (A-> B), -1 (A-| B), 2 (A <-B), -2 (A |-B), 3 (A <-> B), -3 (A |-| B), 4 (A |-> B), -4 (A <-| B) E(g)$arrowType <- 0
arrowLength Arrow length Numeric vector >=0 E(g)$arrowLength <- 15
arrowAngle Arrowhead angle in degrees Numeric vector in [10, 90] E(g)$arrowAngle <- 20
edgeWeight Edge weight Numeric vector >=0 E(g)$edgeWeight <- 0


RedeR provides two wrapper functions to add fixed values to igraph graphs. The att.addv() function adds a new attribute with a fixed value to all nodes or selected nodes, while att.adde() function adds a new attribute with a fixed value to all edges. These functions will require that the vertices are named.

#--- Make sure vertices are named!
V(g1)$name[1:5]
## [1] "V1" "V2" "V3" "V4" "V5"
#--- Add 'nodeLabelSize' attribute from a fixed value
g1 <- att.addv(g1, to = "nodeLabelSize", value = 20)

#--- Same as above, but applied only to three nodes
g1 <- att.addv(g1, to = "nodeLabelSize", value = 70, 
  filter = list("name" = V(g1)$name[1:3]))

#--- Add 'edgeLineType' attribute from a fixed value
g1 <- att.adde(g1, to = "edgeLineType", value = "DOTTED")
#--- Check the new attributes added to 'g1'
summary(g1)
## IGRAPH 7278de3 UN-- 100 99 -- Barabasi graph
## + attr: name (g/c), power (g/n), m (g/n), zero.appeal (g/n), algorithm
## | (g/c), bgcolor (g/c), name (v/c), nodeLineColor (v/c), nodeSize
## | (v/n), nodeLabelSize (v/n), edgeLineColor (e/c), edgeLineWidth (e/n),
## | edgeLineType (e/c)


Alternatively, RedeR's attributes can be set using the att.mapv(), att.setv(), and att.sete() wrapper functions. The att.mapv() will map variables from a data frame to igraph vertices, while the att.setv() and att.sete() will transform vertex and edge variables into valid attribute types. Next, to demonstrate these functions, we will create a data.frame object with compatible identifiers.

#--- Create data frame with IDs compatible to 'g1'
df <- data.frame(ID=sample(V(g1)$name))

#--- Add two random variables for demonstration
df$newAttr1 <- rnorm(nrow(df))
df$newAttr2 <- rnorm(nrow(df))
#--- Map 'df' to 'g1' using the att.mapv() function
#Note: 'refcol' indicates a 'df' column for mapping IDs
g1 <- att.mapv(g=g1, dat=df, refcol=1)

#--- Check the new attributes mapped to 'g2'
summary(g1)
## IGRAPH 7278de3 UN-- 100 99 -- Barabasi graph
## + attr: name (g/c), power (g/n), m (g/n), zero.appeal (g/n), algorithm
## | (g/c), bgcolor (g/c), name (v/c), nodeLineColor (v/c), nodeSize
## | (v/n), nodeLabelSize (v/n), newAttr1 (v/n), newAttr2 (v/n),
## | edgeLineColor (e/c), edgeLineWidth (e/n), edgeLineType (e/c)

Note that new names were included in the g2 graph, but these names are not valid RedeR's attributes yet. Next, the att.setv() and att.sete() functions are used to transform different data types into valid attributes.

# Set 'nodeColor' from 'newAttr1'
g1 <- att.setv(g1, from="newAttr1", to="nodeColor", 
  breaks=seq(-2,2,0.4), pal=2)

# Set 'nodeSize' from 'newAttr1'
g1 <- att.setv(g1, from="newAttr2", to="nodeSize", 
  nquant=10, xlim=c(20,50,1))

#--- Check the new attributes set in 'g1'
summary(g1)
## IGRAPH 7278de3 UN-- 100 99 -- Barabasi graph
## + attr: name (g/c), power (g/n), m (g/n), zero.appeal (g/n), algorithm
## | (g/c), bgcolor (g/c), legNodeColor (g/x), legNodeSize (g/x), name
## | (v/c), nodeLineColor (v/c), nodeSize (v/n), nodeLabelSize (v/n),
## | newAttr1 (v/n), newAttr2 (v/n), nodeColor (v/c), edgeLineColor (e/c),
## | edgeLineWidth (e/n), edgeLineType (e/c)

4 Workflow examples

This section provides some practical examples of how users might integrate its own pre-processed data into a graph visualization workflow. Please refer to Castro et al. (2016) and Cardoso et al. (2021) for more details about the biological background and experimental design of each example.

4.1 Nested subgraphs

Start the RedeR application (i.e. run the startRedeR() function), and then load the ER.limma and hs.inter datasets. The ER.limma is a data frame with results from a time-course differential expression analysis, listing differentially expressed (DE) genes from estrogen-treated MCF-7 cells for 0, 3, 6, and 12 hours (contrasts: t3-t0, t6-t0, and t12-t0). The hs.inter is an igraph graph derived from the Human Protein Reference Database (HPRD, release 9). The next snippets list a step-by-step preparation of three nested subgraphs to display in the RedeR application.

#--- Load required packages
library("RedeR")
library("igraph")
#--- If not running, initialize the ReadeR application
# startRedeR()
resetRedeR()
#--- Load a dataframe and an interactome
data(ER.limma)
data(hs.inter)

Extract a subgraph from the hs.inter graph and set its attributes using the att.setv() function. This subgraph will include DE genes called in the t3-t0 contrast. Note that some genes are not listed in the hs.inter, and that's okay.

#-- Extract a subgraph from the hs.inter graph
deg.t3 <- ER.limma[ER.limma$degenes.t3!=0,]
gt3 <- subg(g=hs.inter, dat=deg.t3, refcol=1)
## ...not all genes found in the network!
#-- Set attributes
gt3 <- att.setv(g=gt3, from="Symbol", to="nodeLabel")
gt3 <- att.setv(g=gt3, from="logFC.t3", to="nodeColor", 
  breaks=seq(-2,2,0.4), pal=2)

Extract another subgraph from the hs.inter graph, for DE genes in the t6-t0 contrast:

#--- Extract another subgraph from the hs.inter graph
deg.t6 <- ER.limma[ER.limma$degenes.t6!=0,]
gt6 <- subg(g=hs.inter, dat=deg.t6, refcol=1)
## ...not all genes found in the network!
#--- Set attributes
gt6 <- att.setv(g=gt6, from="Symbol", to="nodeLabel")
gt6 <- att.setv(g=gt6, from="logFC.t6", to="nodeColor",
  breaks=seq(-2,2,0.4), pal=2)

Extract another subgraph from the hs.inter graph, for DE genes in the t12-t0 contrast:

#--- Extract another subgraph from the hs.inter graph
deg.t12 <- ER.limma[ER.limma$degenes.t12!=0,]
gt12 <- subg(g=hs.inter, dat=deg.t12, refcol=1)
## ...not all genes found in the network!
#--- Set attributes
gt12 <- att.setv(g=gt12, from="Symbol", to="nodeLabel")
gt12 <- att.setv(g=gt12, from="logFC.t12", to="nodeColor", 
  breaks=seq(-2,2,0.4), pal=2)
#--- Customize subgraph names
gt3$nestLabel <- "3h"
gt6$nestLabel <- "6h"
gt12$nestLabel <- "12h"

Now use the addGraphToRedeR() function to send the three subgraphs to the RedeR application, nesting them into containers. This should start building the nested network depicted in Figure 5. The addGraphToRedeR() function will return the container IDs, from N1 to N5, which will be used to identify the graph parents.

#--- Send nested subgraphs to the RedeR application
N1 <- addGraphToRedeR(gt3, gcoord=c(10,25), gscale=20, 
  isNested=TRUE, theme='th1', zoom=30)
N2 <- addGraphToRedeR(gt6, gcoord=c(20,70), gscale=50, 
  isNested=TRUE, theme='th1', zoom=30)
N3 <- addGraphToRedeR(gt12, gcoord=c(70,55), gscale=80, 
  isNested=TRUE, theme='th1', zoom=30)

... and use the nestNodes() function to nest overlapping genes in the time series:

#--- Nest nodes into the sub-subgraphs
N4 <- nestNodes(nodes=V(gt3)$name, parent=N2, theme='th2')
N5 <- nestNodes(nodes=V(gt3)$name, parent=N3, theme='th2')
nestNodes(nodes=V(gt3)$name, parent=N5, theme='th2')

To simplify the graph, the mergeOutEdges() function can be used to assign edges to containers:

#--- Assign edges to containers
mergeOutEdges(nlevels=2)

...then telax the network:

relaxRedeR(p1=100, p2=100, p3=5, p4=150, 
  p5=5, p8=10, p9=20)

...and add a color legend:

scl <- gt3$legNodeColor$scale
names(scl) <- gt3$legNodeColor$legend 
addLegendToRedeR(scl, type="nodecolor",
  title="Node color (logFC)", stretch = 0.1)

Next, the selectNodes() function will zoom-in on the RET gene at different time points:

selectNodes("RET")
# repeat this line to see RET in all graph instances
Nested subnetworks. This graph shows genes differentially expressed in estrogen-treated MCF-7 cells at 3, 6 and 12 h (relative to 0 h). The insets correspond to the overlap between consecutive time points. Adapted from Castro *et al.* (2012, https://doi.org/10.1186/gb-2012-13-4-r29).

Figure 5: Nested subnetworks
This graph shows genes differentially expressed in estrogen-treated MCF-7 cells at 3, 6 and 12 h (relative to 0 h). The insets correspond to the overlap between consecutive time points. Adapted from Castro et al. (2012, https://doi.org/10.1186/gb-2012-13-4-r29).

4.2 Tree-and-leaf diagrams

The TreeAndLeaf package combines tree and force-directed layout algorithms for drawing binary trees, aiming to improve the visualization of dendrogram leaves. RedeR is used to display tree-and-leaf diagrams. Next we will transform an hclust object into a tree-and-leaf object, and then display Figure 6 in the RedeR application. Please refer to the TreeAndLeaf package's documentation for additional details and examples.

#--- Load required packages
library("RedeR")
library("igraph")
library("TreeAndLeaf")
#--- If not running, initialize the ReadeR application
#startRedeR()
resetRedeR()
#-- Generate an 'hclust' object from the 'iris' dataset
hc_iris <- hclust(dist(iris[,-5]))
#-- Convert the 'hclust' object into a 'tree-and-leaf' object
tal <- treeAndLeaf(hc_iris)

#--- Map 'iris' variables to the tree-and-leaf graph
#Note: 'refcol = 0' indicates that 'iris' rownames will be used as mapping IDs
tal <- att.mapv(g = tal, dat = iris, refcol = 0)

#--- Set node attributes using the 'att.setv' wrapper function
cols <- c("#80b1d3","#fb8072","#8dd3c7")
tal <- att.setv(tal, from="Species", to="nodeColor", cols=cols)
tal <- att.setv(tal, from="Species", to="nodeLineColor", cols=cols) 
tal <- att.setv(tal, from="Petal.Width", to="nodeSize", nquant=6, xlim=c(5,50,1))

#--- Set other attributes using igraph shortcuts
V(tal)$nodeLabel <- ""
E(tal)$edgeLineColor <- "grey70"
#--- Send the tree-and-leaf graph to RedeR
addGraphToRedeR(tal, zoom=50)

#--- Suggestion: anchor inner nodes to adjust the final layout
selectNodes(V(tal)$name[!V(tal)$isLeaf], anchor=TRUE)

#--- Call 'relax' to fine-tune the leaf nodes
relaxRedeR(p1=10, p2=100, p3=5, p4=120, p5=1, p6=100)

#--- Add legends
addLegendToRedeR(tal, type="nodecolor", title="Species", stretch=0.2)
addLegendToRedeR(tal, type="nodesize", title="PetalWidth")
A tree-and-leaf diagram. This graph is obtained by transforming an *hclust* object into a *tree-and-leaf* object.

Figure 6: A tree-and-leaf diagram
This graph is obtained by transforming an hclust object into a tree-and-leaf object.

5 Citation

If you use RedeR, please cite:

  • Castro MA, Wang X, Fletcher MN, Meyer KB, Markowetz F (2012). "RedeR: R/Bioconductor package for representing modular structures, nested networks and multiple levels of hierarchical associations." Genome Biology, 13(4), R29. Doi:10.1186/gb-2012-13-4-r29.

7 System requirements

RedeR 2.99.3 will need the Java Runtime Environment (JRE) version 11 or higher (Java >=11). In order to check the Java on your system, please use the RedPort() function with checkJava=TRUE, for example:

RedPort(checkJava=TRUE)
# RedeR will need Java Runtime Environment (Java >=11)
# Checking Java version installed on this system...
# openjdk version "11.0.13" 2021-10-19
# OpenJDK Runtime Environment (build 11.0.13+8-Ubuntu-0ubuntu1.20.04)
# OpenJDK 64-Bit Server VM (build 11.0.13+8-Ubuntu-0ubuntu1.20.04, mixed mode, sharing)

The exact output will vary, but you need to make sure the system meets the minimum version requirement.

8 Session information

## R Under development (unstable) (2024-01-16 r85808)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 22.04.3 LTS
## 
## Matrix products: default
## BLAS:   /home/biocbuild/bbs-3.19-bioc/R/lib/libRblas.so 
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_GB              LC_COLLATE=C              
##  [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/New_York
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] TreeAndLeaf_1.15.0 igraph_2.0.1.1     RedeR_2.99.3       BiocStyle_2.31.0  
## 
## loaded via a namespace (and not attached):
##  [1] nlme_3.1-164        cli_3.6.2           knitr_1.45         
##  [4] rlang_1.1.3         xfun_0.41           highr_0.10         
##  [7] jsonlite_1.8.8      glue_1.7.0          colorspace_2.1-0   
## [10] htmltools_0.5.7     sass_0.4.8          scales_1.3.0       
## [13] rmarkdown_2.25      grid_4.4.0          evaluate_0.23      
## [16] munsell_0.5.0       jquerylib_0.1.4     fastmap_1.1.1      
## [19] ape_5.7-1           yaml_2.3.8          lifecycle_1.0.4    
## [22] bookdown_0.37       BiocManager_1.30.22 compiler_4.4.0     
## [25] Rcpp_1.0.12         pkgconfig_2.0.3     lattice_0.22-5     
## [28] digest_0.6.34       R6_2.5.1            parallel_4.4.0     
## [31] magrittr_2.0.3      bslib_0.6.1         tools_4.4.0        
## [34] cachem_1.0.8