From af3983013dfebf36ff5383477db8a455519a6834 Mon Sep 17 00:00:00 2001 From: northfox Date: Tue, 22 Sep 2026 08:11:05 +0900 Subject: [PATCH 1/3] fix: count distinct scientific names per group in summarize_observations() `n_scientificName` was calculated as the number of distinct scientific names per deployment, summed over all deployments of a group. As distinct counts are not additive, a scientific name observed in more than one deployment was counted more than once when `group_by` did not contain `deploymentID`, e.g. `n_scientificName = 2` when grouping by `scientificName`, which contradicts the documentation. The scientific names are now retained per deployment by grouping by `scientificName` as well, and counted over all deployments of a group afterwards. Refs #432 --- NEWS.md | 2 + R/summarize_observations.R | 24 +++++++-- tests/testthat/test-summarize_observations.R | 52 ++++++++++++++++++++ 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index e12311587..0a459befd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # camtraptor (development version) +- `summarize_observations()` counts a scientific name once per group in `n_scientificName`, also when it is observed in more than one deployment of that group. Before, it was counted once per deployment when `group_by` did not contain `deploymentID` (#432). + # camtraptor 1.0.0 This major release updates the internal data model of camtraptor to Camtrap DP 1.0, drops support for Camtrap DP 0.1.6 and facilitates a step-by-step exploration workflow with new functions. diff --git a/R/summarize_observations.R b/R/summarize_observations.R index 86f245b3e..be98ca457 100644 --- a/R/summarize_observations.R +++ b/R/summarize_observations.R @@ -110,18 +110,36 @@ summarize_observations <- function( formula_n_species <- rlang::expr( "n_scientificName" := sum(.data$n_scientificName, na.rm = TRUE) ) - # Calculate n_scientificName + # Calculate n_scientificName. The number of distinct scientific names is not + # additive across deployments: a scientific name observed in more than one + # deployment would be counted more than once. So, retain the scientific names + # by grouping by `scientificName` as well and count the distinct scientific + # names over all deployments afterwards. n_species_df <- calc_obs_feature( deployment_ids = deployment_ids, deployments = deployments, observations = observations, group_by_deployments = group_by_deployments, - group_by_observations = group_by_observations, + group_by_observations = union(group_by_observations, "scientificName"), group_time_by = group_time_by, col_obs_for_feature = "scientificName", formula_per_deployment = formula_n_species_per_dep, formula_total = formula_n_species - ) + ) %>% + dplyr::group_by( + dplyr::across(dplyr::all_of(c( + group_by_deployments, + group_by_observations, + group_time_by + ))) + ) %>% + dplyr::summarise( + "n_scientificName" := dplyr::n_distinct( + .data$scientificName, + na.rm = TRUE + ), + .groups = "keep" + ) # Define the formula for the number of events per deployment formula_n_events_per_dep <- rlang::expr( "n_events" := dplyr::n_distinct(.data$eventID, na.rm = TRUE) diff --git a/tests/testthat/test-summarize_observations.R b/tests/testthat/test-summarize_observations.R index e8598610a..57cf01613 100644 --- a/tests/testthat/test-summarize_observations.R +++ b/tests/testthat/test-summarize_observations.R @@ -217,6 +217,58 @@ test_that("summarize_observations() returns correct summary for grouping by rai_sum_count_df$rai_count) }) +test_that( + paste0("summarize_observations() returns n_scientificName equal to 1 or 0 ", + "when grouping by scientificName" + ), { + skip_if_offline() + x <- example_dataset() + summary <- summarize_observations(x, group_by = "scientificName") + # A scientific name observed in more than one deployment is counted once + expect_identical( + summary$n_scientificName, + dplyr::if_else(is.na(summary$scientificName), 0L, 1L) + ) + }) + +test_that( + paste0("summarize_observations() counts distinct scientific names over ", + "all deployments of a group" + ), { + skip_if_offline() + x <- example_dataset() + # Group all deployments in one location + deployments(x) <- deployments(x) %>% + dplyr::mutate(locationName = "location") + summary <- summarize_observations(x, group_by = "locationName") + + # Scientific names observed in more than one deployment are counted once + n_species <- x %>% + filter_observations(.data$observationLevel == "event") %>% + observations() %>% + dplyr::pull("scientificName") %>% + dplyr::n_distinct(na.rm = TRUE) + expect_identical(summary$n_scientificName, n_species) + + # Same with time grouping + summary_year <- summarize_observations( + x, group_by = "locationName", group_time_by = "year" + ) + n_species_year <- x %>% + filter_observations(.data$observationLevel == "event") %>% + observations() %>% + dplyr::group_by( + year = lubridate::floor_date(.data$eventStart, "year") + ) %>% + dplyr::summarise( + n_scientificName = dplyr::n_distinct(.data$scientificName, na.rm = TRUE) + ) + expect_identical( + summary_year$n_scientificName, + n_species_year$n_scientificName + ) + }) + testthat::test_that( "Deployments without observations are not included in the summary", { skip_if_offline() From d8a846c008814820bf07690cbce1d89d61c72229 Mon Sep 17 00:00:00 2001 From: northfox Date: Tue, 22 Sep 2026 09:21:44 +0900 Subject: [PATCH 2/3] chore: add northfox as contributor Refs #432 --- DESCRIPTION | 1 + man/camtraptor-package.Rd | 1 + 2 files changed, 2 insertions(+) diff --git a/DESCRIPTION b/DESCRIPTION index 0a3a84a73..ff360b35a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,6 +19,7 @@ Authors@R: c( comment = c(ORCID = "0000-0001-6788-5876", affiliation = "Research Institute for Nature and Forest (INBO)")), person("Nele", "Mullens", role = "ctb", comment = c(ORCID = "0000-0002-3054-9665", affiliation = "Regionaal Landschap Dijleland vzw")), + person("northfox", , , "vulpes.engineer@gmail.com", role = "ctb"), person("Raphaël", "Nussbaumer", , "rafnuss@gmail.com", role = "ctb", comment = c(ORCID = "0000-0002-8185-1020", affilication = "Swiss Ornithological Institute")), person("Thierry", "Onkelinx", , "thierry.onkelinx@inbo.be", role = "ctb", diff --git a/man/camtraptor-package.Rd b/man/camtraptor-package.Rd index ee8715557..1eeabba39 100644 --- a/man/camtraptor-package.Rd +++ b/man/camtraptor-package.Rd @@ -36,6 +36,7 @@ Other contributors: \item Martijn Bollen \email{martijn.bollen@inbo.be} (\href{https://orcid.org/0000-0002-1621-6258}{ORCID}) (affiliation: Research Institute for Nature and Forest (INBO)) [contributor] \item Jim Casaer \email{jim.casaer@inbo.be} (\href{https://orcid.org/0000-0001-6788-5876}{ORCID}) (affiliation: Research Institute for Nature and Forest (INBO)) [contributor] \item Nele Mullens (\href{https://orcid.org/0000-0002-3054-9665}{ORCID}) (affiliation: Regionaal Landschap Dijleland vzw) [contributor] + \item northfox \email{vulpes.engineer@gmail.com} [contributor] \item Raphaël Nussbaumer \email{rafnuss@gmail.com} (\href{https://orcid.org/0000-0002-8185-1020}{ORCID}) (affilication: Swiss Ornithological Institute) [contributor] \item Thierry Onkelinx \email{thierry.onkelinx@inbo.be} (\href{https://orcid.org/0000-0001-8804-4216}{ORCID}) (affiliation: Research Institute for Nature and Forest (INBO)) [contributor] \item Lynn Pallemaerts \email{lynn.pallemaerts@inbo.be} (\href{https://orcid.org/0000-0002-5034-5416}{ORCID}) (affiliation: Research Institute for Nature and Forest (INBO)) [contributor] From dc78cc2c4bcff4f380a54895888548a0175a38b9 Mon Sep 17 00:00:00 2001 From: Damiano Oldoni Date: Fri, 25 Sep 2026 10:24:27 +0200 Subject: [PATCH 3/3] Set version 1.0.1 and improve changelog Mention the new contributor as well. --- DESCRIPTION | 2 +- NEWS.md | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index ff360b35a..77b1d3c2f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: camtraptor Title: Explore and Visualize Camera Trap Data Packages -Version: 1.0.0.9000 +Version: 1.0.1 Date: 2026-09-09 Authors@R: c( person("Damiano", "Oldoni", , "damiano.oldoni@inbo.be", role = c("aut", "cre"), diff --git a/NEWS.md b/NEWS.md index 0a459befd..2f6b6237a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,12 @@ -# camtraptor (development version) +# camtraptor 1.0.1 -- `summarize_observations()` counts a scientific name once per group in `n_scientificName`, also when it is observed in more than one deployment of that group. Before, it was counted once per deployment when `group_by` did not contain `deploymentID` (#432). +## Bug fixes + +- Fixed an issue where `summarize_observations()` counted a scientific name once per deployment when `group_by` did not contain `deploymentID` (#432). + +## Other changes + +- Add [northfox](https://github.com/northfox) as contributor. Welcome northfox! # camtraptor 1.0.0