8

I'm attempting to use the bnlearn package to calculate conditional probabilities, and I'm running into a problem when the "cpquery" function is used within a loop. I've created an example, shown below, using data included with the package. When using the cpquery function in a loop, a variable created in the loop ("evi" in the example) is not recognized by the function. I receive the error:

Error in parse(text = evi) : object 'evi' not found

The creation steps of "evi" are based on examples provided by the author.

Any help you could provide would be great. I'm desperate to find a way that I can apply the cpquery function for a large number of observations.

library(bnlearn)
data(learning.test)
fitted = bn.fit(hc(learning.test), learning.test)

bn.function <- function(network, evidence_data) {
  a <- NULL
  b <- nrow(evidence_data)
  for (i in 1:b) {
    evi <- paste("(", names(evidence_data), "=='",
               sapply(evidence_data[i,], as.character), "')",
               sep = "", collapse = " & ")
    a[i] <- cpquery(network, (C=='c'), eval(parse(text=evi)))
  }
  return(a)
}

test <- bn.function(fitted, learning.test)

Thanks in advance!

H2O_Research
  • 165
  • 8
  • 2
    I've been in contact with the author of the bnlearn package, and it appears the error I am receiving is due to a scoping problem with the cpquery function. This is evident when I'm able to get the cpquery function to work properly in a for-loop that is constructed _outside_ of a user-defined function, but have the error when the same for-loop is used _inside_ of a user-defined function. – H2O_Research Oct 11 '13 at 15:55
  • 1
    I was having a similar issue with cpquery in loops and a quick workaround that worked for me was to include a line `evi <<- evi` before the cpquery call. This defines the variable in the global environment. – Austin May 30 '18 at 21:47
  • I guess your problem was solved here: https://stackoverflow.com/questions/44676501/r-bnlearn-eval-inside-function – drastega Dec 27 '19 at 19:22

3 Answers3

0

To avoid the scoping problem, you can postpone the call to eval and do it inside the cpquery function. If you directly pass evi (the character variable) to cpquery and then parse it inside the definition, the chain of environments gets shifted and cpquery will have access to evi.

You can use m.cpquery <- edit(cpquery) to fork your own version of the function and insert the following line at its beginning:

evidence = parse(text = evidence)

and then save your new function.

So the heading of m.cpquery will look like:

> m.cpquery
function (fitted, event, evidence, cluster = NULL, method = "ls", 
    ..., debug = FALSE) 
{
    evidence = parse(text = evidence)
    check.fit(fitted)
    check.logical(debug)
...

Now you can use m.cpquery in your own function like before, except we'll pass the plain character variable to it:

a[i] <- m.cpquery(network, (C=='c'), evi)

Note that in the first line of m.cpquery, we only parsed the evidence character variable and didn't call eval on it. cpquery is a front-end to conditional.probability.query (see here) and we're relying on conditional.probability.query's subsequent call to eval.

I should say that this is a rather ugly workaround. And it only works if you are using logic sampling (method='ls'). But if you want to use likelihood weighting, the check.mutilated.evidence function will raise an error. I haven't checked if injecting an eval expression before it gets called would result in a mayhem of subsequent errors leading to hell.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
koohyar
  • 356
  • 2
  • 6
0

I don't know if this is due to a bugfix or just because I tried another approach - anyways, looping works if you iteratively build up the evidence list outside of the cpquery-function.

An example for an iteration through a list called evidenceData with all-positive evidences:

for(i in names(evidenceData)){
  loopEvidenceList <- list()
  loopEvidenceList[[i]] <- "TRUE"
  a =cpquery(fitted = bayesNet, event = queryNode == "TRUE", 
             evidence = loopEvidenceList, method = "lw", n = 100000)
  print(a)
  }

Depending on the way your evidence is availible, you might need more sophisticated preparation of the "loopEvidenceList" but once you got that prepared, it works fine.

locom
  • 115
  • 9
0

I feel like the problem is you are using the same variable in evidence as well as event. Learning.test contains the values of "C" variable. then we are trying to predict C as the event. Maybe using a subset of the original dataset excluding C will do the trick