tests/testthat/test-gof.R

context("test goodness-of-fit functions")

test_that("basic GOF functionality works", {
  set.seed(12345)
  networks <- list()
  for(i in 1:10) {               # create 10 random networks with 10 actors
    mat <- matrix(rbinom(100, 1, .25), nrow = 10, ncol = 10)
    diag(mat) <- 0               # loops are excluded
    nw <- network::network(mat)  # create network object
    networks[[i]] <- nw          # add network to the list
  }

  covariates <- list()
  for (i in 1:10) {              # create 10 matrices as covariate
    mat <- matrix(rnorm(100), nrow = 10, ncol = 10)
    covariates[[i]] <- mat       # add matrix to the list
  }

  fit <- suppressWarnings(btergm(networks ~ edges + istar(2) + edgecov(covariates), R = 100, verbose = FALSE))

  sink(nullfile())
  g <- btergm::gof(fit, nsim = 2, MCMC.burnin = 1000, MCMC.interval = 500, verbose = FALSE)
  sink()
  expect_equal(length(g), 7)
  expect_equal(class(g), "gof")
  path <- tempfile(fileext = ".png")
  png(path)
  plot(g)
  dev.off()
  fs <- file.size(path)
  fs <- fs < 100000 && fs > 1000
  expect_true(fs, 5)
})

test_that("checkdegeneracy works with btergm and mtergm", {
  set.seed(12345)
  networks <- list()
  for(i in 1:10) {                # create 10 random networks with 10 actors
    mat <- matrix(rbinom(100, 1, .25), nrow = 10, ncol = 10)
    diag(mat) <- 0               # loops are excluded
    nw <- network::network(mat)  # create network object
    networks[[i]] <- nw          # add network to the list
  }

  covariates <- list()
  for (i in 1:10) {              # create 10 matrices as covariate
    mat <- matrix(rnorm(100), nrow = 10, ncol = 10)
    covariates[[i]] <- mat       # add matrix to the list
  }

  fit <- suppressWarnings(btergm(networks ~ edges + istar(2) + edgecov(covariates), R = 100, verbose = FALSE))
  d <- checkdegeneracy(fit)
  expect_equal(class(d), "degeneracy")
  expect_length(d$target.stats, 10)
  expect_length(d$sim, 10)
  expect_length(d$target.stats[[1]], 3)
  expect_equal(dim(d$sim[[1]]), c(1000, 3))

  fit2 <- mtergm(networks ~ edges + istar(2) + edgecov(covariates), verbose = FALSE)
  expect_output(checkdegeneracy(fit2), "Sample statistics summary")
})

