Skip to content

Commit 21a4329

Browse files
Marco Scutaricran-robot
authored andcommitted
version 5.0
1 parent cf7a93c commit 21a4329

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+2309
-1563
lines changed

Changelog

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,44 @@
1-
bnlearn (4.9.4)
2-
3-
* added some PROTECT()s to pass the CRAN tests.
1+
bnlearn (5.0)
2+
3+
* the "effective" argument of nparams() is now deprecated and will be removed
4+
by the end of 2025.
5+
* the cross-validation loss functions "pred-lw", "cor-lw" and "mse-lw" are
6+
now deprecated in favour of "pred", "cor" and "mse" with optional
7+
arguments predict = "bayes-lw"; they will be removed in 2025.
8+
* the cross-validation loss functions "logl-g" and "logl-cg" are now
9+
deprecated in favour of "logl"; they will be removed in 2025.
10+
* the cross-validation loss functions "pred", "cor" and "mse" can now be
11+
computed with exact inference using predict = "exact".
12+
* completed the implementation of KL(), which now supports conditional
13+
Gaussian networks in addition to discrete and Gaussian ones.
14+
* implemented Shannon's entropy.
15+
* conditional independence tests now have optional arguments like network
16+
scores.
17+
* added a "custom-test" conditional independence test allowing user-provided
18+
test statistics in the same way as "custom" allows user-provided network
19+
scores.
20+
* the custom score now has label "custom-score", instead of just "custom",
21+
for clarity and to make it consistent with "custom-test".
22+
* added a "params.threshold" to hard EM methods in bn.fit(), and renamed
23+
the log-likelihood threshold to "loglik.threshold".
24+
* the log-likelihood stopping rule in hard EM now uses the log-likelihood of
25+
the completed data, which works better in the presence of latent
26+
variables and is more appropriate accoridng to Koller & Friedman (thanks
27+
Laura Azzimonti).
28+
* coefficients(), sigma(), fitted() and residuals() return an error when
29+
called on bn objects instead of failing silently and returning NULL.
30+
* preserve and return the probabilities from predict(..., prob = TRUE) when
31+
using the parallel package (thanks Alex Rudge).
32+
* logLik() now returns an object of class "logLik" with the expected
33+
attributes.
34+
* added an identifiable() function to tell whether a bn.fit object contains NA
35+
parameter values; and a singular() function to tell whether it is
36+
singular (with 0-1 probability distributions, zero standard errors).
37+
* the entropy loss in bn.cv() is now estimated now uses the node-average
38+
(log-)likelihood; it does not produce warnings for incomplete data and
39+
may occasionally return +Inf instead of NA in some corner cases (for
40+
instance, when the model learning from training has NA parameters or
41+
conditional probabilities equal to zero).
442

543
bnlearn (4.9.3)
644

