Interfacing with the graph R package

The graph package (link) is available from Bioconductor and it is one of the most popular packages to work on graphs (both directed and undirected). It implements a variety of algorithms for random graph generation, centrality statistics, graph distances, nodes and arcs manipulation utilities, and it provides a strong foundation for the Rgraphviz package (link). For this reason, bnlearn implements functions to import and export network structures to graph as:

  • graphNEL objects, which encode the graph as a list in which each element refer to one of the nodes in the graph and contains a vector with its children;
  • graphAM objects, which encode the graph as a matrix in which the (i, j) cell is equal to one if there is an arc from the ith node to the jth node and zero otherwise.

Exporting a network structure to graph

Exporting the objects is achieved with the conversion methods as.graphNEL() and as.graphAM().

> library(bnlearn)
> 
> dag.bnlearn = random.graph(LETTERS[1:10])
> dag.bnlearn

  Random/Generated Bayesian network

  model:
   [A][B][C][D][G][E|A:D][F|C][H|B:C:E][I|C:D:F][J|B:F:G] 
  nodes:                                 10 
  arcs:                                  12 
    undirected arcs:                     0 
    directed arcs:                       12 
  average markov blanket size:           4.20 
  average neighbourhood size:            2.40 
  average branching factor:              1.20 

  generation algorithm:                  Full Ordering 
  arc sampling probability:              0.2222222
> dag.graphNEL = as.graphNEL(dag.bnlearn)
> dag.graphNEL
A graphNEL graph with directed edges
Number of Nodes = 10 
Number of Edges = 12
> dag.graphAM = as.graphAM(dag.bnlearn)
> dag.graphAM
A graphAM graph with directed edges
Number of Nodes = 10 
Number of Edges = 12

Both methods accept objects of class bn.fit as well as of class bn, and they implicitly call bn.net() (link) on the former to export them.

> dag.test = hc(learning.test)
> dag.test

  Bayesian network learned via Score-based methods

  model:
   [A][C][F][B|A][D|A:C][E|B:F] 
  nodes:                                 6 
  arcs:                                  5 
    undirected arcs:                     0 
    directed arcs:                       5 
  average markov blanket size:           2.33 
  average neighbourhood size:            1.67 
  average branching factor:              0.83 

  learning algorithm:                    Hill-Climbing 
  score:                                 BIC (disc.) 
  penalization coefficient:              4.258597 
  tests used in the learning procedure:  40 
  optimized:                             TRUE
> fitted = bn.fit(dag.test, learning.test)
> as.graphNEL(fitted)
A graphNEL graph with directed edges
Number of Nodes = 6 
Number of Edges = 5
> as.graphAM(fitted)
A graphAM graph with directed edges
Number of Nodes = 6 
Number of Edges = 5

Undirected arcs are exported as a pair of directed arcs, that is, Xi — Xj becomes {Xi → Xj, Xi ← Xj}.

> as.graphNEL(skeleton(dag.test))
A graphNEL graph with directed edges
Number of Nodes = 6 
Number of Edges = 10

Importing a network structure from graph

Importing network structures works in the same way, with a conversion method as.bn().

> as.bn(dag.graphAM)

  Random/Generated Bayesian network

  model:
   [A][B][C][D][G][E|A:D][F|C][H|B:C:E][I|C:D:F][J|B:F:G] 
  nodes:                                 10 
  arcs:                                  12 
    undirected arcs:                     0 
    directed arcs:                       12 
  average markov blanket size:           4.20 
  average neighbourhood size:            2.40 
  average branching factor:              1.20 

  generation algorithm:                  Empty
> as.bn(dag.graphNEL)

  Random/Generated Bayesian network

  model:
   [A][B][C][D][G][E|A:D][F|C][H|B:C:E][I|C:D:F][J|B:F:G] 
  nodes:                                 10 
  arcs:                                  12 
    undirected arcs:                     0 
    directed arcs:                       12 
  average markov blanket size:           4.20 
  average neighbourhood size:            2.40 
  average branching factor:              1.20 

  generation algorithm:                  Empty

Note that as graphs are imported with as.bn() they are checked to be acyclic, unless the user specifies check.cycles = FALSE.

> library(graph)
> 
> m = matrix(rep(0, 9), nrow = 3, ncol = 3)
> m[1, 2] = m[2, 3] = m[3, 1] = 1
> cyclic = graphAM(m, edgemode = "directed")
> as.bn(cyclic)
## Error: the graphAM object contains directed cycles.
> as.bn(cyclic, check.cycles = FALSE)

  Random/Generated Bayesian network

  model:
   [n1|n3][n2|n1][n3|n2] 
  nodes:                                 3 
  arcs:                                  3 
    undirected arcs:                     0 
    directed arcs:                       3 
  average markov blanket size:           2.00 
  average neighbourhood size:            2.00 
  average branching factor:              1.00 

  generation algorithm:                  Empty
Last updated on Sat Feb 17 23:31:09 2024 with bnlearn 5.0-20240208 and R version 4.3.2 (2023-10-31).