From 36c6a8007932a5ed575138e411d283b7dd0e757b Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 27 Aug 2015 19:10:58 -0400 Subject: [PATCH 01/13] Adding tests --- DESCRIPTION | 4 +++- R/rToJags.R | 2 +- tests/testthat/test-cpt.R | 24 ++++++++++++++++++++++++ tests/testthat/test-modelToNode.R | 19 +++++++++++++++++++ tests/testthat/test-rToJags.R | 13 +++++++++++++ tests/testthat/test-writeJagsFormula.R | 14 ++++++++++++++ 6 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tests/testthat/test-cpt.R create mode 100644 tests/testthat/test-modelToNode.R create mode 100644 tests/testthat/test-rToJags.R create mode 100644 tests/testthat/test-writeJagsFormula.R diff --git a/DESCRIPTION b/DESCRIPTION index 94b2d39..38c0211 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: HydeNet Type: Package Title: Hybrid Bayesian Networks Using R and JAGS Version: 0.9.1 -Date: 2015-07-06 +Date: 2015-08-27 Author: Jarrod E. Dalton and Benjamin Nutter Maintainer: Benjamin Nutter Description: Facilities for easy implementation of hybrid Bayesian networks @@ -38,7 +38,9 @@ Imports: utils Suggests: knitr, + survival, testthat +VignetteBuilder: knitr SystemRequirements: JAGS (http://mcmc-jags.sourceforge.net) LazyLoad: yes LazyData: true diff --git a/R/rToJags.R b/R/rToJags.R index 331ce24..ba7fdbc 100644 --- a/R/rToJags.R +++ b/R/rToJags.R @@ -73,7 +73,7 @@ rToJags <- function(f){ x <- gsub(" ", "", x) if (grepl("logit[(]", x)){ if (grepl("inverse[=]T", x)){ - x <- gsub("(logit[(]|qlogis[(]", "ilogit(", x) + x <- gsub("(logit[(]|qlogis[(])", "ilogit(", x) } x <- gsub(",[[:print:]]+[)]", ")", x) return(x) diff --git a/tests/testthat/test-cpt.R b/tests/testthat/test-cpt.R new file mode 100644 index 0000000..046c584 --- /dev/null +++ b/tests/testthat/test-cpt.R @@ -0,0 +1,24 @@ +context("cpt") + +test_that("cpt.list", +{ + n <- 50000 + data <- data.frame( + di1 = as.factor(1:6 %*% rmultinom(n,1,prob=c(.4,.3,.15,.10,.03,.02))), + di2 = as.factor(1:6 %*% rmultinom(n,1,prob=rev(c(.4,.3,.15,.10,.03,.02)))), + di3 = as.factor(1:6 %*% rmultinom(n,1,prob=c(.15,.10,.02,.3,.4,.03))) + ) + + expect_that(cpt(list(y = "di3", x = c("di1", "di2")), data= data), + not(throws_error())) +}) + +test_that("cpt with weights", +{ + echodata <- cbind(expand.grid(list(echo = c("Negative", "Positive"), + cad = c("No","Yes"))), + data.frame(pr=c(0.83,0.17,0.12,0.88))) + expect_that(cpt(echo ~ cad, data=echodata, wt=echodata$pr), + not(throws_error())) +}) + \ No newline at end of file diff --git a/tests/testthat/test-modelToNode.R b/tests/testthat/test-modelToNode.R new file mode 100644 index 0000000..92d75ee --- /dev/null +++ b/tests/testthat/test-modelToNode.R @@ -0,0 +1,19 @@ +context("modelToNode") + +test_that("modelToNode: coxph should return an error", +{ + library(survival) + test2 <- list(start=c(1,2,5,2,1,7,3,4,8,8), + stop=c(2,3,6,7,8,9,9,9,14,17), + event=c(1,1,1,1,1,1,1,0,0,0), + x=c(1,0,0,1,0,1,1,1,0,0)) + fit <- coxph(Surv(start, stop, event) ~ x, test2) + expect_error(modelToNode(fit)) +}) + +test_that("modelToNode: multinom", +{ + fit.gear <- multinom(gear ~ mpg + factor(am), data=mtcars) + expect_that(modelToNode(fit.gear), not(throws_error())) +}) + \ No newline at end of file diff --git a/tests/testthat/test-rToJags.R b/tests/testthat/test-rToJags.R new file mode 100644 index 0000000..1985e72 --- /dev/null +++ b/tests/testthat/test-rToJags.R @@ -0,0 +1,13 @@ +context("rToJags") + +test_that("rToJags: exponents", +{ + expect_equal(rToJags(y ~ x^2), + "y ~ pow(x,2)") +}) + +test_that("rToJags: logit (from VGAM package)", +{ + expect_equal(rToJags(y ~ logit(x, inverse=TRUE)), + "y ~ ilogit(x)") +}) \ No newline at end of file diff --git a/tests/testthat/test-writeJagsFormula.R b/tests/testthat/test-writeJagsFormula.R new file mode 100644 index 0000000..f4e94fe --- /dev/null +++ b/tests/testthat/test-writeJagsFormula.R @@ -0,0 +1,14 @@ +context("writeJagsFormula") + +test_that("writeJagsFormula: Poisson Regression", +{ + fit <- glm(gear ~ mpg + am, data = mtcars, family = poisson) + expect_that(writeJagsFormula(fit, c("gear", "mpg", "am")), + not(throws_error())) +}) + +test_that("writeJagsFormula: Multinomial Regression", +{ + fit.gear <- multinom(gear ~ mpg + factor(am), data=mtcars) + expect_that(writeJagsFormula(fit.gear), not(throws_error())) +}) From 03535cdc2c6e4d95e767abbbcc680e8156370e13 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 27 Aug 2015 20:37:15 -0400 Subject: [PATCH 02/13] Priming more tests This should let me see what holes are missing in the coverage of some of the major functions. --- R/bindPosterior.R | 13 +++--- R/cpt.R | 13 ++++-- README.md | 1 + tests/testthat/test-HydePosterior.R | 62 +++++++++++++++++++++++++ tests/testthat/test-compileJagsModel.R | 17 +++++++ tests/testthat/test-cpt.R | 62 ++++++++++++++++++++++++- tests/testthat/test-plot.HydeNetwork.R | 9 ++++ tests/testthat/test-print.HydeNetwork.R | 15 ++++++ tests/testthat/test-writeNetworkModel.R | 21 +++++++++ 9 files changed, 200 insertions(+), 13 deletions(-) create mode 100644 tests/testthat/test-HydePosterior.R create mode 100644 tests/testthat/test-compileJagsModel.R create mode 100644 tests/testthat/test-plot.HydeNetwork.R create mode 100644 tests/testthat/test-print.HydeNetwork.R create mode 100644 tests/testthat/test-writeNetworkModel.R diff --git a/R/bindPosterior.R b/R/bindPosterior.R index cf77526..c0d3678 100644 --- a/R/bindPosterior.R +++ b/R/bindPosterior.R @@ -55,12 +55,13 @@ bindPosterior <- function(hydePost, relabel_factor=TRUE){ else bound <- dplyr::bind_rows(lapply(hydePost$codas, bind_chains_list)) - factors_to_relabel <- names(bound)[names(bound) %in% names(hydePost$factorRef)] - - for(i in factors_to_relabel){ - bound[i] <- factor(bound[[i]], - levels=hydePost$factorRef[[i]]$value, - labels=hydePost$factorRef[[i]]$label) + if (relabel_factor){ + factors_to_relabel <- names(bound)[names(bound) %in% names(hydePost$factorRef)] + for(i in factors_to_relabel){ + bound[i] <- factor(bound[[i]], + levels=hydePost$factorRef[[i]]$value, + labels=hydePost$factorRef[[i]]$label) + } } as.data.frame(bound) diff --git a/R/cpt.R b/R/cpt.R index 261e286..5143586 100644 --- a/R/cpt.R +++ b/R/cpt.R @@ -174,10 +174,11 @@ cpt_workhorse <- function(variables, dependentVar, independentVars, "Using only the first element."), Check) wt <- wt[1] + wt_text <- wt_text[1] } if(wt %in% names(data)) { - wt <- data[,"wt"] + wt <- data[,wt] } else{ ArgumentCheck::addError("'wt' must be a numeric vector or the name of a variable in 'data'", @@ -213,7 +214,7 @@ cpt_workhorse <- function(variables, dependentVar, independentVars, joint <- data %>% dplyr::group_by_(.dots = ..vars) %>% dplyr::summarise_(wt = ~sum(wt)) - + marginal <- joint %>% dplyr::group_by_(.dots = ..independentVars) %>% dplyr::summarise_(sumWt = ~sum(wt)) @@ -223,10 +224,12 @@ cpt_workhorse <- function(variables, dependentVar, independentVars, plyr::daply(c(vars[-1], vars[1]), function(x) x$p) cpt[is.na(cpt)] <- 0 - - model <- data[, c(names(dimnames(cpt)), wt_text)] + + model <- data[, c(names(dimnames(cpt)), "wt")] + if ("wt" %in% names(model) && !is.null(wt_text)) + names(model)[length(model)] <- wt_text if (is.null(wt_text)) model <- cbind(model, wt) - + attr(cpt, "model") <- model class(cpt) <- c("cpt", "array") diff --git a/README.md b/README.md index 71184ed..8ea8917 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ +[![Travis-CI Build Status](https://travis-ci.org/nutterb/HydeNet.svg?branch=current-devel)](https://travis-ci.org/nutterb/HydeNet) [![CRAN version](http://www.r-pkg.org/badges/version/HydeNet)](https://cran.r-project.org/web/packages/HydeNet/index.html) ![](http://cranlogs.r-pkg.org/badges/grand-total/HydeNet) [![Coverage Status](https://coveralls.io/repos/nutterb/HydeNet/badge.svg?branch=current-devel&service=github)](https://coveralls.io/github/nutterb/HydeNet?branch=current-devel) diff --git a/tests/testthat/test-HydePosterior.R b/tests/testthat/test-HydePosterior.R new file mode 100644 index 0000000..169e0ee --- /dev/null +++ b/tests/testthat/test-HydePosterior.R @@ -0,0 +1,62 @@ +context("HydePosterior") + +data(PE, package="HydeNet") +Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) +compiledNet <- compileJagsModel(Net, n.chains=5) + +test_that("Unbound HydePosterior returns object of class HydePosterior", +{ + Posterior <- HydePosterior(compiledNet, + variable.names = c("d.dimer", "death"), + n.iter = 1000, + bind = FALSE) + expect_equal(class(Posterior), + "HydePosterior") +}) + +test_that("Bound HydePosterior returns object of class data.frame", +{ + Posterior <- HydePosterior(compiledNet, + variable.names = c("d.dimer", "death"), + n.iter = 1000) + expect_equal(class(Posterior), + "data.frame") +}) + +test_that("Unbound HydePosterior print method", +{ + Posterior <- HydePosterior(compiledNet, + variable.names = c("d.dimer", "death"), + n.iter = 1000, + bind = FALSE) + expect_that(print(Posterior), + not(throws_error())) +}) + +test_that("bindPosterior returns relabeled data", +{ + Posterior <- HydePosterior(compiledNet, + variable.names = c("d.dimer", "death"), + n.iter = 1000, + bind = FALSE) + Bound <- bindPosterior(Posterior) + expect_equal(class(Bound$death), + "factor") +}) + +test_that("bindPosterior returns relabeled data", +{ + Posterior <- HydePosterior(compiledNet, + variable.names = c("d.dimer", "death"), + n.iter = 1000, + bind = FALSE) + Bound <- bindPosterior(Posterior, relabel_factor = FALSE) + expect_equal(class(Bound$death), + "numeric") +}) \ No newline at end of file diff --git a/tests/testthat/test-compileJagsModel.R b/tests/testthat/test-compileJagsModel.R new file mode 100644 index 0000000..571bcb5 --- /dev/null +++ b/tests/testthat/test-compileJagsModel.R @@ -0,0 +1,17 @@ +context("compileJagsModel") + +data(PE, package="HydeNet") +Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) + +test_that("compileJagsModel returns an object of class 'compiledHydeNetwork'", +{ + compiledNet <- compileJagsModel(Net, n.chains=5) + expect_equal(class(compiledNet), + "compiledHydeNetwork") +}) diff --git a/tests/testthat/test-cpt.R b/tests/testthat/test-cpt.R index 046c584..ae39ba8 100644 --- a/tests/testthat/test-cpt.R +++ b/tests/testthat/test-cpt.R @@ -3,13 +3,13 @@ context("cpt") test_that("cpt.list", { n <- 50000 - data <- data.frame( + df <- data.frame( di1 = as.factor(1:6 %*% rmultinom(n,1,prob=c(.4,.3,.15,.10,.03,.02))), di2 = as.factor(1:6 %*% rmultinom(n,1,prob=rev(c(.4,.3,.15,.10,.03,.02)))), di3 = as.factor(1:6 %*% rmultinom(n,1,prob=c(.15,.10,.02,.3,.4,.03))) ) - expect_that(cpt(list(y = "di3", x = c("di1", "di2")), data= data), + expect_that(cpt(list(y = "di3", x = c("di1", "di2")), data= df), not(throws_error())) }) @@ -21,4 +21,62 @@ test_that("cpt with weights", expect_that(cpt(echo ~ cad, data=echodata, wt=echodata$pr), not(throws_error())) }) + +test_that("cpt with character string naming weights", +{ + echodata <- cbind(expand.grid(list(echo = c("Negative", "Positive"), + cad = c("No","Yes"))), + data.frame(pr=c(0.83,0.17,0.12,0.88))) + expect_that(cpt(echo ~ cad, echodata, wt="pr"), + not(throws_error())) +}) + +test_that("cpt with multiple character string naming weights", +{ + echodata <- cbind(expand.grid(list(echo = c("Negative", "Positive"), + cad = c("No","Yes"))), + data.frame(pr=c(0.83,0.17,0.12,0.88))) + expect_warning(cpt(echo ~ cad, echodata, wt=c("pr", "wt"))) +}) + +test_that("cpt with multiple character string naming weights, but choosing non-existent variable", +{ + echodata <- cbind(expand.grid(list(echo = c("Negative", "Positive"), + cad = c("No","Yes"))), + data.frame(pr=c(0.83,0.17,0.12,0.88))) + expect_error(cpt(echo ~ cad, echodata, wt=c("wt", "pr"))) +}) + +test_that("cpt with logical weights should return error", +{ + echodata <- cbind(expand.grid(list(echo = c("Negative", "Positive"), + cad = c("No","Yes"))), + data.frame(pr=c(0.83,0.17,0.12,0.88))) + expect_error(cpt(echo ~ cad, echodata, wt=c(TRUE, TRUE, FALSE, TRUE))) +}) + +test_that("cpt with inappropriate length for weights vector", +{ + echodata <- cbind(expand.grid(list(echo = c("Negative", "Positive"), + cad = c("No","Yes"))), + data.frame(pr=c(0.83,0.17,0.12,0.88))) + expect_error(cpt(echo ~ cad, echodata, wt=c(1, 2, 3))) +}) + +test_that("cpt with negative weights should cast error", +{ + echodata <- cbind(expand.grid(list(echo = c("Negative", "Positive"), + cad = c("No","Yes"))), + data.frame(pr=c(0.83,0.17,0.12,0.88))) + expect_error(cpt(echo ~ cad, echodata, wt=c(-1, 1, 1, 1))) +}) + +test_that("print.cpt succeeds", +{ + echodata <- cbind(expand.grid(list(echo = c("Negative", "Positive"), + cad = c("No","Yes"))), + data.frame(pr=c(0.83,0.17,0.12,0.88))) + x <- cpt(echo ~ cad, echodata, wt="pr") + expect_that(print(x), not(throws_error())) +}) \ No newline at end of file diff --git a/tests/testthat/test-plot.HydeNetwork.R b/tests/testthat/test-plot.HydeNetwork.R new file mode 100644 index 0000000..349a8ee --- /dev/null +++ b/tests/testthat/test-plot.HydeNetwork.R @@ -0,0 +1,9 @@ +context("plot.HydeNetwork") + +data(BlackJack) + +test_that("plot.HydeNetwork returns a plot under default settings", +{ + expect_that(plot(BlackJack), + not(throws_error())) +}) \ No newline at end of file diff --git a/tests/testthat/test-print.HydeNetwork.R b/tests/testthat/test-print.HydeNetwork.R new file mode 100644 index 0000000..10ff9b9 --- /dev/null +++ b/tests/testthat/test-print.HydeNetwork.R @@ -0,0 +1,15 @@ +context("print.HydeNetwork") + +data(BlackJack) + +test_that("print.HydeNetwork works for full network", +{ + expect_that(print(BlackJack), + not(throws_error())) +}) + +test_that("print.HydeNetwork works for selected nodes", +{ + expect_that(print(BlackJack, dealerFinalPoints, payoff, card5), + not(throws_error())) +}) \ No newline at end of file diff --git a/tests/testthat/test-writeNetworkModel.R b/tests/testthat/test-writeNetworkModel.R new file mode 100644 index 0000000..5b811bf --- /dev/null +++ b/tests/testthat/test-writeNetworkModel.R @@ -0,0 +1,21 @@ +context("writeNetworkModel") + +Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) + +# expect_that("writeNetworkModel with pretty output succeeds", +# { +# expect_that(writeNetworkModel(Net, pretty = TRUE), +# not(throws_error())) +# }) +# +# expect_that("writeNetworkModel with non-pretty output succeeds", +# { +# expect_that(writeNetworkModel(Net, pretty = FALSE), +# not(throws_error())) +# }) \ No newline at end of file From ca545b0bd43630becd5ebfb99cb5712732d7dcce Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 11 Sep 2015 06:39:05 -0400 Subject: [PATCH 03/13] finish utility function rewrites Next, write all the tests. but need to submit to Travis to get coverage. --- R/bindPosterior.R | 3 ++ R/compileDecisionModel.R | 109 +++++++++++++++++++++++---------------- R/compileJagsModel.R | 59 +++++++++------------ 3 files changed, 91 insertions(+), 80 deletions(-) diff --git a/R/bindPosterior.R b/R/bindPosterior.R index c0d3678..06cf3d1 100644 --- a/R/bindPosterior.R +++ b/R/bindPosterior.R @@ -67,6 +67,9 @@ bindPosterior <- function(hydePost, relabel_factor=TRUE){ as.data.frame(bound) } + + +#**** UTILITY FUNCTIONS bind_chains_mcmclist <- function(mcmc, hydePost){ as.data.frame(hydePost$codas[[mcmc]]) %>% dplyr::mutate_(chain_index = ~mcmc, diff --git a/R/compileDecisionModel.R b/R/compileDecisionModel.R index 464cc53..66b8c39 100644 --- a/R/compileDecisionModel.R +++ b/R/compileDecisionModel.R @@ -76,48 +76,68 @@ compileDecisionModel <- function(network, policyMatrix = NULL, ...){ msg = "'data' is not an accepted argument in 'compileDecisionModel'", argcheck = Check) + options <- makePolicyMatrix(network, policyMatrix, Check) + + ArgumentCheck::finishArgCheck(Check) + + cpt_arrays <- makeCptArrays(network) + + jags.code <- compileJagsModel(network, ...) + + lapply(options, + runJagsDecisionModel, + jags.code, + cpt_arrays, + ...) + + +} + + +#*********** UTILITY FUNCTIONS + +makePolicyMatrix <- function(network, policyMatrix, argcheck){ if (is.null(policyMatrix)) { decisionNodes <- names(network$nodeDecision)[sapply(network$nodeDecision, any)] if (length(decisionNodes) == 0) - ArgumentCheck::addError( - msg = "No decision nodes indicated in the network", - argcheck = Check) + ArgumentCheck::addError( + msg = "No decision nodes indicated in the network", + argcheck = argcheck) if (length(decisionNodes) == 0) break; # The next argument check isn't meaningful - # when this condition is true. - + # when this condition is true. + validDecision <- sapply(network$nodeType[decisionNodes], function(x) x %in% c("dbern", "dcat", "dbin")) if (!all(validDecision)) - ArgumentCheck::addError( - msg = paste0("Only nodes of type 'dcat', and 'dbin' may be decision nodes.\n ", - paste0(names(validDecision)[!validDecision], collapse=", "), - " cannot be used as decision nodes."), - argcheck = Check) + ArgumentCheck::addError( + msg = paste0("Only nodes of type 'dcat', and 'dbin' may be decision nodes.\n ", + paste0(names(validDecision)[!validDecision], collapse=", "), + " cannot be used as decision nodes."), + argcheck = argcheck) if (!all(validDecision)) break; # Avoids defining 'options' when there are invalid decision nodes - + options <- lapply(decisionNodes, decisionOptions, network) names(options) <- decisionNodes - + options <- expand.grid(options, stringsAsFactors=FALSE) } else { if (!is.data.frame(policyMatrix)) - ArgumentCheck::addError( - msg = "'policyMatrix' must be a data frame", - argcheck = Check) + ArgumentCheck::addError( + msg = "'policyMatrix' must be a data frame", + argcheck = argcheck) if (!is.data.frame(policyMatrix)) break; # avoids defining 'options' when - # the condition is not satisfied + # the condition is not satisfied options <- policyMatrix - } - ArgumentCheck::finishArgCheck(Check) + ArgumentCheck::finishArgCheck(argcheck) options <- lapply(1:nrow(options), function(i){ @@ -130,6 +150,14 @@ compileDecisionModel <- function(network, policyMatrix = NULL, ...){ l }) + return(options) +} + +#** + + + +makeCptArrays <- function(network){ cpt_arrays <- unlist(network$nodeFitter) == "cpt" if(any(cpt_arrays)){ cpt_arrays <- names(cpt_arrays)[cpt_arrays] @@ -143,36 +171,29 @@ compileDecisionModel <- function(network, policyMatrix = NULL, ...){ args <- list(formula = network$nodeFormula[[ca]], data = if (!is.null(network$nodeData[[ca]])) network$nodeData[[ca]] - else network$data) + else network$data) if (!is.null(network$nodeFitterArgs[[ca]])) args <- c(args, network$nodeFitterArgs[[ca]]) return(do.call("cpt", args)) } - }) + }) names(cpt_arrays) <- paste0("cpt.", nms) } else cpt_arrays = list() - # return(cpt_arrays) - - jags.code <- compileJagsModel(network, ...) - - lapply(options, - function(o, j, cpt_arrays, ...) - { - con <- textConnection(paste0(j$jags$model(), - collapse="\n")) - cHN <- list(jags = rjags::jags.model(con, - data = c(o, cpt_arrays), - ...), - observed = o, - dag = j$dag, - factorRef = j$factorRef) - class(cHN) <- c("compiledHydeNetwork") - close(con) - return(cHN) - }, - jags.code, - cpt_arrays, - ...) - - + return(cpt_arrays) } + +#**** + +runJagsDecisionModel <- function(o, j, cpt_arrays, ...){ + con <- textConnection(paste0(j$jags$model(), + collapse="\n")) + cHN <- list(jags = rjags::jags.model(con, + data = c(o, cpt_arrays), + ...), + observed = o, + dag = j$dag, + factorRef = j$factorRef) + class(cHN) <- c("compiledHydeNetwork") + close(con) + return(cHN) +} diff --git a/R/compileJagsModel.R b/R/compileJagsModel.R index a24a7d3..84667f0 100644 --- a/R/compileJagsModel.R +++ b/R/compileJagsModel.R @@ -57,7 +57,27 @@ compileJagsModel <- function(network, data=NULL, ...){ factorRef <- makeFactorRef(network) #* convert label to value + data <- convertLabelToValue(data, factorRef) + + cpt_arrays <- makeCptArrays(network) #* The utilty function is in the + #* file for compileDecisionModel + + jags <- rjags::jags.model(textConnection(writeNetworkModel(network)), + data = if (is.null(data) & length(cpt_arrays) == 0) sys.frame(sys.parent()) + else c(data, cpt_arrays), ...) + #* cHN for compiled Hyde Network + cHN <- list(jags=jags, observed=data, dag=network$dag, factorRef=factorRef) + + class(cHN) <- c("compiledHydeNetwork") + cHN +} + + + + +#****** UTILITY FUNCTIONS +convertLabelToValue <- function(data, factorRef){ msg <- "" for (i in names(data)){ if (!is.numeric(data[[i]])) @@ -75,40 +95,7 @@ compileJagsModel <- function(network, data=NULL, ...){ else data[[i]] <- factorRef[[i]]$value[which(factorRef[[i]]$label == data[[i]])] } } - - if (length(msg) > 1) stop(paste(msg, collapse="\n")) - - cpt_arrays <- unlist(network$nodeFitter) == "cpt" - if(any(cpt_arrays)){ - cpt_arrays <- names(cpt_arrays)[cpt_arrays] - cpt_arrays <- network$nodeModel[cpt_arrays] - nms <- names(cpt_arrays) - cpt_arrays <- - lapply(names(cpt_arrays), - function(ca){ - if ("cpt" %in% class(cpt_arrays[[ca]])) return(cpt_arrays[[ca]]) - else{ - args <- - list(formula = network$nodeFormula[[ca]], - data = if (!is.null(network$nodeData[[ca]])) network$nodeData[[ca]] - else network$data) - if (!is.null(network$nodeFitterArgs[[ca]])) - args <- c(args, network$nodeFitterArgs[[ca]]) - return(do.call("cpt", args)) - } - }) - names(cpt_arrays) <- paste0("cpt.", nms) - } else cpt_arrays = list() - # return(cpt_arrays) - - jags <- rjags::jags.model(textConnection(writeNetworkModel(network)), - data = if (is.null(data) & length(cpt_arrays) == 0) sys.frame(sys.parent()) - else c(data, cpt_arrays), ...) - - #* cHN for compiled Hyde Network - cHN <- list(jags=jags, observed=data, dag=network$dag, factorRef=factorRef) - - class(cHN) <- c("compiledHydeNetwork") - cHN -} + if (length(msg) > 1) stop(paste(msg, collapse="\n")) + return(data) +} \ No newline at end of file From 81c838ca415c2bc18d4e077a176ccbab0cd02ac5 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 11 Sep 2015 08:23:27 -0400 Subject: [PATCH 04/13] resubmitting for new coverage report. --- R/setNode.R | 1 - tests/testthat/test-HydeUtilities.R | 36 +++++++++++++++++ tests/testthat/test-bindPosterior.R | 20 ++++++++++ tests/testthat/test-expectedVariables.R | 15 ++++++++ tests/testthat/test-plot.HydeNetwork.R | 10 +++++ tests/testthat/test-print.HydePosterior.R | 19 +++++++++ tests/testthat/test-writeJagsModel.R | 45 ++++++++++++++++++++++ tests/testthat/test-writeNetworkModel.R | 22 +++++------ tests/testthat/test_compileDecisionModel.R | 16 ++++++++ 9 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 tests/testthat/test-HydeUtilities.R create mode 100644 tests/testthat/test-bindPosterior.R create mode 100644 tests/testthat/test-expectedVariables.R create mode 100644 tests/testthat/test-print.HydePosterior.R create mode 100644 tests/testthat/test-writeJagsModel.R create mode 100644 tests/testthat/test_compileDecisionModel.R diff --git a/R/setNode.R b/R/setNode.R index ea809f9..f9531f0 100644 --- a/R/setNode.R +++ b/R/setNode.R @@ -174,7 +174,6 @@ setNode <- function(network, node, nodeType, } if (!missing(nodeType)) network$nodeType[[node.t]] <- nodeType - exp_param <- eval(substitute(expectedParameters(network, node, TRUE))) params <- list(...)[exp_param] diff --git a/tests/testthat/test-HydeUtilities.R b/tests/testthat/test-HydeUtilities.R new file mode 100644 index 0000000..313a5d8 --- /dev/null +++ b/tests/testthat/test-HydeUtilities.R @@ -0,0 +1,36 @@ +context("HydeUtilities") + +data(PE, package="HydeNet") +Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) + +test_that("decisionOptions - dbern", +{ + expect_equal(decisionOptions("treat", Net), + 0:1) +}) + +test_that("decisionOptions - dcat", +{ + expect_equal(decisionOptions("death", Net), + 1:2) +}) + +test_that("validateParameters", +{ + expect_equal(validateParameters(list(lambda = 5), dist = "dpois"), + c("lambda > 0" = TRUE)) +}) + +test_that("validateParameters - use fromData() and fromFormula()", +{ + expect_equal(validateParameters(list(mu = fromData(), + tau = fromFormula()), + dist = "dnorm"), + c("is.numeric(mu)" = TRUE, "tau >= 0" = TRUE)) +}) \ No newline at end of file diff --git a/tests/testthat/test-bindPosterior.R b/tests/testthat/test-bindPosterior.R new file mode 100644 index 0000000..0ba06f4 --- /dev/null +++ b/tests/testthat/test-bindPosterior.R @@ -0,0 +1,20 @@ +context("bindPosterior") + +data(PE, package="HydeNet") +Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) %>% + setDecisionNodes(treat, angio) +Post <- compileDecisionModel(Net) %>% + HydePosterior(variable.names = c("wells", "treat", "death"), + n.iter = 100, + bind = FALSE) + +test_that("bindPosterior from Decision Model", +{ + expect_that(bindPosterior(Post), not(throws_error())) +}) diff --git a/tests/testthat/test-expectedVariables.R b/tests/testthat/test-expectedVariables.R new file mode 100644 index 0000000..cf7b979 --- /dev/null +++ b/tests/testthat/test-expectedVariables.R @@ -0,0 +1,15 @@ +context("expectedVariables") + +test_that("expectedVariables", +{ + Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) %>% + setDecisionNodes(treat, angio) + expect_equal(expectedVariables(Net, treat, TRUE), + c("d.dimer", "angio")) +}) \ No newline at end of file diff --git a/tests/testthat/test-plot.HydeNetwork.R b/tests/testthat/test-plot.HydeNetwork.R index 349a8ee..8bc48a5 100644 --- a/tests/testthat/test-plot.HydeNetwork.R +++ b/tests/testthat/test-plot.HydeNetwork.R @@ -6,4 +6,14 @@ test_that("plot.HydeNetwork returns a plot under default settings", { expect_that(plot(BlackJack), not(throws_error())) +}) + +test_that("plot.HydeNetwork returns a plot with custome Node settings", +{ + expect_that(plot(BlackJack, + customNodes = customNode(node_id = "hit1", + fillcolor = "purple", shape = "circle", + fontcolor = "white", height = "2", + style="filled")), + not(throws_error())) }) \ No newline at end of file diff --git a/tests/testthat/test-print.HydePosterior.R b/tests/testthat/test-print.HydePosterior.R new file mode 100644 index 0000000..3f0bb18 --- /dev/null +++ b/tests/testthat/test-print.HydePosterior.R @@ -0,0 +1,19 @@ +context("print.HydePosterior") + +data(PE, package="HydeNet") +Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) +compiledNet <- compileJagsModel(Net, n.chains=5, data = list(pe = "Yes")) + +test_that("print.HydePosterior with observed values", +{ + expect_that(HydePosterior(compiledNet, + variable.names = c("wells", "death"), + n.iter = 100, bind = FALSE), + not(throws_error())) +}) \ No newline at end of file diff --git a/tests/testthat/test-writeJagsModel.R b/tests/testthat/test-writeJagsModel.R new file mode 100644 index 0000000..5329431 --- /dev/null +++ b/tests/testthat/test-writeJagsModel.R @@ -0,0 +1,45 @@ +context("writeJagsModel") + +craps <- HydeNetwork(~ d1 + d2 + diceSum | d1*d2 + + firstRollOutcome | diceSum) %>% + setNode(d1, nodeType="dcat", + pi = vectorProbs(p = rep(1/6,6), d1), + validate = FALSE) %>% + setNode(d2, nodeType="dcat", + pi = vectorProbs(p = rep(1/6,6), d2), + validate = FALSE) %>% + setNode(diceSum, nodeType = "determ", + define = fromFormula(), + nodeFormula = diceSum ~ di1 + di2) %>% + setNode(firstRollOutcome, nodeType = "determ", + define = fromFormula(), + nodeFormula = firstRollOutcome ~ + ifelse(diceSum < 4 | diceSum > 11, -1, + ifelse(diceSum == 7 | diceSum == 11, 1,0))) + +test_that("writeJagsModel - determ", +{ + #* Because of how `writeJagsModel` processes the network object + #* it needs to be used within `writeNetworkModel`, which is + #* intended anyway since it is an unexported function + expect_that(writeNetworkModel(craps, TRUE), + not(throws_error())) +}) + +test_that("writeJagsModel - dcat with pi defined by user", +{ + expect_that(writeNetworkModel(craps, TRUE), + not(throws_error())) +}) + +test_that("writeJagsModel - dpois", +{ + carNet <- HydeNetwork(~gear | mpg + am, + data = mtcars) %>% + setNode(gear, nodeType = "dpois", nodeFitter = "glm", + fitterArgs = list(family = poisson), + lambda = fromData()) + expect_that(writeNetworkModel(carNet, TRUE), + not(throws_error())) +}) + diff --git a/tests/testthat/test-writeNetworkModel.R b/tests/testthat/test-writeNetworkModel.R index 5b811bf..cd0ee8a 100644 --- a/tests/testthat/test-writeNetworkModel.R +++ b/tests/testthat/test-writeNetworkModel.R @@ -8,14 +8,14 @@ Net <- HydeNetwork(~ wells + death | pe*treat, data = PE) -# expect_that("writeNetworkModel with pretty output succeeds", -# { -# expect_that(writeNetworkModel(Net, pretty = TRUE), -# not(throws_error())) -# }) -# -# expect_that("writeNetworkModel with non-pretty output succeeds", -# { -# expect_that(writeNetworkModel(Net, pretty = FALSE), -# not(throws_error())) -# }) \ No newline at end of file +test_that("writeNetworkModel with pretty output succeeds", +{ + expect_that(writeNetworkModel(Net, pretty = TRUE), + not(throws_error())) +}) + +test_that("writeNetworkModel with non-pretty output succeeds", +{ + expect_that(writeNetworkModel(Net, pretty = FALSE), + not(throws_error())) +}) \ No newline at end of file diff --git a/tests/testthat/test_compileDecisionModel.R b/tests/testthat/test_compileDecisionModel.R new file mode 100644 index 0000000..73293ea --- /dev/null +++ b/tests/testthat/test_compileDecisionModel.R @@ -0,0 +1,16 @@ +context("compileDecisionModel") + +Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) %>% + setDecisionNodes(angio, treat) + +test_that("compileDecisionModel", +{ + expect_that(compileDecisionModel(Net), + not(throws_error())) +}) From 11ec343c2c585337da4018425dceba6da17c5c14 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 11 Sep 2015 09:36:26 -0400 Subject: [PATCH 05/13] Hopefully the last big batch of unit tests These should catch the majority of errors we introduce in new code. --- DESCRIPTION | 4 +- NEWS | 4 +- tests/testthat/test-plot.HydeNetwork.R | 22 +++++++++ tests/testthat/test-policyMatrix.R | 33 +++++++++++++ tests/testthat/test-setNode.R | 60 ++++++++++++++++++++++++ tests/testthat/test-setNodeModels.R | 45 ++++++++++++++++++ tests/testthat/test-setUtilityNodes.R | 15 ++++++ tests/testthat/test-update.HydeNetwork.R | 16 +++++++ 8 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 tests/testthat/test-policyMatrix.R create mode 100644 tests/testthat/test-setNode.R create mode 100644 tests/testthat/test-setNodeModels.R create mode 100644 tests/testthat/test-setUtilityNodes.R create mode 100644 tests/testthat/test-update.HydeNetwork.R diff --git a/DESCRIPTION b/DESCRIPTION index 38c0211..69b1700 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: HydeNet Type: Package Title: Hybrid Bayesian Networks Using R and JAGS -Version: 0.9.1 -Date: 2015-08-27 +Version: 0.9.2 +Date: 2015-09-11 Author: Jarrod E. Dalton and Benjamin Nutter Maintainer: Benjamin Nutter Description: Facilities for easy implementation of hybrid Bayesian networks diff --git a/NEWS b/NEWS index 8be16bd..3f5fd6f 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,8 @@ -### 0.9.2 (19 Aug 2015) +### 0.9.2 (11 Sept 2015) * Fixed a bug in `compileJagsModel` and `compileDecisionModel` that prevented `cpt` objects from being passed to JAGS models correctly. * Added `nodeData` argument to `setNode`. -* Began writing unit tests. +* Added unit tests. ### 0.9.1 (6 July 2015) * Conversion of argument checks to using the `ArgumentCheck` package diff --git a/tests/testthat/test-plot.HydeNetwork.R b/tests/testthat/test-plot.HydeNetwork.R index 8bc48a5..a9f1c3f 100644 --- a/tests/testthat/test-plot.HydeNetwork.R +++ b/tests/testthat/test-plot.HydeNetwork.R @@ -16,4 +16,26 @@ test_that("plot.HydeNetwork returns a plot with custome Node settings", fontcolor = "white", height = "2", style="filled")), not(throws_error())) +}) + +test_that("HydePlotOptions", +{ + expect_that({ + HydePlotOptions(variable=list(shape = "rect", fillcolor = "#A6DBA0"), + determ = list(shape = "rect", fillcolor = "#E7D4E8", + fontcolor = "#1B7837", linecolor = "#1B7837"), + decision = list(shape = "triangle", fillcolor = "#1B7837", + linecolor = "white"), + utility = list(shape = "circle", fillcolor = "#762A83", + fontcolor = "white")) + plot(BlackJack)}, + not(throws_error())) +}) + +test_that("HydePlotOptions - restoreDefaults", +{ + expect_that({ + HydePlotOptions(restorePackageDefaults = TRUE) + plot(BlackJack)}, + not(throws_error())) }) \ No newline at end of file diff --git a/tests/testthat/test-policyMatrix.R b/tests/testthat/test-policyMatrix.R new file mode 100644 index 0000000..60b5df6 --- /dev/null +++ b/tests/testthat/test-policyMatrix.R @@ -0,0 +1,33 @@ +context("policyMatrix") + +Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) %>% + setDecisionNodes(angio, treat) + +test_that("Get the default policy matrix", +{ + expect_equal(policyMatrix(Net), + expand.grid(angio = 1:2, treat = 0:1)) +}) + +test_that("Get a customized policy matrix", +{ + expect_equal(policyMatrix(Net, angio = 1, treat = 0:1), + expand.grid(angio = 1, treat = 0:1)) +}) + +test_that("Get a customized policy matrix with a continuous variable", +{ + expect_equal(policyMatrix(Net, angio = 1, treat = 0:1, d.dimer = 3), + expand.grid(angio = 1, treat = 0:1, d.dimer = 3)) +}) + +test_that("Cast error when using a variable not in the network", +{ + expect_error(policyMatrix(Net, angio = 1, treat = 0:1, x = 2)) +}) \ No newline at end of file diff --git a/tests/testthat/test-setNode.R b/tests/testthat/test-setNode.R new file mode 100644 index 0000000..a7b6b3c --- /dev/null +++ b/tests/testthat/test-setNode.R @@ -0,0 +1,60 @@ +context("setNode") + +Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) + +test_that("decision argument warning", +{ + expect_warning(setNode(Net, treat, nodeType = "dbern", decision = "yes", p = .5)) +}) + +test_that("utility error: utilties must be deterministic and not have children", +{ + expect_error(setNode(Net, treat, nodeType = "dbern", utility = TRUE, p =.5)) +}) + +test_that("validation error", +{ + expect_error(setNode(Net, treat, nodeType = "dbern", p = 1.2)) +}) + +test_that("fit the model for dbern", +{ + expect_that(setNode(Net, treat, nodeType = "dbern", p = fromData(), + fitModel = TRUE), + not(throws_error())) +}) + +test_that("fit the model for dcat", +{ + expect_that(setNode(Net, pregnant, nodeType = "dcat", pi = fromData(), + fitModel = TRUE), + not(throws_error())) +}) + +test_that("fit the model for dnorm", +{ + expect_that(setNode(Net, d.dimer, nodeType = "dnorm", + mu = fromData(), tau = fromData(), + fitModel = TRUE), + not(throws_error())) +}) + +test_that("fit the model for dpois", +{ + carNet <- HydeNetwork(~gear | mpg + am, + data = mtcars) + + expect_that( + setNode(carNet, gear, nodeType = "dpois", nodeFitter = "glm", + fitterArgs = list(family = poisson), + lambda = fromData(), + fitModel = TRUE), + not(throws_error())) +}) + \ No newline at end of file diff --git a/tests/testthat/test-setNodeModels.R b/tests/testthat/test-setNodeModels.R new file mode 100644 index 0000000..2c0fb13 --- /dev/null +++ b/tests/testthat/test-setNodeModels.R @@ -0,0 +1,45 @@ +context("setNodeModels") + +Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat) + +g1 <- lm(wells ~ 1, data=PE) +g2 <- glm(pe ~ wells, data=PE, family="binomial") +g3 <- lm(d.dimer ~ pe + pregnant, data=PE) +g4 <- xtabs(~ pregnant, data=PE) +g5 <- cpt(angio ~ pe, data=PE) +g6 <- glm(treat ~ d.dimer + angio, data=PE, family="binomial") +g7 <- cpt(death ~ pe + treat, data=PE) + +bagOfModels <- list(g1,g2,g3,g4,g5,g6,g7) + +test_that("Returns a network", +{ + expect_that(setNodeModels(Net, g1,g2,g3,g4,g5,g6,g7), + not(throws_error())) +}) + +test_that("Cast error when no models given", +{ + expect_error(setNodeModels(Net)) +}) + +test_that("Cast error when not applying to a HydeNetwork", +{ + expect_error(setNodeModels(bagOfModels, g1)) +}) + +test_that("Response variable is a node in the HydeNetwork", +{ + expect_error(setNodeModels(Net, lm(mpg ~ am, data = mtcars))) +}) + +test_that("Check that all regression variables are parents of the response", +{ + expect_error(setNodeModels(Net, lm(d.dimer ~ pe + pregnant + treat, data = PE))) +}) + \ No newline at end of file diff --git a/tests/testthat/test-setUtilityNodes.R b/tests/testthat/test-setUtilityNodes.R new file mode 100644 index 0000000..4c7bbf1 --- /dev/null +++ b/tests/testthat/test-setUtilityNodes.R @@ -0,0 +1,15 @@ +context("setUtilityNodes") + +test_that("setUtiltyNodes", +{ + expect_that( + Net <- HydeNetwork(~ wells + + pe | wells + + d.dimer | pregnant*pe + + angio | pe + + treat | d.dimer*angio + + death | pe*treat, + data = PE) %>% + setUtilityNodes(treat, angio), + not(throws_error())) +}) \ No newline at end of file diff --git a/tests/testthat/test-update.HydeNetwork.R b/tests/testthat/test-update.HydeNetwork.R new file mode 100644 index 0000000..cf7c411 --- /dev/null +++ b/tests/testthat/test-update.HydeNetwork.R @@ -0,0 +1,16 @@ +context("update.HydeNetwork") + +carNet <- HydeNetwork(~gear | mpg * am, + data = mtcars) + +test_that("update by adding a node", +{ + expect_that(update(carNet, ~ . + cyl | am), + not(throws_error())) +}) + +test_that("update and lose a parent", +{ + expect_warning( update(carNet, ~ . - am) ) +}) + \ No newline at end of file From 717c526cf96ce322e083e9a602ea56147db2854e Mon Sep 17 00:00:00 2001 From: Benjamin Date: Wed, 23 Sep 2015 20:25:35 -0400 Subject: [PATCH 06/13] Simple bug fix In the case where a categorical node does not have a `nodeFitter` attribute, it wasn't producing a logical value to evaluate in `network$nodeFitter[[node]] == "cpt"`, causing `compileDecisionModel` to fail. This test is now only run in the case that `nodeFitter[[node]]` is not null. --- R/HydeUtilities.R | 10 ++++++---- R/compileDecisionModel.R | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/R/HydeUtilities.R b/R/HydeUtilities.R index f817f89..4d6971c 100644 --- a/R/HydeUtilities.R +++ b/R/HydeUtilities.R @@ -86,10 +86,12 @@ termName <- function(term, reg){ #' @param network A Hyde Network Object decisionOptions <- function(node, network){ - if (network$nodeFitter[[node]] == "cpt"){ - D <- {if (!is.null(network$nodeData[[node]])) network$nodeData[[node]][[node]] - else network$data[[node]]} - dist <- 1:length(unique(D)) + if (!is.null(network$nodeFitter[[node]])){ + if (network$nodeFitter[[node]] == "cpt"){ + D <- {if (!is.null(network$nodeData[[node]])) network$nodeData[[node]][[node]] + else network$data[[node]]} + dist <- 1:length(unique(D)) + } } #* This uses a regular expression to extract the level number from #* the node JAGS model. For instance diff --git a/R/compileDecisionModel.R b/R/compileDecisionModel.R index 66b8c39..456f4ed 100644 --- a/R/compileDecisionModel.R +++ b/R/compileDecisionModel.R @@ -75,9 +75,9 @@ compileDecisionModel <- function(network, policyMatrix = NULL, ...){ ArgumentCheck::addError( msg = "'data' is not an accepted argument in 'compileDecisionModel'", argcheck = Check) - - options <- makePolicyMatrix(network, policyMatrix, Check) + options <- makePolicyMatrix(network, policyMatrix, Check) + ArgumentCheck::finishArgCheck(Check) cpt_arrays <- makeCptArrays(network) From 69a80206d5e84fdafe332701e8401d26f4a9d7e4 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Wed, 23 Sep 2015 20:49:55 -0400 Subject: [PATCH 07/13] Some more bug tweaks. --- DESCRIPTION | 4 ++-- NEWS | 4 ++++ R/HydeUtilities.R | 7 ++++++- R/compileDecisionModel.R | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 69b1700..ecb1aeb 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: HydeNet Type: Package Title: Hybrid Bayesian Networks Using R and JAGS -Version: 0.9.2 -Date: 2015-09-11 +Version: 0.9.3 +Date: 2015-09-24 Author: Jarrod E. Dalton and Benjamin Nutter Maintainer: Benjamin Nutter Description: Facilities for easy implementation of hybrid Bayesian networks diff --git a/NEWS b/NEWS index 3f5fd6f..4261204 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +### 0.9.3 (24 Sept 2015) +* Minor bug fix related to creating policy matrices with nodes that + don't have a 'nodeFitter' specified. + ### 0.9.2 (11 Sept 2015) * Fixed a bug in `compileJagsModel` and `compileDecisionModel` that prevented `cpt` objects from being passed to JAGS models correctly. diff --git a/R/HydeUtilities.R b/R/HydeUtilities.R index 4d6971c..b7fee1c 100644 --- a/R/HydeUtilities.R +++ b/R/HydeUtilities.R @@ -86,18 +86,23 @@ termName <- function(term, reg){ #' @param network A Hyde Network Object decisionOptions <- function(node, network){ + #* In some cases, nodeFitter isn't set for a node. When nodeFitter is NULL, + #* we want to skip the "cpt" check and move on to other possibilities. + #* If it isn't NULL and "cpt" is the fitter, we return dist immediately + #* to avoid overwriting it in subsequent checks if (!is.null(network$nodeFitter[[node]])){ if (network$nodeFitter[[node]] == "cpt"){ D <- {if (!is.null(network$nodeData[[node]])) network$nodeData[[node]][[node]] else network$data[[node]]} dist <- 1:length(unique(D)) + return(dist) } } #* This uses a regular expression to extract the level number from #* the node JAGS model. For instance #* pi.var[1] <- .123; pi.var[2] <- .321; ... #* the regular expression pulls out the numbers in between each set of []. - else if (network$nodeType[[node]] == "dcat"){ + if (network$nodeType[[node]] == "dcat"){ dist <- writeJagsModel(network, node)[1] dist <- unlist(strsplit(dist, ";")) dist <- as.numeric(stringr::str_extract(dist, stringr::regex("(?<=[\\[]).*(?=[\\]])"))) diff --git a/R/compileDecisionModel.R b/R/compileDecisionModel.R index 456f4ed..145e0ea 100644 --- a/R/compileDecisionModel.R +++ b/R/compileDecisionModel.R @@ -75,7 +75,7 @@ compileDecisionModel <- function(network, policyMatrix = NULL, ...){ ArgumentCheck::addError( msg = "'data' is not an accepted argument in 'compileDecisionModel'", argcheck = Check) - + options <- makePolicyMatrix(network, policyMatrix, Check) ArgumentCheck::finishArgCheck(Check) From f38851fec2572db5ea97e42c414853a540d23ce3 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 25 Sep 2015 11:55:49 -0400 Subject: [PATCH 08/13] 0.10.0 ready (we hope) Fixes Issue #81. Next step is to build and test on all the different system and revise the CRAN comment in preparation for submission. --- DESCRIPTION | 4 +-- NEWS | 4 +++ R/HydeNetwork.R | 17 ++++++++++ R/HydeUtilities.R | 35 +++++++++++++++++--- R/modelToNode.R | 11 ++++--- R/setNode.R | 55 ++++++++++++++++++++++++++++++- R/setNodeModels.R | 7 ++-- man/HydeNetwork.Rd | 6 ++++ man/setNode.Rd | 13 +++++++- man/setNodeModels.Rd | 2 +- tests/testthat/test-HydeNetwork.R | 4 ++- tests/testthat/test-setNode.R | 50 ++++++++++++++++++++++++++++ 12 files changed, 191 insertions(+), 17 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index ecb1aeb..07c291a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: HydeNet Type: Package Title: Hybrid Bayesian Networks Using R and JAGS -Version: 0.9.3 -Date: 2015-09-24 +Version: 0.10.0 +Date: 2015-09-26 Author: Jarrod E. Dalton and Benjamin Nutter Maintainer: Benjamin Nutter Description: Facilities for easy implementation of hybrid Bayesian networks diff --git a/NEWS b/NEWS index 4261204..a474205 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +### 0.10.0 (26 Sept 2015) +* Implements the `factorLevels` element in network objects + and arguments in `setNode`. See Issue #81 + ### 0.9.3 (24 Sept 2015) * Minor bug fix related to creating policy matrices with nodes that don't have a 'nodeFitter' specified. diff --git a/R/HydeNetwork.R b/R/HydeNetwork.R index bd8b92b..b618259 100644 --- a/R/HydeNetwork.R +++ b/R/HydeNetwork.R @@ -49,6 +49,12 @@ #' \item \code{nodeData} A named list with the data for each node. If a node's #' entry in \code{fromData} is \code{TRUE} and \code{nodeData} is \code{NULL}, #' it will look to the \code{data} attribute instead. +#' \item \code{factorLevels} If the vector associated with the node is a factor +#' (or character), the levels of the factor are stored here. Although it +#' may seem redundant, it allows factor levels to be specified in cases +#' where the node is not define with data. If data are provided to the +#' node, this element is determined from the data and cannot be +#' manually overwritten. #' \item \code{nodeModel} A list of model objects. This is a storing place for #' models that have already been fit so that they don't have to be refit #' again. @@ -161,6 +167,15 @@ HydeNetwork.formula <- function(nodes, data=NULL, ...){ nodeUtility <- lapply(seq_along(node_names), function(x) return(FALSE)) names(nodeUtility) <- node_names + factorLevels <- lapply(seq_along(node_names), function(x) return(NULL)) + names(factorLevels) <- node_names + if (!is.null(data)){ + factor_vars <- names(data)[vapply(data, is.factor, logical(1))] + factorLevels[factor_vars] <- + lapply(data[, factor_vars, drop = FALSE], + levels) + } + #* Define the HydeNetwork object network <- list(nodes = node_names, parents=parents, nodeType=nodeType, nodeFormula=nodeFormula, @@ -168,6 +183,7 @@ HydeNetwork.formula <- function(nodes, data=NULL, ...){ nodeParams=nodeParams, fromData=fromData, nodeData = nodeData, + factorLevels = factorLevels, nodeModel = nodeModel, nodeDecision = nodeDecision, nodeUtility = nodeUtility, @@ -212,6 +228,7 @@ HydeNetwork.list <- function(nodes, ...){ network$nodeDecision[[i]] <- Attrs[[i]]$nodeDecision network$nodeUtility[[i]] <- Attrs[[i]]$nodeUtility network$fromData[[i]] <- TRUE + network$factorLevels[[i]] <- Attrs[[i]]$factorLevels } return(network) diff --git a/R/HydeUtilities.R b/R/HydeUtilities.R index b7fee1c..0889fb6 100644 --- a/R/HydeUtilities.R +++ b/R/HydeUtilities.R @@ -227,11 +227,19 @@ validateParameters <- function(params, dist){ #' makeFactorRef <- function(network) { - dataList <- c(list(network$data), network$nodeData) - names(dataList) <- NULL - Ref <- do.call("c", lapply(dataList, dataframeFactors)) + network_factors <- + names(network$factorLevels)[!vapply(network$factorLevels, is.null, logical(1))] - types <- unlist(network$nodeType[names(Ref)]) + if (length(network_factors) == 0) return(NULL) + + Ref <- lapply(network_factors, + function(f){ + data.frame(value = 1:length(network$factorLevels[[f]]), + label = network$factorLevels[[f]]) + }) + names(Ref) <- network_factors + + types <- unlist(network$nodeType[network_factors]) types <- types[types %in% "dbern"] Ref[names(types)] <- @@ -240,7 +248,26 @@ makeFactorRef <- function(network) f$value <- f$value - 1 f }) + Ref[unique(names(Ref))] + #* The code below was the old way of doing this + #* before we implemented the `factorLevels` element. + #* I'm just hesitant to give it up before the + #* new system is well tested. +# dataList <- c(list(network$data), network$nodeData) +# names(dataList) <- NULL +# Ref <- do.call("c", lapply(dataList, dataframeFactors)) +# +# types <- unlist(network$nodeType[names(Ref)]) +# types <- types[types %in% "dbern"] +# +# Ref[names(types)] <- +# lapply(Ref[names(types)], +# function(f){ +# f$value <- f$value - 1 +# f +# }) +# Ref[unique(names(Ref))] } #' @rdname HydeUtilities diff --git a/R/modelToNode.R b/R/modelToNode.R index e91ac81..09f18e9 100644 --- a/R/modelToNode.R +++ b/R/modelToNode.R @@ -58,6 +58,7 @@ modelToNode.cpt <- function(model, nodes, ...) nodeUtility = FALSE, fromData = TRUE, nodeData = attributes(model)$model, + factorLevels = levels(attributes(model)$model[[1]]), nodeModel = model) } @@ -87,6 +88,7 @@ modelToNode.glm <- function(model, nodes, ...){ if (is.null(model$model)) stats::update(model, model=TRUE)$model else model$model } else NULL, + factorLevels = if (is.factor(model$model[[1]])) levels(model$model[[1]]) else NULL, nodeModel = model) } @@ -117,6 +119,7 @@ modelToNode.lm <- function(model, nodes, ...){ if (is.null(model$model)) stats::update(model, model=TRUE)$model else model$model } else NULL, + factorLevels = NULL, nodeModel = model) } @@ -124,6 +127,7 @@ modelToNode.lm <- function(model, nodes, ...){ #' @export modelToNode.multinom <- function(model, nodes, ...){ + if (is.null(model$model)) model <- stats::update(model, model=TRUE) if (missing(nodes)) nodes <- nodeFromFunction(names(attributes(stats::terms(model))$dataClasses)) list(nodes = as.character(stats::terms(model))[2], @@ -142,10 +146,8 @@ modelToNode.multinom <- function(model, nodes, ...){ nodeDecision = FALSE, nodeUtility = FALSE, fromData = TRUE, - nodeData = if ("data" %in% names(as.list(model$call)[-c(1, which(names(as.list(model$call)) == "formula"))])){ - if (is.null(model$model)) stats::update(model, model=TRUE)$model - else model$model - } else NULL, + nodeData = if (!is.null(model$model)) model$model else NULL, + factorLevels = if (is.factor(model$model[[1]])) levels(model$model[[1]]) else NULL, nodeModel = model) } @@ -166,6 +168,7 @@ modelToNode.xtabs <- function(model, nodes, ...){ nodeUtility = FALSE, fromData = FALSE, nodeData = NULL, + factorLevels = names(model), nodeModel = model) } diff --git a/R/setNode.R b/R/setNode.R index f9531f0..d613f94 100644 --- a/R/setNode.R +++ b/R/setNode.R @@ -53,6 +53,15 @@ #' Data passed in this argument are applied only to this specific node. No checks are #' performed to ensure that all of the appropriate variables (the node and its parents) #' are included. +#' @param factorLevels A character vector used to specify the levels of factors +#' when data are not provided for a node. The order of factors follows the +#' order provided by the user. This argument is only used when the node type +#' is either \code{dcat} or \code{dbern}, the node Fitter is not \code{cpt}, +#' \code{nodeData} is \code{NULL}, and no variable for the node exists in +#' the network's \code{data} element. If any of those conditions is not met, +#' \code{factorLevels} is ignored. This proves particularly important when +#' data are specified in order to prevent a user specification from conflicting +#' with expected factors across nodes. #' @param validate Logical. Toggles validation of parameters given in \code{...}. #' When passing raw JAGS code (ie, character strings), this will be ignored #' (with a message), @@ -123,7 +132,7 @@ setNode <- function(network, node, nodeType, decision = "current", utility = "current", fromData=!is.null(network$data), ..., - nodeData = NULL, + nodeData = NULL, factorLevels = NULL, validate=TRUE, fitModel=getOption("Hyde_fitModel")){ network.t <- as.character(substitute(network)) @@ -229,6 +238,50 @@ setNode <- function(network, node, nodeType, if (!missing(nodeFitter)) network$nodeFitter[[node.t]] <- nodeFitter if (length(fitterArgs)) network$nodeFitterArgs[[node.t]] <- fitterArgs if (!is.null(nodeData)) network$nodeData[[node.t]] <- nodeData + + if (!is.null(factorLevels)){ + nodeFitter <- if (is.null(network$nodeFitter[[node.t]])) "" else network$nodeFitter[[node.t]] + if (!(network$nodeType[[node.t]] %in% c("dcat", "dbern")) || + nodeFitter == "cpt" || + !is.null(network$nodeData[[node.t]]) || + (node.t %in% names(network$data))){ + ArgumentCheck::addWarning( + msg = paste0("'", node.t, "' does not satisfy the conditions ", + "to use 'factorLevels'. See '?setNode' for details."), + argcheck = Check) + + if (nodeFitter == "cpt"){ + if (!is.null(network$nodeData[[node.t]])){ + network$factorLevels[[node.t]] <- + if (!is.factor(network$nodeData[[node.t]][[node.t]])) + sort(unique(network$nodeData[[node.t]][[node.t]])) + else levels(network$nodeData[[node.t]][[node.t]]) + } + else if (!is.null(network$data[[node.t]])){ + network$factorLevels[[node.t]] <- + if (!is.factor(network$data[[node.t]])) + sort(unique(network$data[[node.t]])) + else levels(network$data[[node.t]]) + } + } + } + else{ + network$factorLevels[[node.t]] <- factorLevels + } + } + else{ + if (!is.null(network$nodeData[[node.t]])){ + network$factorLevels[[node.t]] <- + levels(network$nodeData[[node.t]][[node.t]]) + } + else if (!is.null(network$data)){ + network$factorLevels[[node.t]] <- + levels(network$data[[node.t]]) + } + else{ + network$factorLevels[[node.t]] <- NULL + } + } network$nodeDecision[[node.t]] <- decision network$nodeUtility[[node.t]] <- utility diff --git a/R/setNodeModels.R b/R/setNodeModels.R index 19011fc..3d8dba4 100644 --- a/R/setNodeModels.R +++ b/R/setNodeModels.R @@ -32,7 +32,7 @@ #' g6 <- glm(treat ~ d.dimer + angio, data=PE, family="binomial") #' g7 <- glm(death ~ pe + treat, data=PE, family="binomial") #' -#' Net <- setNodeModels(Net, g1, g2, g3, g4, g5, g6, g7) +#' Net2 <- setNodeModels(Net, g1, g2, g3, g4, g5, g6, g7) #' print(Net) #' #' writeNetworkModel(Net, pretty=TRUE) @@ -84,17 +84,18 @@ setNodeModels <- function(network, ...){ Check) ArgumentCheck::finishArgCheck(Check) - + #* Translate new node features into network object for (i in names(Attrs)){ network$parents[[i]] <- Attrs[[i]]$parents network$nodeType[[i]] <- Attrs[[i]]$nodeType network$nodeFormula[[i]] <- Attrs[[i]]$nodeFormula network$nodeFitter[[i]] <- Attrs[[i]]$nodeFitter - network$nodeFitterargs[[i]] <- Attrs[[i]]$nodeFitterArgs + network$nodeFitterArgs[[i]] <- Attrs[[i]]$nodeFitterArgs network$nodeParams[[i]] <- Attrs[[i]]$nodeParams network$nodeData[[i]] <- Attrs[[i]]$nodeData network$nodeModel[[i]] <- Attrs[[i]]$nodeModel + network$factorLevels[[i]] <- Attrs[[i]]$factorLevels } return(network) diff --git a/man/HydeNetwork.Rd b/man/HydeNetwork.Rd index 869e472..b04ae27 100644 --- a/man/HydeNetwork.Rd +++ b/man/HydeNetwork.Rd @@ -42,6 +42,12 @@ list with the following components: \item \code{nodeData} A named list with the data for each node. If a node's entry in \code{fromData} is \code{TRUE} and \code{nodeData} is \code{NULL}, it will look to the \code{data} attribute instead. + \item \code{factorLevels} If the vector associated with the node is a factor + (or character), the levels of the factor are stored here. Although it + may seem redundant, it allows factor levels to be specified in cases + where the node is not define with data. If data are provided to the + node, this element is determined from the data and cannot be + manually overwritten. \item \code{nodeModel} A list of model objects. This is a storing place for models that have already been fit so that they don't have to be refit again. diff --git a/man/setNode.Rd b/man/setNode.Rd index 79e2545..29d1f16 100644 --- a/man/setNode.Rd +++ b/man/setNode.Rd @@ -9,7 +9,8 @@ setNode(network, node, nodeType, nodeFitter, nodeFormula, fitterArgs = list(), decision = "current", utility = "current", fromData = !is.null(network$data), ..., nodeData = NULL, - validate = TRUE, fitModel = getOption("Hyde_fitModel")) + factorLevels = NULL, validate = TRUE, + fitModel = getOption("Hyde_fitModel")) fromData() @@ -71,6 +72,16 @@ Data passed in this argument are applied only to this specific node. No checks performed to ensure that all of the appropriate variables (the node and its parents) are included.} +\item{factorLevels}{A character vector used to specify the levels of factors +when data are not provided for a node. The order of factors follows the +order provided by the user. This argument is only used when the node type +is either \code{dcat} or \code{dbern}, the node Fitter is not \code{cpt}, +\code{nodeData} is \code{NULL}, and no variable for the node exists in +the network's \code{data} element. If any of those conditions is not met, +\code{factorLevels} is ignored. This proves particularly important when +data are specified in order to prevent a user specification from conflicting +with expected factors across nodes.} + \item{validate}{Logical. Toggles validation of parameters given in \code{...}. When passing raw JAGS code (ie, character strings), this will be ignored (with a message), diff --git a/man/setNodeModels.Rd b/man/setNodeModels.Rd index cc4d1c5..77599c9 100644 --- a/man/setNodeModels.Rd +++ b/man/setNodeModels.Rd @@ -37,7 +37,7 @@ g5 <- glm(angio ~ pe, data=PE, family="binomial") g6 <- glm(treat ~ d.dimer + angio, data=PE, family="binomial") g7 <- glm(death ~ pe + treat, data=PE, family="binomial") -Net <- setNodeModels(Net, g1, g2, g3, g4, g5, g6, g7) +Net2 <- setNodeModels(Net, g1, g2, g3, g4, g5, g6, g7) print(Net) writeNetworkModel(Net, pretty=TRUE) diff --git a/tests/testthat/test-HydeNetwork.R b/tests/testthat/test-HydeNetwork.R index 5a3ca1c..d30cfd2 100644 --- a/tests/testthat/test-HydeNetwork.R +++ b/tests/testthat/test-HydeNetwork.R @@ -13,7 +13,8 @@ test_that("HydeNetwork.formula returns expected attributes", { expect_equal(names(Net), c("nodes", "parents", "nodeType", "nodeFormula", "nodeFitter", - "nodeFitterArgs", "nodeParams", "fromData", "nodeData", + "nodeFitterArgs", "nodeParams", "fromData", "nodeData", + "factorLevels", "nodeModel", "nodeDecision", "nodeUtility", "dag", "data", "network_formula")) }) @@ -47,6 +48,7 @@ test_that("HydeNetwork.list returns expected attributes", expect_equal(names(bagNet), c("nodes", "parents", "nodeType", "nodeFormula", "nodeFitter", "nodeFitterArgs", "nodeParams", "fromData", "nodeData", + "factorLevels", "nodeModel", "nodeDecision", "nodeUtility", "dag", "network_formula")) }) \ No newline at end of file diff --git a/tests/testthat/test-setNode.R b/tests/testthat/test-setNode.R index a7b6b3c..96bf9ca 100644 --- a/tests/testthat/test-setNode.R +++ b/tests/testthat/test-setNode.R @@ -57,4 +57,54 @@ test_that("fit the model for dpois", fitModel = TRUE), not(throws_error())) }) + +test_that("setNode factorLevels with non-dcat or dbern", +{ + NetLevels <- HydeNetwork(~ gear | mpg + am) + expect_warning( + setNode(NetLevels, am, nodeType = "dpois", + factorLevels = c("Automatic", "Manual"), + lambda = 1)) +}) + +test_that("setNode factorLevels with cpt fitter", +{ + NetLevels <- HydeNetwork( ~ gear | mpg + am) + expect_warning( + setNode(NetLevels, am, nodeType = "dcat", + nodeFitter = "cpt", + factorLevels = c("Automatic", "Manual"), + pi = fromData())) +}) + +test_that("setNode factorLevels with nodeData", +{ + NetLevels <- HydeNetwork( ~ gear | mpg + am) + expect_warning( + setNode(NetLevels, am, nodeType = "dcat", + nodeFitter = "cpt", + nodeData = mtcars, + factorLevels = c("Automatic", "Manual"), + pi = fromData())) +}) + +test_that("setNode factorLevels with network data", +{ + NetLevels <- HydeNetwork( ~ gear | mpg + am, data = mtcars) + expect_warning( + setNode(NetLevels, am, nodeType = "dcat", + nodeFitter = "cpt", + factorLevels = c("Automatic", "Manual"), + pi = fromData())) +}) + +test_that("setNode factorLevels as intended to be used", +{ + NetLevels <- HydeNetwork( ~ gear | mpg + am) + expect_equal( + setNode(NetLevels, am, nodeType = "dcat", + factorLevels = c("Automatic", "Manual"), + pi = vectorProbs(c(15, 25), "am"))$factorLevels, + list(gear = NULL, mpg = NULL, am = c("Automatic", "Manual"))) +}) \ No newline at end of file From 15ddb95040d3153d73eddcbfc8fb598278974bd0 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Fri, 25 Sep 2015 21:21:00 -0400 Subject: [PATCH 09/13] Fixing Issue #81....again --- R/HydeUtilities.R | 3 ++- R/modelToNode.R | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/R/HydeUtilities.R b/R/HydeUtilities.R index 0889fb6..8380fdb 100644 --- a/R/HydeUtilities.R +++ b/R/HydeUtilities.R @@ -235,7 +235,8 @@ makeFactorRef <- function(network) Ref <- lapply(network_factors, function(f){ data.frame(value = 1:length(network$factorLevels[[f]]), - label = network$factorLevels[[f]]) + label = network$factorLevels[[f]], + stringsAsFactors = FALSE) }) names(Ref) <- network_factors diff --git a/R/modelToNode.R b/R/modelToNode.R index 09f18e9..b9221c1 100644 --- a/R/modelToNode.R +++ b/R/modelToNode.R @@ -38,7 +38,9 @@ modelToNode.cpt <- function(model, nodes, ...) { if (missing(nodes)) nodes <- names(dimnames(model)) - list(nodes = utils::tail(names(dimnames(model)), 1), + + node_name = utils::tail(names(dimnames(model)), 1) + list(nodes = node_name, parents = if (length(dimnames(model)) == 1) NULL else @@ -58,7 +60,7 @@ modelToNode.cpt <- function(model, nodes, ...) nodeUtility = FALSE, fromData = TRUE, nodeData = attributes(model)$model, - factorLevels = levels(attributes(model)$model[[1]]), + factorLevels = levels(attributes(model)$model[[node_name]]), nodeModel = model) } From 145849a7bc5ddb9403f73f26dfa8dafe9a3ccf1b Mon Sep 17 00:00:00 2001 From: nutterb Date: Fri, 9 Oct 2015 10:34:35 -0400 Subject: [PATCH 10/13] Issue#93 and Issue#92 --- DESCRIPTION | 2 +- NEWS | 8 ++++++++ R/compileDecisionModel.R | 41 ++++++++++++++++++++++++++++++++-------- R/plot.HydeNetwork.R | 16 ++++++++-------- 4 files changed, 50 insertions(+), 17 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 07c291a..4d213cc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -27,7 +27,7 @@ Depends: Imports: ArgumentCheck, broom (>= 0.3.7), - DiagrammeR (>= 0.7), + DiagrammeR (>= 0.8), plyr, dplyr, graph, diff --git a/NEWS b/NEWS index a474205..2a23712 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,14 @@ ### 0.10.0 (26 Sept 2015) * Implements the `factorLevels` element in network objects and arguments in `setNode`. See Issue #81 +* Changes to `plot.HydeNetwork` relevant to changes in + `DiagrammeR` 0.8.0. Note that the column names in custom node + data frames no longer contain `node_id` but use `nodes` + instead. However, the `HydeNet` function arguments have no + changed names, in order to maintain compatibility with 0.9.0. + Future versions may allow for either `node_id` or `nodes` to + be used. In custom edge data frames, `edge_from` and `edge_to` + are changed to `from` and `to`, respectively. ### 0.9.3 (24 Sept 2015) * Minor bug fix related to creating policy matrices with nodes that diff --git a/R/compileDecisionModel.R b/R/compileDecisionModel.R index 145e0ea..6d8898c 100644 --- a/R/compileDecisionModel.R +++ b/R/compileDecisionModel.R @@ -17,6 +17,10 @@ #' @param ... Additional arguments to pass to \code{jags.model}, excepting #' the \code{data} argument. The \code{data} argument is created by #' \code{compileDecisionModel}, and cannot be passed manually. +#' @param data An optional list of data values to be observed in the nodes. +#' It is passed to the \code{data} argument of \code{rjags::jags}. Any +#' values given in \code{data} will override values provided in +#' \code{policyMatrix} with a warning. #' #' @details \code{compileDecisionModel} only accepts nodes of type \code{"dbern"} #' (Bernoulli random variable taking either 0 or 1) or \code{"dcat"} @@ -66,17 +70,19 @@ #' angio = c("Negative", "Positive")) #' decision3 <- compileDecisionModel(Net, custom_policy) #' -compileDecisionModel <- function(network, policyMatrix = NULL, ...){ +compileDecisionModel <- function(network, policyMatrix = NULL, ..., data = NULL){ Check <- ArgumentCheck::newArgCheck() dots <- list(...) - if ("data" %in% names(dots)) - ArgumentCheck::addError( - msg = "'data' is not an accepted argument in 'compileDecisionModel'", - argcheck = Check) - - options <- makePolicyMatrix(network, policyMatrix, Check) +# if ("data" %in% names(dots)) +# ArgumentCheck::addError( +# msg = "'data' is not an accepted argument in 'compileDecisionModel'", +# argcheck = Check) +# + options <- makePolicyMatrix(network, policyMatrix, data, Check) + + return(options) ArgumentCheck::finishArgCheck(Check) @@ -96,7 +102,7 @@ compileDecisionModel <- function(network, policyMatrix = NULL, ...){ #*********** UTILITY FUNCTIONS -makePolicyMatrix <- function(network, policyMatrix, argcheck){ +makePolicyMatrix <- function(network, policyMatrix, data, argcheck){ if (is.null(policyMatrix)) { decisionNodes <- names(network$nodeDecision)[sapply(network$nodeDecision, any)] @@ -137,6 +143,25 @@ makePolicyMatrix <- function(network, policyMatrix, argcheck){ options <- policyMatrix } + #* This is the part that pushes values from `data` into the + #* policy matrix. + if (!is.null(data)){ + conflicts <- names(data)[names(data) %in% names(options)] + if (length(conflicts) > 0){ + ArgumentCheck::addWarning( + msg = paste0("The following variables in 'data' are overriding ", + "values in 'policyMatrix': ", + paste0(conflicts, collapse = ", ")), + argcheck = argcheck) + } + + for (i in names(data)){ + options[[i]] <- data[[i]] + } + #* Remove duplicated rows + options <- options[!duplicated(options), , drop = FALSE] + } + ArgumentCheck::finishArgCheck(argcheck) options <- lapply(1:nrow(options), diff --git a/R/plot.HydeNetwork.R b/R/plot.HydeNetwork.R index 6dfc32f..bfd8769 100644 --- a/R/plot.HydeNetwork.R +++ b/R/plot.HydeNetwork.R @@ -113,7 +113,7 @@ plot.HydeNetwork <- function(x, ..., useHydeDefaults = TRUE) { - node_df <- data.frame(node_id = x$nodes, + node_df <- data.frame(nodes = x$nodes, stringsAsFactors = FALSE) if (useHydeDefaults) node_df <- mergeDefaultPlotOpts(x, node_df) @@ -151,8 +151,8 @@ mergeDefaultPlotOpts <- function(network, node_df){ by="type") %>% dplyr::select_("-type") - node_df[, -which(names(node_df) == "node_id")] <- - lapply(node_df[, -which(names(node_df) == "node_id"), drop=FALSE], + node_df[, -which(names(node_df) == "nodes")] <- + lapply(node_df[, -which(names(node_df) == "nodes"), drop=FALSE], function(x) ifelse(is.na(x), "", x)) node_df } @@ -165,7 +165,7 @@ mergeCustomNodes <- function(node_df, customNodes) # node_df <- dplyr::mutate(node_df, index=2) # customNodes <- dplyr::mutate(customNodes, index=1) node_df <- dplyr::full_join(customNodes, node_df, - by = c("node_id" = "node_id")) + by = c("nodes" = "nodes")) duplicated_names.x <- names(node_df)[grepl("[.]x", names(node_df))] if (length(duplicated_names.x) > 0) @@ -185,8 +185,8 @@ mergeCustomNodes <- function(node_df, customNodes) names(node_df) <- gsub("[.]x", "", names(node_df)) - node_df[, -which(names(node_df) == "node_id")] <- - lapply(node_df[, -which(names(node_df) == "node_id")], + node_df[, -which(names(node_df) == "nodes")] <- + lapply(node_df[, -which(names(node_df) == "nodes")], function(x) ifelse(is.na(x), "", x)) return(node_df) } @@ -205,7 +205,7 @@ mergeCustomEdges <- function(edge_df, customEdges) edge_df <- dplyr::mutate(edge_df, index = 2) customEdges <- dplyr::mutate(customEdges, index = 1) edge_df <- dplyr::bind_rows(customEdges, edge_df) %>% - dplyr::group_by_("edge_from", "edge_to") %>% + dplyr::group_by_("from", "to") %>% dplyr::filter_("rank(index, ties.method='first')==1") %>% dplyr::select_("-index") edge_df @@ -218,7 +218,7 @@ mergeCustomEdges <- function(edge_df, customEdges) #' customNode <- function(node_id, ...){ node_id <- as.character(substitute(node_id)) - nodeAttrs <- as.data.frame(c(list(node_id = node_id), + nodeAttrs <- as.data.frame(c(list(nodes = node_id), list(...)), stringsAsFactors=FALSE) if (length(nodeAttrs) > 0) return(nodeAttrs) From 8cd564c1730efa5b0c995587939bd09a74c601de Mon Sep 17 00:00:00 2001 From: Benjamin Date: Sat, 10 Oct 2015 09:00:52 -0400 Subject: [PATCH 11/13] See if we can get travis to work --- .travis.yml | 2 ++ R/compileDecisionModel.R | 2 -- cran-comments.md | 26 ++++++-------------------- man/compileDecisionModel.Rd | 11 ++++++++--- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index ccb44ec..b5f63f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,8 @@ r_github_packages: r_packages: - rjags + - DiagrammeR + - V8 notifications: email: diff --git a/R/compileDecisionModel.R b/R/compileDecisionModel.R index 6d8898c..faf87ac 100644 --- a/R/compileDecisionModel.R +++ b/R/compileDecisionModel.R @@ -82,8 +82,6 @@ compileDecisionModel <- function(network, policyMatrix = NULL, ..., data = NULL) # options <- makePolicyMatrix(network, policyMatrix, data, Check) - return(options) - ArgumentCheck::finishArgCheck(Check) cpt_arrays <- makeCptArrays(network) diff --git a/cran-comments.md b/cran-comments.md index 6ca2342..796e48b 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,27 +1,13 @@ ## Test environments -* local Windows install (devel) -* win-builder (devel and release) +* local Windows install (release) +* win-builder (devel[2015-10-09 r69501] and release) +* Ubuntu 12.04 LTS (Travis-CI) ## R CMD check results -Use of the LICENSE file has been corrected to fit with expectations. -I apologize for this misunderstanding. -`check` on win-builder returns the NOTE: - -Possibly mis-spelled words in DESCRIPTION: - acyclic (9:45) - conditionality (11:26) - -We believe these words to be spelled -correctly and wish to overlook this note, if possible. - - - -`check` on win-builder returns the NOTE: - -No repository set, so cyclic dependency check skipped - -We did not observe this NOTE on the local build. +The local Windows install using the release version +returned the expected NOTE about the package maintainer and +license file. ## Downstream dependencies There are no downstream dependencies for this package diff --git a/man/compileDecisionModel.Rd b/man/compileDecisionModel.Rd index ad91b1b..506d169 100644 --- a/man/compileDecisionModel.Rd +++ b/man/compileDecisionModel.Rd @@ -4,7 +4,7 @@ \alias{compileDecisionModel} \title{Compile JAGS Models to Evaluate the Effect of Decisions in a Network} \usage{ -compileDecisionModel(network, policyMatrix = NULL, ...) +compileDecisionModel(network, policyMatrix = NULL, ..., data = NULL) } \arguments{ \item{network}{A HydeNet object with decision nodes defined.} @@ -14,8 +14,13 @@ for comparing networks under different conditions. See \code{\link{policyMatrix}}.} \item{...}{Additional arguments to pass to \code{jags.model}, excepting - the \code{data} argument. The \code{data} argument is created by - \code{compileDecisionModel}, and cannot be passed manually.} +the \code{data} argument. The \code{data} argument is created by +\code{compileDecisionModel}, and cannot be passed manually.} + +\item{data}{An optional list of data values to be observed in the nodes. + It is passed to the \code{data} argument of \code{rjags::jags}. Any + values given in \code{data} will override values provided in + \code{policyMatrix} with a warning.} } \value{ Returns a list of \code{compiledHydeNetwork} objects. From 832754a51e94b15d69cb6bd474e54da70e598285 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Sun, 11 Oct 2015 17:45:57 -0400 Subject: [PATCH 12/13] One more attempt at travis before I give up on it --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b5f63f5..3e3eaf4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,10 +22,11 @@ env: r_github_packages: - Rexamine/stringi +apt_packages: + - libv8-dev + r_packages: - rjags - - DiagrammeR - - V8 notifications: email: From 5faa2c31ea56fc4138a7f5f0b6ca286816b51390 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Sun, 11 Oct 2015 18:14:45 -0400 Subject: [PATCH 13/13] update the CRAN comments preparing for release --- cran-comments.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cran-comments.md b/cran-comments.md index 796e48b..8fe6147 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -9,6 +9,12 @@ The local Windows install using the release version returned the expected NOTE about the package maintainer and license file. +The Ubuntu checks also returned a NOTE about not checking +for cyclic dependencies. This NOTE was note returned on +any of the other checks. + ## Downstream dependencies There are no downstream dependencies for this package -at this time. \ No newline at end of file +at this time. + +Many thanks, and have a great day. \ No newline at end of file