DESCRIPTION

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ Package: bnlearn
22
Type: Package
33
Title: Bayesian Network Structure Learning, Parameter Learning and
44
Inference
5-
Version: 4.9.4
6-
Date: 2024-05-02
7-
Depends: R (>= 4.3.0), methods
5+
Version: 5.0
6+
Date: 2024-07-29
7+
Depends: R (>= 4.4.0), methods
88
Suggests: parallel, graph, Rgraphviz, igraph, lattice, gRbase, gRain
9-
(>= 1.3-3), ROCR, Rmpfr, gmp
10-
Author: Marco Scutari [aut, cre], Tomi Silander [ctb], Robert Ness [ctb]
9+
(>= 1.3-3), Rmpfr, gmp
10+
Authors@R: c(person(given = "Marco", family = "Scutari", role = c("aut", "cre"),
11+
email = "[email protected]"),
12+
person(given = "Tomi", family = "Silander", role = "ctb"))
1113
Maintainer: Marco Scutari <[email protected]>
1214
Description: Bayesian network structure learning, parameter learning and inference.
1315
This package implements constraint-based (PC, GS, IAMB, Inter-IAMB, Fast-IAMB, MMPC,
@@ -26,6 +28,8 @@ SystemRequirements: USE_C17
2628
License: GPL (>= 2)
2729
LazyData: yes
2830
NeedsCompilation: yes
29-
Packaged: 2024-05-02 17:13:25 UTC; fizban
31+
Packaged: 2024-07-30 17:02:42 UTC; fizban
32+
Author: Marco Scutari [aut, cre],
33+
Tomi Silander [ctb]
3034
Repository: CRAN
31-
Date/Publication: 2024-05-02 22:30:02 UTC
35+
Date/Publication: 2024-07-30 17:30:02 UTC

MD5

Lines changed: 101 additions & 100 deletions
Large diffs are not rendered by default.

NAMESPACE

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export(
2626
"set.edge", "drop.edge", "add.node", "remove.node", "rename.nodes",
2727
# get, set and count sets of nodes: parents, children, etc.
2828
"parents", "parents<-", "children", "children<-", "spouses", "ancestors",
29-
"descendants", "root.nodes", "leaf.nodes", "nnodes",
29+
"descendants", "root.nodes", "leaf.nodes", "isolated.nodes", "nnodes",
3030
# get and set adjacency matrices.
3131
"amat", "amat<-",
3232
# model string formulas.
@@ -54,19 +54,21 @@ export(
5454
# data preprocessing and imputation.
5555
"discretize", "dedup", "impute",
5656
# inference.
57-
"KL", "cpquery",
57+
"H", "KL", "cpquery",
5858
# import/export functions for varous file formats.
5959
"read.bif", "write.bif", "read.dsc", "write.dsc", "read.net", "write.net",
6060
"write.dot",
6161
# utility functions to manipulate test/score counters.
6262
"test.counter", "increment.test.counter", "reset.test.counter",
6363
# assorted functions involving network structures.
6464
"acyclic", "directed", "path.exists", "node.ordering", "subgraph",
65+
# assorted functions involving fitted networks.
66+
"identifiable", "singular",
6567
# assorted functions to extract information.
6668
"configs", "nparams", "ntests",
6769
# assorted conversion functions.
6870
"as.bn", "as.bn.fit", "as.grain", "as.graphNEL", "as.graphAM", "as.igraph",
69-
"as.prediction", "as.lm",
71+
"as.lm",
7072
# graph enumeration.
7173
"count.graphs"
7274
)
@@ -110,7 +112,6 @@ S3method(as.graphAM, "bn.fit")
110112
S3method(as.igraph, "bn")
111113
S3method(as.igraph, "bn.fit")
112114
S3method(as.bn, "igraph")
113-
S3method(as.prediction, "bn.strength")
114115
S3method(as.lm, "bn")
115116
S3method(as.lm, "bn.fit")
116117
S3method(as.lm, "bn.fit.gnode")
@@ -120,21 +121,25 @@ S3method(print, "bn.fit.dnode")
120121
S3method(print, "bn.fit.onode")
121122
S3method(print, "bn.fit.gnode")
122123
S3method(print, "bn.fit.cgnode")
124+
S3method(residuals, "bn")
123125
S3method(residuals, "bn.fit")
124126
S3method(residuals, "bn.fit.dnode")
125127
S3method(residuals, "bn.fit.onode")
126128
S3method(residuals, "bn.fit.gnode")
127129
S3method(residuals, "bn.fit.cgnode")
130+
S3method(fitted, "bn")
128131
S3method(fitted, "bn.fit")
129132
S3method(fitted, "bn.fit.dnode")
130133
S3method(fitted, "bn.fit.onode")
131134
S3method(fitted, "bn.fit.gnode")
132135
S3method(fitted, "bn.fit.cgnode")
136+
S3method(sigma, "bn")
133137
S3method(sigma, "bn.fit")
134138
S3method(sigma, "bn.fit.dnode")
135139
S3method(sigma, "bn.fit.onode")
136140
S3method(sigma, "bn.fit.gnode")
137141
S3method(sigma, "bn.fit.cgnode")
142+
S3method(coef, "bn")
138143
S3method(coef, "bn.fit")
139144
S3method(coef, "bn.fit.dnode")
140145
S3method(coef, "bn.fit.onode")

R/arc.strength.R

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
# compute arcs' strength as the p-value of the test for their removal.
3-
arc.strength.test = function(network, data, test, alpha, B, debug = FALSE) {
3+
arc.strength.test = function(network, data, test, alpha, extra.args,
4+
debug = FALSE) {
45

56
drop = function(arc) {
67

@@ -15,7 +16,7 @@ arc.strength.test = function(network, data, test, alpha, B, debug = FALSE) {
1516
network$nodes[[arc[2]]]$parents[network$nodes[[arc[2]]]$parents != arc[1]]
1617

1718
a = indep.test(arc[1], arc[2], parents, data = data, test = test,
18-
B = B, alpha = alpha)
19+
extra.args = extra.args, alpha = alpha)
1920

2021
if (debug) {
2122

@@ -228,7 +229,7 @@ arc.strength.boot = function(data, cluster = NULL, R, m, algorithm,
228229
# compute an approximation of arc and direction strength from the Bayes factors
229230
# that can be computed from a single MAP network.
230231
bf.strength.backend = function(x, data, score, extra.args, precBits = 200,
231-
debug = FALSE) {
232+
debug = FALSE) {
232233

233234
# construct all pairs of nodes.
234235
nodes = names(x$nodes)

R/backend-indep.R

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# common steps in constraint-based structure learning: infer arc orientation
33
# from the skeleton and the data.
44
learn.arc.directions = function(x, cluster = NULL, local.structure, whitelist,
5-
blacklist, test, alpha, B = NULL, data, max.sx = ncol(data),
5+
blacklist, test, alpha, extra.args = list(), data, max.sx = ncol(data),
66
debug = FALSE) {
77

88
nodes = names(x)
@@ -18,8 +18,8 @@ learn.arc.directions = function(x, cluster = NULL, local.structure, whitelist,
1818
# 3.1 detect v-structures.
1919
vs = do.call("rbind",
2020
vstruct.detect(nodes = nodes, arcs = arcs, mb = local.structure,
21-
data = x, alpha = alpha, B = B, test = test, blacklist = blacklist,
22-
max.sx = max.sx, debug = debug))
21+
data = x, alpha = alpha, extra.args = extra.args, test = test,
22+
blacklist = blacklist, max.sx = max.sx, debug = debug))
2323
rownames(vs) = NULL
2424

2525
if (!is.null(vs)) {
@@ -31,21 +31,14 @@ learn.arc.directions = function(x, cluster = NULL, local.structure, whitelist,
3131

3232
}#THEN
3333

34-
# save the status of the learning algorithm.
35-
learning = list(whitelist = whitelist, blacklist = blacklist,
36-
test = test, args = list(alpha = alpha), ntests = test.counter())
37-
# include also the number of permutations/bootstrap samples if it makes sense.
38-
if (!is.null(B))
39-
learning$args$B = B
40-
4134
# 4. propagate directions.
42-
pdag = list(learning = learning,
35+
pdag = list(learning = list(whitelist = whitelist, blacklist = blacklist),
4336
nodes = structure(rep(0, length(nodes)), names = nodes),
4437
arcs = arcs)
4538

46-
return(cpdag.backend(pdag, fix = TRUE, debug = debug))
39+
return(cpdag.backend(pdag, fix = TRUE, debug = debug)$arcs)
4740

48-
}#SECOND.PRINCIPLE
41+
}#LEARN.ARC.DIRECTIONS
4942

5043
# construct a fake markov blanket using all the nodes within distance 2.
5144
fake.markov.blanket = function(learn, target) {
@@ -59,8 +52,9 @@ fake.markov.blanket = function(learn, target) {
5952
}#FAKE.MARKOV.BLANKET
6053

6154
# build the neighbourhood of a node from the markov blanket.
62-
neighbour = function(x, mb, data, alpha, B = NULL, whitelist, blacklist,
63-
test, empty.dsep = TRUE, markov = TRUE, max.sx = ncol(x), debug = FALSE) {
55+
neighbour = function(x, mb, data, alpha, extra.args = list(),
56+
whitelist, blacklist, test, empty.dsep = TRUE, markov = TRUE,
57+
max.sx = ncol(x), debug = FALSE) {
6458

6559
# initialize the neighbourhood using the markov blanket.
6660
candidate.neighbours = mb[[x]]
@@ -121,7 +115,8 @@ neighbour = function(x, mb, data, alpha, B = NULL, whitelist, blacklist,
121115

122116
a = allsubs.test(x = x, y = y, sx = dsep.set,
123117
min = ifelse(empty.dsep, 0, 1), max = min(length(dsep.set), max.sx),
124-
data = data, test = test, alpha = alpha, B = B, debug = debug)
118+
data = data, test = test, alpha = alpha, extra.args = extra.args,
119+
debug = debug)
125120

126121
# update the neighbourhood.
127122
if (a["p.value"] > alpha)
@@ -138,8 +133,8 @@ neighbour = function(x, mb, data, alpha, B = NULL, whitelist, blacklist,
138133
}#NEIGHBOUR
139134

140135
# detect v-structures in the graph.
141-
vstruct.detect = function(nodes, arcs, mb, data, alpha, B = NULL, test,
142-
blacklist, max.sx = ncol(data), debug = FALSE) {
136+
vstruct.detect = function(nodes, arcs, mb, data, alpha, extra.args = list(),
137+
test, blacklist, max.sx = ncol(data), debug = FALSE) {
143138

144139
vstruct.centered.on = function(x, mb, data, dsep.set) {
145140

@@ -209,8 +204,8 @@ vstruct.detect = function(nodes, arcs, mb, data, alpha, B = NULL, test,
209204
cat(" > chosen d-separating set: '", sx, "'\n")
210205

211206
a = allsubs.test(x = y, y = z, fixed = x, sx = sx, data = data,
212-
test = test, B = B, alpha = alpha, max = min(max.sx, length(sx)),
213-
debug = debug)
207+
test = test, extra.args = extra.args, alpha = alpha,
208+
max = min(max.sx, length(sx)), debug = debug)
214209

215210
if (a["p.value"] <= alpha) {
216211

R/ci.test.R

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11

22
# do a single conditional independence test.
3-
ci.test = function(x, y, z, data, test, B, debug = FALSE) {
3+
ci.test = function(x, y, z, data, test, ..., debug = FALSE) {
44

55
if (missing(x))
66
stop("one or both of the variables to test are missing.")
77

88
if (is.string(x)) {
99

10-
ci.test.character(x = x, y = y, z = z, data = data, test = test, B = B,
11-
debug = debug)
10+
ci.test.character(x = x, y = y, z = z, data = data, test = test,
11+
extra.args = list(...), debug = debug)
1212

1313
}#THEN
1414
else if (is(x, c("matrix", "data.frame"))) {
@@ -22,7 +22,7 @@ ci.test = function(x, y, z, data, test, B, debug = FALSE) {
2222

2323
nodes = names(x)
2424
ci.test.character(x = nodes[1], y = nodes[2], z = nodes[-(1:2)],
25-
data = x, test = test, B = B, debug = debug)
25+
data = x, test = test, extra.args = list(...), debug = debug)
2626

2727
}#THEN
2828
else if (is.vector(x)) {
@@ -32,7 +32,7 @@ ci.test = function(x, y, z, data, test, B, debug = FALSE) {
3232

3333
ci.test.vector(x = x, y = y, z = z, xlab = deparse(substitute(x)),
3434
ylab = deparse(substitute(y)), zlab = deparse(substitute(z)),
35-
test = test, B = B, debug = debug)
35+
test = test, extra.args = list(...), debug = debug)
3636

3737
}#THEN
3838
else {
@@ -44,7 +44,7 @@ ci.test = function(x, y, z, data, test, B, debug = FALSE) {
4444
}#CI.TEST
4545

4646
# do a single conditional independence test (nodes as character strings).
47-
ci.test.character = function(x, y, z, data, test, B, debug = FALSE) {
47+
ci.test.character = function(x, y, z, data, test, extra.args, debug = FALSE) {
4848

4949
# the original data set is needed.
5050
data = check.data(data, allow.missing = TRUE, stop.if.all.missing = TRUE)
@@ -74,12 +74,13 @@ ci.test.character = function(x, y, z, data, test, B, debug = FALSE) {
7474
}#ELSE
7575
# check the test label.
7676
test = check.test(test, data = .data.frame.column(data, c(x, y, z)))
77-
# check B (the number of permutation samples).
78-
B = check.B(B, test)
77+
# check the optional arguments to the test.
78+
extra.args = check.test.args(test = test, extra.args = extra.args,
79+
data = .data.frame.column(data, c(x, y, z)))
7980

8081
# create the htest object.
81-
htest = indep.test(x = x, y = y, sx = z, data = data, test = test, B = B,
82-
alpha = 1, learning = FALSE)
82+
htest = indep.test(x = x, y = y, sx = z, data = data, test = test,
83+
extra.args = extra.args, alpha = 1, learning = FALSE)
8384
htest$method = test.labels[test]
8485
htest$data.name = paste(x, "~", y, ifelse(length(z) > 0, "|", ""),
8586
paste(z, collapse = " + "))
@@ -89,7 +90,8 @@ ci.test.character = function(x, y, z, data, test, B, debug = FALSE) {
8990
}#CI.TEST.CHARACTER
9091

9192
# do a single conditional independence test (data vectors).
92-
ci.test.vector = function(x, y, z, xlab, ylab, zlab, test, B, debug = FALSE) {
93+
ci.test.vector = function(x, y, z, xlab, ylab, zlab, test, extra.args,
94+
debug = FALSE) {
9395

9496
# check debug.
9597
check.logical(debug)
@@ -141,12 +143,12 @@ ci.test.vector = function(x, y, z, xlab, ylab, zlab, test, B, debug = FALSE) {
141143
data = check.data(data, allow.missing = TRUE, stop.if.all.missing = TRUE)
142144
# check the test label.
143145
test = check.test(test, data = data)
144-
# check B (the number of permutation samples).
145-
B = check.B(B, test)
146+
# check the optional arguments to the test.
147+
extra.args = check.test.args(test = test, extra.args = extra.args, data = data)
146148

147149
# create the htest object.
148-
htest = indep.test(x = 1L, y = 2L, sx = sx, data = data, test = test, B = B,
149-
alpha = 1, learning = FALSE)
150+
htest = indep.test(x = 1L, y = 2L, sx = sx, data = data, test = test,
151+
extra.args = extra.args, alpha = 1, learning = FALSE)
150152
htest$method = test.labels[test]
151153
htest$data.name = paste(xlab, "~", ylab,
152154
ifelse(length(z) > 0, paste("|", zlab), ""))

R/cpdag.R

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
# reconstruct the equivalence class of a network.
3-
cpdag.backend = function(x, moral = FALSE, fix = FALSE, wlbl = TRUE, debug = FALSE) {
3+
cpdag.backend = function(x, moral = FALSE, fix = FALSE, wlbl = TRUE,
4+
debug = FALSE) {
45

56
nodes = names(x$nodes)
67

R/cpq.R

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ weighting.sampling = function(fitted, event, evidence, n, batch, debug = FALSE)
434434
matching = r & !is.na(r)
435435

436436
# compute the probabilities and use them as weigths.
437+
attr(generated.data, "metadata") = collect.metadata(generated.data)
437438
w = weights(generated.data)
438439
cpe = cpe + sum(w[!is.na(r)])
439440
cpxe = cpxe + sum(w[matching])
@@ -460,7 +461,8 @@ weighting.sampling = function(fitted, event, evidence, n, batch, debug = FALSE)
460461

461462
# generate random observations from conditional distributions with likelihood
462463
# weighting.
463-
weighting.distribution = function(fitted, nodes, evidence, n, batch, debug = FALSE) {
464+
weighting.distribution = function(fitted, nodes, evidence, n, batch,
465+
debug = FALSE) {
464466

465467
.Call(call_cpdist_lw,
466468
fitted = fitted,

0 commit comments

Comments
 (0)