Skip to content

Commit 8aafec6

Browse files
authored
Fix guard ordering and over-broad grep in native_anywhere_in_australia() (#288)
Closes #280. ## 1. `is.null(resources)` guard ran too late The guard sat *below* the call to `create_species_state_origin_matrix()`. Because that function has its own guard, an offline call didn't error outright — it did a pointless call and then printed *"Not finding taxonomic resources; check internet connection?"* twice, once from each function giving up. Moving the guard to the top of the function makes it one message and no wasted work. ## 2. `apply(..., grepl("native", x))` scanned every column `apply(full_lookup, 1, ...)` coerces the whole frame to a character matrix, so the grep ran over `family`, `species` and `taxon_ID` as well as the state columns. Any taxon whose name contained the substring "native" would have been classified native regardless of its actual distribution. That is not hypothetical: APC 2024-10-11 has three taxa with a `nativitatis` epithet — *Flickingeria nativitatis*, *Pittosporum nativitatis* and *Ischaemum nativitatis*, all Christmas Island endemics. All three happen to be genuinely native, so **no results change**, but the check shouldn't depend on that coincidence. The logic now lives in a small internal `is_native_anywhere()` that selects the state columns explicitly and tests them column-wise. Column-wise `grepl` also avoids the frame-to-matrix coercion, which matters over the 30k+ rows of the infrataxa matrix. ## Verification - Confirmed old and new native flags are `identical()` over the full 2024-10-11 state-origin matrix, so `benchmarks/native_check.csv` is untouched. - New test `is_native_anywhere() reads only the state columns` (in `test-state_diversity.R`) uses a synthetic lookup with a `nativitatis` name marked `naturalised`, which the old code got wrong. Needs no resources. - New test in `test-connection.R` pins the guard: `resources = NULL` returns `NULL` with exactly one message. - `test-state_diversity.R` and `test-connection.R` pass: 36 passing, 0 failures. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent bb51629 commit 8aafec6

4 files changed

Lines changed: 51 additions & 11 deletions

File tree

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# APCalign 2.0.0
22

3+
- `native_anywhere_in_australia()` now checks for missing taxonomic resources before building the state-origin matrix, so an offline call reports the problem once instead of once per function that gives up. Its native/introduced test also now reads only the state columns, so a taxon whose name contains "native" (e.g. the `nativitatis` epithets) can no longer be misclassified.
34
- New function `synonyms_for_accepted_names()` to list synonyms for currently accepted taxon names.
45
- `load_taxonomic_resources()` now caches results in memory for the duration of the R session, so repeated calls with the same version return immediately without re-downloading or re-processing data.
56
- New function `clear_cached_resources()` to remove the session cache and force a reload.

R/native_anywhere_in_australia.R

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,23 @@
2929
#' \donttest{native_anywhere_in_australia(c("Eucalyptus globulus","Pinus radiata","Banksis notaspecies"))}
3030

3131
native_anywhere_in_australia <- function(species, resources = load_taxonomic_resources()) {
32-
33-
# Create lookup tables
34-
full_lookup <- create_species_state_origin_matrix(resources = resources, include_infrataxa = TRUE)
35-
32+
3633
if(is.null(resources)){
3734
message("Not finding taxonomic resources; check internet connection?")
3835
return(NULL)
3936
}
40-
37+
38+
# Create lookup tables
39+
full_lookup <- create_species_state_origin_matrix(resources = resources, include_infrataxa = TRUE)
40+
4141
if (any(!species %in% full_lookup$species)) {
4242
warning("At least one input not found in APC; consider using `create_taxonomic_update_lookup` first and ensure you've correctly specified the `include_infrataxa` parameter.")
4343
}
44-
44+
4545
# Filter for native species
46-
full_lookup$native_anywhere <-
47-
apply(full_lookup, 1, function(x)
48-
any(grepl("native", x)))
49-
native_only<-dplyr::filter(full_lookup, native_anywhere)
50-
46+
full_lookup$native_anywhere <- is_native_anywhere(full_lookup)
47+
native_only <- dplyr::filter(full_lookup, native_anywhere)
48+
5149
# Check membership
5250
natives <- species %in% native_only$species
5351
fulllist <- species %in% full_lookup$species
@@ -65,3 +63,18 @@ native_anywhere_in_australia <- function(species, resources = load_taxonomic_res
6563
return(result)
6664
}
6765

66+
#' For each row of a species-by-state origin matrix, is the taxon native in at
67+
#' least one state or territory?
68+
#'
69+
#' Only the state/territory columns hold an origin status; the identifying
70+
#' columns (`family`, `species`, `taxon_ID`) must be excluded, or a taxon whose
71+
#' name happens to contain "native" would be read as a native record.
72+
#'
73+
#' @noRd
74+
is_native_anywhere <- function(state_origin_matrix) {
75+
states <- dplyr::select(state_origin_matrix, -dplyr::any_of(c("family", "species", "taxon_ID")))
76+
77+
Reduce(`|`, lapply(states, grepl, pattern = "native"),
78+
init = rep(FALSE, nrow(state_origin_matrix)))
79+
}
80+

tests/testthat/test-connection.R

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ test_that("get_versions() messages and returns NULL when offline", {
8080
})
8181
})
8282

83+
test_that("native_anywhere_in_australia() bails out before doing any work", {
84+
# The missing-resources guard runs first, so the user gets one message rather
85+
# than one per downstream function that also has to give up.
86+
msgs <- capture_messages(
87+
result <- native_anywhere_in_australia("Eucalyptus globulus", resources = NULL)
88+
)
89+
90+
expect_null(result)
91+
expect_length(msgs, 1)
92+
})
93+
8394
test_that("functions return visibly when online", {
8495
skip_on_ci()
8596
skip_on_cran()

tests/testthat/test-state_diversity.R

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,21 @@ test_that("native_anywhere_in_australia() works", {
6565
})
6666

6767

68+
test_that("is_native_anywhere() reads only the state columns", {
69+
# `nativitatis` is a real APC epithet (three Christmas Island taxa), so the
70+
# taxon name must never be allowed to decide native status on its own.
71+
lookup <- dplyr::tibble(
72+
family = c("Orchidaceae", "Poaceae", "Pittosporaceae"),
73+
species = c("Flickingeria nativitatis", "Ischaemum nativitatis", "Pittosporum undulatum"),
74+
taxon_ID = c("id-1", "id-2", "id-3"),
75+
ChI = c("native", "naturalised", "not present"),
76+
NSW = c("not present", "not present", "native and naturalised")
77+
)
78+
79+
expect_equal(is_native_anywhere(lookup), c(TRUE, FALSE, TRUE))
80+
})
81+
82+
6883
test_that("get_apc_genus_family_lookup() works", {
6984
expect_warning(family_check <-
7085
get_apc_genus_family_lookup(

0 commit comments

Comments
 (0)