test_that("gof statistics for vectors work", {
  skip_on_cran()
  skip_if_not_installed("ergm", minimum_version = "4.8.1")
  skip_if_not_installed("network", minimum_version = "1.19.0")
  
  set.seed(12345)
  mat <- matrix(rbinom(100, 1, .25), nrow = 10, ncol = 10)
  diag(mat) <- 0               # loops are excluded
  nw <- network::network(mat)  # create network object
  
  # dsp
  expect_no_error(s <- dsp(mat))
  expect_equal(attributes(s)$label, "Dyad-wise shared partners")
  expect_equal(names(s), as.character(0:8))
  expect_length(s, 9)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 90)
  
  # esp
  expect_no_error(s <- esp(mat))
  expect_equal(attributes(s)$label, "Edge-wise shared partners")
  expect_equal(names(s), as.character(0:8))
  expect_length(s, 9)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 23)
  
  # nsp
  expect_no_error(s <- nsp(mat))
  expect_equal(attributes(s)$label, "Non-edge-wise shared partners")
  expect_equal(names(s), as.character(0:8))
  expect_length(s, 9)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 67)
  
  # deg
  expect_no_error(s <- deg(mat))
  expect_equal(attributes(s)$label, "Degree")
  expect_equal(names(s), as.character(0:9))
  expect_length(s, 10)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 10)
  
  # b1deg
  expect_no_error(s <- b1deg(mat))
  expect_equal(attributes(s)$label, "Degree (first mode)")
  expect_equal(names(s), as.character(0:10))
  expect_length(s, 11)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 10)
  
  # b2deg
  expect_no_error(s <- b2deg(mat))
  expect_equal(attributes(s)$label, "Degree (second mode)")
  expect_equal(names(s), as.character(0:10))
  expect_length(s, 11)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 10)
  
  # odeg
  expect_no_error(s <- odeg(mat))
  expect_equal(attributes(s)$label, "Outdegree")
  expect_equal(names(s), as.character(0:9))
  expect_length(s, 10)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 10)
  
  # ideg
  expect_no_error(s <- ideg(mat))
  expect_equal(attributes(s)$label, "Indegree")
  expect_equal(names(s), as.character(0:9))
  expect_length(s, 10)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 10)
  
  # kstar
  expect_no_error(s <- kstar(mat))
  expect_equal(attributes(s)$label, "k-star")
  expect_equal(names(s), as.character(0:9))
  expect_length(s, 10)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 442)
  
  # b1star
  expect_no_error(s <- b1star(mat))
  expect_equal(attributes(s)$label, "k-star (first mode)")
  expect_equal(names(s), as.character(0:10))
  expect_length(s, 11)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 100)
  
  # b2star
  expect_no_error(s <- b2star(mat))
  expect_equal(attributes(s)$label, "k-star (second mode)")
  expect_equal(names(s), as.character(0:10))
  expect_length(s, 11)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 71)
  
  # ostar
  expect_no_error(s <- ostar(mat))
  expect_equal(attributes(s)$label, "Outgoing k-star")
  expect_equal(names(s), as.character(0:9))
  expect_length(s, 10)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 100)
  
  # istar
  expect_no_error(s <- istar(mat))
  expect_equal(attributes(s)$label, "Incoming k-star")
  expect_equal(names(s), as.character(0:9))
  expect_length(s, 10)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 71)
  
  # kcycle
  expect_warning(s <- kcycle(mat), "cycles of length less than 2 cannot exist")
  expect_equal(attributes(s)$label, "Cycle")
  expect_equal(names(s), as.character(0:7))
  expect_length(s, 8)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 14)
  
  # geodesic
  expect_no_error(s <- geodesic(mat))
  expect_equal(attributes(s)$label, "Geodesic distances")
  expect_equal(names(s), c(as.character(1:9), "Inf"))
  expect_length(s, 10)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 90)
  
  # triad.directed
  expect_no_error(s <- triad.directed(mat))
  expect_equal(attributes(s)$label, "Triad census")
  expect_equal(names(s), c("003", "012", "102", "021D", "021U", "021C", "111D", "111U", "030T", "030C", "201", "120D", "120U", "120C", "210", "300"))
  expect_length(s, 16)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 120)
  
  # triad.undirected
  expect_no_error(s <- triad.undirected(mat))
  expect_equal(attributes(s)$label, "Triad census")
  expect_equal(names(s), as.character(0:3))
  expect_length(s, 4)
  expect_contains(class(s), "numeric")
  expect_equal(sum(s), 120)
})

