Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

## Bugfixes
* `[` on a `worldArray` assumed the array had exactly two layers, so extracting patch values from a `worldArray` with any other number of layers failed with "dims [product 2] do not match the length of object". It now uses the actual number of layers.
* stacking a `worldMatrix` of character values alongside a numeric one no longer corrupts both layers (#49). An `array` holds a single type, so the character values used to turn every layer into character, and were then coerced back to `NA` on the way out. `worldArray` now stores character layers as integer codes with their categories in a new `levels` slot, exactly as `agentMatrix` already does for character columns. `of()` reports the character values, `plot()` draws the layer as a categorical raster instead of warning "NAs introduced by coercion", `NLwith()` and `NLset()` accept and return the characters, and `[[` and `$` give back a character `worldMatrix`. Numeric layers stacked alongside a character one keep their own type.
* stacking a `worldMatrix` of character values alongside a numeric one no longer corrupts both layers (#49). An `array` holds a single type, so the character values used to turn every layer into character, and were then coerced back to `NA` on the way out. `worldArray` now stores character layers as integer codes with their categories in a new `levels` slot, exactly as `agentMatrix` already does for character columns. `of()` reports the character values, `plot()` draws the layer as a categorical raster instead of warning "NAs introduced by coercion", `NLwith()` and `NLset()` accept and return the characters, and `[[` and `$` give back a character `worldMatrix`. Numeric layers stacked alongside a character one keep their own type. Assigning character values to a numeric layer with `NLset()` turns that whole layer into a character one, as assigning a string into a numeric vector does in base R, keeping the values of the `patches` that were not assigned.
* `inRadius()` with `torus = TRUE` no longer reports patches that were not among the `agents2` supplied;
* `of()` on an `agentMatrix` now returns columns in the order given by `var` when a mix of factor and numeric variables is requested.

Expand Down
19 changes: 12 additions & 7 deletions R/helpers.R
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,19 @@ sampleWithin <- function(group) {
return(list(world = world, val = asNum))
}

if (!allPatches) {
stop(
"cannot assign character values to only some patches of the numeric ",
"worldArray layer '", var, "'.\n",
"Assign the whole layer instead, e.g. world[['", var, "']] <- aCharacterWorld."
)
## A layer holds one type, so giving a numeric layer genuine character
## values turns the whole layer into a character one, the way assigning a
## string into a numeric vector does in base R. The numbers already in the
## patches that are *not* being assigned become categories in their own
## right, so that they survive rather than being reread as codes.
if (allPatches) {
lvls <- character(0)
} else {
existing <- world@.Data[, , var]
lvls <- as.character(sort(unique(existing[!is.na(existing)])))
world@.Data[, , var] <- match(as.character(existing), lvls)
world@levels[[var]] <- lvls
}
lvls <- character(0)
}

newLvls <- union(lvls, setdiff(unique(as.character(val)), NA_character_))
Expand Down
7 changes: 7 additions & 0 deletions R/worldNLR-classes-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,13 @@ setMethod(
#' character layer reports the characters, and `[[` and `$` return a character
#' `worldMatrix`. The codes are visible only in the `.Data` slot itself.
#'
#' Because a layer holds a single type, giving a numeric layer character values
#' with [NLset()] turns that whole layer into a character one, just as assigning
#' a string into a numeric vector does in base R. The values in the `patches`
#' that were not assigned are kept, as characters. Values that merely arrive as
#' strings but are numbers, as they do from `cbind(a = 1, b = "x")`, are left
#' numeric; pass `val` as a `data.frame` to keep column types distinct.
#'
#' @aliases worldArray
#' @name worldArray-class
#' @rdname worldArray-class
Expand Down
7 changes: 7 additions & 0 deletions man/worldArray-class.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 16 additions & 5 deletions tests/testthat/test-NetLogoR-classes.R
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,22 @@ test_that("NLwith and NLset work on a character worldArray layer", {
w4 <- NLset(world = w, agents = cbind(pxcor = 0, pycor = 0), var = "num", val = -99)
expect_identical(of(world = w4, agents = cbind(pxcor = 0, pycor = 0), var = "num"), -99)

## turning only some patches of a numeric layer into characters is rejected
expect_error(
NLset(world = w, agents = cbind(pxcor = 0, pycor = 0), var = "num", val = "oops"),
"only some patches"
)
## giving a numeric layer character values turns the whole layer into a
## character one, as assigning a string into a numeric vector does in base R
w5 <- NLset(world = w, agents = cbind(pxcor = 0, pycor = 0), var = "num", val = "oops")
expect_identical(of(world = w5, agents = cbind(pxcor = 0, pycor = 0), var = "num"), "oops")
## the patches that were not assigned keep their values, as characters
expect_identical(of(world = w5, agents = cbind(pxcor = 1, pycor = 1), var = "num"), "17")
expect_false(is.character(w5@.Data))
## every original value survived, none were reread as codes
expect_setequal(w5@levels$num, c(as.character(1:25), "oops"))
## and the other layer is untouched
expect_identical(of(world = w5, agents = cbind(pxcor = 1, pycor = 1), var = "hab"), "land")

## numbers that merely arrive as strings are still numbers
w6 <- NLset(world = w, agents = cbind(pxcor = 0, pycor = 0), var = "num", val = "42")
expect_identical(of(world = w6, agents = cbind(pxcor = 0, pycor = 0), var = "num"), 42)
expect_null(w6@levels$num)
})

test_that("[[<- re-encodes a replaced worldArray layer", {
Expand Down
Loading