Interfacing with the gRain R package

The gRain package (link) is available from CRAN and provides the only implementation of exact inference in R; currently it only supports discrete Bayesian networks. The main data structure in gRain is the grain class, which stores a fitted Bayesian network as a list of conditional probability tables (much like bnlearn's bn.fit objects) and makes it possible for setEvidence() and querygrain() to perform posterior inference via belief propagation.

Exporting a fitted Bayesian networks to gRain

bn.fit objects can be exported with the conversion method as.grain().

> library(bnlearn)
> 
> dag = hc(learning.test)
> fitted.bnlearn = bn.fit(dag, learning.test)
> fitted.grain = as.grain(fitted.bnlearn)
> fitted.grain
Independence network: Compiled: TRUE Propagated: FALSE 
  Nodes: chr [1:6] "A" "B" "C" "D" "E" "F"

Only discrete networks can be exported, since gRain does not support networks with other parametric assumptions.

> dag.gbn = hc(gaussian.test)
> fitted.gbn = bn.fit(dag.gbn, gaussian.test)
> as.grain(fitted.gbn)
## Error: the gRain package only supports discrete networks.

The other important limitation of gRain, compared to bnlearn, is that it does not allow for conditional probabilities to be NaN. (This happens when estimating them via maximum likelihood and some parent configurations are not observed in the data.) In that case as.grain() will give a warning and replace the NaNs with uniform distributions, much like the Bayesian posterior estimator would.

> fitted.sparse = bn.fit(dag, learning.test[1:10, ])
> as.grain(fitted.sparse)
## Warning in from.bn.fit.to.grain(x): NaN conditional probabilities in D,
## replaced with a uniform distribution.
Independence network: Compiled: TRUE Propagated: FALSE 
  Nodes: chr [1:6] "A" "B" "C" "D" "E" "F"

Importing a network structure from gRain

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

> fitted.import = as.bn.fit(fitted.grain)
> all.equal(fitted.bnlearn, fitted.import)
[1] TRUE

For convenience, bnlearn also provides an as.bn() conversion function that returns the network structure underlying the grain object as a bn object.

> dag.import = as.bn(fitted.grain)
> all.equal(dag, dag.import)
[1] TRUE

Note that by default as.bn.fit() disregards any evidence that has been set in the grain object with setEvidence(). So, for instance, setting B equal to b does not have any effect on the conditional probability table for node B in the returned bn.fit object.

> library(gRain)
> fitted.with.evidence = setEvidence(fitted.grain, node = "B", state = "b")
> as.bn.fit(fitted.with.evidence)$B

  Parameters of node B (multinomial distribution)

Conditional probability table:
 
   A
B            a          b          c
  a 0.85611511 0.44491018 0.11492178
  b 0.02517986 0.22095808 0.09446450
  c 0.11870504 0.33413174 0.79061372

Using the argument including.evidence will modify the conditional probability table to reflect the evidence in the grain object.

> as.bn.fit(fitted.with.evidence, including.evidence = TRUE)$B
## Error in prob[] <- ev$evi_weight[[which(node == ev$nodes)]]: replacement has length zero
Last updated on Sat Feb 17 23:31:46 2024 with bnlearn 5.0-20240208 and R version 4.3.2 (2023-10-31).