Skip to content

Commit

Permalink
Merge pull request #272 from stan-dev/factor_not_levels
Browse files Browse the repository at this point in the history
Do not automatically create an rvar_factor when rvar() is passed a numeric array with levels
  • Loading branch information
paul-buerkner authored Feb 3, 2023
2 parents 1cec318 + 1b7570c commit 3ce46ad
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 23 deletions.
15 changes: 2 additions & 13 deletions R/rvar-.R
Original file line number Diff line number Diff line change
Expand Up @@ -806,22 +806,11 @@ cleanup_rvar_draws <- function(x) {
rownames(x) <- as.character(seq_rows(x))
}

# if x is factor-like (character or with "levels" attr), make it a factor
x <- as_factor_if_factor_like(x)

x
}

#' convert factor-like objects (character vectors or numerics with "levels"
#' attributes) to factors. Leaves non-factor-like objects alone.
#' @param x a vector or array (including factor-like arrays with "levels" attributes)
#' @noRd
as_factor_if_factor_like <- function(x) {
# if x is a character array, make it a factor
if (is.character(x)) {
x <- while_preserving_dims(factor, x)
} else if (!is.factor(x) && !is.null(attr(x, "levels"))) {
x <- while_preserving_dims(factor, x, labels = attr(x, "levels"))
}

x
}

Expand Down
12 changes: 9 additions & 3 deletions R/rvar-factor.R
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,25 @@
#' # Unlike base factors, rvar factors can be matrices or arrays:
#' rvar_factor(x_array, dim = c(2, 2))
#'
#' # If the input to rvar() is an array with a `"levels"` attribute, it
#' # will automatically be treated as an `rvar_factor()`:
#' # If the input to rvar_factor() is an array with a `"levels"` attribute, it
#' # will use those as the levels of the factor
#' y_array <- t(array(rbinom(3000, 1, c(0.1, 0.5, 0.9)) + 1, dim = c(3, 1000)))
#' rvar(y_array)
#' # with levels
#' attr(y_array, "levels") = c("a", "b")
#' rvar(y_array)
#' rvar_factor(y_array)
#'
#' @export
rvar_factor <- function(
x = factor(), dim = NULL, dimnames = NULL, nchains = NULL, with_chains = FALSE, ...
) {

# to ensure we pick up levels already attached to x (if there are any), we
# need to convert x to a factor here if it has levels
if (!is.factor(x) && !is.null(attr(x, "levels"))) {
x <- while_preserving_dims(factor, x, labels = attr(x, "levels"))
}

out <- rvar(
x, dim = dim, dimnames = dimnames, nchains = nchains, with_chains = with_chains
)
Expand Down
4 changes: 2 additions & 2 deletions tests/testthat/test-rvar-factor.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ test_that("0-length rvar factors are constructured correctly", {
expect_equal(draws_of(rvar(ordered(NULL))), ref)
})

test_that("rvar() input with a levels attribute creates an rvar_factor", {
test_that("rvar_factor() input with a levels attribute preserves levels", {
x_array <- matrix(1:10, nrow = 2)
attr(x_array, "levels") <- letters[1:10]

ref_draws <- factor(letters[1:10])
dim(ref_draws) <- c(2, 5)
dimnames(ref_draws) <- list(1:2, NULL)

x <- rvar(x_array)
x <- rvar_factor(x_array)
expect_equal(draws_of(x), ref_draws)
expect_true(inherits(x, "rvar_factor"))
})
Expand Down
10 changes: 5 additions & 5 deletions vignettes/rvar.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,17 @@ to constructing and manipulating `rvar`s are described below.

You can also use `rvar`s to represent discrete distributions, using the `rvar_factor()`
and `rvar_ordered()` subtypes. If you attempt to create an `rvar` using character
values, it will automatically be treated as an `rvar_factor`:
values or a `factor`, it will automatically be treated as an `rvar_factor`:

```{r rvar_factor}
x <- rvar(sample(c("a","b","c"), 4000, prob = c(0.7, 0.2, 0.1), replace = TRUE))
x
```

Objects with a `levels` attribute will also automatically be converted to an
`rvar_factor()`. This (along with automatic conversion of character values)
means output from `rstanarm::posterior_predict()` and `brms::posterior_predict()`
will be converted into `rvar_factor`s if wrapped in `rvar()`.
Numeric arrays with a `"levels"` attribute can also be passed to `rvar_factor()`.
This (along with conversion of character values) means output from
`rstanarm::posterior_predict()` and `brms::posterior_predict()` on categorical
models can be passed directly to `rvar_factor()`.

The default display shows the mode (as returned by `modal_category()`) and
normalized entropy (`entropy()`), which is Shannon entropy scaled by the maximum
Expand Down

0 comments on commit 3ce46ad

Please sign in to comment.