Boostrap-based inference

Measuring arc strength

Measuring the degree of confidence in particular graphical features of a Bayesian network has always been one key problem in the inference on the network structure. In the case of single arcs this quantity has also been called arc strength.

Friedman introduced a very simple way of quantifying such a confidence: generating multiple network structures by applying nonparametric bootstrap to the data and estimating the relative frequency of the feature of interest. The simplest application of this approach allows an easy computation of the arc strength for the arcs present in a given network:

> res = hc(learning.test)
> arc.strength(res, learning.test, criterion = "bootstrap")
  from to strength
1    A  B        1
2    A  D        1
3    C  D        1
4    B  E        1
5    F  E        1

Note that this approach does not require discrete data; it works fundamentally in the same way for any type of data supported by the learning algorithms.

Another interesting application is checking how the strength coefficients vary with the sample size:

> a50 = arc.strength(res, learning.test, criterion = "bootstrap", m = 50)
> a100 = arc.strength(res, learning.test, criterion = "bootstrap", m = 100)
> a200 = arc.strength(res, learning.test, criterion = "bootstrap", m = 200)
> a300 = arc.strength(res, learning.test, criterion = "bootstrap", m = 300)
> a400 = arc.strength(res, learning.test, criterion = "bootstrap", m = 400)
> data.frame(arcs(res), strength50 = a50$strength, strength100 = a100$strength, 
+   strength200 = a200$strength, strength300 = a300$strength, strength400 = a400$strength)
  from to strength50 strength100 strength200 strength300 strength400
1    A  B      0.250       0.930        1.00        1.00       1.000
2    A  D      0.240       0.925        1.00        1.00       1.000
3    C  D      0.000       0.000        0.14        0.79       0.995
4    B  E      0.020       0.340        0.93        1.00       1.000
5    F  E      0.125       0.635        0.97        1.00       1.000

Measuring arc strength and confidence in direction

Another, related, problem is to assess arc strength (irrespective of its direction) and confidence in its direction (given its presence in the graph) at the same time. The solution presented in Imoto et al. is essentally the same as in the previous case.

> boot.strength(learning.test, R = 200, m = 5000, "gs")
   from to strength  direction
1     A  B    1.000 0.49250000
2     A  C    0.060 0.25000000
3     A  D    1.000 0.96750000
4     A  E    0.295 0.68644068
5     A  F    0.030 0.58333333
6     B  A    1.000 0.50750000
7     B  C    0.385 0.35064935
8     B  D    0.465 0.93548387
9     B  E    1.000 0.90750000
10    B  F    0.165 0.15151515
11    C  A    0.060 0.75000000
12    C  B    0.385 0.64935065
13    C  D    1.000 0.97250000
14    C  E    0.210 0.91666667
15    C  F    0.035 0.42857143
16    D  A    1.000 0.03250000
17    D  B    0.465 0.06451613
18    D  C    1.000 0.02750000
19    D  E    0.340 0.26470588
20    D  F    0.005 0.00000000
21    E  A    0.295 0.31355932
22    E  B    1.000 0.09250000
23    E  C    0.210 0.08333333
24    E  D    0.340 0.73529412
25    E  F    1.000 0.03000000
26    F  A    0.030 0.41666667
27    F  B    0.165 0.84848485
28    F  C    0.035 0.57142857
29    F  D    0.005 1.00000000
30    F  E    1.000 0.97000000

Interpretation of the output of boot.strength (documented here) is usefult both to identify arcs representing strong relationships and to distinguish compelled arcs from score equivalent ones.

The general case

A general-purpose bootstrap implementation, similar in scope to the boot function in package boot, is provided by the bn.boot function (documented here). bn.boot takes a data set, a structure learning algorithm and an arbitrary function (whose first argument must be an object of class bn) and returns a list of the values returned by said function for the network structures learned from the bootstrapped samples.

Let's assume for example that we want to know how many arcs we can expect in a network learned with a hill climbing search from the learning.test data set. It can be done in the following way:

> unlist(bn.boot(learning.test, statistic = function(x) { nrow(arcs(x)) },
+   algorithm = "hc", R = 10))
 [1] 5 5 5 5 5 5 5 5 6 5

Or maybe we want to compare the computational complexity (measured with the numbers of test/score comparisons) between hill climbing and Grow-Shrink for a sample size of 500:

> unlist(bn.boot(learning.test, statistic = function(x) { x$learning$ntests },
+   algorithm = "hc", R = 10, m = 50))
 [1] 35 20 30 25 30 35 30 35 20 25
> unlist(bn.boot(learning.test, statistic = function(x) { x$learning$ntests },
+   algorithm = "gs", R = 10, m = 50))
 [1] 26 16 31 26 27 25 17 27 22 22

Many other questions can be answered with this approach; essentially any function of the structure of the network can be used for the statistic parameter.