test_that("gof statistics for single values work", {
  skip_on_cran()
  skip_if_not_installed("ergm", minimum_version = "4.8.1")
  skip_if_not_installed("network", minimum_version = "1.19.0")
  
  set.seed(12345)
  mat <- matrix(rbinom(100, 1, .25), nrow = 10, ncol = 10)
  diag(mat) <- 0               # loops are excluded
  nw <- network::network(mat)  # create network object
  
  # walktrap.modularity
  expect_no_error(s <- walktrap.modularity(mat))
  expect_equal(attributes(s)$label, "Modularity (walktrap)")
  expect_length(s, 1)
  expect_contains(class(s), "numeric")
  expect_gt(s, 0)
  expect_lt(s, 1)
  
  # fastgreedy.modularity
  expect_no_error(s <- fastgreedy.modularity(mat))
  expect_equal(attributes(s)$label, "Modularity (fast & greedy)")
  expect_length(s, 1)
  expect_contains(class(s), "numeric")
  expect_gt(s, 0)
  expect_lt(s, 1)
  
  # louvain.modularity
  expect_no_error(s <- louvain.modularity(mat))
  expect_equal(attributes(s)$label, "Modularity (Louvain)")
  expect_length(s, 1)
  expect_contains(class(s), "numeric")
  expect_gt(s, 0)
  expect_lt(s, 1)
  
  # maxmod.modularity
  expect_no_error(s <- maxmod.modularity(mat))
  expect_equal(attributes(s)$label, "Maximum modularity")
  expect_length(s, 1)
  expect_contains(class(s), "numeric")
  expect_gt(s, 0)
  expect_lt(s, 1)
  
  # edgebetweenness.modularity
  expect_no_error(s <- edgebetweenness.modularity(mat))
  expect_equal(attributes(s)$label, "Modularity (edge betweenness)")
  expect_length(s, 1)
  expect_contains(class(s), "numeric")
  expect_gt(s, 0)
  expect_lt(s, 1)
  
  # spinglass.modularity
  expect_no_error(s <- spinglass.modularity(mat))
  expect_equal(attributes(s)$label, "Modularity (spinglass)")
  expect_length(s, 1)
  expect_contains(class(s), "numeric")
  expect_gt(s, 0)
  expect_lt(s, 1)
})

