# Tests for spread_draws # # Author: mjskay ############################################################################### library(dplyr) library(tidyr) #set up datasets data(RankCorr, package = "ggdist") # subset of RankCorr (for speed) RankCorr_s = RankCorr[[1]][1:10,] # version of RankCorr with i index labeled i_labels = c("a", "b", "c") RankCorr_i = recover_types(RankCorr_s, list(i = factor(i_labels))) # version of RankCorr with i and j dimensions labeled i_labels = c("a", "b", "c") j_labels = c("A", "B", "C", "D") RankCorr_ij = recover_types(RankCorr_s, list(i = factor(i_labels), j = factor(j_labels))) # tests for helpers ========================================================== test_that("all_names works on various expressions", { expect_equal(all_names(quote(a + b + c[i, j] + 1)), c("a","b","c","i","j")) invalid_expr = quote(a + b) invalid_expr[[3]] = list() #replace `b` with a list object expect_error(all_names(invalid_expr), "Don't know how to handle type `list`") }) test_that("parse_variable_spec rejects incorrect usage of `|`", { expect_error(parse_variable_spec(quo(a | b | c)), "Left-hand side of `|` cannot contain `|`") expect_error(parse_variable_spec(quo(a | cbind(b, c))), "Right-hand side of `|` must be exactly one name") }) # tests for spread_draws =================================================== test_that("spread_draws correctly rejects missing variables", { data("RankCorr", package = "ggdist") expect_error(spread_draws(RankCorr, c(a, b)), "No variables found matching spec: c\\(a,b\\)") expect_error(spread_draws(RankCorr, a[b]), "No variables found matching spec: a\\[b\\]") expect_error(spread_draws(RankCorr, c(a, x)[b]), "No variables found matching spec: c\\(a,x\\)\\[b\\]") }) test_that("spread_draws works on a simple variable with no dimensions", { ref = tibble( .chain = as.integer(1), .iteration = seq_len(nrow(RankCorr_s)), .draw = .iteration, typical_r = as.vector(RankCorr_s[, "typical_r"]) ) expect_equivalent(spread_draws(RankCorr_s, typical_r), ref) }) test_that("spread_draws(seed = ) works", { set.seed(1234) ref = spread_draws(RankCorr_s, typical_r, ndraws = 2) expect_equivalent(spread_draws(RankCorr_s, typical_r, ndraws = 2, seed = 1234), ref) }) test_that("spread_draws works on two variables with no dimensions and multiple chains", { data(line, package = "coda") ref = tibble( .chain = c(rep(1L, nrow(line[[1]])), rep(2L, nrow(line[[2]]))), .iteration = c(seq_len(nrow(line[[1]])), seq_len(nrow(line[[2]]))), .draw = seq_len(nrow(line[[1]]) + nrow(line[[2]])), alpha = as.vector(c(line[[1]][,"alpha"], line[[2]][,"alpha"])), beta = as.vector(c(line[[1]][,"beta"], line[[2]][,"beta"])) ) expect_equal(spread_draws(line, alpha, beta), ref) }) test_that("spread_draws works on a variable with one unnamed index", { ref = bind_rows(lapply(1:3, function(i) { data.frame( i = i, tau = RankCorr_s[, paste0("tau[", i, "]")], .chain = as.integer(1), .iteration = seq_len(nrow(RankCorr_s)), .draw = seq_len(nrow(RankCorr_s)) ) })) %>% group_by(i) expect_equal(spread_draws(RankCorr_s, tau[i]) %>% arrange(i), ref) }) test_that("spread_draws works on a variable with one named index", { ref = bind_rows(lapply(1:3, function(i) { data.frame( i = factor(i_labels[i]), tau = RankCorr_i[, paste0("tau[", i, "]")], .chain = as.integer(1), .iteration = seq_len(nrow(RankCorr_i)), .draw = seq_len(nrow(RankCorr_i)) ) })) %>% group_by(i) expect_equal(spread_draws(RankCorr_i, tau[i]) %>% arrange(i), ref) }) test_that("spread_draws works on a variable with one anonymous wide index", { ref = data.frame( .chain = as.integer(1), .iteration = seq_len(nrow(RankCorr_s)), .draw = seq_len(nrow(RankCorr_s)) ) for (i in 1:3) { refcol = data.frame(RankCorr_s[, paste0("tau[", i, "]")]) names(refcol) = paste0("tau.", i) ref = cbind(ref, refcol) } ref = as_tibble(ref) expect_equal(spread_draws(RankCorr_s, tau[..]), ref) }) test_that("spread_draws works on a variable with one named wide index", { ref = data.frame( .chain = as.integer(1), .iteration = seq_len(nrow(RankCorr_i)), .draw = seq_len(nrow(RankCorr_i)) ) for (i in 1:3) { refcol = data.frame(RankCorr_i[, paste0("tau[", i, "]")]) names(refcol) = i_labels[i] ref = cbind(ref, refcol) } ref = as_tibble(ref) expect_equal(spread_draws(RankCorr_i, tau[i] | i), ref) }) test_that("spread_draws works on a variable with two named dimensions", { ref = bind_rows(lapply(1:4, function(j) { bind_rows(lapply(1:3, function(i) { data.frame( i = factor(i_labels[i]), j = factor(j_labels[j]), b = RankCorr_ij[, paste0("b[", i, ",", j, "]")], .chain = as.integer(1), .iteration = seq_len(nrow(RankCorr_ij)), .draw = seq_len(nrow(RankCorr_ij)) ) })) })) %>% group_by(i, j) expect_equal(spread_draws(RankCorr_ij, b[i, j]) %>% arrange(j, i), ref) }) test_that("spread_draws works on a variable with two named dimensions, one that is wide", { ref = bind_rows(lapply(1:4, function(j) { bind_rows(lapply(1:3, function(i) { data.frame( i = factor(i_labels[i]), .chain = as.integer(1), .iteration = seq_len(nrow(RankCorr_ij)), .draw = seq_len(nrow(RankCorr_ij)), j = factor(j_labels[j]), b = RankCorr_ij[, paste0("b[", i, ",", j, "]")] ) })) })) %>% spread(j, b) # grouping attributes are too finicky on this one for an exact comparison expect_equivalent(spread_draws(RankCorr_ij, b[i, j] | j) %>% arrange(i, .iteration), ref) }) test_that("spread_draws works on a variable with one named index and one wide anonymous index", { ref = bind_rows(lapply(1:4, function(j) { bind_rows(lapply(1:3, function(i) { data.frame( i = factor(i_labels[i]), .chain = as.integer(1), .iteration = seq_len(nrow(RankCorr_i)), .draw = seq_len(nrow(RankCorr_i)), j = factor(paste0("b.", j)), b = RankCorr_i[, paste0("b[", i, ",", j, "]")] ) })) })) %>% spread(j, b) # grouping attributes are too finicky on this one for an exact comparison expect_equivalent(spread_draws(RankCorr_i, b[i, ..]) %>% arrange(i, .iteration), ref) }) test_that("spread_draws does not allow extraction of two variables simultaneously with a wide index", { error_message = "Cannot extract draws from multiple variables in wide format." expect_error(spread_draws(RankCorr_s, c(tau, u_tau)[..]), error_message) expect_error(spread_draws(RankCorr_s, c(tau, u_tau)[i] | i), error_message) }) test_that("spread_draws correctly extracts multiple variables simultaneously", { expect_equal(spread_draws(RankCorr_i, c(tau, u_tau)[i]), spread_draws(RankCorr_i, tau[i]) %>% inner_join(spread_draws(RankCorr_i, u_tau[i]), by = c(".chain", ".iteration", ".draw", "i"), multiple = "all") %>% select(i, tau, u_tau, everything()) ) expect_equal(spread_draws(RankCorr_i, cbind(tau)[i]), spread_draws(RankCorr_i, c(tau)[i])) expect_equal(spread_draws(RankCorr_i, cbind(tau, u_tau)[i]), spread_draws(RankCorr_i, c(tau, u_tau)[i])) }) test_that("spread_draws correctly extracts multiple variables simultaneously when those variables have no dimensions", { RankCorr_t = RankCorr_s dimnames(RankCorr_t)[[2]][[1]] = "tr2" ref1 = spread_draws(RankCorr_t, typical_r) expect_equal(spread_draws(RankCorr_t, c(typical_r)), ref1) ref2 = spread_draws(RankCorr_t, tr2) %>% inner_join(spread_draws(RankCorr_t, typical_r), by = c(".chain", ".iteration", ".draw"), multiple = "all") expect_equal(spread_draws(RankCorr_t, c(tr2, typical_r)), ref2) }) test_that("spread_draws multispec syntax joins results correctly", { ref = spread_draws(RankCorr_s, typical_r) %>% inner_join(spread_draws(RankCorr_s, tau[i]), by = c(".chain", ".iteration", ".draw"), multiple = "all") %>% inner_join(spread_draws(RankCorr_s, b[i, v]), by = c(".chain", ".iteration", ".draw", "i"), multiple = "all") %>% group_by(i, v) expect_equal(spread_draws(RankCorr_s, typical_r, tau[i], b[i, v]), ref) }) test_that("spread_draws multispec with different dimensions retains grouping information with all dimensions", { groups_ = RankCorr_s %>% spread_draws(typical_r, tau[i], b[i, j]) %>% groups() %>% as.character() expect_equal(groups_, c("i", "j")) }) test_that("groups from spread_draws retain factor level names", { draws = RankCorr_i %>% spread_draws(tau[i]) expect_equivalent(attr(draws, "groups")$i, factor(i_labels)) }) test_that("empty dimensions are dropped", { ref = RankCorr_s %>% spread_draws(tau[i]) %>% ungroup() %>% select(-i) expect_equal(spread_draws(RankCorr_s, tau[]), ref) ref2 = RankCorr_s %>% spread_draws(b[i, j]) %>% group_by(j) %>% select(-i) expect_equal(spread_draws(RankCorr_s, b[, j]), ref2) ref3 = RankCorr_s %>% spread_draws(b[i, j]) %>% group_by(i) %>% select(-j) expect_equal(spread_draws(RankCorr_s, b[i, ]), ref3) ref4 = RankCorr_s %>% spread_draws(b[i, j]) %>% ungroup() %>% select(-i, -j) expect_equal(spread_draws(RankCorr_s, b[, ]), ref4) }) test_that("dimensions with existing names as strings are made wide as strings with `..`", { RankCorr_t = RankCorr_s dimnames(RankCorr_t)[[2]][1] = "x[a]" dimnames(RankCorr_t)[[2]][2] = "x[b]" ref = RankCorr_t %>% spread_draws(x[k]) %>% spread(k, x) %>% rename(x.a = a, x.b = b) expect_equal(spread_draws(RankCorr_t, x[..]), ref) }) test_that("regular expressions for variable names work on non-indexed variables", { ref = spread_draws(RankCorr_s, typical_r) expect_equal(spread_draws(RankCorr_s, `typical..`, regex = TRUE), ref) }) test_that("regular expressions for variable names work on indexed variables", { ref = spread_draws(RankCorr_s, c(tau, u_tau)[i]) expect_equal(spread_draws(RankCorr_s, `.*tau`[i], regex = TRUE), ref) }) test_that("variable names containing regex special chars work", { RankCorr_t = RankCorr_s dimnames(RankCorr_t)[[2]][[1]] = "(Intercept)" ref = RankCorr_t %>% tidy_draws() %>% select(.chain, .iteration, .draw, `(Intercept)`) expect_equal(spread_draws(RankCorr_t, `(Intercept)`), ref) }) test_that("draw_indices works", { df = data.frame( .chain = rep(1:4, each = 4), .iteration = rep(1:4, 4), .draw = 1:16, .warmup = rep(c(TRUE, TRUE, FALSE, FALSE), 4), `x[1]` = 2:17, `x[2]` = 3:18, check.names = FALSE ) ref = tibble( i = rep(1:2, each = 16), x = c(2:17, 3:18), .chain = rep(1:4, each = 4, times = 2), .iteration = rep(1:4, 8), .draw = rep(1:16, 2), .warmup = rep(c(TRUE, TRUE, FALSE, FALSE), 8) ) %>% group_by(i) result = spread_draws(df, x[i], draw_indices = c(".chain", ".iteration", ".draw", ".warmup")) expect_equivalent(result, ref) expect_equal(group_vars(result), group_vars(ref)) }) # tests for nested syntax ------------------------------------------------- test_that("nested matrices are correctly extracted", { test_draws = tibble( .chain = NA, .iteration = NA, .draw = 1, `x[1,1]` = 1, `x[1,3]` = 3, `x[1,2]` = 2, `x[1,4]` = 4, `x[1,5]` = 5, `x[1,6]` = 6, `x[1,7]` = 7, `x[1,8]` = 8, `x[1,9]` = 9, `x[1,10]` = 10, `x[2,1]` = 2, `x[2,4]` = 5, `x[2,2]` = 3, `x[2,3]` = 4, `x[2,5]` = 6, `x[2,6]` = 7, `x[2,7]` = 8, `x[2,8]` = 9, `x[2,9]` = 10, `x[2,10]` = 11, ) %>% rbind(. + 10) null_dimnames = function(v) { dimnames(v) = list(NULL, NULL) v } expect_equal(spread_draws_long_(test_draws, "x", c("i","."))[["x"]][[2]], 2:11) expect_equal(spread_draws_long_(test_draws, "x", c(".","."))[["x"]][[1]], null_dimnames(rbind(1:10, 2:11))) expect_equal(spread_draws_long_(test_draws, "x", c("1","2"))[["x"]][[1]], null_dimnames(rbind(1:10, 2:11))) expect_equal(spread_draws_long_(test_draws, "x", c("2","1"))[["x"]][[1]], null_dimnames(cbind(1:10, 2:11))) expect_equal(spread_draws_long_(test_draws, "x", c(".","."))[["x"]][[2]], null_dimnames(rbind(11:20, 12:21))) expect_equal(spread_draws_long_(test_draws, "x", c("2","1"))[["x"]][[2]], null_dimnames(cbind(11:20, 12:21))) }) test_that("nested 3d arrays are correctly extracted", { test_draws = tibble( .chain = NA, .iteration = NA, .draw = 1, `x[1,1,1]` = 111, `x[1,1,2]` = 112, `x[1,1,3]` = 113, `x[1,2,1]` = 121, `x[1,2,2]` = 122, `x[1,2,3]` = 123, `x[2,2,1]` = 221, `x[2,2,2]` = 222, `x[2,2,3]` = 223, `x[2,1,1]` = 211, `x[2,1,2]` = 212, `x[2,1,3]` = 213 ) %>% rbind(. + 10) #column-major order of the above 3d array ref = array(c(111, 211, 121, 221, 112, 212, 122, 222, 113, 213, 123, 223), dim = c(2,2,3)) dimnames(ref) = list(NULL, NULL, NULL) expect_equal(spread_draws_long_(test_draws, "x", c(".",".","."))[["x"]][[1]], ref) }) test_that("nested matrices with multiple variables are correctly extracted", { test_draws = tibble( .chain = NA, .iteration = NA, .draw = 1, `x[2,1]` = 5, `x[2,3]` = 7, `x[2,2]` = 6, `x[2,4]` = 8, `x[1,1]` = 1, `x[1,2]` = 2, `x[1,4]` = 4, `x[1,3]` = 3, `y[1,1]` = 11, `y[1,2]` = 12, `y[1,4]` = 14, `y[1,3]` = 13, `y[2,1]` = 15, `y[2,2]` = 16, `y[2,3]` = 17, `y[2,4]` = 18 ) %>% rbind(. + 10) ref = rbind(11:14, 15:18) dimnames(ref) = list(NULL, NULL) expect_equal(spread_draws_long_(test_draws, c("x", "y"), c(".","."))[["y"]][[1]], ref) }) test_that("nested matrices with named columns correctly extracted", { test_draws = tibble( .chain = NA, .iteration = NA, .draw = 1, `x[1,b]` = 11, `x[1,a]` = 12, `x[2,b]` = 21, `x[2,a]` = 22, ) %>% rbind(. + 10) expect_equal(spread_draws_long_(test_draws, "x", c(".","."))[["x"]][[1]], array(c(11,21,12,22), dim = c(2, 2), dimnames = list(NULL, c("b", "a")))) }) test_that("nested ragged arrays are correctly extracted", { test_draws = tibble( .chain = NA, .iteration = NA, .draw = 1, `x[d,a]` = 11, `x[d,b]` = 12, `x[c,1]` = 23, `x[c,b]` = 22, ) %>% rbind(. + 10) expect_equal(spread_draws_long_(test_draws, "x", c(".","."))[["x"]][[1]], array(c(11,NA,12,22,NA,23), dim = c(2, 3), dimnames = list(c("d", "c"), c("a", "b", "1")))) }) test_that("nested vectors are correctly extracted", { test_draws = tibble( .chain = NA, .iteration = NA, .draw = 1, `x[2]` = 2, `x[1]` = 1, `x[3]` = 3, ) expect_equal(spread_draws_long_(test_draws, "x", ".")[["x"]][[1]], c(1,2,3)) test_draws[["x[d]"]] = 4 expect_equal(spread_draws_long_(test_draws, "x", ".")[["x"]][[1]], c(`2` = 2, `1` = 1, `3` = 3, d = 4)) }) test_that("nested arrays with string names are correctly extracted", { test_draws = tibble( .chain = NA, .iteration = NA, .draw = 1, `x[a]` = 1, `x[c]` = 3, `x[b]` = 2, ) expect_equal(spread_draws_long_(test_draws, "x", ".")[["x"]][[1]], c(a = 1, c = 3, b = 2)) }) test_that("nested arrays with numeric indices that aren't 1:N are extracted properly", { test_draws = tibble( .chain = NA, .iteration = NA, .draw = 1, `x[2]` = 2, `x[5]` = 5, `x[3]` = 3, ) expect_equal(spread_draws_long_(test_draws, "x", ".")[["x"]][[1]], c(NA, 2, 3, NA, 5)) }) test_that("nested arrays with numeric indices that aren't 1:N are extracted properly", { test_draws = tibble( .chain = NA, .iteration = NA, .draw = 1, `x[1]` = 4, `x[0]` = 2, # will be treated as string, so non-numerical order is kept `x[2]` = 3, ) expect_equal(spread_draws_long_(test_draws, "x", ".")[["x"]][[1]], c(`1` = 4, `0` = 2, `2` = 3)) }) # all_elements_identical ------------------------------------------------------------------ test_that("all_elements_identical base case: on empty list is TRUE", { expect_equal(all_elements_identical(list()), TRUE) }) # abind0 ------------------------------------------------------------------ test_that("abind0 base cases", { expect_equal(abind0(c(1,2,3)), c(1,2,3)) expect_equal(abind0(list(c(1,2,3))), c(1,2,3)) })