test_that("tie prediction gof statistics work", {
  skip_on_cran()
  skip_if_not_installed("ergm", minimum_version = "4.8.1")
  skip_if_not_installed("network", minimum_version = "1.19.0")
  
  set.seed(16) # note that a specific random seed is used because the spinglass algorithm requires connected networks in both the observed and target networks
  networks <- list()
  for(i in 1:7) {                # create 7 random networks with 10 actors
    mat <- matrix(rbinom(100, 1, .25), nrow = 10, ncol = 10)
    diag(mat) <- 0               # loops are excluded
    nw <- network::network(mat)  # create network object
    networks[[i]] <- nw          # add network to the list
  }
  
  covariates <- list()
  for (i in 1:7) {              # create 7 matrices as covariate
    mat <- matrix(rnorm(100), nrow = 10, ncol = 10)
    covariates[[i]] <- mat       # add matrix to the list
  }
  
  fit <- suppressWarnings(btergm(networks ~ edges + istar(2) + edgecov(covariates), R = 100, verbose = FALSE))
  
  # spinglass.roc
  expect_no_error(g <- btergm::gof(fit, statistics = spinglass.roc, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.roc", "auc.roc.rgraph", "roc", "roc.rgraph", "label"))
  expect_equal(g[[1]]$label, "Spinglass community comembership prediction")
  expect_gt(g[[1]]$auc.roc, 0)
  expect_lt(g[[1]]$auc.roc, 1)
  
  # spinglass.pr
  set.seed(3) # note that a specific random seed is used because the spinglass algorithm requires connected networks in both the observed and target networks
  expect_no_error(g <- btergm::gof(fit, statistics = spinglass.pr, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.pr", "auc.pr.rgraph", "pr", "pr.rgraph", "label"))
  expect_equal(g[[1]]$label, "Spinglass community comembership prediction")
  expect_gt(g[[1]]$auc.pr, 0)
  expect_lt(g[[1]]$auc.pr, 1)
  
  # walktrap.roc
  expect_no_error(g <- btergm::gof(fit, statistics = walktrap.roc, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.roc", "auc.roc.rgraph", "roc", "roc.rgraph", "label"))
  expect_equal(g[[1]]$label, "Walktrap community comembership prediction")
  expect_gt(g[[1]]$auc.roc, 0)
  expect_lt(g[[1]]$auc.roc, 1)
  
  # walktrap.pr
  expect_no_error(g <- btergm::gof(fit, statistics = walktrap.pr, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.pr", "auc.pr.rgraph", "pr", "pr.rgraph", "label"))
  expect_equal(g[[1]]$label, "Walktrap community comembership prediction")
  expect_gt(g[[1]]$auc.pr, 0)
  expect_lt(g[[1]]$auc.pr, 1)
  
  # fastgreedy.roc
  expect_no_error(g <- btergm::gof(fit, statistics = fastgreedy.roc, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.roc", "auc.roc.rgraph", "roc", "roc.rgraph", "label"))
  expect_equal(g[[1]]$label, "Fast & greedy community comembership prediction")
  expect_gt(g[[1]]$auc.roc, 0)
  expect_lt(g[[1]]$auc.roc, 1)
  
  # fastgreedy.pr
  expect_no_error(g <- btergm::gof(fit, statistics = fastgreedy.pr, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.pr", "auc.pr.rgraph", "pr", "pr.rgraph", "label"))
  expect_equal(g[[1]]$label, "Fast & greedy community comembership prediction")
  expect_gt(g[[1]]$auc.pr, 0)
  expect_lt(g[[1]]$auc.pr, 1)
  
  # louvain.roc
  expect_no_error(g <- btergm::gof(fit, statistics = louvain.roc, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.roc", "auc.roc.rgraph", "roc", "roc.rgraph", "label"))
  expect_equal(g[[1]]$label, "Louvain community comembership prediction")
  expect_gt(g[[1]]$auc.roc, 0)
  expect_lt(g[[1]]$auc.roc, 1)
  
  # louvain.pr
  expect_no_error(g <- btergm::gof(fit, statistics = louvain.pr, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.pr", "auc.pr.rgraph", "pr", "pr.rgraph", "label"))
  expect_equal(g[[1]]$label, "Louvain community comembership prediction")
  expect_gt(g[[1]]$auc.pr, 0)
  expect_lt(g[[1]]$auc.pr, 1)
  
  # maxmod.roc
  expect_no_error(g <- btergm::gof(fit, statistics = maxmod.roc, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.roc", "auc.roc.rgraph", "roc", "roc.rgraph", "label"))
  expect_equal(g[[1]]$label, "Maximum modularity community comembership prediction")
  expect_gt(g[[1]]$auc.roc, 0)
  expect_lt(g[[1]]$auc.roc, 1)
  
  # maxmod.pr
  expect_no_error(g <- btergm::gof(fit, statistics = maxmod.pr, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.pr", "auc.pr.rgraph", "pr", "pr.rgraph", "label"))
  expect_equal(g[[1]]$label, "Maximum modularity community comembership prediction")
  expect_gt(g[[1]]$auc.pr, 0)
  expect_lt(g[[1]]$auc.pr, 1)
  
  # edgebetweenness.roc
  expect_no_error(g <- btergm::gof(fit, statistics = edgebetweenness.roc, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.roc", "auc.roc.rgraph", "roc", "roc.rgraph", "label"))
  expect_equal(g[[1]]$label, "Edge betweenness community comembership prediction")
  expect_gt(g[[1]]$auc.roc, 0)
  expect_lt(g[[1]]$auc.roc, 1)
  
  # edgebetweenness.pr
  expect_no_error(g <- btergm::gof(fit, statistics = edgebetweenness.pr, nsim = 10, verbose = FALSE))
  expect_contains(class(g), "gof")
  expect_length(g, 1)
  expect_length(g[[1]], 6)
  expect_equal(names(g), "Tie prediction")
  expect_equal(names(g[[1]]), c("type", "auc.pr", "auc.pr.rgraph", "pr", "pr.rgraph", "label"))
  expect_equal(g[[1]]$label, "Edge betweenness community comembership prediction")
  expect_gt(g[[1]]$auc.pr, 0)
  expect_lt(g[[1]]$auc.pr, 1)
})

Try the btergm package in your browser

Any scripts or data that you put into this service are public.

btergm documentation built on April 3, 2025, 9:01 p.m.