From 11e0d9d5f437c4e72467bc2faece8324cc23a44a Mon Sep 17 00:00:00 2001 From: wlangera Date: Mon, 29 Jul 2024 16:44:03 +0200 Subject: [PATCH 01/57] update package version --- .zenodo.json | 2 +- CITATION.cff | 2 +- DESCRIPTION | 2 +- inst/CITATION | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index 396fba9..0af4a5e 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,6 +1,6 @@ { "title": "gcube: Simulating Biodiversity Data Cubes", - "version": "0.3.0", + "version": "0.4.0", "license": "MIT", "upload_type": "software", "description": "

Simulation framework for biodiversity data cubes.<\/p>", diff --git a/CITATION.cff b/CITATION.cff index b99e8f4..f59ae65 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -25,4 +25,4 @@ abstract: "Simulation framework for biodiversity data cubes." identifiers: - type: url value: https://b-cubed-eu.github.io/gcube/ -version: 0.3.0 +version: 0.4.0 diff --git a/DESCRIPTION b/DESCRIPTION index 00b02c6..64343e1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: gcube Title: Simulating Biodiversity Data Cubes -Version: 0.3.0 +Version: 0.4.0 Authors@R: c( person("Ward", "Langeraert", , "ward.langeraert@inbo.be", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-5900-8109", affiliation = "Research Institute for Nature and Forest (INBO)")), diff --git a/inst/CITATION b/inst/CITATION index ef0a8fe..cfdcf06 100644 --- a/inst/CITATION +++ b/inst/CITATION @@ -2,12 +2,12 @@ citHeader("To cite `gcube` in publications please use:") # begin checklist entry bibentry( bibtype = "Manual", - title = "gcube: Simulating Biodiversity Data Cubes. Version 0.3.0", + title = "gcube: Simulating Biodiversity Data Cubes. Version 0.4.0", author = c( author = c(person(given = "Ward", family = "Langeraert"))), year = 2024, url = "https://b-cubed-eu.github.io/gcube/", abstract = "Simulation framework for biodiversity data cubes.", - textVersion = "Langeraert, Ward (2024) gcube: Simulating Biodiversity Data Cubes. Version 0.3.0. https://b-cubed-eu.github.io/gcube/", + textVersion = "Langeraert, Ward (2024) gcube: Simulating Biodiversity Data Cubes. Version 0.4.0. https://b-cubed-eu.github.io/gcube/", keywords = "simulation; data cubes; B-Cubed; biodiversity; Monte-Carlo", ) # end checklist entry From 89ec548866f5843b3fef68cc1ce744424a04d57f Mon Sep 17 00:00:00 2001 From: wlangera Date: Mon, 29 Jul 2024 16:44:17 +0200 Subject: [PATCH 02/57] update news new version --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index 3d21923..ad9b092 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# gcube 0.4.0 + +* Consolidate documentation throughout all functions, readme, and vignettes. + # gcube 0.3.0 * `generate_taxonomy()` also creates species key. From 704ef4fcd7efe976743b04c0bb3ae5a746696cd6 Mon Sep 17 00:00:00 2001 From: wlangera Date: Mon, 29 Jul 2024 17:29:19 +0200 Subject: [PATCH 03/57] simulate occurrences argument descriptions --- R/create_spatial_pattern.R | 33 +++++++++++---------- R/generate_taxonomy.R | 4 +-- R/grid_designation.R | 4 +-- R/sample_from_binormal_circle.R | 4 +-- R/sample_from_uniform_circle.R | 4 +-- R/sample_observations.R | 4 +-- R/sample_occurrences_from_raster.R | 10 +++---- R/simulate_occurrences.R | 46 ++++++++++++++---------------- R/simulate_random_walk.R | 15 +++++----- R/simulate_timeseries.R | 37 +++++++++++------------- 10 files changed, 76 insertions(+), 85 deletions(-) diff --git a/R/create_spatial_pattern.R b/R/create_spatial_pattern.R index 6720cef..330c991 100644 --- a/R/create_spatial_pattern.R +++ b/R/create_spatial_pattern.R @@ -1,27 +1,26 @@ #' Create spatial pattern within a polygon #' -#' It creates a raster with a spatial pattern for the area of a polygon. +#' This function creates a raster with a spatial pattern for the area of a +#' polygon. #' #' @param polygon An sf object with POLYGON geometry. -#' @param resolution A numeric value defining the resolution of the raster cell -#' @param spatial_pattern Define the spatial pattern. It could be a character -#' string `"random"` or `"clustered"`, in which `"random"` is the default. -#' The user is able to provide a numeric value >= 1 (1 is "random" and -#' 10 is "clustered"). A larger number means a broader size of the clusters -#' area. See details. -#' @param seed The seed for random number generation to make results -#' reproducible. If `NA` (the default), no seed is used. +#' @param resolution A numeric value defining the resolution of the raster cell. +#' @param spatial_pattern Specifies the desired spatial pattern. It can +#' be a character string (`"random"` or `"clustered"`) or a numeric value ≥ 1 +#' (1 means random distribution, larger values indicate more clustering). +#' The default is `"random"`. `"clustered"` corresponds to a value of 10. +#' See details. +#' @param seed A positive numeric value setting the seed for random number +#' generation to ensure reproducibility. If `NA` (default), no seed is used. #' @param n_sim Number of simulations. Each simulation is a different layer in -#' the raster. Default 1. +#' the raster. Default is 1. #' -#' @details -#' the \code{spatial_pattern} argument change the range parameter of the -#' spherical variogram model. \code{spatial_pattern = 1} means the range has -#' the same size of the grid cell, which is defined in \code{resolution} -#' argument. We use the function [gstat::vgm()] to implement the -#' spherical variogram model +#' @details The `spatial_pattern` argument changes the range parameter of the +#' spherical variogram model. `spatial_pattern = 1` means the range has the same +#' size as the grid cell, which is defined in the `resolution` argument. The +#' function [gstat::vgm()] is used to implement the spherical variogram model. #' -#' @seealso [gstat::vgm()] and its \code{range} argument +#' @seealso [gstat::vgm()] and its `range` argument #' #' @return An object of class SpatRaster with a spatial pattern for the area of #' the given polygon. diff --git a/R/generate_taxonomy.R b/R/generate_taxonomy.R index 5b5053d..1355293 100644 --- a/R/generate_taxonomy.R +++ b/R/generate_taxonomy.R @@ -20,8 +20,8 @@ #' @param num_classes Number of classes to generate. Defaults to 1. #' @param num_phyla Number of phyla to generate. Defaults to 1. #' @param num_kingdoms Number of kingdoms to generate. Defaults to 1. -#' @param seed The seed for random number generation to make results -#' reproducible. If `NA` (the default), no seed is used. +#' @param seed A positive numeric value setting the seed for random number +#' generation to ensure reproducibility. If `NA` (default), no seed is used. #' #' @return A data frame with the taxonomic classification of each species. If #' `num_species` is a dataframe, the taxonomic classification is added to this diff --git a/R/grid_designation.R b/R/grid_designation.R index e3ba0ee..8e78271 100644 --- a/R/grid_designation.R +++ b/R/grid_designation.R @@ -12,8 +12,8 @@ #' @param id_col The column name of the column with unique ids for each grid #' cell. If `"row_names"` (the default), a new column `id` is created were the #' row names represent the unique ids. -#' @param seed The seed for random number generation to make results -#' reproducible. If `NA` (the default), no seed is used. +#' @param seed A positive numeric value setting the seed for random number +#' generation to ensure reproducibility. If `NA` (default), no seed is used. #' @param aggregate Logical. If `TRUE` (default), return data cube in #' aggregated form (grid with number of observations per grid cell). Otherwise #' return sampled points in uncertainty circle. diff --git a/R/sample_from_binormal_circle.R b/R/sample_from_binormal_circle.R index 6b0da97..ca06699 100644 --- a/R/sample_from_binormal_circle.R +++ b/R/sample_from_binormal_circle.R @@ -14,8 +14,8 @@ #' @param p_norm A numeric value between 0 and 1. The proportion of all #' possible samples from a a bivariate Normal distribution that fall within the #' uncertainty circle. If no value is given, the default `p_norm` value is 0.95. -#' @param seed A positive numeric value. The seed for random number generation -#' to make results reproducible. If `NA` (the default), no seed is used. +#' @param seed A positive numeric value setting the seed for random number +#' generation to ensure reproducibility. If `NA` (default), no seed is used. #' #' @returns An sf object with POINT geometry containing the locations of the #' sampled occurrences and a `coordinateUncertaintyInMeters` column containing diff --git a/R/sample_from_uniform_circle.R b/R/sample_from_uniform_circle.R index c9b0622..1cb0f3c 100644 --- a/R/sample_from_uniform_circle.R +++ b/R/sample_from_uniform_circle.R @@ -7,8 +7,8 @@ #' `coordinateUncertaintyInMeters` column. If this last column is not present, #' the function will assume no (zero meters) uncertainty around the observation #' points. -#' @param seed A positive numeric value. The seed for random number generation -#' to make results reproducible. If `NA` (the default), no seed is used. +#' @param seed A positive numeric value setting the seed for random number +#' generation to ensure reproducibility. If `NA` (default), no seed is used. #' #' @returns An sf object with POINT geometry containing the locations of the #' sampled occurrences and a `coordinateUncertaintyInMeters` column containing diff --git a/R/sample_observations.R b/R/sample_observations.R index bbfe802..c8be653 100644 --- a/R/sample_observations.R +++ b/R/sample_observations.R @@ -26,8 +26,8 @@ #' applied to the sampling of occurrences. Higher weights mean a higher #' probability of sampling. Weights can be numeric values between 0 and 1 or #' positive integers that will be rescaled to values between 0 and 1. -#' @param seed A positive numeric value. The seed for random number generation -#' to make results reproducible. If `NA` (the default), no seed is used. +#' @param seed A positive numeric value setting the seed for random number +#' generation to ensure reproducibility. If `NA` (default), no seed is used. #' #' @returns An sf object with POINT geometry containing the locations of the #' sampled observations, a `detection_probability` column containing the diff --git a/R/sample_occurrences_from_raster.R b/R/sample_occurrences_from_raster.R index 59a0de2..132643e 100644 --- a/R/sample_occurrences_from_raster.R +++ b/R/sample_occurrences_from_raster.R @@ -1,13 +1,13 @@ #' Sample occurrences from spatial random field #' -#' Draws occurrences (points) from a spatial random field (raster) +#' This function draws point occurrences from a spatial random field (raster). #' -#' @param rs A SpatRaster object (terra). +#' @param rs A SpatRaster object (see [terra::rast()]). #' @param ts A vector with the number of occurrences per time point. -#' @param seed A positive numeric value. The seed for random number generation -#' to make results reproducible. If `NA` (the default), no seed is used. +#' @param seed A positive numeric value setting the seed for random number +#' generation to ensure reproducibility. If `NA` (default), no seed is used. #' -#' @return An sf object with POINT geometry +#' @return An sf object with POINT geometry. #' #' @export #' diff --git a/R/simulate_occurrences.R b/R/simulate_occurrences.R index 3dc12c1..1c28c2f 100644 --- a/R/simulate_occurrences.R +++ b/R/simulate_occurrences.R @@ -1,34 +1,32 @@ -#' Simulate occurrences within a spatiotemporal scope +#' Simulate species occurrences within a spatiotemporal scope #' -#' The function simulates occurrences of a species within a given spatial -#' and/or temporal extend. +#' This function simulates occurrences of a species within a specified spatial +#' and/or temporal extent. #' #' @param plgn An sf object with POLYGON geometry indicating the spatial #' extend to simulate occurrences. #' @param initial_average_abundance A positive numeric value indicating the -#' average number of occurrences to be simulated within the extend of `polygon` -#' at time point 1. This value will be used as mean of a Poisson distribution -#' (lambda parameter). -#' @param spatial_autocorr Define the spatial pattern. It could be a character -#' string `"random"` or `"clustered"`, in which `"random"` is the default. -#' The user is able to provide a numeric value >= 1 (1 is "random" and -#' 10 is "clustered"). A larger number means a broader size of the clusters -#' area. See details. -#' @param n_time_points A positive integer value indicating the number of time -#' points to simulate. -#' @param temporal_function `NA` (default), or a function which generates -#' a trend in abundance over time. Only used if `n_time_points > 1`. By default, -#' the function will sample `n_time_points` times from a Poisson -#' distribution with average (lambda) `initial_average_occurrences`. When a -#' function is specified (e.g. the internal `simulate_random_walk()` function). -#' @param ... Additional argument to be passed to the `temporal_function` -#' function. -#' @param seed A positive numeric value. The seed for random number generation -#' to make results reproducible. If `NA` (the default), no seed is used. +#' average number of occurrences to be simulated within the extent of `plgn` +#' at the first time point. This value serves as the mean (lambda) of a Poisson +#' distribution. +#' @param n_time_points A positive integer specifying the number of time points +#' to simulate. +#' @param temporal_function A function generating a trend in number of +#' occurrences over time, or `NA` (default). If `n_time_points` > 1 and a +#' function is provided, it defines the temporal pattern of number of +#' occurrences. +#' @param ... Additional arguments to be passed to `temporal_function`. +#' @param spatial_autocorr Specifies the spatial pattern of occurrences. It can +#' be a character string (`"random"` or `"clustered"`) or a numeric value ≥ 1 +#' (1 means random distribution, larger values indicate more clustering). +#' The default is `"random"`. `"clustered"` corresponds to a value of 10. +#' See `create_spatial_pattern()`. +#' @param seed A positive numeric value setting the seed for random number +#' generation to ensure reproducibility. If `NA` (default), no seed is used. #' #' @returns An sf object with POINT geometry containing the locations of the -#' simulated occurrences and a `time_point` column containing the time point -#' associated with each occurrence. +#' simulated occurrences and a `time_point` column indicating the associated +#' time point for each occurrence. #' #' @export #' diff --git a/R/simulate_random_walk.R b/R/simulate_random_walk.R index 8cf4ae7..a3fe110 100644 --- a/R/simulate_random_walk.R +++ b/R/simulate_random_walk.R @@ -1,17 +1,16 @@ #' Simulate a random walk over time #' -#' The function simulates occurrences of a species in a temporal extent. +#' This function simulates a timeseries for the average number of occurrences of +#' a species using a random walk over time. #' #' @param initial_average_occurrences A positive numeric value indicating the -#' average number of occurrences to be simulated within the extend of `polygon` -#' at time point 1. This value will be used as mean of a Poisson distribution -#' (lambda parameter). -#' @param n_time_points A positive integer value indicating the number of time -#' points to simulate. +#' average number of occurrences to be simulated at the first time point. +#' @param n_time_points A positive integer specifying the number of time points +#' to simulate. #' @param sd_step A positive numeric value indicating the standard deviation of #' the random steps. -#' @param seed A positive numeric value. The seed for random number generation -#' to make results reproducible. If `NA` (the default), no seed is used. +#' @param seed A positive numeric value setting the seed for random number +#' generation to ensure reproducibility. If `NA` (default), no seed is used. #' #' @returns A vector of integers of length `n_time_points` with the average #' number of occurrences. diff --git a/R/simulate_timeseries.R b/R/simulate_timeseries.R index 9fdbd20..2f96c67 100644 --- a/R/simulate_timeseries.R +++ b/R/simulate_timeseries.R @@ -1,27 +1,22 @@ -#' Simulate timeseries for species abundances +#' Simulate timeseries for species occurrences #' -#' The function simulates a timeseries for the abundance of a species. +#' This function simulates a timeseries for the number of occurrences of a +#' species. #' #' @param initial_average_occurrences A positive numeric value indicating the -#' average number of occurrences to be simulated within the extend of `polygon` -#' at the first time point. This value will be used as mean of a Poisson -#' distribution (lambda parameter). -#' @param n_time_points A positive integer value indicating the number of time -#' points to simulate. -#' @param temporal_function `NA` (default), or a function which generates -#' a trend in abundance over time. Only used if `n_time_points > 1`. By default, -#' the function will sample `n_time_points` times from a Poisson -#' distribution with average (lambda) `initial_average_occurrences`. When a -#' function is specified (e.g. the internal `simulate_random_walk()` function) -#' `n_time_points` average abundances (lambdas) are calculated using -#' `initial_average_occurrences` and any additional arguments passed. -#' See examples. -#' @param ... Additional argument to be passed to the `temporal_function` -#' function. -#' @param seed A positive numeric value. The seed for random number generation -#' to make results reproducible. If `NA` (the default), no seed is used. -#' -#' @returns A vector of integers of length n_time_points with the number of +#' average number of occurrences to be simulated at the first time point. This +#' value serves as the mean (lambda) of a Poisson distribution. +#' @param n_time_points A positive integer specifying the number of time points +#' to simulate. +#' @param temporal_function A function generating a trend in number of +#' occurrences over time, or `NA` (default). If `n_time_points` > 1 and a +#' function is provided, it defines the temporal pattern of number of +#' occurrences. +#' @param ... Additional arguments to be passed to `temporal_function`. +#' @param seed A positive numeric value setting the seed for random number +#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' +#' @return A vector of integers of length `n_time_points` with the number of #' occurrences. #' #' @export From e3bc6f20546ab3c04e3d3c617fe0ca37a17157ee Mon Sep 17 00:00:00 2001 From: wlangera Date: Mon, 29 Jul 2024 17:50:53 +0200 Subject: [PATCH 04/57] improve argument documentation detection process --- R/apply_manual_sampling_bias.R | 31 +++++++------- R/apply_polygon_sampling_bias.R | 25 ++++++++---- R/sample_observations.R | 72 +++++++++++++++++++++------------ 3 files changed, 80 insertions(+), 48 deletions(-) diff --git a/R/apply_manual_sampling_bias.R b/R/apply_manual_sampling_bias.R index 215f4c1..7e0bfcb 100644 --- a/R/apply_manual_sampling_bias.R +++ b/R/apply_manual_sampling_bias.R @@ -1,18 +1,19 @@ -#' Generate a sampling bias via a grid -#' -#' The function adds a sampling bias weight column containing the sample -#' probability based on bias weights within each cell of a given grid layer. -#' -#' @param occurrences_sf An sf object with POINT geometry. -#' @param bias_weights A raster layer (sf object with POLYGON geometry). The -#' raster of bias weights to be applied to the sampling of occurrences. This sf -#' object should contain a `bias_weight` and `geometry` column. Higher weights -#' indicate a higher probability of sampling. Weights must be numeric values -#' between 0 and 1 OR positive integers that will be rescaled to values between -#' 0 and 1. -#' -#' @returns An sf object with POINT geometry with a bias_weight column -#' containing the sampling probability based on sampling bias. +#' Apply manual sampling bias to occurrences via a grid +#' +#' This function adds a sampling bias weight column to an sf object containing +#' occurrences. The sampling probabilities are based on bias weights within each +#' cell of a provided grid layer. +#' +#' @param occurrences_sf An sf object with POINT geometry representing the +#' occurrences. +#' @param bias_weights An `sf` object with POLYGON geometry representing the +#' grid with bias weights. This sf object should contain a `bias_weight` column +#' and a `geometry` column. Higher weights indicate a higher probability of +#' sampling. Weights must be numeric values between 0 and 1 or positive +#' integers, which will be rescaled to values between 0 and 1. +#' +#' @return An sf object with POINT geometry that includes a `bias_weight` +#' column containing the sampling probabilities based on the sampling bias. #' #' @export #' diff --git a/R/apply_polygon_sampling_bias.R b/R/apply_polygon_sampling_bias.R index d001d61..352ec5d 100644 --- a/R/apply_polygon_sampling_bias.R +++ b/R/apply_polygon_sampling_bias.R @@ -1,10 +1,13 @@ -#' Generate a sampling bias via a polygon +#' Apply sampling bias to occurrences via a polygon #' -#' The function adds a sampling bias weight column containing the sample -#' probability based on bias strength within a given polygon. +#' This function adds a sampling bias weight column to an `sf` object containing +#' occurrences based on a given polygonal area. The bias is determined by the +#' specified bias strength, which adjusts the probability of sampling within +#' the polygonal area. #' -#' @param occurrences_sf An sf object with POINT geometry. -#' @param bias_area An sf object with POLYGON geometry. The area in which the +#' @param occurrences_sf An sf object with POINT geometry representing the +#' occurrences. +#' @param bias_area An sf object with POLYGON geometry specifying the area where #' sampling will be biased. #' @param bias_strength A positive numeric value. The strength of the bias to #' be applied in the biased area (as a multiplier). Above 1, area will be @@ -12,9 +15,17 @@ #' will result in 50 times more samples within the bias_area than outside. #' Conversely, a value of 0.5 will result in half less samples within the #' bias_area than outside. +#' @param bias_strength A positive numeric value that represents the strength of +#' the bias to be applied within the `bias_area`. Values greater than 1 will +#' increase the sampling probability within the polygon relative to outside +#' (oversampling), while values between 0 and 1 will decrease it +#' (undersampling). For instance, a value of 50 will make the probability 50 +#' times higher within the `bias_area` compared to outside, whereas a value of +#' 0.5 will make it half as likely. #' -#' @returns An sf object with POINT geometry with a bias_weight column -#' containing the sampling probability based on sampling bias. +#' @returns An sf object with POINT geometry that includes a `bias_weight` +#' column containing the sampling probabilities based on the bias area and +#' strength. #' #' @export #' diff --git a/R/sample_observations.R b/R/sample_observations.R index c8be653..d2dba9d 100644 --- a/R/sample_observations.R +++ b/R/sample_observations.R @@ -1,42 +1,62 @@ #' Sample observations from a larger occurrence dataset #' -#' The function samples observations from occurrences based on detection +#' The function computes observations from occurrences based on detection #' probability and sampling bias by implementing a Bernoulli trial. #' -#' @param occurrences An sf object with POINT geometry. -#' @param detection_probability A numeric value between 0 and 1, corresponding -#' to the probability of detection of the species. +#' @param occurrences An sf object with POINT geometry representing the +#' occurrences. +#' @param detection_probability A numeric value between 0 and 1 representing the +#' probability of detecting the species. #' @param sampling_bias `"no_bias"`, `"polygon"` or `"manual"`. The method used #' to generate a sampling bias. `"polygon"`: bias the sampling in a polygon. #' Provide your polygon to `bias_area`. Provide bias strength to #' `bias_strength`. `"manual"`: bias the sampling manually via a raster. #' Provide your raster layer in which each cell contains the probability to be #' sampled to `bias_weights`. -#' @param bias_area `NA` or an sf object with POLYGON geometry. Only used if -#' `sampling_bias = "polygon"`. The area in which the sampling will be biased. -#' @param bias_strength `NA` or a positive numeric value. Only used if -#' `sampling_bias = "polygon"`. The strength of the bias to be applied in the -#' biased area (as a multiplier). Above 1, area will be oversampled. Below 1, -#' area will be undersampled. For example, a value of 50 will result in a 50 -#' times sampling probability within the `bias_area` than outside. Conversely, -#' a value of 0.5 will result in half less samples within the `bias_area` than -#' outside. -#' @param bias_weights `NA` or a raster layer (sf object with POLYGON geometry). -#' Only used if `sampling_bias = "manual"`. The raster of bias weights to be -#' applied to the sampling of occurrences. Higher weights mean a higher -#' probability of sampling. Weights can be numeric values between 0 and 1 or -#' positive integers that will be rescaled to values between 0 and 1. +#' @param sampling_bias A character string specifying the method to generate a +#' sampling bias. +#' Options are `"no_bias"`, `"polygon"`, or `"manual"`. +#' \describe{ +#' \item{`"no_bias"`}{No bias is applied (default).} +#' \item{`"polygon"`}{Bias the sampling within a polygon. Provide the polygon +#' to `bias_area` and the bias strength to `bias_strength`.} +#' \item{`"manual"`}{Bias the sampling manually using a grid. Provide the grid +#' layer in which each cell contains the probability of being sampled to +#' `bias_weights`.} +#' } +#' @param bias_area An `sf` object with POLYGON geometry, or `NA`. Only used if +#' `sampling_bias = "polygon"`. This defines the area in which the sampling will +#' be biased. +#' @param bias_strength A positive numeric value, or `NA`. Only used if +#' `sampling_bias = "polygon"`. The value represents the strength of +#' the bias to be applied within the `bias_area`. Values greater than 1 will +#' increase the sampling probability within the polygon relative to outside +#' (oversampling), while values between 0 and 1 will decrease it +#' (undersampling). For instance, a value of 50 will make the probability 50 +#' times higher within the `bias_area` compared to outside, whereas a value of +#' 0.5 will make it half as likely. +#' @param bias_weights A grid layer (an sf object with POLYGON geometry), or +#' `NA`. Only used if `sampling_bias = "manual"`. The grid of bias weights to be +#' applied. This sf object should contain a `bias_weight` column with the +#' weights per grid cell. Higher weights increase the probability of sampling. +#' Weights can be numeric values between 0 and 1 or positive integers, which +#' will be rescaled to values between 0 and 1. #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. #' -#' @returns An sf object with POINT geometry containing the locations of the -#' sampled observations, a `detection_probability` column containing the -#' detection probability for each observation (will be the same for all), a -#' `bias_weight` column containing the sampling probability based on sampling -#' bias, a `sampling_probability` column containing the combined sampling -#' probability from detection probability and sampling bias for each -#' observation, and a `sampling_status` column indicating whether the -#' occurrence was detected (observations) or not (unobserved occurrences). +#' @return An sf object with POINT geometry containing the locations of the +#' occurrence with detection status. The object includes the following columns: +#' \describe{ +#' \item{`detection_probability`}{The detection probability for each +#' occurrence (will be the same for all).} +#' \item{`bias_weight`}{The sampling probability based on sampling bias for +#' each occurrence.} +#' \item{`sampling_probability`}{The combined sampling probability from +#' detection probability and sampling bias for each occurrence.} +#' \item{`sampling_status`}{Indicates whether the occurrence was detected +#' (`"detected"`) or not (`"undetected"`). Detected occurrences are called +#' observations.} +#' } #' #' @export #' From 39815d9e44fd09ea5ff4b3f659be892aaea6b0e6 Mon Sep 17 00:00:00 2001 From: wlangera Date: Mon, 29 Jul 2024 17:52:36 +0200 Subject: [PATCH 05/57] spelling --- R/apply_polygon_sampling_bias.R | 6 ------ inst/en_gb.dic | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/R/apply_polygon_sampling_bias.R b/R/apply_polygon_sampling_bias.R index 352ec5d..0bf01c5 100644 --- a/R/apply_polygon_sampling_bias.R +++ b/R/apply_polygon_sampling_bias.R @@ -9,12 +9,6 @@ #' occurrences. #' @param bias_area An sf object with POLYGON geometry specifying the area where #' sampling will be biased. -#' @param bias_strength A positive numeric value. The strength of the bias to -#' be applied in the biased area (as a multiplier). Above 1, area will be -#' oversampled. Below 1, area will be undersampled. For example, a value of 50 -#' will result in 50 times more samples within the bias_area than outside. -#' Conversely, a value of 0.5 will result in half less samples within the -#' bias_area than outside. #' @param bias_strength A positive numeric value that represents the strength of #' the bias to be applied within the `bias_area`. Values greater than 1 will #' increase the sampling probability within the polygon relative to outside diff --git a/inst/en_gb.dic b/inst/en_gb.dic index c7ef1d1..67f113b 100644 --- a/inst/en_gb.dic +++ b/inst/en_gb.dic @@ -9,5 +9,6 @@ prob spatiotemporal timeseries undersampled +undersampling unnested viz From f35bcaa4ad41e3eecb3ccb935cf87adad5d2367e Mon Sep 17 00:00:00 2001 From: wlangera Date: Mon, 29 Jul 2024 17:53:25 +0200 Subject: [PATCH 06/57] update argument explanation --- man/apply_manual_sampling_bias.Rd | 25 +++++---- man/apply_polygon_sampling_bias.Rd | 31 ++++++----- man/create_spatial_pattern.Rd | 30 +++++----- man/generate_taxonomy.Rd | 4 +- man/grid_designation.Rd | 4 +- man/sample_from_binormal_circle.Rd | 4 +- man/sample_from_uniform_circle.Rd | 4 +- man/sample_observations.Rd | 80 ++++++++++++++++----------- man/sample_occurrences_from_raster.Rd | 10 ++-- man/simulate_occurrences.Rd | 46 ++++++++------- man/simulate_random_walk.Rd | 15 +++-- man/simulate_timeseries.Rd | 43 +++++++------- 12 files changed, 154 insertions(+), 142 deletions(-) diff --git a/man/apply_manual_sampling_bias.Rd b/man/apply_manual_sampling_bias.Rd index 60f848f..9c44b3b 100644 --- a/man/apply_manual_sampling_bias.Rd +++ b/man/apply_manual_sampling_bias.Rd @@ -2,27 +2,28 @@ % Please edit documentation in R/apply_manual_sampling_bias.R \name{apply_manual_sampling_bias} \alias{apply_manual_sampling_bias} -\title{Generate a sampling bias via a grid} +\title{Apply manual sampling bias to occurrences via a grid} \usage{ apply_manual_sampling_bias(occurrences_sf, bias_weights) } \arguments{ -\item{occurrences_sf}{An sf object with POINT geometry.} +\item{occurrences_sf}{An sf object with POINT geometry representing the +occurrences.} -\item{bias_weights}{A raster layer (sf object with POLYGON geometry). The -raster of bias weights to be applied to the sampling of occurrences. This sf -object should contain a \code{bias_weight} and \code{geometry} column. Higher weights -indicate a higher probability of sampling. Weights must be numeric values -between 0 and 1 OR positive integers that will be rescaled to values between -0 and 1.} +\item{bias_weights}{An \code{sf} object with POLYGON geometry representing the +grid with bias weights. This sf object should contain a \code{bias_weight} column +and a \code{geometry} column. Higher weights indicate a higher probability of +sampling. Weights must be numeric values between 0 and 1 or positive +integers, which will be rescaled to values between 0 and 1.} } \value{ -An sf object with POINT geometry with a bias_weight column -containing the sampling probability based on sampling bias. +An sf object with POINT geometry that includes a \code{bias_weight} +column containing the sampling probabilities based on the sampling bias. } \description{ -The function adds a sampling bias weight column containing the sample -probability based on bias weights within each cell of a given grid layer. +This function adds a sampling bias weight column to an sf object containing +occurrences. The sampling probabilities are based on bias weights within each +cell of a provided grid layer. } \examples{ # Load packages diff --git a/man/apply_polygon_sampling_bias.Rd b/man/apply_polygon_sampling_bias.Rd index 2e295ca..34235f6 100644 --- a/man/apply_polygon_sampling_bias.Rd +++ b/man/apply_polygon_sampling_bias.Rd @@ -2,30 +2,35 @@ % Please edit documentation in R/apply_polygon_sampling_bias.R \name{apply_polygon_sampling_bias} \alias{apply_polygon_sampling_bias} -\title{Generate a sampling bias via a polygon} +\title{Apply sampling bias to occurrences via a polygon} \usage{ apply_polygon_sampling_bias(occurrences_sf, bias_area, bias_strength = 1) } \arguments{ -\item{occurrences_sf}{An sf object with POINT geometry.} +\item{occurrences_sf}{An sf object with POINT geometry representing the +occurrences.} -\item{bias_area}{An sf object with POLYGON geometry. The area in which the +\item{bias_area}{An sf object with POLYGON geometry specifying the area where sampling will be biased.} -\item{bias_strength}{A positive numeric value. The strength of the bias to -be applied in the biased area (as a multiplier). Above 1, area will be -oversampled. Below 1, area will be undersampled. For example, a value of 50 -will result in 50 times more samples within the bias_area than outside. -Conversely, a value of 0.5 will result in half less samples within the -bias_area than outside.} +\item{bias_strength}{A positive numeric value that represents the strength of +the bias to be applied within the \code{bias_area}. Values greater than 1 will +increase the sampling probability within the polygon relative to outside +(oversampling), while values between 0 and 1 will decrease it +(undersampling). For instance, a value of 50 will make the probability 50 +times higher within the \code{bias_area} compared to outside, whereas a value of +0.5 will make it half as likely.} } \value{ -An sf object with POINT geometry with a bias_weight column -containing the sampling probability based on sampling bias. +An sf object with POINT geometry that includes a \code{bias_weight} +column containing the sampling probabilities based on the bias area and +strength. } \description{ -The function adds a sampling bias weight column containing the sample -probability based on bias strength within a given polygon. +This function adds a sampling bias weight column to an \code{sf} object containing +occurrences based on a given polygonal area. The bias is determined by the +specified bias strength, which adjusts the probability of sampling within +the polygonal area. } \examples{ # Load packages diff --git a/man/create_spatial_pattern.Rd b/man/create_spatial_pattern.Rd index 20a4363..8a6afb0 100644 --- a/man/create_spatial_pattern.Rd +++ b/man/create_spatial_pattern.Rd @@ -15,33 +15,33 @@ create_spatial_pattern( \arguments{ \item{polygon}{An sf object with POLYGON geometry.} -\item{resolution}{A numeric value defining the resolution of the raster cell} +\item{resolution}{A numeric value defining the resolution of the raster cell.} -\item{spatial_pattern}{Define the spatial pattern. It could be a character -string \code{"random"} or \code{"clustered"}, in which \code{"random"} is the default. -The user is able to provide a numeric value >= 1 (1 is "random" and -10 is "clustered"). A larger number means a broader size of the clusters -area. See details.} +\item{spatial_pattern}{Specifies the desired spatial pattern. It can +be a character string (\code{"random"} or \code{"clustered"}) or a numeric value ≥ 1 +(1 means random distribution, larger values indicate more clustering). +The default is \code{"random"}. \code{"clustered"} corresponds to a value of 10. +See details.} -\item{seed}{The seed for random number generation to make results -reproducible. If \code{NA} (the default), no seed is used.} +\item{seed}{A positive numeric value setting the seed for random number +generation to ensure reproducibility. If \code{NA} (default), no seed is used.} \item{n_sim}{Number of simulations. Each simulation is a different layer in -the raster. Default 1.} +the raster. Default is 1.} } \value{ An object of class SpatRaster with a spatial pattern for the area of the given polygon. } \description{ -It creates a raster with a spatial pattern for the area of a polygon. +This function creates a raster with a spatial pattern for the area of a +polygon. } \details{ -the \code{spatial_pattern} argument change the range parameter of the -spherical variogram model. \code{spatial_pattern = 1} means the range has -the same size of the grid cell, which is defined in \code{resolution} -argument. We use the function \code{\link[gstat:vgm]{gstat::vgm()}} to implement the -spherical variogram model +The \code{spatial_pattern} argument changes the range parameter of the +spherical variogram model. \code{spatial_pattern = 1} means the range has the same +size as the grid cell, which is defined in the \code{resolution} argument. The +function \code{\link[gstat:vgm]{gstat::vgm()}} is used to implement the spherical variogram model. } \examples{ # Load packages diff --git a/man/generate_taxonomy.Rd b/man/generate_taxonomy.Rd index 6cc2f63..a300b28 100644 --- a/man/generate_taxonomy.Rd +++ b/man/generate_taxonomy.Rd @@ -33,8 +33,8 @@ output.} \item{num_kingdoms}{Number of kingdoms to generate. Defaults to 1.} -\item{seed}{The seed for random number generation to make results -reproducible. If \code{NA} (the default), no seed is used.} +\item{seed}{A positive numeric value setting the seed for random number +generation to ensure reproducibility. If \code{NA} (default), no seed is used.} } \value{ A data frame with the taxonomic classification of each species. If diff --git a/man/grid_designation.Rd b/man/grid_designation.Rd index 3a24a07..8017db5 100644 --- a/man/grid_designation.Rd +++ b/man/grid_designation.Rd @@ -27,8 +27,8 @@ observations should be designated.} cell. If \code{"row_names"} (the default), a new column \code{id} is created were the row names represent the unique ids.} -\item{seed}{The seed for random number generation to make results -reproducible. If \code{NA} (the default), no seed is used.} +\item{seed}{A positive numeric value setting the seed for random number +generation to ensure reproducibility. If \code{NA} (default), no seed is used.} \item{aggregate}{Logical. If \code{TRUE} (default), return data cube in aggregated form (grid with number of observations per grid cell). Otherwise diff --git a/man/sample_from_binormal_circle.Rd b/man/sample_from_binormal_circle.Rd index a5caae2..77c6d65 100644 --- a/man/sample_from_binormal_circle.Rd +++ b/man/sample_from_binormal_circle.Rd @@ -16,8 +16,8 @@ points.} possible samples from a a bivariate Normal distribution that fall within the uncertainty circle. If no value is given, the default \code{p_norm} value is 0.95.} -\item{seed}{A positive numeric value. The seed for random number generation -to make results reproducible. If \code{NA} (the default), no seed is used.} +\item{seed}{A positive numeric value setting the seed for random number +generation to ensure reproducibility. If \code{NA} (default), no seed is used.} } \value{ An sf object with POINT geometry containing the locations of the diff --git a/man/sample_from_uniform_circle.Rd b/man/sample_from_uniform_circle.Rd index f2df954..34abf6c 100644 --- a/man/sample_from_uniform_circle.Rd +++ b/man/sample_from_uniform_circle.Rd @@ -12,8 +12,8 @@ sample_from_uniform_circle(observations, seed = NA) the function will assume no (zero meters) uncertainty around the observation points.} -\item{seed}{A positive numeric value. The seed for random number generation -to make results reproducible. If \code{NA} (the default), no seed is used.} +\item{seed}{A positive numeric value setting the seed for random number +generation to ensure reproducibility. If \code{NA} (default), no seed is used.} } \value{ An sf object with POINT geometry containing the locations of the diff --git a/man/sample_observations.Rd b/man/sample_observations.Rd index 1cc88c6..17eaa6e 100644 --- a/man/sample_observations.Rd +++ b/man/sample_observations.Rd @@ -15,50 +15,64 @@ sample_observations( ) } \arguments{ -\item{occurrences}{An sf object with POINT geometry.} +\item{occurrences}{An sf object with POINT geometry representing the +occurrences.} -\item{detection_probability}{A numeric value between 0 and 1, corresponding -to the probability of detection of the species.} +\item{detection_probability}{A numeric value between 0 and 1 representing the +probability of detecting the species.} -\item{sampling_bias}{\code{"no_bias"}, \code{"polygon"} or \code{"manual"}. The method used -to generate a sampling bias. \code{"polygon"}: bias the sampling in a polygon. -Provide your polygon to \code{bias_area}. Provide bias strength to -\code{bias_strength}. \code{"manual"}: bias the sampling manually via a raster. -Provide your raster layer in which each cell contains the probability to be -sampled to \code{bias_weights}.} +\item{sampling_bias}{A character string specifying the method to generate a +sampling bias. +Options are \code{"no_bias"}, \code{"polygon"}, or \code{"manual"}. +\describe{ +\item{\code{"no_bias"}}{No bias is applied (default).} +\item{\code{"polygon"}}{Bias the sampling within a polygon. Provide the polygon +to \code{bias_area} and the bias strength to \code{bias_strength}.} +\item{\code{"manual"}}{Bias the sampling manually using a grid. Provide the grid +layer in which each cell contains the probability of being sampled to +\code{bias_weights}.} +}} -\item{bias_area}{\code{NA} or an sf object with POLYGON geometry. Only used if -\code{sampling_bias = "polygon"}. The area in which the sampling will be biased.} +\item{bias_area}{An \code{sf} object with POLYGON geometry, or \code{NA}. Only used if +\code{sampling_bias = "polygon"}. This defines the area in which the sampling will +be biased.} -\item{bias_strength}{\code{NA} or a positive numeric value. Only used if -\code{sampling_bias = "polygon"}. The strength of the bias to be applied in the -biased area (as a multiplier). Above 1, area will be oversampled. Below 1, -area will be undersampled. For example, a value of 50 will result in a 50 -times sampling probability within the \code{bias_area} than outside. Conversely, -a value of 0.5 will result in half less samples within the \code{bias_area} than -outside.} +\item{bias_strength}{A positive numeric value, or \code{NA}. Only used if +\code{sampling_bias = "polygon"}. The value represents the strength of +the bias to be applied within the \code{bias_area}. Values greater than 1 will +increase the sampling probability within the polygon relative to outside +(oversampling), while values between 0 and 1 will decrease it +(undersampling). For instance, a value of 50 will make the probability 50 +times higher within the \code{bias_area} compared to outside, whereas a value of +0.5 will make it half as likely.} -\item{bias_weights}{\code{NA} or a raster layer (sf object with POLYGON geometry). -Only used if \code{sampling_bias = "manual"}. The raster of bias weights to be -applied to the sampling of occurrences. Higher weights mean a higher -probability of sampling. Weights can be numeric values between 0 and 1 or -positive integers that will be rescaled to values between 0 and 1.} +\item{bias_weights}{A grid layer (an sf object with POLYGON geometry), or +\code{NA}. Only used if \code{sampling_bias = "manual"}. The grid of bias weights to be +applied. This sf object should contain a \code{bias_weight} column with the +weights per grid cell. Higher weights increase the probability of sampling. +Weights can be numeric values between 0 and 1 or positive integers, which +will be rescaled to values between 0 and 1.} -\item{seed}{A positive numeric value. The seed for random number generation -to make results reproducible. If \code{NA} (the default), no seed is used.} +\item{seed}{A positive numeric value setting the seed for random number +generation to ensure reproducibility. If \code{NA} (default), no seed is used.} } \value{ An sf object with POINT geometry containing the locations of the -sampled observations, a \code{detection_probability} column containing the -detection probability for each observation (will be the same for all), a -\code{bias_weight} column containing the sampling probability based on sampling -bias, a \code{sampling_probability} column containing the combined sampling -probability from detection probability and sampling bias for each -observation, and a \code{sampling_status} column indicating whether the -occurrence was detected (observations) or not (unobserved occurrences). +occurrence with detection status. The object includes the following columns: +\describe{ +\item{\code{detection_probability}}{The detection probability for each +occurrence (will be the same for all).} +\item{\code{bias_weight}}{The sampling probability based on sampling bias for +each occurrence.} +\item{\code{sampling_probability}}{The combined sampling probability from +detection probability and sampling bias for each occurrence.} +\item{\code{sampling_status}}{Indicates whether the occurrence was detected +(\code{"detected"}) or not (\code{"undetected"}). Detected occurrences are called +observations.} +} } \description{ -The function samples observations from occurrences based on detection +The function computes observations from occurrences based on detection probability and sampling bias by implementing a Bernoulli trial. } \examples{ diff --git a/man/sample_occurrences_from_raster.Rd b/man/sample_occurrences_from_raster.Rd index 44940eb..c65de83 100644 --- a/man/sample_occurrences_from_raster.Rd +++ b/man/sample_occurrences_from_raster.Rd @@ -7,18 +7,18 @@ sample_occurrences_from_raster(rs, ts, seed = NA) } \arguments{ -\item{rs}{A SpatRaster object (terra).} +\item{rs}{A SpatRaster object (see \code{\link[terra:rast]{terra::rast()}}).} \item{ts}{A vector with the number of occurrences per time point.} -\item{seed}{A positive numeric value. The seed for random number generation -to make results reproducible. If \code{NA} (the default), no seed is used.} +\item{seed}{A positive numeric value setting the seed for random number +generation to ensure reproducibility. If \code{NA} (default), no seed is used.} } \value{ -An sf object with POINT geometry +An sf object with POINT geometry. } \description{ -Draws occurrences (points) from a spatial random field (raster) +This function draws point occurrences from a spatial random field (raster). } \examples{ # Load packages diff --git a/man/simulate_occurrences.Rd b/man/simulate_occurrences.Rd index 586357f..66ca7ce 100644 --- a/man/simulate_occurrences.Rd +++ b/man/simulate_occurrences.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/simulate_occurrences.R \name{simulate_occurrences} \alias{simulate_occurrences} -\title{Simulate occurrences within a spatiotemporal scope} +\title{Simulate species occurrences within a spatiotemporal scope} \usage{ simulate_occurrences( plgn, @@ -19,39 +19,37 @@ simulate_occurrences( extend to simulate occurrences.} \item{initial_average_abundance}{A positive numeric value indicating the -average number of occurrences to be simulated within the extend of \code{polygon} -at time point 1. This value will be used as mean of a Poisson distribution -(lambda parameter).} +average number of occurrences to be simulated within the extent of \code{plgn} +at the first time point. This value serves as the mean (lambda) of a Poisson +distribution.} -\item{spatial_autocorr}{Define the spatial pattern. It could be a character -string \code{"random"} or \code{"clustered"}, in which \code{"random"} is the default. -The user is able to provide a numeric value >= 1 (1 is "random" and -10 is "clustered"). A larger number means a broader size of the clusters -area. See details.} +\item{spatial_autocorr}{Specifies the spatial pattern of occurrences. It can +be a character string (\code{"random"} or \code{"clustered"}) or a numeric value ≥ 1 +(1 means random distribution, larger values indicate more clustering). +The default is \code{"random"}. \code{"clustered"} corresponds to a value of 10. +See \code{create_spatial_pattern()}.} -\item{n_time_points}{A positive integer value indicating the number of time -points to simulate.} +\item{n_time_points}{A positive integer specifying the number of time points +to simulate.} -\item{temporal_function}{\code{NA} (default), or a function which generates -a trend in abundance over time. Only used if \code{n_time_points > 1}. By default, -the function will sample \code{n_time_points} times from a Poisson -distribution with average (lambda) \code{initial_average_occurrences}. When a -function is specified (e.g. the internal \code{simulate_random_walk()} function).} +\item{temporal_function}{A function generating a trend in number of +occurrences over time, or \code{NA} (default). If \code{n_time_points} > 1 and a +function is provided, it defines the temporal pattern of number of +occurrences.} -\item{...}{Additional argument to be passed to the \code{temporal_function} -function.} +\item{...}{Additional arguments to be passed to \code{temporal_function}.} -\item{seed}{A positive numeric value. The seed for random number generation -to make results reproducible. If \code{NA} (the default), no seed is used.} +\item{seed}{A positive numeric value setting the seed for random number +generation to ensure reproducibility. If \code{NA} (default), no seed is used.} } \value{ An sf object with POINT geometry containing the locations of the -simulated occurrences and a \code{time_point} column containing the time point -associated with each occurrence. +simulated occurrences and a \code{time_point} column indicating the associated +time point for each occurrence. } \description{ -The function simulates occurrences of a species within a given spatial -and/or temporal extend. +This function simulates occurrences of a species within a specified spatial +and/or temporal extent. } \examples{ # Load packages diff --git a/man/simulate_random_walk.Rd b/man/simulate_random_walk.Rd index 37ee9c0..d28f9a3 100644 --- a/man/simulate_random_walk.Rd +++ b/man/simulate_random_walk.Rd @@ -13,25 +13,24 @@ simulate_random_walk( } \arguments{ \item{initial_average_occurrences}{A positive numeric value indicating the -average number of occurrences to be simulated within the extend of \code{polygon} -at time point 1. This value will be used as mean of a Poisson distribution -(lambda parameter).} +average number of occurrences to be simulated at the first time point.} -\item{n_time_points}{A positive integer value indicating the number of time -points to simulate.} +\item{n_time_points}{A positive integer specifying the number of time points +to simulate.} \item{sd_step}{A positive numeric value indicating the standard deviation of the random steps.} -\item{seed}{A positive numeric value. The seed for random number generation -to make results reproducible. If \code{NA} (the default), no seed is used.} +\item{seed}{A positive numeric value setting the seed for random number +generation to ensure reproducibility. If \code{NA} (default), no seed is used.} } \value{ A vector of integers of length \code{n_time_points} with the average number of occurrences. } \description{ -The function simulates occurrences of a species in a temporal extent. +This function simulates a timeseries for the average number of occurrences of +a species using a random walk over time. } \examples{ diff --git a/man/simulate_timeseries.Rd b/man/simulate_timeseries.Rd index 86c5ab0..b602516 100644 --- a/man/simulate_timeseries.Rd +++ b/man/simulate_timeseries.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/simulate_timeseries.R \name{simulate_timeseries} \alias{simulate_timeseries} -\title{Simulate timeseries for species abundances} +\title{Simulate timeseries for species occurrences} \usage{ simulate_timeseries( initial_average_occurrences = 50, @@ -14,34 +14,29 @@ simulate_timeseries( } \arguments{ \item{initial_average_occurrences}{A positive numeric value indicating the -average number of occurrences to be simulated within the extend of \code{polygon} -at the first time point. This value will be used as mean of a Poisson -distribution (lambda parameter).} - -\item{n_time_points}{A positive integer value indicating the number of time -points to simulate.} - -\item{temporal_function}{\code{NA} (default), or a function which generates -a trend in abundance over time. Only used if \code{n_time_points > 1}. By default, -the function will sample \code{n_time_points} times from a Poisson -distribution with average (lambda) \code{initial_average_occurrences}. When a -function is specified (e.g. the internal \code{simulate_random_walk()} function) -\code{n_time_points} average abundances (lambdas) are calculated using -\code{initial_average_occurrences} and any additional arguments passed. -See examples.} - -\item{...}{Additional argument to be passed to the \code{temporal_function} -function.} - -\item{seed}{A positive numeric value. The seed for random number generation -to make results reproducible. If \code{NA} (the default), no seed is used.} +average number of occurrences to be simulated at the first time point. This +value serves as the mean (lambda) of a Poisson distribution.} + +\item{n_time_points}{A positive integer specifying the number of time points +to simulate.} + +\item{temporal_function}{A function generating a trend in number of +occurrences over time, or \code{NA} (default). If \code{n_time_points} > 1 and a +function is provided, it defines the temporal pattern of number of +occurrences.} + +\item{...}{Additional arguments to be passed to \code{temporal_function}.} + +\item{seed}{A positive numeric value setting the seed for random number +generation to ensure reproducibility. If \code{NA} (default), no seed is used.} } \value{ -A vector of integers of length n_time_points with the number of +A vector of integers of length \code{n_time_points} with the number of occurrences. } \description{ -The function simulates a timeseries for the abundance of a species. +This function simulates a timeseries for the number of occurrences of a +species. } \examples{ library(ggplot2) From ca9f899fe0c4f2fced41f6b11406c83092f8fdd6 Mon Sep 17 00:00:00 2001 From: wlangera Date: Mon, 29 Jul 2024 17:55:49 +0200 Subject: [PATCH 07/57] occurrences --- R/filter_observations.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/filter_observations.R b/R/filter_observations.R index be36cc0..0fb153a 100644 --- a/R/filter_observations.R +++ b/R/filter_observations.R @@ -1,6 +1,6 @@ -#' Filter detected observations +#' Filter detected occurrences #' -#' The function filters observations from all observations based on a +#' The function filters observations from all occurrences based on a #' `sampling_status` column, e.g. created by `sample_observations()`. #' #' @param observations_total An sf object with POINT geometry or a simple From 4be7f7621966aa9e0e9c16177c82a31a3f927143 Mon Sep 17 00:00:00 2001 From: wlangera Date: Mon, 29 Jul 2024 17:56:05 +0200 Subject: [PATCH 08/57] whiteline --- R/add_coordinate_uncertainty.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/add_coordinate_uncertainty.R b/R/add_coordinate_uncertainty.R index 3b350d1..d74736f 100644 --- a/R/add_coordinate_uncertainty.R +++ b/R/add_coordinate_uncertainty.R @@ -3,7 +3,6 @@ #' Adds a column to the observations sf object with the coordinate uncertainty #' in meters. #' -#' #' @param observations An sf object with POINT geometry. #' @param coords_uncertainty_meters A numeric value or a vector of numeric #' values indicating the coordinate uncertainty associated with observations. From 7d4ef833ead91a6aba6cd1b5292f8d158bf8904f Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 10:10:48 +0200 Subject: [PATCH 09/57] improve argument explanation --- R/add_coordinate_uncertainty.R | 18 ++++++++++++------ R/filter_observations.R | 17 ++++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/R/add_coordinate_uncertainty.R b/R/add_coordinate_uncertainty.R index d74736f..bdf5777 100644 --- a/R/add_coordinate_uncertainty.R +++ b/R/add_coordinate_uncertainty.R @@ -1,14 +1,20 @@ #' Add coordinate uncertainty to observations #' -#' Adds a column to the observations sf object with the coordinate uncertainty -#' in meters. +#' This function adds a column to the input sf object containing the coordinate +#' uncertainty for each observation, measured in meters. #' -#' @param observations An sf object with POINT geometry. +#' @param observations An sf object with POINT geometry representing the +#' observations. This object contains the spatial points to which the coordinate +#' uncertainty will be added. #' @param coords_uncertainty_meters A numeric value or a vector of numeric -#' values indicating the coordinate uncertainty associated with observations. +#' values representing the coordinate uncertainty (in meters) associated with +#' each observation. If a single numeric value is provided, it will be applied +#' to all observations. If a numeric vector is provided, it must be the same +#' length as the number of observations. #' -#' @return An sf object with POINT geometry with an additional column -#' `coordinateUncertaintyInMeters`. +#' @return The input sf object with POINT geometry, with an additional column +#' named `coordinateUncertaintyInMeters` that contains the coordinate uncertainty +#' values in meters. #' #' @export #' diff --git a/R/filter_observations.R b/R/filter_observations.R index 0fb153a..8acdc9d 100644 --- a/R/filter_observations.R +++ b/R/filter_observations.R @@ -1,16 +1,19 @@ #' Filter detected occurrences #' -#' The function filters observations from all occurrences based on a -#' `sampling_status` column, e.g. created by `sample_observations()`. +#' This function filters observations from all occurrences based on the +#' `sampling_status` column, typically created by the `sample_observations()` +#' function. #' #' @param observations_total An sf object with POINT geometry or a simple #' dataframe with `sampling_status` column containing values `"detected"`. -#' This format is created by `sample_observations()`. -#' @param invert Logical. If `FALSE` (default), filter `"detected"` -#' occurrences. Otherwise, filter all other occurrences. +#' This format is typically created by the `sample_observations()` function. +#' @param invert Logical. If `FALSE` (default), the function filters to retain +#' only `"detected"` occurrences. If `TRUE`, it filters out `"detected"` +#' occurrences and retains all other occurrences. #' -#' @returns A dataframe or an sf object with POINT geometry containing detected -#' occurrences (if `invert = FALSE`), or other occurrences (if `invert = TRUE`). +#' @returns A data frame or an sf object with POINT geometry containing the +#' filtered observations. If `invert = FALSE`, the function returns detected +#' occurrences. If `invert = TRUE`, it returns all other occurrences. #' #' @export #' From 767c87b819e713c99306116d8bcc1c1b1bfb2bd7 Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 10:37:29 +0200 Subject: [PATCH 10/57] updated argument explanation --- R/create_spatial_pattern.R | 4 +-- R/grid_designation.R | 57 ++++++++++++++++----------------- R/sample_from_binormal_circle.R | 27 +++++++++------- R/sample_from_uniform_circle.R | 8 ++--- 4 files changed, 49 insertions(+), 47 deletions(-) diff --git a/R/create_spatial_pattern.R b/R/create_spatial_pattern.R index 330c991..970db65 100644 --- a/R/create_spatial_pattern.R +++ b/R/create_spatial_pattern.R @@ -9,7 +9,7 @@ #' be a character string (`"random"` or `"clustered"`) or a numeric value ≥ 1 #' (1 means random distribution, larger values indicate more clustering). #' The default is `"random"`. `"clustered"` corresponds to a value of 10. -#' See details. +#' See Details. #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. #' @param n_sim Number of simulations. Each simulation is a different layer in @@ -22,7 +22,7 @@ #' #' @seealso [gstat::vgm()] and its `range` argument #' -#' @return An object of class SpatRaster with a spatial pattern for the area of +#' @returns An object of class SpatRaster with a spatial pattern for the area of #' the given polygon. #' #' @export diff --git a/R/grid_designation.R b/R/grid_designation.R index 8e78271..0e0c435 100644 --- a/R/grid_designation.R +++ b/R/grid_designation.R @@ -1,44 +1,41 @@ #' Observations to grid designation to create a data cube #' -#' The function designates observations to cells of a given grid to create an +#' This function designates observations to cells of a given grid to create an #' aggregated data cube. #' #' @param observations An sf object with POINT geometry and a `time_point` and -#' `coordinateUncertaintyInMeters` column. If this last column is not present, -#' the function will assume no (zero meters) uncertainty around the observation +#' `coordinateUncertaintyInMeters` column. If the latter column is not present, +#' the function will assume no uncertainty (zero meters) around the observation #' points. #' @param grid An sf object with POLYGON geometry (usually a grid) to which #' observations should be designated. -#' @param id_col The column name of the column with unique ids for each grid -#' cell. If `"row_names"` (the default), a new column `id` is created were the -#' row names represent the unique ids. +#' @param id_col The column name containing unique IDs for each grid cell. If +#' `"row_names"` (the default), a new column `id` is created where the row names +#' represent the unique IDs. #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. -#' @param aggregate Logical. If `TRUE` (default), return data cube in -#' aggregated form (grid with number of observations per grid cell). Otherwise -#' return sampled points in uncertainty circle. -#' @param randomisation `"uniform"` or `"normal"`. Randomisation method used -#' for sampling within uncertainty circle around each observation. By default -#' `"uniform"` which means each point uncertainty circle has an equal -#' probability to be selected. The other option is `"normal"` where a point is -#' sampled from a bivariate Normal distribution with means equal to the -#' observation point and the variance equal to -#' (-`coordinateUncertaintyInMeters`^2) / (2 * log(1 - `p_norm`)) such that -#' `p_norm` % of all possible samples from this Normal distribution fall -#' within the uncertainty circle. -#' @param p_norm A numeric value between 0 and 1. Only used if -#' `randomisation = "normal"`. The proportion of all possible samples from a a -#' bivariate Normal distribution that fall within the uncertainty circle. If -#' normal randomisation is used and no value is given, the default `p_norm` -#' value is 0.95. +#' @param aggregate Logical. If `TRUE` (default), returns data cube in +#' aggregated form (grid with the number of observations per grid cell). +#' Otherwise, returns sampled points within the uncertainty circle. +#' @param randomisation Character. Method used for sampling within the +#' uncertainty circle around each observation. `"uniform"` (default) means each +#' point in the uncertainty circle has an equal probability of being selected. +#' The other option is `"normal"`, where a point is sampled from a bivariate +#' Normal distribution with means equal to the observation point and variance +#' such that `p_norm` % of all possible samples from this Normal distribution +#' fall within the uncertainty circle. +#' See `sample_from_binormal_circle()`. +#' @param p_norm A numeric value between 0 and 1, used only if +#' `randomisation = "normal"`. The proportion of all possible samples from a +#' bivariate Normal distribution that fall within the uncertainty circle. +#' Default is 0.95. #' -#' @returns In case of `aggregate = TRUE`, an sf object with POLYGON geometry -#' containing the locations of the grid cells, an `n` column with the number of -#' observations per grid cell, and a `min_coord_uncertainty` column containing -#' the minimal coordinate uncertainty per grid cell. In case of -#' `aggregate = FALSE`, an sf object with POINT geometry containing the -#' locations of the sampled observations within the uncertainty circle, and a -#' `coordinateUncertaintyInMeters` column containing the coordinate uncertainty +#' @return If `aggregate = TRUE`, an sf object with POLYGON geometry +#' containing the grid cells, an `n` column with the number of observations per +#' grid cell, and a `min_coord_uncertainty` column with the minimum coordinate +#' uncertainty per grid cell. If `aggregate = FALSE`, an sf object with POINT +#' geometry containing the sampled observations within the uncertainty circles, +#' and a `coordinateUncertaintyInMeters` column with the coordinate uncertainty #' for each observation. #' #' @export diff --git a/R/sample_from_binormal_circle.R b/R/sample_from_binormal_circle.R index ca06699..ca0d61c 100644 --- a/R/sample_from_binormal_circle.R +++ b/R/sample_from_binormal_circle.R @@ -1,22 +1,27 @@ #' Sample from a circle using the bivariate Normal distribution #' -#' The function samples occurrences of a species within the uncertainty circle -#' around each observation assuming a bivariate Normal distribution with means -#' equal to the observation point and the variance equal to -#' (-`coordinateUncertaintyInMeters`^2) / (2 * log(1 - `p_norm`)) such that -#' `p_norm` % of all possible samples from this Normal distribution fall within -#' the uncertainty circle. +#' This function samples a new observations point of a species within the +#' uncertainty circle around each observation assuming a bivariate Normal +#' distribution. #' #' @param observations An sf object with POINT geometry and a `time_point` and -#' `coordinateUncertaintyInMeters` column. If this last column is not present, -#' the function will assume no (zero meters) uncertainty around the observation +#' `coordinateUncertaintyInMeters` column. If the latter column is not present, +#' the function will assume no uncertainty (zero meters) around the observation #' points. -#' @param p_norm A numeric value between 0 and 1. The proportion of all -#' possible samples from a a bivariate Normal distribution that fall within the -#' uncertainty circle. If no value is given, the default `p_norm` value is 0.95. +#' @param p_norm A numeric value between 0 and 1. The proportion of all possible +#' samples from a bivariate Normal distribution that fall within the uncertainty +#' circle. Default is 0.95. +#' See Details. #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. #' +#' @details A new observation point is sampled from a bivariate Normal +#' distribution with means equal to the X and Y coordinates of its original +#' observation point and variances equal to +#' (-`coordinateUncertaintyInMeters`^2) / (2 * log(1 - `p_norm`)), +#' ensuring `p_norm` % of all possible samples fall within the uncertainty +#' circle. +#' #' @returns An sf object with POINT geometry containing the locations of the #' sampled occurrences and a `coordinateUncertaintyInMeters` column containing #' the coordinate uncertainty for each observation. diff --git a/R/sample_from_uniform_circle.R b/R/sample_from_uniform_circle.R index 1cb0f3c..991b3c3 100644 --- a/R/sample_from_uniform_circle.R +++ b/R/sample_from_uniform_circle.R @@ -1,11 +1,11 @@ #' Sample from a circle using the Uniform distribution #' -#' The function samples occurrences of a species within the uncertainty circle -#' around each observation assuming a Uniform distribution. +#' This function samples a new observations point of a species within the +#' uncertainty circle around each observation assuming a Uniform distribution. #' #' @param observations An sf object with POINT geometry and a `time_point` and -#' `coordinateUncertaintyInMeters` column. If this last column is not present, -#' the function will assume no (zero meters) uncertainty around the observation +#' `coordinateUncertaintyInMeters` column. If the latter column is not present, +#' the function will assume no uncertainty (zero meters) around the observation #' points. #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. From 5bdfbb06334dbea248676b92b024ed2858ed18ea Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 10:41:47 +0200 Subject: [PATCH 11/57] improve documentation --- R/generate_taxonomy.R | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/R/generate_taxonomy.R b/R/generate_taxonomy.R index 1355293..9322768 100644 --- a/R/generate_taxonomy.R +++ b/R/generate_taxonomy.R @@ -4,12 +4,6 @@ #' of species, genera, families, orders, classes, phyla, and kingdoms. The #' output is a data frame with the hierarchical classification for each species. #' -#' The function works by randomly assigning species to genera, genera to -#' families, families to orders, orders to classes, classes to phyla, and phyla -#' to kingdoms. Sampling is done with replacement, meaning that multiple -#' lower-level taxa (e.g., species) can be assigned to the same higher-level -#' taxon (e.g., genus). -#' #' @param num_species Number of species to generate, or a dataframe. With a #' dataframe, the function will create a species with taxonomic hierarchy for #' each row. The original columns of the dataframe will be retained in the @@ -23,7 +17,13 @@ #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. #' -#' @return A data frame with the taxonomic classification of each species. If +#' @details The function works by randomly assigning species to genera, genera +#' to families, families to orders, orders to classes, classes to phyla, and +#' phyla to kingdoms. Sampling is done with replacement, allowing multiple +#' lower-level taxa (e.g., species) to be assigned to the same higher-level +#' taxon (e.g., genus). +#' +#' @returns A data frame with the taxonomic classification of each species. If #' `num_species` is a dataframe, the taxonomic classification is added to this #' input dataframe. The original columns of the dataframe will be retained in #' the output. From 1bf3e18f891619cc779324e3cbb28fce2363909f Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 11:07:04 +0200 Subject: [PATCH 12/57] improve documentation --- R/add_coordinate_uncertainty.R | 6 +-- R/apply_manual_sampling_bias.R | 2 +- R/grid_designation.R | 2 +- R/map_add_coordinate_uncertainty.R | 30 +++++++------- R/map_filter_observations.R | 33 ++++++++-------- R/map_grid_designation.R | 26 ++++++------ R/map_sample_observations.R | 33 ++++++++-------- R/map_simulate_occurrences.R | 34 ++++++++-------- R/map_simulation_functions.R | 27 ++++++------- R/sample_observations.R | 2 +- R/sample_occurrences_from_raster.R | 2 +- R/simulate_timeseries.R | 2 +- R/utils.R | 49 ++++++++++++++++------- man/add_coordinate_uncertainty.Rd | 18 ++++++--- man/create_spatial_pattern.Rd | 2 +- man/filter_observations.Rd | 19 +++++---- man/generate_taxonomy.Rd | 8 ++-- man/grid_designation.Rd | 57 +++++++++++++-------------- man/map_add_coordinate_uncertainty.Rd | 30 +++++++------- man/map_filter_observations.Rd | 31 ++++++++------- man/map_grid_designation.Rd | 26 ++++++------ man/map_sample_observations.Rd | 31 ++++++++------- man/map_simulate_occurrences.Rd | 32 +++++++-------- man/map_simulation_functions.Rd | 27 ++++++------- man/sample_from_binormal_circle.Rd | 28 +++++++------ man/sample_from_uniform_circle.Rd | 8 ++-- 26 files changed, 300 insertions(+), 265 deletions(-) diff --git a/R/add_coordinate_uncertainty.R b/R/add_coordinate_uncertainty.R index bdf5777..4c58816 100644 --- a/R/add_coordinate_uncertainty.R +++ b/R/add_coordinate_uncertainty.R @@ -12,9 +12,9 @@ #' to all observations. If a numeric vector is provided, it must be the same #' length as the number of observations. #' -#' @return The input sf object with POINT geometry, with an additional column -#' named `coordinateUncertaintyInMeters` that contains the coordinate uncertainty -#' values in meters. +#' @returns The input sf object with POINT geometry, with an additional column +#' named `coordinateUncertaintyInMeters` that contains the coordinate +#' uncertainty values in meters. #' #' @export #' diff --git a/R/apply_manual_sampling_bias.R b/R/apply_manual_sampling_bias.R index 7e0bfcb..c9c070a 100644 --- a/R/apply_manual_sampling_bias.R +++ b/R/apply_manual_sampling_bias.R @@ -12,7 +12,7 @@ #' sampling. Weights must be numeric values between 0 and 1 or positive #' integers, which will be rescaled to values between 0 and 1. #' -#' @return An sf object with POINT geometry that includes a `bias_weight` +#' @returns An sf object with POINT geometry that includes a `bias_weight` #' column containing the sampling probabilities based on the sampling bias. #' #' @export diff --git a/R/grid_designation.R b/R/grid_designation.R index 0e0c435..dccd79d 100644 --- a/R/grid_designation.R +++ b/R/grid_designation.R @@ -30,7 +30,7 @@ #' bivariate Normal distribution that fall within the uncertainty circle. #' Default is 0.95. #' -#' @return If `aggregate = TRUE`, an sf object with POLYGON geometry +#' @returns If `aggregate = TRUE`, an sf object with POLYGON geometry #' containing the grid cells, an `n` column with the number of observations per #' grid cell, and a `min_coord_uncertainty` column with the minimum coordinate #' uncertainty per grid cell. If `aggregate = FALSE`, an sf object with POINT diff --git a/R/map_add_coordinate_uncertainty.R b/R/map_add_coordinate_uncertainty.R index 13e9ae6..5c15bea 100644 --- a/R/map_add_coordinate_uncertainty.R +++ b/R/map_add_coordinate_uncertainty.R @@ -1,27 +1,27 @@ -#' Map `add_coordinate_uncertainty()` function over multiple species +#' Map `add_coordinate_uncertainty()` over multiple species #' -#' The function executes `add_coordinate_uncertainty()` over multiple rows of a -#' dataframe, representing multiple different species, containing potentially +#' This function executes `add_coordinate_uncertainty()` over multiple rows of a +#' dataframe, representing different species, with potentially #' different function arguments over multiple columns. #' -#' @param df A dataframe containing multiple rows. Each row is considered a +#' @param df A dataframe containing multiple rows, each representing a #' different species. The columns are function arguments with values used for -#' mapping `add_coordinate_uncertainty()` for each species. `df` can have -#' columns that are not used by this function. They will be retained in the -#' output. -#' @param nested Logical. If `TRUE` (default), retain list-column containing -#' sf objects calculated by `add_coordinate_uncertainty()`. Otherwise, expand +#' mapping `add_coordinate_uncertainty()` for each species. Columns not used by +#' this function will be retained in the output. +#' @param nested Logical. If `TRUE` (default), retains list-column containing +#' sf objects calculated by `add_coordinate_uncertainty()`. Otherwise, expands #' this list-column into rows and columns. #' @param arg_list A named list or `NA`. If `NA` (default), the function assumes #' column names in `df` are identical to argument names of -#' `add_coordinate_uncertainty()`. If column names are not identical, they need +#' `add_coordinate_uncertainty()`. If column names differ, they must #' to be specified as a named list where the names are the argument names of -#' `add_coordinate_uncertainty()`. +#' `add_coordinate_uncertainty()`, and the associated values are the +#' corresponding column names in `df`. #' -#' @returns In case of `nested = TRUE`, a dataframe identical to the input -#' dataframe `df`, but each sf object with POINT geometry in the list-column -#' `observations` now has an additional column `coordinateUncertaintyInMeters` -#' added by `add_coordinate_uncertainty()`. In case of `nested = FALSE`, this +#' @returns In case of `nested = TRUE`, a dataframe identical to `df`, but each +#' sf object with POINT geometry in the list-column `observations` now has an +#' additional column `coordinateUncertaintyInMeters` added by +#' `add_coordinate_uncertainty()`. In case of `nested = FALSE`, this #' list-column is expanded into additional rows and columns. #' #' @export diff --git a/R/map_filter_observations.R b/R/map_filter_observations.R index 8347012..7c620b0 100644 --- a/R/map_filter_observations.R +++ b/R/map_filter_observations.R @@ -1,27 +1,28 @@ -#' Map `filter_observations()` function over multiple species +#' Map `filter_observations()` over multiple species #' -#' The function executes `filter_observations()` over multiple rows of a -#' dataframe, representing multiple different species, containing potentially +#' This function executes `filter_observations()` over multiple rows of a +#' dataframe, representing different species, with potentially #' different function arguments over multiple columns. #' -#' @param df A dataframe containing multiple rows. Each row is considered a +#' @param df A dataframe containing multiple rows, each representing a #' different species. The columns are function arguments with values used for -#' mapping `filter_observations()` for each species. `df` can have columns that -#' are not used by this function. They will be retained in the output. -#' @param nested Logical. If `TRUE` (default), retain list-column containing +#' mapping `filter_observations()` for each species. Columns not used by this +#' function will be retained in the output. +#' @param nested Logical. If `TRUE` (default), retains list-column containing #' sf objects/dataframes calculated by `filter_observations()`. Otherwise, -#' expand this list-column into rows and columns. +#' expands this list-column into rows and columns. #' @param arg_list A named list or `NA`. If `NA` (default), the function assumes #' column names in `df` are identical to argument names of -#' `filter_observations()`. If column names are not identical, they need to be +#' `filter_observations()`. If column names differ, they must be #' specified as a named list where the names are the argument names of -#' `filter_observations()`. -#' -#' @returns In case of `nested = TRUE`, a dataframe identical to the input -#' dataframe `df`, but with an extra list-column called `observations` -#' containing an sf object with POINT geometry or simple dataframe for each row -#' computed by `filter_observations()`. In case of `nested = FALSE`, this -#' list-column is expanded into additional rows and columns. +#' `filter_observations()`, and the associated values are the corresponding +#' column names in `df`. +#' +#' @returns In case of `nested = TRUE`, a dataframe identical to `df`, with an +#' extra list-column called `observations` containing an sf object with POINT +#' geometry or simple dataframe for each row computed by +#' `filter_observations()`. In case of `nested = FALSE`, this list-column is +#' expanded into additional rows and columns. #' #' @export #' diff --git a/R/map_grid_designation.R b/R/map_grid_designation.R index 1572dec..88b4a4c 100644 --- a/R/map_grid_designation.R +++ b/R/map_grid_designation.R @@ -1,25 +1,25 @@ -#' Map `grid_designation()` function over multiple species +#' Map `grid_designation()` over multiple species #' -#' The function executes `grid_designation()` over multiple rows of a -#' dataframe, representing multiple different species, containing potentially +#' This function executes `grid_designation()` over multiple rows of a +#' dataframe, representing different species, with potentially #' different function arguments over multiple columns. #' -#' @param df A dataframe containing multiple rows. Each row is considered a +#' @param df A dataframe containing multiple rows, each representing a #' different species. The columns are function arguments with values used for -#' mapping `grid_designation()` for each species. `df` can have -#' columns that are not used by this function. They will be retained in the -#' output. -#' @param nested Logical. If `TRUE` (default), retain list-column containing -#' sf objects calculated by `grid_designation()`. Otherwise, expand +#' mapping `grid_designation()` for each species. Columns not used by this +#' function will be retained in the output. +#' @param nested Logical. If `TRUE` (default), retains list-column containing +#' sf objects calculated by `grid_designation()`. Otherwise, expands #' this list-column into rows and columns. #' @param arg_list A named list or `NA`. If `NA` (default), the function assumes #' column names in `df` are identical to argument names of -#' `grid_designation()`. If column names are not identical, they need +#' `grid_designation()`. If column names differ, they must #' to be specified as a named list where the names are the argument names of -#' `grid_designation()`. +#' `grid_designation()`, and the associated values are the corresponding column +#' names in `df`. #' -#' @returns In case of `nested = TRUE`, a dataframe identical to the input -#' dataframe `df`, but each sf object with POINT geometry in the list-column +#' @returns In case of `nested = TRUE`, a dataframe identical to `df`, but each +#' sf object with POINT geometry in the list-column #' `observations` now has an additional column `coordinateUncertaintyInMeters` #' added by `grid_designation()`. In case of `nested = FALSE`, this #' list-column is expanded into additional rows and columns. diff --git a/R/map_sample_observations.R b/R/map_sample_observations.R index a478dc3..e458a3a 100644 --- a/R/map_sample_observations.R +++ b/R/map_sample_observations.R @@ -1,27 +1,28 @@ -#' Map `sample_observations()` function over multiple species +#' Map `sample_observations()` over multiple species #' -#' The function executes `sample_observations()` over multiple rows of a -#' dataframe, representing multiple different species, containing potentially +#' This function executes `sample_observations()` over multiple rows of a +#' dataframe, representing different species, with potentially #' different function arguments over multiple columns. #' -#' @param df A dataframe containing multiple rows. Each row is considered a +#' @param df A dataframe containing multiple rows, each representing a #' different species. The columns are function arguments with values used for -#' mapping `sample_observations()` for each species. `df` can have columns that -#' are not used by this function. They will be retained in the output. -#' @param nested Logical. If `TRUE` (default), retain list-column containing -#' sf objects calculated by `sample_observations()`. Otherwise, expand this +#' mapping `sample_observations()` for each species. Columns not used by this +#' function will be retained in the output. +#' @param nested Logical. If `TRUE` (default), retains list-column containing +#' sf objects calculated by `sample_observations()`. Otherwise, expands this #' list-column into rows and columns. #' @param arg_list A named list or `NA`. If `NA` (default), the function assumes #' column names in `df` are identical to argument names of -#' `sample_observations()`. If column names are not identical, they need to be +#' `sample_observations()`. If column names differ, they must be #' specified as a named list where the names are the argument names of -#' `sample_observations()`. -#' -#' @returns In case of `nested = TRUE`, a dataframe identical to the input -#' dataframe `df`, but with an extra list-column called `occurrences` containing -#' an sf object with POINT geometry for each row computed by -#' `sample_observations()`. In case of `nested = FALSE`, this list-column is -#' expanded into additional rows and columns. +#' `sample_observations()`, and the associated values are the corresponding +#' column names in `df`. +#' +#' @returns In case of `nested = TRUE`, a dataframe identical to `df`, with an +#' extra list-column called `occurrences` containing an sf object with POINT +#' geometry for each row computed by `sample_observations()`. In case of +#' `nested = FALSE`, this list-column is expanded into additional rows and +#' columns. #' #' @export #' diff --git a/R/map_simulate_occurrences.R b/R/map_simulate_occurrences.R index b0a98c8..fc484f9 100644 --- a/R/map_simulate_occurrences.R +++ b/R/map_simulate_occurrences.R @@ -1,30 +1,30 @@ -#' Map `simulate_occurrences()` function over multiple species +#' Map `simulate_occurrences()` over multiple species #' -#' The function executes `simulate_occurrences()` over multiple rows of a -#' dataframe, representing multiple different species, containing potentially +#' This function executes `simulate_occurrences()` over multiple rows of a +#' dataframe, representing different species, with potentially #' different function arguments over multiple columns. #' -#' @param df A dataframe containing multiple rows. Each row is considered a +#' @param df A dataframe containing multiple rows, each representing a #' different species. The columns are function arguments with values used for -#' mapping `simulate_occurrences()` for each species. `df` can have columns that -#' are not used by this function. They will be retained in the output. -#' @param nested Logical. If `TRUE` (default), retain list-column containing -#' sf objects calculated by `simulate_occurrences()`. Otherwise, expand this +#' mapping `simulate_occurrences()` for each species. Columns not used by this +#' function will be retained in the output. +#' @param nested Logical. If `TRUE` (default), retains list-column containing +#' sf objects calculated by `simulate_occurrences()`. Otherwise, expands this #' list-column into rows and columns. #' @param arg_list A named list or `NA`. If `NA` (default), the function assumes #' column names in `df` are identical to argument names of #' `simulate_occurrences()` and the function specified in its -#' `temporal_function` argument. If column names are not identical, they need to +#' `temporal_function` argument. If column names differ, they must #' be specified as a named list where the names are the argument names of #' `simulate_occurrences()` or the function specified in its `temporal_function` -#' argument, and their associated values a string of the corresponding column -#' name in `df`. -#' -#' @returns In case of `nested = TRUE`, a dataframe identical to the input -#' dataframe `df`, but with an extra list-column called `occurrences` containing -#' an sf object with POINT geometry for each row computed by -#' `simulate_occurrences()`. In case of `nested = FALSE`, this list-column is -#' expanded into additional rows and columns. +#' argument, and the associated values are the corresponding column +#' names in `df`. +#' +#' @returns In case of `nested = TRUE`, a dataframe identical to `df`, with an +#' extra list-column called `occurrences` containing an sf object with POINT +#' geometry for each row computed by `simulate_occurrences()`. In case of +#' `nested = FALSE`, this list-column is expanded into additional rows and +#' columns. #' #' @export #' diff --git a/R/map_simulation_functions.R b/R/map_simulation_functions.R index 86dc6d7..bd966a2 100644 --- a/R/map_simulation_functions.R +++ b/R/map_simulation_functions.R @@ -1,27 +1,26 @@ -#' Map cube simulation functions over multiple rows of a dataframe +#' Map a cube simulation function over multiple rows of a dataframe #' -#' The function executes a cube simulation function (`simulate_occurrences()`, +#' This function executes a cube simulation function (`simulate_occurrences()`, #' `sample_observations()`, `filter_observations()`, #' `add_coordinate_uncertainty()`, or `grid_designation()`) over multiple rows -#' of a dataframe containing potentially different function arguments over -#' multiple columns. +#' of a dataframe with potentially different function arguments over multiple +#' columns. #' #' @param f One of five cube simulation functions: `simulate_occurrences()`, #' `sample_observations()`, `filter_observations()`, #' `add_coordinate_uncertainty()`, or `grid_designation()`. -#' @param df A dataframe containing multiple rows. Each row is considered a +#' @param df A dataframe containing multiple rows, each representing a #' different species. The columns are function arguments with values used for -#' mapping `f` for each species. `df` can have columns that are not used by this -#' function. They will be retained in the output. -#' @param nested Logical. If `TRUE` (default), retain list-column containing -#' dataframes calculated by `f`. Otherwise, expand this list-column into rows +#' mapping `f` for each species. Columns not used by this +#' function will be retained in the output. +#' @param nested Logical. If `TRUE` (default), retains list-column containing +#' dataframes calculated by `f`. Otherwise, expands this list-column into rows #' and columns. #' -#' @returns In case of `nested = TRUE`, a dataframe identical to the input -#' dataframe `df`, but with an extra list-column called `mapped_col` containing -#' an sf object for each row computed by the function specified in `f`. In case -#' of `nested = FALSE`, this list-column is expanded into additional rows and -#' columns. +#' @returns In case of `nested = TRUE`, a dataframe identical to `df`, with an +#' extra list-column called `mapped_col` containing an sf object for each row +#' computed by the function specified in `f`. In case of `nested = FALSE`, this +#' list-column is expanded into additional rows and columns. #' #' @export #' diff --git a/R/sample_observations.R b/R/sample_observations.R index d2dba9d..deb84e1 100644 --- a/R/sample_observations.R +++ b/R/sample_observations.R @@ -44,7 +44,7 @@ #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. #' -#' @return An sf object with POINT geometry containing the locations of the +#' @returns An sf object with POINT geometry containing the locations of the #' occurrence with detection status. The object includes the following columns: #' \describe{ #' \item{`detection_probability`}{The detection probability for each diff --git a/R/sample_occurrences_from_raster.R b/R/sample_occurrences_from_raster.R index 132643e..6d41dee 100644 --- a/R/sample_occurrences_from_raster.R +++ b/R/sample_occurrences_from_raster.R @@ -7,7 +7,7 @@ #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. #' -#' @return An sf object with POINT geometry. +#' @returns An sf object with POINT geometry. #' #' @export #' diff --git a/R/simulate_timeseries.R b/R/simulate_timeseries.R index 2f96c67..d50b59f 100644 --- a/R/simulate_timeseries.R +++ b/R/simulate_timeseries.R @@ -16,7 +16,7 @@ #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. #' -#' @return A vector of integers of length `n_time_points` with the number of +#' @returns A vector of integers of length `n_time_points` with the number of #' occurrences. #' #' @export diff --git a/R/utils.R b/R/utils.R index e489284..0628b2d 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,19 +1,26 @@ -#' Function to collect arguments of cube simulation functions +#' Function to collect arguments from cube simulation functions #' -#' This function collects all arguments of a cube simulation function -#' (`simulate_occurrences()`, `sample_observations()` or `grid_designation()`) +#' This function collects all arguments from a cube simulation function +#' (`simulate_occurrences()`, `sample_observations()`, `filter_observations()`, +#' `add_coordinate_uncertainty()`, or `grid_designation()`) #' also taking into account the ellipsis (`...`) and `temporal_function` #' of `simulate_occurrences()`. #' -#' @param f One of three cube simulation functions: `simulate_occurrences()`, -#' `sample_observations()` or `grid_designation()`. -#' @param df A dataframe containing multiple rows. Each row is considered a +#' @param f One of five cube simulation functions: `simulate_occurrences()`, +#' `sample_observations()`, `filter_observations()`, +#' `add_coordinate_uncertainty()`, or `grid_designation()`. +#' @param df A dataframe containing multiple rows, each representing a #' different species. The columns are function arguments with values used for -#' mapping `simulate_occurrences()` for each species. `df` can have columns that -#' are not used by this function. They will be retained in the output. +#' mapping `f` for each species. This dataframe is used to get the correct +#' arguments of the `temporal_function` of if `f` is `simulate_occurrences()`. #' #' @importFrom methods formalArgs #' +#' @returns A character vector of argument names for the specified function `f`. +#' If `f` is `simulate_occurrences` and the dataframe `df` contains a +#' `temporal_function` column, the returned vector includes arguments from the +#' `temporal_function`. +#' #' @noRd get_function_arguments <- function(f, df) { @@ -34,15 +41,29 @@ get_function_arguments <- function(f, df) { } -#' Function to return repeated warnings during mapping +#' Handle repeated warnings during mapping #' -#' This function stores all warnings captured by `purrr::quietly()` that -#' occurred during mapping and reports them in a clean way. +#' This function captures and reports warnings that occurred during the +#' execution of a mapping function wrapped with `purrr::quietly()`. The function +#' extracts warnings from the results stored in a list-column of a dataframe, +#' counts their occurrences, and prints a summary of these warnings. #' #' @param df A dataframe containing a list-column with the output of a mapped -#' function wrapped with `purrr::quietly()`. -#' @param mapped_col The name of the list-column described above. Defaults to -#' `"mapped_col"`. +#' function wrapped with `purrr::quietly()`. This list-column should include +#' a `$warnings` component for capturing warnings. +#' @param mapped_col The name of the list-column in `df` that contains the output +#' from the mapped function. Defaults to `"mapped_col"`. +#' +#' @returns A dataframe identical to the input `df`, but with the list-column +#' specified by `mapped_col` updated to retain only the results from the mapping +#' function, excluding any warnings. +#' +#' @details This function first verifies that the specified `mapped_col` is +#' present in the dataframe and that it is a list-column. It then collects all +#' warnings from the list-column, counts their occurrences, and prints a summary +#' of these warnings. Finally, the function updates the dataframe to include +#' only the results from the mapping function, discarding any warning +#' information. #' #' @noRd diff --git a/man/add_coordinate_uncertainty.Rd b/man/add_coordinate_uncertainty.Rd index 117d5b5..92b561f 100644 --- a/man/add_coordinate_uncertainty.Rd +++ b/man/add_coordinate_uncertainty.Rd @@ -7,18 +7,24 @@ add_coordinate_uncertainty(observations, coords_uncertainty_meters = 25) } \arguments{ -\item{observations}{An sf object with POINT geometry.} +\item{observations}{An sf object with POINT geometry representing the +observations. This object contains the spatial points to which the coordinate +uncertainty will be added.} \item{coords_uncertainty_meters}{A numeric value or a vector of numeric -values indicating the coordinate uncertainty associated with observations.} +values representing the coordinate uncertainty (in meters) associated with +each observation. If a single numeric value is provided, it will be applied +to all observations. If a numeric vector is provided, it must be the same +length as the number of observations.} } \value{ -An sf object with POINT geometry with an additional column -\code{coordinateUncertaintyInMeters}. +The input sf object with POINT geometry, with an additional column +named \code{coordinateUncertaintyInMeters} that contains the coordinate +uncertainty values in meters. } \description{ -Adds a column to the observations sf object with the coordinate uncertainty -in meters. +This function adds a column to the input sf object containing the coordinate +uncertainty for each observation, measured in meters. } \examples{ diff --git a/man/create_spatial_pattern.Rd b/man/create_spatial_pattern.Rd index 8a6afb0..f71b037 100644 --- a/man/create_spatial_pattern.Rd +++ b/man/create_spatial_pattern.Rd @@ -21,7 +21,7 @@ create_spatial_pattern( be a character string (\code{"random"} or \code{"clustered"}) or a numeric value ≥ 1 (1 means random distribution, larger values indicate more clustering). The default is \code{"random"}. \code{"clustered"} corresponds to a value of 10. -See details.} +See Details.} \item{seed}{A positive numeric value setting the seed for random number generation to ensure reproducibility. If \code{NA} (default), no seed is used.} diff --git a/man/filter_observations.Rd b/man/filter_observations.Rd index 440bb8e..a355c88 100644 --- a/man/filter_observations.Rd +++ b/man/filter_observations.Rd @@ -2,25 +2,28 @@ % Please edit documentation in R/filter_observations.R \name{filter_observations} \alias{filter_observations} -\title{Filter detected observations} +\title{Filter detected occurrences} \usage{ filter_observations(observations_total, invert = FALSE) } \arguments{ \item{observations_total}{An sf object with POINT geometry or a simple dataframe with \code{sampling_status} column containing values \code{"detected"}. -This format is created by \code{sample_observations()}.} +This format is typically created by the \code{sample_observations()} function.} -\item{invert}{Logical. If \code{FALSE} (default), filter \code{"detected"} -occurrences. Otherwise, filter all other occurrences.} +\item{invert}{Logical. If \code{FALSE} (default), the function filters to retain +only \code{"detected"} occurrences. If \code{TRUE}, it filters out \code{"detected"} +occurrences and retains all other occurrences.} } \value{ -A dataframe or an sf object with POINT geometry containing detected -occurrences (if \code{invert = FALSE}), or other occurrences (if \code{invert = TRUE}). +A data frame or an sf object with POINT geometry containing the +filtered observations. If \code{invert = FALSE}, the function returns detected +occurrences. If \code{invert = TRUE}, it returns all other occurrences. } \description{ -The function filters observations from all observations based on a -\code{sampling_status} column, e.g. created by \code{sample_observations()}. +This function filters observations from all occurrences based on the +\code{sampling_status} column, typically created by the \code{sample_observations()} +function. } \examples{ # Load packages diff --git a/man/generate_taxonomy.Rd b/man/generate_taxonomy.Rd index a300b28..fce04cf 100644 --- a/man/generate_taxonomy.Rd +++ b/man/generate_taxonomy.Rd @@ -48,10 +48,10 @@ of species, genera, families, orders, classes, phyla, and kingdoms. The output is a data frame with the hierarchical classification for each species. } \details{ -The function works by randomly assigning species to genera, genera to -families, families to orders, orders to classes, classes to phyla, and phyla -to kingdoms. Sampling is done with replacement, meaning that multiple -lower-level taxa (e.g., species) can be assigned to the same higher-level +The function works by randomly assigning species to genera, genera +to families, families to orders, orders to classes, classes to phyla, and +phyla to kingdoms. Sampling is done with replacement, allowing multiple +lower-level taxa (e.g., species) to be assigned to the same higher-level taxon (e.g., genus). } \examples{ diff --git a/man/grid_designation.Rd b/man/grid_designation.Rd index 8017db5..f7eb330 100644 --- a/man/grid_designation.Rd +++ b/man/grid_designation.Rd @@ -16,52 +16,49 @@ grid_designation( } \arguments{ \item{observations}{An sf object with POINT geometry and a \code{time_point} and -\code{coordinateUncertaintyInMeters} column. If this last column is not present, -the function will assume no (zero meters) uncertainty around the observation +\code{coordinateUncertaintyInMeters} column. If the latter column is not present, +the function will assume no uncertainty (zero meters) around the observation points.} \item{grid}{An sf object with POLYGON geometry (usually a grid) to which observations should be designated.} -\item{id_col}{The column name of the column with unique ids for each grid -cell. If \code{"row_names"} (the default), a new column \code{id} is created were the -row names represent the unique ids.} +\item{id_col}{The column name containing unique IDs for each grid cell. If +\code{"row_names"} (the default), a new column \code{id} is created where the row names +represent the unique IDs.} \item{seed}{A positive numeric value setting the seed for random number generation to ensure reproducibility. If \code{NA} (default), no seed is used.} -\item{aggregate}{Logical. If \code{TRUE} (default), return data cube in -aggregated form (grid with number of observations per grid cell). Otherwise -return sampled points in uncertainty circle.} +\item{aggregate}{Logical. If \code{TRUE} (default), returns data cube in +aggregated form (grid with the number of observations per grid cell). +Otherwise, returns sampled points within the uncertainty circle.} -\item{randomisation}{\code{"uniform"} or \code{"normal"}. Randomisation method used -for sampling within uncertainty circle around each observation. By default -\code{"uniform"} which means each point uncertainty circle has an equal -probability to be selected. The other option is \code{"normal"} where a point is -sampled from a bivariate Normal distribution with means equal to the -observation point and the variance equal to -(-\code{coordinateUncertaintyInMeters}^2) / (2 * log(1 - \code{p_norm})) such that -\code{p_norm} \% of all possible samples from this Normal distribution fall -within the uncertainty circle.} +\item{randomisation}{Character. Method used for sampling within the +uncertainty circle around each observation. \code{"uniform"} (default) means each +point in the uncertainty circle has an equal probability of being selected. +The other option is \code{"normal"}, where a point is sampled from a bivariate +Normal distribution with means equal to the observation point and variance +such that \code{p_norm} \% of all possible samples from this Normal distribution +fall within the uncertainty circle. +See \code{sample_from_binormal_circle()}.} -\item{p_norm}{A numeric value between 0 and 1. Only used if -\code{randomisation = "normal"}. The proportion of all possible samples from a a -bivariate Normal distribution that fall within the uncertainty circle. If -normal randomisation is used and no value is given, the default \code{p_norm} -value is 0.95.} +\item{p_norm}{A numeric value between 0 and 1, used only if +\code{randomisation = "normal"}. The proportion of all possible samples from a +bivariate Normal distribution that fall within the uncertainty circle. +Default is 0.95.} } \value{ -In case of \code{aggregate = TRUE}, an sf object with POLYGON geometry -containing the locations of the grid cells, an \code{n} column with the number of -observations per grid cell, and a \code{min_coord_uncertainty} column containing -the minimal coordinate uncertainty per grid cell. In case of -\code{aggregate = FALSE}, an sf object with POINT geometry containing the -locations of the sampled observations within the uncertainty circle, and a -\code{coordinateUncertaintyInMeters} column containing the coordinate uncertainty +If \code{aggregate = TRUE}, an sf object with POLYGON geometry +containing the grid cells, an \code{n} column with the number of observations per +grid cell, and a \code{min_coord_uncertainty} column with the minimum coordinate +uncertainty per grid cell. If \code{aggregate = FALSE}, an sf object with POINT +geometry containing the sampled observations within the uncertainty circles, +and a \code{coordinateUncertaintyInMeters} column with the coordinate uncertainty for each observation. } \description{ -The function designates observations to cells of a given grid to create an +This function designates observations to cells of a given grid to create an aggregated data cube. } \examples{ diff --git a/man/map_add_coordinate_uncertainty.Rd b/man/map_add_coordinate_uncertainty.Rd index 8e2c6fb..72f12e8 100644 --- a/man/map_add_coordinate_uncertainty.Rd +++ b/man/map_add_coordinate_uncertainty.Rd @@ -2,37 +2,37 @@ % Please edit documentation in R/map_add_coordinate_uncertainty.R \name{map_add_coordinate_uncertainty} \alias{map_add_coordinate_uncertainty} -\title{Map \code{add_coordinate_uncertainty()} function over multiple species} +\title{Map \code{add_coordinate_uncertainty()} over multiple species} \usage{ map_add_coordinate_uncertainty(df, nested = TRUE, arg_list = NA) } \arguments{ -\item{df}{A dataframe containing multiple rows. Each row is considered a +\item{df}{A dataframe containing multiple rows, each representing a different species. The columns are function arguments with values used for -mapping \code{add_coordinate_uncertainty()} for each species. \code{df} can have -columns that are not used by this function. They will be retained in the -output.} +mapping \code{add_coordinate_uncertainty()} for each species. Columns not used by +this function will be retained in the output.} -\item{nested}{Logical. If \code{TRUE} (default), retain list-column containing -sf objects calculated by \code{add_coordinate_uncertainty()}. Otherwise, expand +\item{nested}{Logical. If \code{TRUE} (default), retains list-column containing +sf objects calculated by \code{add_coordinate_uncertainty()}. Otherwise, expands this list-column into rows and columns.} \item{arg_list}{A named list or \code{NA}. If \code{NA} (default), the function assumes column names in \code{df} are identical to argument names of -\code{add_coordinate_uncertainty()}. If column names are not identical, they need +\code{add_coordinate_uncertainty()}. If column names differ, they must to be specified as a named list where the names are the argument names of -\code{add_coordinate_uncertainty()}.} +\code{add_coordinate_uncertainty()}, and the associated values are the +corresponding column names in \code{df}.} } \value{ -In case of \code{nested = TRUE}, a dataframe identical to the input -dataframe \code{df}, but each sf object with POINT geometry in the list-column -\code{observations} now has an additional column \code{coordinateUncertaintyInMeters} -added by \code{add_coordinate_uncertainty()}. In case of \code{nested = FALSE}, this +In case of \code{nested = TRUE}, a dataframe identical to \code{df}, but each +sf object with POINT geometry in the list-column \code{observations} now has an +additional column \code{coordinateUncertaintyInMeters} added by +\code{add_coordinate_uncertainty()}. In case of \code{nested = FALSE}, this list-column is expanded into additional rows and columns. } \description{ -The function executes \code{add_coordinate_uncertainty()} over multiple rows of a -dataframe, representing multiple different species, containing potentially +This function executes \code{add_coordinate_uncertainty()} over multiple rows of a +dataframe, representing different species, with potentially different function arguments over multiple columns. } \examples{ diff --git a/man/map_filter_observations.Rd b/man/map_filter_observations.Rd index 796afb8..c6122a4 100644 --- a/man/map_filter_observations.Rd +++ b/man/map_filter_observations.Rd @@ -2,36 +2,37 @@ % Please edit documentation in R/map_filter_observations.R \name{map_filter_observations} \alias{map_filter_observations} -\title{Map \code{filter_observations()} function over multiple species} +\title{Map \code{filter_observations()} over multiple species} \usage{ map_filter_observations(df, nested = TRUE, arg_list = NA) } \arguments{ -\item{df}{A dataframe containing multiple rows. Each row is considered a +\item{df}{A dataframe containing multiple rows, each representing a different species. The columns are function arguments with values used for -mapping \code{filter_observations()} for each species. \code{df} can have columns that -are not used by this function. They will be retained in the output.} +mapping \code{filter_observations()} for each species. Columns not used by this +function will be retained in the output.} -\item{nested}{Logical. If \code{TRUE} (default), retain list-column containing +\item{nested}{Logical. If \code{TRUE} (default), retains list-column containing sf objects/dataframes calculated by \code{filter_observations()}. Otherwise, -expand this list-column into rows and columns.} +expands this list-column into rows and columns.} \item{arg_list}{A named list or \code{NA}. If \code{NA} (default), the function assumes column names in \code{df} are identical to argument names of -\code{filter_observations()}. If column names are not identical, they need to be +\code{filter_observations()}. If column names differ, they must be specified as a named list where the names are the argument names of -\code{filter_observations()}.} +\code{filter_observations()}, and the associated values are the corresponding +column names in \code{df}.} } \value{ -In case of \code{nested = TRUE}, a dataframe identical to the input -dataframe \code{df}, but with an extra list-column called \code{observations} -containing an sf object with POINT geometry or simple dataframe for each row -computed by \code{filter_observations()}. In case of \code{nested = FALSE}, this -list-column is expanded into additional rows and columns. +In case of \code{nested = TRUE}, a dataframe identical to \code{df}, with an +extra list-column called \code{observations} containing an sf object with POINT +geometry or simple dataframe for each row computed by +\code{filter_observations()}. In case of \code{nested = FALSE}, this list-column is +expanded into additional rows and columns. } \description{ -The function executes \code{filter_observations()} over multiple rows of a -dataframe, representing multiple different species, containing potentially +This function executes \code{filter_observations()} over multiple rows of a +dataframe, representing different species, with potentially different function arguments over multiple columns. } \examples{ diff --git a/man/map_grid_designation.Rd b/man/map_grid_designation.Rd index 92fa9ed..60b1ac0 100644 --- a/man/map_grid_designation.Rd +++ b/man/map_grid_designation.Rd @@ -2,37 +2,37 @@ % Please edit documentation in R/map_grid_designation.R \name{map_grid_designation} \alias{map_grid_designation} -\title{Map \code{grid_designation()} function over multiple species} +\title{Map \code{grid_designation()} over multiple species} \usage{ map_grid_designation(df, nested = TRUE, arg_list = NA) } \arguments{ -\item{df}{A dataframe containing multiple rows. Each row is considered a +\item{df}{A dataframe containing multiple rows, each representing a different species. The columns are function arguments with values used for -mapping \code{grid_designation()} for each species. \code{df} can have -columns that are not used by this function. They will be retained in the -output.} +mapping \code{grid_designation()} for each species. Columns not used by this +function will be retained in the output.} -\item{nested}{Logical. If \code{TRUE} (default), retain list-column containing -sf objects calculated by \code{grid_designation()}. Otherwise, expand +\item{nested}{Logical. If \code{TRUE} (default), retains list-column containing +sf objects calculated by \code{grid_designation()}. Otherwise, expands this list-column into rows and columns.} \item{arg_list}{A named list or \code{NA}. If \code{NA} (default), the function assumes column names in \code{df} are identical to argument names of -\code{grid_designation()}. If column names are not identical, they need +\code{grid_designation()}. If column names differ, they must to be specified as a named list where the names are the argument names of -\code{grid_designation()}.} +\code{grid_designation()}, and the associated values are the corresponding column +names in \code{df}.} } \value{ -In case of \code{nested = TRUE}, a dataframe identical to the input -dataframe \code{df}, but each sf object with POINT geometry in the list-column +In case of \code{nested = TRUE}, a dataframe identical to \code{df}, but each +sf object with POINT geometry in the list-column \code{observations} now has an additional column \code{coordinateUncertaintyInMeters} added by \code{grid_designation()}. In case of \code{nested = FALSE}, this list-column is expanded into additional rows and columns. } \description{ -The function executes \code{grid_designation()} over multiple rows of a -dataframe, representing multiple different species, containing potentially +This function executes \code{grid_designation()} over multiple rows of a +dataframe, representing different species, with potentially different function arguments over multiple columns. } \examples{ diff --git a/man/map_sample_observations.Rd b/man/map_sample_observations.Rd index 8958c13..bc13236 100644 --- a/man/map_sample_observations.Rd +++ b/man/map_sample_observations.Rd @@ -2,36 +2,37 @@ % Please edit documentation in R/map_sample_observations.R \name{map_sample_observations} \alias{map_sample_observations} -\title{Map \code{sample_observations()} function over multiple species} +\title{Map \code{sample_observations()} over multiple species} \usage{ map_sample_observations(df, nested = TRUE, arg_list = NA) } \arguments{ -\item{df}{A dataframe containing multiple rows. Each row is considered a +\item{df}{A dataframe containing multiple rows, each representing a different species. The columns are function arguments with values used for -mapping \code{sample_observations()} for each species. \code{df} can have columns that -are not used by this function. They will be retained in the output.} +mapping \code{sample_observations()} for each species. Columns not used by this +function will be retained in the output.} -\item{nested}{Logical. If \code{TRUE} (default), retain list-column containing -sf objects calculated by \code{sample_observations()}. Otherwise, expand this +\item{nested}{Logical. If \code{TRUE} (default), retains list-column containing +sf objects calculated by \code{sample_observations()}. Otherwise, expands this list-column into rows and columns.} \item{arg_list}{A named list or \code{NA}. If \code{NA} (default), the function assumes column names in \code{df} are identical to argument names of -\code{sample_observations()}. If column names are not identical, they need to be +\code{sample_observations()}. If column names differ, they must be specified as a named list where the names are the argument names of -\code{sample_observations()}.} +\code{sample_observations()}, and the associated values are the corresponding +column names in \code{df}.} } \value{ -In case of \code{nested = TRUE}, a dataframe identical to the input -dataframe \code{df}, but with an extra list-column called \code{occurrences} containing -an sf object with POINT geometry for each row computed by -\code{sample_observations()}. In case of \code{nested = FALSE}, this list-column is -expanded into additional rows and columns. +In case of \code{nested = TRUE}, a dataframe identical to \code{df}, with an +extra list-column called \code{occurrences} containing an sf object with POINT +geometry for each row computed by \code{sample_observations()}. In case of +\code{nested = FALSE}, this list-column is expanded into additional rows and +columns. } \description{ -The function executes \code{sample_observations()} over multiple rows of a -dataframe, representing multiple different species, containing potentially +This function executes \code{sample_observations()} over multiple rows of a +dataframe, representing different species, with potentially different function arguments over multiple columns. } \examples{ diff --git a/man/map_simulate_occurrences.Rd b/man/map_simulate_occurrences.Rd index 293e67c..808499a 100644 --- a/man/map_simulate_occurrences.Rd +++ b/man/map_simulate_occurrences.Rd @@ -2,39 +2,39 @@ % Please edit documentation in R/map_simulate_occurrences.R \name{map_simulate_occurrences} \alias{map_simulate_occurrences} -\title{Map \code{simulate_occurrences()} function over multiple species} +\title{Map \code{simulate_occurrences()} over multiple species} \usage{ map_simulate_occurrences(df, nested = TRUE, arg_list = NA) } \arguments{ -\item{df}{A dataframe containing multiple rows. Each row is considered a +\item{df}{A dataframe containing multiple rows, each representing a different species. The columns are function arguments with values used for -mapping \code{simulate_occurrences()} for each species. \code{df} can have columns that -are not used by this function. They will be retained in the output.} +mapping \code{simulate_occurrences()} for each species. Columns not used by this +function will be retained in the output.} -\item{nested}{Logical. If \code{TRUE} (default), retain list-column containing -sf objects calculated by \code{simulate_occurrences()}. Otherwise, expand this +\item{nested}{Logical. If \code{TRUE} (default), retains list-column containing +sf objects calculated by \code{simulate_occurrences()}. Otherwise, expands this list-column into rows and columns.} \item{arg_list}{A named list or \code{NA}. If \code{NA} (default), the function assumes column names in \code{df} are identical to argument names of \code{simulate_occurrences()} and the function specified in its -\code{temporal_function} argument. If column names are not identical, they need to +\code{temporal_function} argument. If column names differ, they must be specified as a named list where the names are the argument names of \code{simulate_occurrences()} or the function specified in its \code{temporal_function} -argument, and their associated values a string of the corresponding column -name in \code{df}.} +argument, and the associated values are the corresponding column +names in \code{df}.} } \value{ -In case of \code{nested = TRUE}, a dataframe identical to the input -dataframe \code{df}, but with an extra list-column called \code{occurrences} containing -an sf object with POINT geometry for each row computed by -\code{simulate_occurrences()}. In case of \code{nested = FALSE}, this list-column is -expanded into additional rows and columns. +In case of \code{nested = TRUE}, a dataframe identical to \code{df}, with an +extra list-column called \code{occurrences} containing an sf object with POINT +geometry for each row computed by \code{simulate_occurrences()}. In case of +\code{nested = FALSE}, this list-column is expanded into additional rows and +columns. } \description{ -The function executes \code{simulate_occurrences()} over multiple rows of a -dataframe, representing multiple different species, containing potentially +This function executes \code{simulate_occurrences()} over multiple rows of a +dataframe, representing different species, with potentially different function arguments over multiple columns. } \examples{ diff --git a/man/map_simulation_functions.Rd b/man/map_simulation_functions.Rd index 9fe30af..9c257d3 100644 --- a/man/map_simulation_functions.Rd +++ b/man/map_simulation_functions.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/map_simulation_functions.R \name{map_simulation_functions} \alias{map_simulation_functions} -\title{Map cube simulation functions over multiple rows of a dataframe} +\title{Map a cube simulation function over multiple rows of a dataframe} \usage{ map_simulation_functions(f, df, nested = TRUE) } @@ -11,28 +11,27 @@ map_simulation_functions(f, df, nested = TRUE) \code{sample_observations()}, \code{filter_observations()}, \code{add_coordinate_uncertainty()}, or \code{grid_designation()}.} -\item{df}{A dataframe containing multiple rows. Each row is considered a +\item{df}{A dataframe containing multiple rows, each representing a different species. The columns are function arguments with values used for -mapping \code{f} for each species. \code{df} can have columns that are not used by this -function. They will be retained in the output.} +mapping \code{f} for each species. Columns not used by this +function will be retained in the output.} -\item{nested}{Logical. If \code{TRUE} (default), retain list-column containing -dataframes calculated by \code{f}. Otherwise, expand this list-column into rows +\item{nested}{Logical. If \code{TRUE} (default), retains list-column containing +dataframes calculated by \code{f}. Otherwise, expands this list-column into rows and columns.} } \value{ -In case of \code{nested = TRUE}, a dataframe identical to the input -dataframe \code{df}, but with an extra list-column called \code{mapped_col} containing -an sf object for each row computed by the function specified in \code{f}. In case -of \code{nested = FALSE}, this list-column is expanded into additional rows and -columns. +In case of \code{nested = TRUE}, a dataframe identical to \code{df}, with an +extra list-column called \code{mapped_col} containing an sf object for each row +computed by the function specified in \code{f}. In case of \code{nested = FALSE}, this +list-column is expanded into additional rows and columns. } \description{ -The function executes a cube simulation function (\code{simulate_occurrences()}, +This function executes a cube simulation function (\code{simulate_occurrences()}, \code{sample_observations()}, \code{filter_observations()}, \code{add_coordinate_uncertainty()}, or \code{grid_designation()}) over multiple rows -of a dataframe containing potentially different function arguments over -multiple columns. +of a dataframe with potentially different function arguments over multiple +columns. } \examples{ # Load packages diff --git a/man/sample_from_binormal_circle.Rd b/man/sample_from_binormal_circle.Rd index 77c6d65..b401c33 100644 --- a/man/sample_from_binormal_circle.Rd +++ b/man/sample_from_binormal_circle.Rd @@ -8,13 +8,14 @@ sample_from_binormal_circle(observations, p_norm = 0.95, seed = NA) } \arguments{ \item{observations}{An sf object with POINT geometry and a \code{time_point} and -\code{coordinateUncertaintyInMeters} column. If this last column is not present, -the function will assume no (zero meters) uncertainty around the observation +\code{coordinateUncertaintyInMeters} column. If the latter column is not present, +the function will assume no uncertainty (zero meters) around the observation points.} -\item{p_norm}{A numeric value between 0 and 1. The proportion of all -possible samples from a a bivariate Normal distribution that fall within the -uncertainty circle. If no value is given, the default \code{p_norm} value is 0.95.} +\item{p_norm}{A numeric value between 0 and 1. The proportion of all possible +samples from a bivariate Normal distribution that fall within the uncertainty +circle. Default is 0.95. +See Details.} \item{seed}{A positive numeric value setting the seed for random number generation to ensure reproducibility. If \code{NA} (default), no seed is used.} @@ -25,12 +26,17 @@ sampled occurrences and a \code{coordinateUncertaintyInMeters} column containing the coordinate uncertainty for each observation. } \description{ -The function samples occurrences of a species within the uncertainty circle -around each observation assuming a bivariate Normal distribution with means -equal to the observation point and the variance equal to -(-\code{coordinateUncertaintyInMeters}^2) / (2 * log(1 - \code{p_norm})) such that -\code{p_norm} \% of all possible samples from this Normal distribution fall within -the uncertainty circle. +This function samples a new observations point of a species within the +uncertainty circle around each observation assuming a bivariate Normal +distribution. +} +\details{ +A new observation point is sampled from a bivariate Normal +distribution with means equal to the X and Y coordinates of its original +observation point and variances equal to +(-\code{coordinateUncertaintyInMeters}^2) / (2 * log(1 - \code{p_norm})), +ensuring \code{p_norm} \% of all possible samples fall within the uncertainty +circle. } \examples{ library(sf) diff --git a/man/sample_from_uniform_circle.Rd b/man/sample_from_uniform_circle.Rd index 34abf6c..9159f2d 100644 --- a/man/sample_from_uniform_circle.Rd +++ b/man/sample_from_uniform_circle.Rd @@ -8,8 +8,8 @@ sample_from_uniform_circle(observations, seed = NA) } \arguments{ \item{observations}{An sf object with POINT geometry and a \code{time_point} and -\code{coordinateUncertaintyInMeters} column. If this last column is not present, -the function will assume no (zero meters) uncertainty around the observation +\code{coordinateUncertaintyInMeters} column. If the latter column is not present, +the function will assume no uncertainty (zero meters) around the observation points.} \item{seed}{A positive numeric value setting the seed for random number @@ -21,8 +21,8 @@ sampled occurrences and a \code{coordinateUncertaintyInMeters} column containing the coordinate uncertainty for each observation. } \description{ -The function samples occurrences of a species within the uncertainty circle -around each observation assuming a Uniform distribution. +This function samples a new observations point of a species within the +uncertainty circle around each observation assuming a Uniform distribution. } \examples{ library(sf) From 98ff25c8799c1a20c18df4873446aacb856ddc43 Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 11:17:53 +0200 Subject: [PATCH 13/57] update version --- codemeta.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codemeta.json b/codemeta.json index e92bf94..abf6436 100644 --- a/codemeta.json +++ b/codemeta.json @@ -8,7 +8,7 @@ "codeRepository": "https://github.com/b-cubed-eu/gcube", "issueTracker": "https://github.com/b-cubed-eu/gcube/issues", "license": "https://spdx.org/licenses/MIT", - "version": "0.3.0", + "version": "0.4.0", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", @@ -346,7 +346,7 @@ }, "SystemRequirements": null }, - "fileSize": "549.025KB", + "fileSize": "549.627KB", "citation": [ { "@type": "SoftwareSourceCode", @@ -358,7 +358,7 @@ "familyName": "Langeraert" } }, - "name": "gcube: Simulating Biodiversity Data Cubes. Version 0.3.0", + "name": "gcube: Simulating Biodiversity Data Cubes. Version 0.4.0", "url": "https://b-cubed-eu.github.io/gcube/" } ], From 5143aaed07a12e395befd13e239c8bf627c8d02e Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 11:18:03 +0200 Subject: [PATCH 14/57] coding style --- R/utils.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/utils.R b/R/utils.R index 0628b2d..ab6c4eb 100644 --- a/R/utils.R +++ b/R/utils.R @@ -51,8 +51,8 @@ get_function_arguments <- function(f, df) { #' @param df A dataframe containing a list-column with the output of a mapped #' function wrapped with `purrr::quietly()`. This list-column should include #' a `$warnings` component for capturing warnings. -#' @param mapped_col The name of the list-column in `df` that contains the output -#' from the mapped function. Defaults to `"mapped_col"`. +#' @param mapped_col The name of the list-column in `df` that contains the +#' output from the mapped function. Defaults to `"mapped_col"`. #' #' @returns A dataframe identical to the input `df`, but with the list-column #' specified by `mapped_col` updated to retain only the results from the mapping From 9afe14596474aee91dd798c5164b86b24b1f647f Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 11:36:45 +0200 Subject: [PATCH 15/57] update abundance argument --- R/map_add_coordinate_uncertainty.R | 2 +- R/map_filter_observations.R | 2 +- R/map_grid_designation.R | 2 +- R/map_sample_observations.R | 2 +- R/map_simulate_occurrences.R | 2 +- R/map_simulation_functions.R | 2 +- R/simulate_occurrences.R | 16 +++++++------- man/map_add_coordinate_uncertainty.Rd | 2 +- man/map_filter_observations.Rd | 2 +- man/map_grid_designation.Rd | 2 +- man/map_sample_observations.Rd | 2 +- man/map_simulate_occurrences.Rd | 2 +- man/map_simulation_functions.Rd | 2 +- man/simulate_occurrences.Rd | 8 +++---- .../test-map_add_coordinate_uncertainty.R | 2 +- tests/testthat/test-map_filter_observations.R | 2 +- tests/testthat/test-map_grid_designation.R | 2 +- tests/testthat/test-map_sample_observations.R | 4 ++-- .../testthat/test-map_simulate_occurrences.R | 2 +- .../testthat/test-map_simulation_functions.R | 2 +- tests/testthat/test-simulate_occurrences.R | 22 +++++++++---------- vignettes/articles/detection-process.Rmd | 2 +- .../articles/grid-designation-process.Rmd | 2 +- vignettes/articles/multi_species_approach.Rmd | 2 +- vignettes/articles/occurrence-process.Rmd | 2 +- 25 files changed, 46 insertions(+), 46 deletions(-) diff --git a/R/map_add_coordinate_uncertainty.R b/R/map_add_coordinate_uncertainty.R index 5c15bea..f5469f5 100644 --- a/R/map_add_coordinate_uncertainty.R +++ b/R/map_add_coordinate_uncertainty.R @@ -45,7 +45,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' plgn = rep(list(plgn), 3), -#' initial_average_abundance = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), diff --git a/R/map_filter_observations.R b/R/map_filter_observations.R index 7c620b0..fcfb48b 100644 --- a/R/map_filter_observations.R +++ b/R/map_filter_observations.R @@ -45,7 +45,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' plgn = rep(list(plgn), 3), -#' initial_average_abundance = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), diff --git a/R/map_grid_designation.R b/R/map_grid_designation.R index 88b4a4c..9dee10c 100644 --- a/R/map_grid_designation.R +++ b/R/map_grid_designation.R @@ -52,7 +52,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' plgn = rep(list(plgn), 3), -#' initial_average_abundance = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), diff --git a/R/map_sample_observations.R b/R/map_sample_observations.R index e458a3a..e971777 100644 --- a/R/map_sample_observations.R +++ b/R/map_sample_observations.R @@ -45,7 +45,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' plgn = rep(list(plgn), 3), -#' initial_average_abundance = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), diff --git a/R/map_simulate_occurrences.R b/R/map_simulate_occurrences.R index fc484f9..020f93b 100644 --- a/R/map_simulate_occurrences.R +++ b/R/map_simulate_occurrences.R @@ -47,7 +47,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' plgn = rep(list(plgn), 3), -#' initial_average_abundance = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), diff --git a/R/map_simulation_functions.R b/R/map_simulation_functions.R index bd966a2..017cb34 100644 --- a/R/map_simulation_functions.R +++ b/R/map_simulation_functions.R @@ -45,7 +45,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' plgn = rep(list(plgn), 3), -#' initial_average_abundance = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), diff --git a/R/simulate_occurrences.R b/R/simulate_occurrences.R index 1c28c2f..79cce27 100644 --- a/R/simulate_occurrences.R +++ b/R/simulate_occurrences.R @@ -5,7 +5,7 @@ #' #' @param plgn An sf object with POLYGON geometry indicating the spatial #' extend to simulate occurrences. -#' @param initial_average_abundance A positive numeric value indicating the +#' @param initial_average_occurrences A positive numeric value indicating the #' average number of occurrences to be simulated within the extent of `plgn` #' at the first time point. This value serves as the mean (lambda) of a Poisson #' distribution. @@ -50,7 +50,7 @@ #' occ_sf <- simulate_occurrences( #' plgn, #' n_time_points = 4, -#' initial_average_abundance = 100, +#' initial_average_occurrences = 100, #' seed = 123) #' #' ggplot() + @@ -67,7 +67,7 @@ #' plgn, #' spatial_autocorr = 100, #' n_time_points = 4, -#' initial_average_abundance = 100, +#' initial_average_occurrences = 100, #' seed = 123) #' #' ggplot() + @@ -81,7 +81,7 @@ simulate_occurrences <- function( plgn, - initial_average_abundance = 50, + initial_average_occurrences = 50, spatial_autocorr = c("random", "clustered"), n_time_points = 1, temporal_function = NA, @@ -96,9 +96,9 @@ simulate_occurrences <- function( # Check if initial_average_occurrences is a positive number stopifnot( - "`initial_average_abundance` must be a single positive number." = - assertthat::is.number(initial_average_abundance) & - initial_average_abundance >= 0) + "`initial_average_occurrences` must be a single positive number." = + assertthat::is.number(initial_average_occurrences) & + initial_average_occurrences >= 0) if (!(assertthat::is.number(spatial_autocorr) && spatial_autocorr >= 1)) { # Check if spatial_autocorr is random or clustered @@ -129,7 +129,7 @@ simulate_occurrences <- function( # Simulate the timeseries ts <- simulate_timeseries( - initial_average_occurrences = initial_average_abundance, + initial_average_occurrences = initial_average_occurrences, n_time_points = n_time_points, temporal_function = temporal_function, ..., diff --git a/man/map_add_coordinate_uncertainty.Rd b/man/map_add_coordinate_uncertainty.Rd index 72f12e8..0515343 100644 --- a/man/map_add_coordinate_uncertainty.Rd +++ b/man/map_add_coordinate_uncertainty.Rd @@ -48,7 +48,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/man/map_filter_observations.Rd b/man/map_filter_observations.Rd index c6122a4..c6e122b 100644 --- a/man/map_filter_observations.Rd +++ b/man/map_filter_observations.Rd @@ -48,7 +48,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/man/map_grid_designation.Rd b/man/map_grid_designation.Rd index 60b1ac0..ea27188 100644 --- a/man/map_grid_designation.Rd +++ b/man/map_grid_designation.Rd @@ -55,7 +55,7 @@ cube_grid <- st_make_grid( species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/man/map_sample_observations.Rd b/man/map_sample_observations.Rd index bc13236..82c8193 100644 --- a/man/map_sample_observations.Rd +++ b/man/map_sample_observations.Rd @@ -48,7 +48,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/man/map_simulate_occurrences.Rd b/man/map_simulate_occurrences.Rd index 808499a..96fa10f 100644 --- a/man/map_simulate_occurrences.Rd +++ b/man/map_simulate_occurrences.Rd @@ -50,7 +50,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/man/map_simulation_functions.Rd b/man/map_simulation_functions.Rd index 9c257d3..bb719fe 100644 --- a/man/map_simulation_functions.Rd +++ b/man/map_simulation_functions.Rd @@ -46,7 +46,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/man/simulate_occurrences.Rd b/man/simulate_occurrences.Rd index 66ca7ce..57a5221 100644 --- a/man/simulate_occurrences.Rd +++ b/man/simulate_occurrences.Rd @@ -6,7 +6,7 @@ \usage{ simulate_occurrences( plgn, - initial_average_abundance = 50, + initial_average_occurrences = 50, spatial_autocorr = c("random", "clustered"), n_time_points = 1, temporal_function = NA, @@ -18,7 +18,7 @@ simulate_occurrences( \item{plgn}{An sf object with POLYGON geometry indicating the spatial extend to simulate occurrences.} -\item{initial_average_abundance}{A positive numeric value indicating the +\item{initial_average_occurrences}{A positive numeric value indicating the average number of occurrences to be simulated within the extent of \code{plgn} at the first time point. This value serves as the mean (lambda) of a Poisson distribution.} @@ -66,7 +66,7 @@ ggplot() + occ_sf <- simulate_occurrences( plgn, n_time_points = 4, - initial_average_abundance = 100, + initial_average_occurrences = 100, seed = 123) ggplot() + @@ -83,7 +83,7 @@ occ_sf_100 <- simulate_occurrences( plgn, spatial_autocorr = 100, n_time_points = 4, - initial_average_abundance = 100, + initial_average_occurrences = 100, seed = 123) ggplot() + diff --git a/tests/testthat/test-map_add_coordinate_uncertainty.R b/tests/testthat/test-map_add_coordinate_uncertainty.R index 5f76f64..860929c 100644 --- a/tests/testthat/test-map_add_coordinate_uncertainty.R +++ b/tests/testthat/test-map_add_coordinate_uncertainty.R @@ -7,7 +7,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/tests/testthat/test-map_filter_observations.R b/tests/testthat/test-map_filter_observations.R index aa7ea2f..f678ba5 100644 --- a/tests/testthat/test-map_filter_observations.R +++ b/tests/testthat/test-map_filter_observations.R @@ -7,7 +7,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/tests/testthat/test-map_grid_designation.R b/tests/testthat/test-map_grid_designation.R index 76ad89f..1a24bd7 100644 --- a/tests/testthat/test-map_grid_designation.R +++ b/tests/testthat/test-map_grid_designation.R @@ -14,7 +14,7 @@ cube_grid <- st_make_grid( species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/tests/testthat/test-map_sample_observations.R b/tests/testthat/test-map_sample_observations.R index 80b5c2d..fe82500 100644 --- a/tests/testthat/test-map_sample_observations.R +++ b/tests/testthat/test-map_sample_observations.R @@ -21,7 +21,7 @@ road_polygon <- st_linestring(road_points) %>% species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), @@ -46,7 +46,7 @@ arg_conv_list <- list( species_dataset_df3 <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/tests/testthat/test-map_simulate_occurrences.R b/tests/testthat/test-map_simulate_occurrences.R index 431b112..1efe00e 100644 --- a/tests/testthat/test-map_simulate_occurrences.R +++ b/tests/testthat/test-map_simulate_occurrences.R @@ -12,7 +12,7 @@ species_dataset_df <- tibble( species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/tests/testthat/test-map_simulation_functions.R b/tests/testthat/test-map_simulation_functions.R index 4808dff..669d46b 100644 --- a/tests/testthat/test-map_simulation_functions.R +++ b/tests/testthat/test-map_simulation_functions.R @@ -14,7 +14,7 @@ cube_grid <- st_make_grid( species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), plgn = rep(list(plgn), 3), - initial_average_abundance = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), diff --git a/tests/testthat/test-simulate_occurrences.R b/tests/testthat/test-simulate_occurrences.R index a16af3e..5705004 100644 --- a/tests/testthat/test-simulate_occurrences.R +++ b/tests/testthat/test-simulate_occurrences.R @@ -11,7 +11,7 @@ plgn_sf <- st_sf(geometry = plgn_sf, crs = example_crs) ## Unit Tests test_that("simulate_occurrences returns an sf object with POINT geometry", { result <- simulate_occurrences(plgn, - initial_average_abundance = 50, + initial_average_occurrences = 50, n_time_points = 1) expect_s3_class(result, "sf") @@ -22,11 +22,11 @@ test_that("simulate_occurrences returns an sf object with POINT geometry", { test_that("simulate_occurrences returns reproducible results with a seed", { seed <- 123 result1 <- simulate_occurrences(plgn, - initial_average_abundance = 50, + initial_average_occurrences = 50, n_time_points = 1, seed = seed) result2 <- simulate_occurrences(plgn, - initial_average_abundance = 50, + initial_average_occurrences = 50, n_time_points = 1, seed = seed) @@ -35,11 +35,11 @@ test_that("simulate_occurrences returns reproducible results with a seed", { test_that("simulate_occurrences handles different spatial_autocorr values", { result_random <- simulate_occurrences(plgn, - initial_average_abundance = 50, + initial_average_occurrences = 50, spatial_autocorr = "random", n_time_points = 1) result_clustered <- simulate_occurrences(plgn, - initial_average_abundance = 50, + initial_average_occurrences = 50, spatial_autocorr = "clustered", n_time_points = 1) @@ -54,7 +54,7 @@ test_that("simulate_occurrences handles different spatial_autocorr values", { test_that("simulate_occurrences handles different n_time_points values", { n_time_points <- 4 result <- simulate_occurrences(plgn, - initial_average_abundance = 50, + initial_average_occurrences = 50, temporal_function = simulate_random_walk, n_time_points = n_time_points) @@ -72,8 +72,8 @@ test_that("simulate_occurrences raises an error for incorrect plgn type", { test_that("simulate_occurrences raises an error for non-numeric abundance", { expect_error(simulate_occurrences(plgn, - initial_average_abundance = "not_numeric"), - "`initial_average_abundance` must be a single positive number.") + initial_average_occurrences = "not_numeric"), + "`initial_average_occurrences` must be a single positive number.") }) test_that("simulate_occurrences raises an error for invalid spatial_autocorr", { @@ -102,10 +102,10 @@ test_that("simulate_occurrences raises an error for invalid seed type", { test_that("simulate_occurrences returns different results without a seed", { result1 <- simulate_occurrences(plgn, - initial_average_abundance = 50, + initial_average_occurrences = 50, n_time_points = 1) result2 <- simulate_occurrences(plgn, - initial_average_abundance = 50, + initial_average_occurrences = 50, n_time_points = 1) expect_false(identical(result1, result2)) @@ -113,7 +113,7 @@ test_that("simulate_occurrences returns different results without a seed", { test_that("simulate_occurrences handles CRS correctly", { result <- simulate_occurrences(plgn_sf, - initial_average_abundance = 50, + initial_average_occurrences = 50, temporal_function = simulate_random_walk, n_time_points = 5) diff --git a/vignettes/articles/detection-process.Rmd b/vignettes/articles/detection-process.Rmd index 9690d74..bd2b75e 100644 --- a/vignettes/articles/detection-process.Rmd +++ b/vignettes/articles/detection-process.Rmd @@ -54,7 +54,7 @@ We can for example sample randomly within the polygon over 6 time points were we ```{r} occurrences_df <- simulate_occurrences( plgn = polygon, - initial_average_abundance = 100, + initial_average_occurrences = 100, n_time_points = 6, temporal_function = simulate_random_walk, sd_step = 1, diff --git a/vignettes/articles/grid-designation-process.Rmd b/vignettes/articles/grid-designation-process.Rmd index e02d286..62cb7ab 100644 --- a/vignettes/articles/grid-designation-process.Rmd +++ b/vignettes/articles/grid-designation-process.Rmd @@ -85,7 +85,7 @@ We can for example sample randomly within the polygon over 6 time points were we ```{r} occurrences_df <- simulate_occurrences( plgn = polygon, - initial_average_abundance = 100, + initial_average_occurrences = 100, n_time_points = 6, temporal_function = simulate_random_walk, sd_step = 1, diff --git a/vignettes/articles/multi_species_approach.Rmd b/vignettes/articles/multi_species_approach.Rmd index 0601a1a..7add1b1 100644 --- a/vignettes/articles/multi_species_approach.Rmd +++ b/vignettes/articles/multi_species_approach.Rmd @@ -107,7 +107,7 @@ The values within these columns can change between species. # Create dataframe with simulation function arguments multi_species_args <- tibble( plgn = rep(list(polygon), 6), - initial_average_abundance = rep(c(50, 100, 500), 2), + initial_average_occurrences = rep(c(50, 100, 500), 2), n_time_points = rep(6, 6), temporal_function = c(simulate_random_walk, simulate_random_walk, rep(NA, 4)), sd_step = c(1, 1, rep(NA, 4)), diff --git a/vignettes/articles/occurrence-process.Rmd b/vignettes/articles/occurrence-process.Rmd index 4d05843..c67cbc0 100644 --- a/vignettes/articles/occurrence-process.Rmd +++ b/vignettes/articles/occurrence-process.Rmd @@ -324,7 +324,7 @@ We can for example sample randomly within the polygon over 6 time points were we ```{r} occurrences_df <- simulate_occurrences( plgn = polygon, - initial_average_abundance = 100, + initial_average_occurrences = 100, n_time_points = 6, temporal_function = simulate_random_walk, sd_step = 1, From c24aff1bf5c622544b254534f7f720f51bd66801 Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 12:04:49 +0200 Subject: [PATCH 16/57] fix bug --- R/simulate_timeseries.R | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/R/simulate_timeseries.R b/R/simulate_timeseries.R index d50b59f..78bcd75 100644 --- a/R/simulate_timeseries.R +++ b/R/simulate_timeseries.R @@ -193,15 +193,14 @@ simulate_timeseries <- function( if (length_pars == 0) { # Generate timeseries using the provided function lambdas <- temporal_function( - initial_average_occurrences, - n_time_points, - seed = seed + initial_average_occurrences = initial_average_occurrences, + n_time_points = n_time_points ) } else { # Generate timeseries using the provided function lambdas <- temporal_function( - initial_average_occurrences, - n_time_points, + initial_average_occurrences = initial_average_occurrences, + n_time_points = n_time_points, ... ) } From ef0ae5ed2c7a3fa712bb3837596eb055ee7a59f5 Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 12:04:55 +0200 Subject: [PATCH 17/57] coding style --- tests/testthat/test-simulate_occurrences.R | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-simulate_occurrences.R b/tests/testthat/test-simulate_occurrences.R index 5705004..dcafa40 100644 --- a/tests/testthat/test-simulate_occurrences.R +++ b/tests/testthat/test-simulate_occurrences.R @@ -71,9 +71,11 @@ test_that("simulate_occurrences raises an error for incorrect plgn type", { }) test_that("simulate_occurrences raises an error for non-numeric abundance", { - expect_error(simulate_occurrences(plgn, - initial_average_occurrences = "not_numeric"), - "`initial_average_occurrences` must be a single positive number.") + expect_error( + simulate_occurrences( + plgn, + initial_average_occurrences = "not_numeric"), + "`initial_average_occurrences` must be a single positive number.") }) test_that("simulate_occurrences raises an error for invalid spatial_autocorr", { From 9560d70aa6159332c4a2d3234802979a9841b577 Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 12:09:32 +0200 Subject: [PATCH 18/57] update description --- R/sample_occurrences_from_raster.R | 4 +++- man/sample_occurrences_from_raster.Rd | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/R/sample_occurrences_from_raster.R b/R/sample_occurrences_from_raster.R index 6d41dee..ecb9e9c 100644 --- a/R/sample_occurrences_from_raster.R +++ b/R/sample_occurrences_from_raster.R @@ -1,6 +1,8 @@ #' Sample occurrences from spatial random field #' -#' This function draws point occurrences from a spatial random field (raster). +#' This function draws point occurrences from a spatial random field represented +#' by a raster. Points are sampled based on the values in the raster, with the +#' number of occurrences specified for each time step. #' #' @param rs A SpatRaster object (see [terra::rast()]). #' @param ts A vector with the number of occurrences per time point. diff --git a/man/sample_occurrences_from_raster.Rd b/man/sample_occurrences_from_raster.Rd index c65de83..d4daa69 100644 --- a/man/sample_occurrences_from_raster.Rd +++ b/man/sample_occurrences_from_raster.Rd @@ -18,7 +18,9 @@ generation to ensure reproducibility. If \code{NA} (default), no seed is used.} An sf object with POINT geometry. } \description{ -This function draws point occurrences from a spatial random field (raster). +This function draws point occurrences from a spatial random field represented +by a raster. Points are sampled based on the values in the raster, with the +number of occurrences specified for each time step. } \examples{ # Load packages From a66d1069ac5ea31f2b0e512ab34e4a3dbe626478 Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 12:16:03 +0200 Subject: [PATCH 19/57] spelling --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index ad9b092..d3fb0a7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # gcube 0.4.0 -* Consolidate documentation throughout all functions, readme, and vignettes. +* Consolidate documentation throughout all functions, README, and vignettes. # gcube 0.3.0 From 133af8e47cd3386a91c2f1fc2111fbd1db5a1c65 Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 30 Jul 2024 13:26:13 +0200 Subject: [PATCH 20/57] change argument names --- R/sample_occurrences_from_raster.R | 38 ++++++----- R/simulate_occurrences.R | 4 +- man/sample_occurrences_from_raster.Rd | 14 ++-- .../test-sample_occurrences_from_raster.R | 68 +++++++++---------- vignettes/articles/occurrence-process.Rmd | 4 +- 5 files changed, 65 insertions(+), 63 deletions(-) diff --git a/R/sample_occurrences_from_raster.R b/R/sample_occurrences_from_raster.R index ecb9e9c..63b316f 100644 --- a/R/sample_occurrences_from_raster.R +++ b/R/sample_occurrences_from_raster.R @@ -4,8 +4,8 @@ #' by a raster. Points are sampled based on the values in the raster, with the #' number of occurrences specified for each time step. #' -#' @param rs A SpatRaster object (see [terra::rast()]). -#' @param ts A vector with the number of occurrences per time point. +#' @param raster A SpatRaster object (see [terra::rast()]). +#' @param time_series A vector with the number of occurrences per time point. #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. #' @@ -41,8 +41,8 @@ #' #' # Sample 200 occurrences from random field #' pts_occ_clustered <- sample_occurrences_from_raster( -#' rs = rs_pattern_clustered, -#' ts = 200, +#' raster = rs_pattern_clustered, +#' time_series = 200, #' seed = 123) #' #' ggplot() + @@ -61,8 +61,8 @@ #' #' # Sample 200 occurrences from random field #' pts_occ_large <- sample_occurrences_from_raster( -#' rs = rs_pattern_large, -#' ts = 200, +#' raster = rs_pattern_large, +#' time_series = 200, #' seed = 123) #' #' ggplot() + @@ -72,18 +72,20 @@ #' theme_minimal() sample_occurrences_from_raster <- function( - rs, - ts, + raster, + time_series, seed = NA) { ### Start checks # 1. Check input type and length - # Check if rs is a SpatRaster object - stopifnot("`rs` must be a SpatRaster object." = - inherits(rs, "SpatRaster")) + # Check if raster is a SpatRaster object + stopifnot("`raster` must be a SpatRaster object." = + inherits(raster, "SpatRaster")) - # Check if ts is a numeric vector - stopifnot("`ts` must be a positive numeric vector." = is.numeric(ts)) - stopifnot("`ts` must be a positive numeric vector." = all(ts >= 0)) + # Check if time_series is a numeric vector + stopifnot("`time_series` must be a positive numeric vector." = + is.numeric(time_series)) + stopifnot("`time_series` must be a positive numeric vector." = + all(time_series >= 0)) # Check if seed is NA or a number stopifnot("`seed` must be a numeric vector of length 1 or NA." = @@ -92,8 +94,8 @@ sample_occurrences_from_raster <- function( ### End checks # centre the values of the raster (mean = 0) - rs_mean <- terra::global(rs, "mean", na.rm = TRUE)[, 1] - rs2 <- rs - rs_mean + rs_mean <- terra::global(raster, "mean", na.rm = TRUE)[, 1] + rs2 <- raster - rs_mean # increase contrast between high and low values a <- 30 # a = 1 -> logistic a > 1 => steeper sigmoid (higher contrast) @@ -109,9 +111,9 @@ sample_occurrences_from_raster <- function( withr::local_seed(seed) } - for (t in seq_along(ts)) { + for (t in seq_along(time_series)) { occ_p <- terra::spatSample( - x = rs3, size = ts[t], method = "weights", + x = rs3, size = time_series[t], method = "weights", replace = TRUE, as.points = TRUE ) occ_sf <- sf::st_as_sf(occ_p) diff --git a/R/simulate_occurrences.R b/R/simulate_occurrences.R index 79cce27..c1b8338 100644 --- a/R/simulate_occurrences.R +++ b/R/simulate_occurrences.R @@ -149,8 +149,8 @@ simulate_occurrences <- function( # Sample occurrences from raster occ <- sample_occurrences_from_raster( - rs = rs_pattern, - ts = ts, + raster = rs_pattern, + time_series = ts, seed = seed) # Return the occurences (sf point geometry) diff --git a/man/sample_occurrences_from_raster.Rd b/man/sample_occurrences_from_raster.Rd index d4daa69..d60ec55 100644 --- a/man/sample_occurrences_from_raster.Rd +++ b/man/sample_occurrences_from_raster.Rd @@ -4,12 +4,12 @@ \alias{sample_occurrences_from_raster} \title{Sample occurrences from spatial random field} \usage{ -sample_occurrences_from_raster(rs, ts, seed = NA) +sample_occurrences_from_raster(raster, time_series, seed = NA) } \arguments{ -\item{rs}{A SpatRaster object (see \code{\link[terra:rast]{terra::rast()}}).} +\item{raster}{A SpatRaster object (see \code{\link[terra:rast]{terra::rast()}}).} -\item{ts}{A vector with the number of occurrences per time point.} +\item{time_series}{A vector with the number of occurrences per time point.} \item{seed}{A positive numeric value setting the seed for random number generation to ensure reproducibility. If \code{NA} (default), no seed is used.} @@ -44,8 +44,8 @@ rs_pattern_clustered <- create_spatial_pattern( # Sample 200 occurrences from random field pts_occ_clustered <- sample_occurrences_from_raster( - rs = rs_pattern_clustered, - ts = 200, + raster = rs_pattern_clustered, + time_series = 200, seed = 123) ggplot() + @@ -64,8 +64,8 @@ rs_pattern_large <- create_spatial_pattern( # Sample 200 occurrences from random field pts_occ_large <- sample_occurrences_from_raster( - rs = rs_pattern_large, - ts = 200, + raster = rs_pattern_large, + time_series = 200, seed = 123) ggplot() + diff --git a/tests/testthat/test-sample_occurrences_from_raster.R b/tests/testthat/test-sample_occurrences_from_raster.R index 170a983..3158b61 100644 --- a/tests/testthat/test-sample_occurrences_from_raster.R +++ b/tests/testthat/test-sample_occurrences_from_raster.R @@ -10,73 +10,73 @@ create_sample_raster <- function() { ## Unit Tests test_that("sample_occurrences_from_raster returns an sf object", { - rs <- create_sample_raster() - ts <- 10:20 - result <- sample_occurrences_from_raster(rs, ts) + raster <- create_sample_raster() + time_series <- 10:20 + result <- sample_occurrences_from_raster(raster, time_series) expect_s3_class(result, "sf") expect_true(all(st_geometry_type(result) == "POINT")) - expect_equal(nrow(result), sum(ts)) + expect_equal(nrow(result), sum(time_series)) }) test_that("sample_occurrences_from_raster returns reproducible results", { - rs <- create_sample_raster() - ts <- 10:20 + raster <- create_sample_raster() + time_series <- 10:20 seed <- 123 - result1 <- sample_occurrences_from_raster(rs, ts, seed) - result2 <- sample_occurrences_from_raster(rs, ts, seed) + result1 <- sample_occurrences_from_raster(raster, time_series, seed) + result2 <- sample_occurrences_from_raster(raster, time_series, seed) expect_equal(result1, result2) }) -test_that("sample_occurrences_from_raster raises an error for incorrect rs", { - rs <- list() # Not a SpatRaster object - ts <- 10 +test_that("sample_occurrences_from_raster raises error for raster", { + raster <- list() # Not a SpatRaster object + time_series <- 10 - expect_error(sample_occurrences_from_raster(rs, ts), - "`rs` must be a SpatRaster object.") + expect_error(sample_occurrences_from_raster(raster, time_series), + "`raster` must be a SpatRaster object.") }) -test_that("sample_occurrences_from_raster raises an error for non-numeric ts", { - rs <- create_sample_raster() - ts <- "not_numeric" +test_that("sample_occurrences_from_raster raises error time_series", { + raster <- create_sample_raster() + time_series <- "not_numeric" - expect_error(sample_occurrences_from_raster(rs, ts), - "`ts` must be a positive numeric vector.") + expect_error(sample_occurrences_from_raster(raster, time_series), + "`time_series` must be a positive numeric vector.") }) -test_that("sample_occurrences_from_raster raises an error for negative ts", { - rs <- create_sample_raster() - ts <- -10 +test_that("sample_occurrences_from_raster raises error for neg. time_series", { + raster <- create_sample_raster() + time_series <- -10 - expect_error(sample_occurrences_from_raster(rs, ts), - "`ts` must be a positive numeric vector.") + expect_error(sample_occurrences_from_raster(raster, time_series), + "`time_series` must be a positive numeric vector.") }) test_that("sample_occurrences_from_raster raises an error for incorrect seed", { - rs <- create_sample_raster() - ts <- 10 + raster <- create_sample_raster() + time_series <- 10 seed <- "not_numeric" - expect_error(sample_occurrences_from_raster(rs, ts, seed), + expect_error(sample_occurrences_from_raster(raster, time_series, seed), "`seed` must be a numeric vector of length 1 or NA.") }) test_that("sample_occurrences_from_raster returns diff. results without seed", { - rs <- create_sample_raster() - ts <- 10 + raster <- create_sample_raster() + time_series <- 10 - result1 <- sample_occurrences_from_raster(rs, ts) - result2 <- sample_occurrences_from_raster(rs, ts) + result1 <- sample_occurrences_from_raster(raster, time_series) + result2 <- sample_occurrences_from_raster(raster, time_series) expect_false(identical(result1, result2)) }) test_that("sample_occurrences_from_raster handles CRS correctly", { - rs <- create_sample_raster() - ts <- 10:20 - result <- sample_occurrences_from_raster(rs, ts) + raster <- create_sample_raster() + time_series <- 10:20 + result <- sample_occurrences_from_raster(raster, time_series) - expect_equal(terra::crs(rs), sf::st_crs(result)$wkt) + expect_equal(terra::crs(raster), sf::st_crs(result)$wkt) }) diff --git a/vignettes/articles/occurrence-process.Rmd b/vignettes/articles/occurrence-process.Rmd index c67cbc0..95e4901 100644 --- a/vignettes/articles/occurrence-process.Rmd +++ b/vignettes/articles/occurrence-process.Rmd @@ -305,8 +305,8 @@ If we for example sample 500 occurrences from the last raster, we see the sampli #| fig.alt: > #| 500 sampled occurrences from highly clustered spatial pattern. pts_occ_clustered3 <- sample_occurrences_from_raster( - rs = rs_pattern_clustered3, - ts = 500, + raster = rs_pattern_clustered3, + time_series = 500, seed = 123) ggplot() + From 24b5d8f9b808c275276d53e68c9fae6b5f5f6243 Mon Sep 17 00:00:00 2001 From: wlangera Date: Mon, 5 Aug 2024 09:52:26 +0200 Subject: [PATCH 21/57] change plgn argument name --- R/map_add_coordinate_uncertainty.R | 6 ++-- R/map_filter_observations.R | 6 ++-- R/map_grid_designation.R | 6 ++-- R/map_sample_observations.R | 6 ++-- R/map_simulate_occurrences.R | 6 ++-- R/map_simulation_functions.R | 2 +- R/simulate_occurrences.R | 35 ++++++++++--------- README.Rmd | 2 +- README.md | 2 +- inst/en_gb.dic | 3 +- man/map_add_coordinate_uncertainty.Rd | 6 ++-- man/map_filter_observations.Rd | 6 ++-- man/map_grid_designation.Rd | 6 ++-- man/map_sample_observations.Rd | 6 ++-- man/map_simulate_occurrences.Rd | 6 ++-- man/map_simulation_functions.Rd | 2 +- man/simulate_occurrences.Rd | 16 ++++----- .../test-map_add_coordinate_uncertainty.R | 10 +++--- tests/testthat/test-map_filter_observations.R | 10 +++--- tests/testthat/test-map_grid_designation.R | 14 ++++---- tests/testthat/test-map_sample_observations.R | 12 +++---- .../testthat/test-map_simulate_occurrences.R | 12 +++---- .../testthat/test-map_simulation_functions.R | 2 +- tests/testthat/test-simulate_occurrences.R | 2 +- vignettes/articles/detection-process.Rmd | 2 +- .../articles/grid-designation-process.Rmd | 2 +- vignettes/articles/multi_species_approach.Rmd | 8 ++--- vignettes/articles/occurrence-process.Rmd | 2 +- 28 files changed, 101 insertions(+), 97 deletions(-) diff --git a/R/map_add_coordinate_uncertainty.R b/R/map_add_coordinate_uncertainty.R index f5469f5..ef5c118 100644 --- a/R/map_add_coordinate_uncertainty.R +++ b/R/map_add_coordinate_uncertainty.R @@ -44,7 +44,7 @@ #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), -#' plgn = rep(list(plgn), 3), +#' species_range = rep(list(plgn), 3), #' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -78,7 +78,7 @@ #' ## Example with deviating column names #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df2 <- species_dataset_df %>% -#' rename(polygon = plgn, +#' rename(polygon = species_range, #' sd = sd_step, #' det_prob = detection_probability, #' inv = invert, @@ -86,7 +86,7 @@ #' #' # Create named list for argument conversion #' arg_conv_list <- list( -#' plgn = "polygon", +#' species_range = "polygon", #' sd_step = "sd", #' detection_probability = "det_prob", #' invert = "inv", diff --git a/R/map_filter_observations.R b/R/map_filter_observations.R index fcfb48b..3075904 100644 --- a/R/map_filter_observations.R +++ b/R/map_filter_observations.R @@ -44,7 +44,7 @@ #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), -#' plgn = rep(list(plgn), 3), +#' species_range = rep(list(plgn), 3), #' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -74,14 +74,14 @@ #' ## Example with deviating column names #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df2 <- species_dataset_df %>% -#' rename(polygon = plgn, +#' rename(polygon = species_range, #' sd = sd_step, #' det_prob = detection_probability, #' inv = invert) #' #' # Create named list for argument conversion #' arg_conv_list <- list( -#' plgn = "polygon", +#' species_range = "polygon", #' sd_step = "sd", #' detection_probability = "det_prob", #' invert = "inv" diff --git a/R/map_grid_designation.R b/R/map_grid_designation.R index 9dee10c..9a4fc9a 100644 --- a/R/map_grid_designation.R +++ b/R/map_grid_designation.R @@ -51,7 +51,7 @@ #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), -#' plgn = rep(list(plgn), 3), +#' species_range = rep(list(plgn), 3), #' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -92,7 +92,7 @@ #' ## Example with deviating column names #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df2 <- species_dataset_df %>% -#' rename(polygon = plgn, +#' rename(polygon = species_range, #' sd = sd_step, #' det_prob = detection_probability, #' inv = invert, @@ -101,7 +101,7 @@ #' #' # Create named list for argument conversion #' arg_conv_list <- list( -#' plgn = "polygon", +#' species_range = "polygon", #' sd_step = "sd", #' detection_probability = "det_prob", #' invert = "inv", diff --git a/R/map_sample_observations.R b/R/map_sample_observations.R index e971777..0a285cd 100644 --- a/R/map_sample_observations.R +++ b/R/map_sample_observations.R @@ -44,7 +44,7 @@ #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), -#' plgn = rep(list(plgn), 3), +#' species_range = rep(list(plgn), 3), #' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -69,13 +69,13 @@ #' ## Example with deviating column names #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df2 <- species_dataset_df %>% -#' rename(polygon = plgn, +#' rename(polygon = species_range, #' sd = sd_step, #' det_prob = detection_probability) #' #' # Create named list for argument conversion #' arg_conv_list <- list( -#' plgn = "polygon", +#' species_range = "polygon", #' sd_step = "sd", #' detection_probability = "det_prob" #' ) diff --git a/R/map_simulate_occurrences.R b/R/map_simulate_occurrences.R index 020f93b..18cc60e 100644 --- a/R/map_simulate_occurrences.R +++ b/R/map_simulate_occurrences.R @@ -46,7 +46,7 @@ #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), -#' plgn = rep(list(plgn), 3), +#' species_range = rep(list(plgn), 3), #' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -68,12 +68,12 @@ #' ## Example with deviating column names #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df2 <- species_dataset_df %>% -#' rename(polygon = plgn, +#' rename(polygon = species_range, #' sd = sd_step) #' #' # Create named list for argument conversion #' arg_conv_list <- list( -#' plgn = "polygon", +#' species_range = "polygon", #' sd_step = "sd" #' ) #' diff --git a/R/map_simulation_functions.R b/R/map_simulation_functions.R index 017cb34..5d28cc6 100644 --- a/R/map_simulation_functions.R +++ b/R/map_simulation_functions.R @@ -44,7 +44,7 @@ #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), -#' plgn = rep(list(plgn), 3), +#' species_range = rep(list(plgn), 3), #' initial_average_occurrences = c(50, 100, 500), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), diff --git a/R/simulate_occurrences.R b/R/simulate_occurrences.R index c1b8338..2677eab 100644 --- a/R/simulate_occurrences.R +++ b/R/simulate_occurrences.R @@ -3,12 +3,12 @@ #' This function simulates occurrences of a species within a specified spatial #' and/or temporal extent. #' -#' @param plgn An sf object with POLYGON geometry indicating the spatial -#' extend to simulate occurrences. +#' @param species_range An sf object with POLYGON geometry indicating the +#' spatial extend to simulate occurrences. #' @param initial_average_occurrences A positive numeric value indicating the -#' average number of occurrences to be simulated within the extent of `plgn` -#' at the first time point. This value serves as the mean (lambda) of a Poisson -#' distribution. +#' average number of occurrences to be simulated within the extent of +#' `species_range` at the first time point. This value serves as the mean +#' (lambda) of a Poisson distribution. #' @param n_time_points A positive integer specifying the number of time points #' to simulate. #' @param temporal_function A function generating a trend in number of @@ -48,7 +48,7 @@ #' #' ## Random spatial pattern with 4 time points #' occ_sf <- simulate_occurrences( -#' plgn, +#' species_range = plgn, #' n_time_points = 4, #' initial_average_occurrences = 100, #' seed = 123) @@ -64,7 +64,7 @@ #' #' ## Clustered spatial pattern with 4 time points #' occ_sf_100 <- simulate_occurrences( -#' plgn, +#' species_range = plgn, #' spatial_autocorr = 100, #' n_time_points = 4, #' initial_average_occurrences = 100, @@ -80,7 +80,7 @@ #' theme_bw() simulate_occurrences <- function( - plgn, + species_range, initial_average_occurrences = 50, spatial_autocorr = c("random", "clustered"), n_time_points = 1, @@ -89,10 +89,12 @@ simulate_occurrences <- function( seed = NA) { ### Start checks # 1. Check input type and length - # Check if plgn is an sf object - stopifnot("`plgn` must be an sf object with POLYGON geometry." = - inherits(plgn, "POLYGON") | inherits(plgn, "sfc_POLYGON") | - (inherits(plgn, "sf") && sf::st_geometry_type(plgn) == "POLYGON")) + # Check if species_range is an sf object + stopifnot("`species_range` must be an sf object with POLYGON geometry." = + inherits(species_range, "POLYGON") | + inherits(species_range, "sfc_POLYGON") | + (inherits(species_range, "sf") && + sf::st_geometry_type(species_range) == "POLYGON")) # Check if initial_average_occurrences is a positive number stopifnot( @@ -136,12 +138,13 @@ simulate_occurrences <- function( seed = seed) # Create the random field - boxplgn <- sf::st_bbox(plgn) - plgn_maxr <- max(boxplgn[3] - boxplgn[1], boxplgn[4] - boxplgn[2]) - res <- plgn_maxr / 100 + box_plgn <- sf::st_bbox(species_range) + species_range_maxr <- max(box_plgn[3] - box_plgn[1], + box_plgn[4] - box_plgn[2]) + res <- species_range_maxr / 100 rs_pattern <- create_spatial_pattern( - polygon = plgn, + polygon = species_range, resolution = res, spatial_pattern = spatial_autocorr, seed = seed, diff --git a/README.Rmd b/README.Rmd index 32020a4..5102e91 100644 --- a/README.Rmd +++ b/README.Rmd @@ -91,7 +91,7 @@ In the `simulate_occurrences()` function, the user can specify different levels #| Spatial distribution of occurrences within the polygon. # Simulate occurrences within polygon occurrences_df <- simulate_occurrences( - plgn = polygon, + species_range = polygon, seed = 123) # Visualise diff --git a/README.md b/README.md index aa2adf0..fe30920 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ over time. ``` r # Simulate occurrences within polygon occurrences_df <- simulate_occurrences( - plgn = polygon, + species_range = polygon, seed = 123) #> [using unconditional Gaussian simulation] diff --git a/inst/en_gb.dic b/inst/en_gb.dic index 67f113b..d390749 100644 --- a/inst/en_gb.dic +++ b/inst/en_gb.dic @@ -1,10 +1,11 @@ + CMD SpatRaster codecov cube’ +cube’ det etc -plgn prob spatiotemporal timeseries diff --git a/man/map_add_coordinate_uncertainty.Rd b/man/map_add_coordinate_uncertainty.Rd index 0515343..a8d9d50 100644 --- a/man/map_add_coordinate_uncertainty.Rd +++ b/man/map_add_coordinate_uncertainty.Rd @@ -47,7 +47,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) # Specify dataframe for 3 species with custom function arguments species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -81,7 +81,7 @@ obs_uncertainty_unnested \%>\% ## Example with deviating column names # Specify dataframe for 3 species with custom function arguments species_dataset_df2 <- species_dataset_df \%>\% - rename(polygon = plgn, + rename(polygon = species_range, sd = sd_step, det_prob = detection_probability, inv = invert, @@ -89,7 +89,7 @@ species_dataset_df2 <- species_dataset_df \%>\% # Create named list for argument conversion arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = "inv", diff --git a/man/map_filter_observations.Rd b/man/map_filter_observations.Rd index c6e122b..05e4564 100644 --- a/man/map_filter_observations.Rd +++ b/man/map_filter_observations.Rd @@ -47,7 +47,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) # Specify dataframe for 3 species with custom function arguments species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -77,14 +77,14 @@ filter_obs_unnested \%>\% ## Example with deviating column names # Specify dataframe for 3 species with custom function arguments species_dataset_df2 <- species_dataset_df \%>\% - rename(polygon = plgn, + rename(polygon = species_range, sd = sd_step, det_prob = detection_probability, inv = invert) # Create named list for argument conversion arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = "inv" diff --git a/man/map_grid_designation.Rd b/man/map_grid_designation.Rd index ea27188..369886d 100644 --- a/man/map_grid_designation.Rd +++ b/man/map_grid_designation.Rd @@ -54,7 +54,7 @@ cube_grid <- st_make_grid( # Specify dataframe for 3 species with custom function arguments species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -95,7 +95,7 @@ occ_cube_unnested \%>\% ## Example with deviating column names # Specify dataframe for 3 species with custom function arguments species_dataset_df2 <- species_dataset_df \%>\% - rename(polygon = plgn, + rename(polygon = species_range, sd = sd_step, det_prob = detection_probability, inv = invert, @@ -104,7 +104,7 @@ species_dataset_df2 <- species_dataset_df \%>\% # Create named list for argument conversion arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = "inv", diff --git a/man/map_sample_observations.Rd b/man/map_sample_observations.Rd index 82c8193..1c3eece 100644 --- a/man/map_sample_observations.Rd +++ b/man/map_sample_observations.Rd @@ -47,7 +47,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) # Specify dataframe for 3 species with custom function arguments species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -72,13 +72,13 @@ samp_obs_unnested \%>\% ## Example with deviating column names # Specify dataframe for 3 species with custom function arguments species_dataset_df2 <- species_dataset_df \%>\% - rename(polygon = plgn, + rename(polygon = species_range, sd = sd_step, det_prob = detection_probability) # Create named list for argument conversion arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob" ) diff --git a/man/map_simulate_occurrences.Rd b/man/map_simulate_occurrences.Rd index 96fa10f..570b315 100644 --- a/man/map_simulate_occurrences.Rd +++ b/man/map_simulate_occurrences.Rd @@ -49,7 +49,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) # Specify dataframe for 3 species with custom function arguments species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -71,12 +71,12 @@ sim_occ_unnested \%>\% ## Example with deviating column names # Specify dataframe for 3 species with custom function arguments species_dataset_df2 <- species_dataset_df \%>\% - rename(polygon = plgn, + rename(polygon = species_range, sd = sd_step) # Create named list for argument conversion arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd" ) diff --git a/man/map_simulation_functions.Rd b/man/map_simulation_functions.Rd index bb719fe..71bf8b6 100644 --- a/man/map_simulation_functions.Rd +++ b/man/map_simulation_functions.Rd @@ -45,7 +45,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) # Specify dataframe for 3 species with custom function arguments species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), diff --git a/man/simulate_occurrences.Rd b/man/simulate_occurrences.Rd index 57a5221..b6a2e59 100644 --- a/man/simulate_occurrences.Rd +++ b/man/simulate_occurrences.Rd @@ -5,7 +5,7 @@ \title{Simulate species occurrences within a spatiotemporal scope} \usage{ simulate_occurrences( - plgn, + species_range, initial_average_occurrences = 50, spatial_autocorr = c("random", "clustered"), n_time_points = 1, @@ -15,13 +15,13 @@ simulate_occurrences( ) } \arguments{ -\item{plgn}{An sf object with POLYGON geometry indicating the spatial -extend to simulate occurrences.} +\item{species_range}{An sf object with POLYGON geometry indicating the +spatial extend to simulate occurrences.} \item{initial_average_occurrences}{A positive numeric value indicating the -average number of occurrences to be simulated within the extent of \code{plgn} -at the first time point. This value serves as the mean (lambda) of a Poisson -distribution.} +average number of occurrences to be simulated within the extent of +\code{species_range} at the first time point. This value serves as the mean +(lambda) of a Poisson distribution.} \item{spatial_autocorr}{Specifies the spatial pattern of occurrences. It can be a character string (\code{"random"} or \code{"clustered"}) or a numeric value ≥ 1 @@ -64,7 +64,7 @@ ggplot() + ## Random spatial pattern with 4 time points occ_sf <- simulate_occurrences( - plgn, + species_range = plgn, n_time_points = 4, initial_average_occurrences = 100, seed = 123) @@ -80,7 +80,7 @@ ggplot() + ## Clustered spatial pattern with 4 time points occ_sf_100 <- simulate_occurrences( - plgn, + species_range = plgn, spatial_autocorr = 100, n_time_points = 4, initial_average_occurrences = 100, diff --git a/tests/testthat/test-map_add_coordinate_uncertainty.R b/tests/testthat/test-map_add_coordinate_uncertainty.R index 860929c..41eb797 100644 --- a/tests/testthat/test-map_add_coordinate_uncertainty.R +++ b/tests/testthat/test-map_add_coordinate_uncertainty.R @@ -6,7 +6,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) # Dataframe with column names equal to arguments for simple polygon species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -20,14 +20,14 @@ species_dataset_df1 <- tibble( # Dataframe with custom column names and named list for argument conversion for # simple polygon. Create named list for argument conversion. species_dataset_df2 <- species_dataset_df1 %>% - rename(polygon = plgn, + rename(polygon = species_range, sd = sd_step, det_prob = detection_probability, inv = invert, coord_uncertainty = coords_uncertainty_meters) arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = "inv", @@ -132,7 +132,7 @@ test_that("map_add_coordinate_uncertainty handles invalid inputs", { # Invalid arg_list invalid_arg_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = "inv", @@ -145,7 +145,7 @@ test_that("map_add_coordinate_uncertainty handles invalid inputs", { ) invalid_arg_list2 <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = "inv", diff --git a/tests/testthat/test-map_filter_observations.R b/tests/testthat/test-map_filter_observations.R index f678ba5..bfd2b04 100644 --- a/tests/testthat/test-map_filter_observations.R +++ b/tests/testthat/test-map_filter_observations.R @@ -6,7 +6,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) # Dataframe with column names equal to arguments for simple polygon species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -19,13 +19,13 @@ species_dataset_df1 <- tibble( # Dataframe with custom column names and named list for argument conversion for # simple polygon. Create named list for argument conversion. species_dataset_df2 <- species_dataset_df1 %>% - rename(polygon = plgn, + rename(polygon = species_range, sd = sd_step, det_prob = detection_probability, inv = invert) arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = "inv" @@ -105,7 +105,7 @@ test_that("map_filter_observations handles invalid inputs", { # Invalid arg_list invalid_arg_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = 1 @@ -117,7 +117,7 @@ test_that("map_filter_observations handles invalid inputs", { ) invalid_arg_list2 <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = "invert_col" diff --git a/tests/testthat/test-map_grid_designation.R b/tests/testthat/test-map_grid_designation.R index 1a24bd7..319305e 100644 --- a/tests/testthat/test-map_grid_designation.R +++ b/tests/testthat/test-map_grid_designation.R @@ -13,7 +13,7 @@ cube_grid <- st_make_grid( # Dataframe with column names equal to arguments for simple polygon species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -28,7 +28,7 @@ species_dataset_df1 <- tibble( # Dataframe with custom column names and named list for argument conversion for # simple polygon. Create named list for argument conversion. species_dataset_df2 <- species_dataset_df1 %>% - rename(polygon = plgn, + rename(polygon = species_range, sd = sd_step, det_prob = detection_probability, inv = invert, @@ -36,7 +36,7 @@ species_dataset_df2 <- species_dataset_df1 %>% raster = grid) arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = "inv", @@ -109,7 +109,7 @@ test_that("map_grid_designation works with simple column names", { test_that("map_grid_designation works with pipes", { occ_cube_piped <- tibble( species = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), grid = rep(list(cube_grid), 3), seed = 123 ) %>% @@ -124,7 +124,7 @@ test_that("map_grid_designation works with pipes", { # Do we have the expected columns? expect_equal( sort(colnames(occ_cube_piped)), - sort(c("species", "plgn", "grid", "seed", "occurrences", + sort(c("species", "species_range", "grid", "seed", "occurrences", "observations_total", "observations", "time_point", "id", "n", "min_coord_uncertainty", "geometry"))) }) @@ -210,7 +210,7 @@ test_that("map_grid_designation handles invalid inputs", { # Invalid arg_list invalid_arg_list <- arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = 123, detection_probability = "det_prob", invert = "inv", @@ -224,7 +224,7 @@ test_that("map_grid_designation handles invalid inputs", { ) invalid_arg_list2 <- arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob", invert = "inv", diff --git a/tests/testthat/test-map_sample_observations.R b/tests/testthat/test-map_sample_observations.R index fe82500..8c82d7b 100644 --- a/tests/testthat/test-map_sample_observations.R +++ b/tests/testthat/test-map_sample_observations.R @@ -20,7 +20,7 @@ road_polygon <- st_linestring(road_points) %>% # Dataframe with column names equal to arguments for simple polygon species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -32,12 +32,12 @@ species_dataset_df1 <- tibble( # Dataframe with custom column names and named list for argument conversion for # simple polygon. Create named list for argument conversion. species_dataset_df2 <- species_dataset_df1 %>% - rename(polygon = plgn, + rename(polygon = species_range, sd = sd_step, det_prob = detection_probability) arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob" ) @@ -45,7 +45,7 @@ arg_conv_list <- list( # Dataframe with column names equal to arguments for road polygon species_dataset_df3 <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -156,7 +156,7 @@ test_that("map_sample_observations handles invalid inputs", { # Invalid arg_list invalid_arg_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = TRUE ) @@ -167,7 +167,7 @@ test_that("map_sample_observations handles invalid inputs", { ) invalid_arg_list2 <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_probab" ) diff --git a/tests/testthat/test-map_simulate_occurrences.R b/tests/testthat/test-map_simulate_occurrences.R index 1efe00e..8451cc4 100644 --- a/tests/testthat/test-map_simulate_occurrences.R +++ b/tests/testthat/test-map_simulate_occurrences.R @@ -5,13 +5,13 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) # Specify dataframe for 3 species with custom function arguments # Dataframe with column names equal to arguments species_dataset_df <- tibble( - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), n_time_points = rep(6, 3), seed = 123) species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), @@ -21,7 +21,7 @@ species_dataset_df1 <- tibble( # Dataframe with custom column names and named list for argument conversion species_dataset_df2 <- species_dataset_df1 %>% - rename(polygon = plgn, + rename(polygon = species_range, sd = sd_step) @@ -68,7 +68,7 @@ test_that("map_simulate_occurrences works with simple column names", { test_that("map_simulate_occurrences works with arg_list for renaming columns", { # Test with arg_list arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd" ) @@ -97,7 +97,7 @@ test_that("map_simulate_occurrences handles invalid inputs", { # Invalid arg_list invalid_arg_list <- list( - plgn = "polygon", + species_range = "polygon", sd_step = 123 ) expect_error( @@ -107,7 +107,7 @@ test_that("map_simulate_occurrences handles invalid inputs", { ) invalid_arg_list2 <- list( - plgn = "polygon", + species_range = "polygon", sd_step = "sd", detection_probability = "det_prob" ) diff --git a/tests/testthat/test-map_simulation_functions.R b/tests/testthat/test-map_simulation_functions.R index 669d46b..0d352f5 100644 --- a/tests/testthat/test-map_simulation_functions.R +++ b/tests/testthat/test-map_simulation_functions.R @@ -13,7 +13,7 @@ cube_grid <- st_make_grid( # Dataframe with column names equal to arguments for simple polygon species_dataset_df1 <- tibble( taxonID = c("species1", "species2", "species3"), - plgn = rep(list(plgn), 3), + species_range = rep(list(plgn), 3), initial_average_occurrences = c(50, 100, 500), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), diff --git a/tests/testthat/test-simulate_occurrences.R b/tests/testthat/test-simulate_occurrences.R index dcafa40..afb126b 100644 --- a/tests/testthat/test-simulate_occurrences.R +++ b/tests/testthat/test-simulate_occurrences.R @@ -67,7 +67,7 @@ test_that("simulate_occurrences handles different n_time_points values", { test_that("simulate_occurrences raises an error for incorrect plgn type", { plgn <- list() # Not an sf object expect_error(simulate_occurrences(plgn), - "`plgn` must be an sf object with POLYGON geometry.") + "`species_range` must be an sf object with POLYGON geometry.") }) test_that("simulate_occurrences raises an error for non-numeric abundance", { diff --git a/vignettes/articles/detection-process.Rmd b/vignettes/articles/detection-process.Rmd index bd2b75e..4ec087a 100644 --- a/vignettes/articles/detection-process.Rmd +++ b/vignettes/articles/detection-process.Rmd @@ -53,7 +53,7 @@ We can for example sample randomly within the polygon over 6 time points were we ```{r} occurrences_df <- simulate_occurrences( - plgn = polygon, + species_range = polygon, initial_average_occurrences = 100, n_time_points = 6, temporal_function = simulate_random_walk, diff --git a/vignettes/articles/grid-designation-process.Rmd b/vignettes/articles/grid-designation-process.Rmd index 62cb7ab..3456756 100644 --- a/vignettes/articles/grid-designation-process.Rmd +++ b/vignettes/articles/grid-designation-process.Rmd @@ -84,7 +84,7 @@ We can for example sample randomly within the polygon over 6 time points were we ```{r} occurrences_df <- simulate_occurrences( - plgn = polygon, + species_range = polygon, initial_average_occurrences = 100, n_time_points = 6, temporal_function = simulate_random_walk, diff --git a/vignettes/articles/multi_species_approach.Rmd b/vignettes/articles/multi_species_approach.Rmd index 7add1b1..d8268d4 100644 --- a/vignettes/articles/multi_species_approach.Rmd +++ b/vignettes/articles/multi_species_approach.Rmd @@ -106,7 +106,7 @@ The values within these columns can change between species. ```{r} # Create dataframe with simulation function arguments multi_species_args <- tibble( - plgn = rep(list(polygon), 6), + species_range = rep(list(polygon), 6), initial_average_occurrences = rep(c(50, 100, 500), 2), n_time_points = rep(6, 6), temporal_function = c(simulate_random_walk, simulate_random_walk, rep(NA, 4)), @@ -256,14 +256,14 @@ The mapping functions will retain the original column names in the output. We rename for example three column names such that they do not longer match the argument names of the single-species functions: -- plgn $\rightarrow$ polygon (argument of `simulate_occurrences()`) +- species_range $\rightarrow$ polygon (argument of `simulate_occurrences()`) - detection_probability $\rightarrow$ det_prob (argument of `sample_observations()`) - grid $\rightarrow$ raster (argument of `grid_designation()`) ```{r} # Create dataframe with custom column names multi_species_dataset2_renamed <- multi_species_dataset2 %>% - rename("polygon" = "plgn", + rename("polygon" = "species_range", "det_prob" = "detection_probability", "raster" = "grid") ``` @@ -274,7 +274,7 @@ The names are the argument names of the single-species functions and the values ```{r} # Create named list for argument conversion arg_conv_list <- list( - plgn = "polygon", + species_range = "polygon", detection_probability = "det_prob", grid = "raster" ) diff --git a/vignettes/articles/occurrence-process.Rmd b/vignettes/articles/occurrence-process.Rmd index 95e4901..b6a6bcb 100644 --- a/vignettes/articles/occurrence-process.Rmd +++ b/vignettes/articles/occurrence-process.Rmd @@ -323,7 +323,7 @@ We can for example sample randomly within the polygon over 6 time points were we ```{r} occurrences_df <- simulate_occurrences( - plgn = polygon, + species_range = polygon, initial_average_occurrences = 100, n_time_points = 6, temporal_function = simulate_random_walk, From 81f15d723cd591a30ca214c6bcf6678143362f6c Mon Sep 17 00:00:00 2001 From: wlangera Date: Mon, 5 Aug 2024 10:19:05 +0200 Subject: [PATCH 22/57] change autocor argument name --- NEWS.md | 2 +- R/create_spatial_pattern.R | 3 ++- R/map_add_coordinate_uncertainty.R | 2 +- R/map_filter_observations.R | 2 +- R/map_grid_designation.R | 2 +- R/map_sample_observations.R | 2 +- R/map_simulate_occurrences.R | 2 +- R/map_simulation_functions.R | 2 +- R/simulate_occurrences.R | 18 +++++++++--------- man/create_spatial_pattern.Rd | 3 ++- man/map_add_coordinate_uncertainty.Rd | 2 +- man/map_filter_observations.Rd | 2 +- man/map_grid_designation.Rd | 2 +- man/map_sample_observations.Rd | 2 +- man/map_simulate_occurrences.Rd | 2 +- man/map_simulation_functions.Rd | 2 +- man/simulate_occurrences.Rd | 6 +++--- .../test-map_add_coordinate_uncertainty.R | 2 +- tests/testthat/test-map_filter_observations.R | 2 +- tests/testthat/test-map_grid_designation.R | 2 +- tests/testthat/test-map_sample_observations.R | 4 ++-- tests/testthat/test-map_simulate_occurrences.R | 2 +- tests/testthat/test-simulate_occurrences.R | 12 ++++++------ vignettes/articles/detection-process.Rmd | 2 +- .../articles/grid-designation-process.Rmd | 2 +- vignettes/articles/multi_species_approach.Rmd | 2 +- vignettes/articles/occurrence-process.Rmd | 2 +- 27 files changed, 45 insertions(+), 43 deletions(-) diff --git a/NEWS.md b/NEWS.md index d3fb0a7..c1bd5d6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # gcube 0.4.0 -* Consolidate documentation throughout all functions, README, and vignettes. +* Consolidate documentation across all functions, README, and vignettes. # gcube 0.3.0 diff --git a/R/create_spatial_pattern.R b/R/create_spatial_pattern.R index 970db65..060bcef 100644 --- a/R/create_spatial_pattern.R +++ b/R/create_spatial_pattern.R @@ -4,7 +4,8 @@ #' polygon. #' #' @param polygon An sf object with POLYGON geometry. -#' @param resolution A numeric value defining the resolution of the raster cell. +#' @param resolution A numeric value defining the resolution of the raster +#' cells. #' @param spatial_pattern Specifies the desired spatial pattern. It can #' be a character string (`"random"` or `"clustered"`) or a numeric value ≥ 1 #' (1 means random distribution, larger values indicate more clustering). diff --git a/R/map_add_coordinate_uncertainty.R b/R/map_add_coordinate_uncertainty.R index ef5c118..acb7c90 100644 --- a/R/map_add_coordinate_uncertainty.R +++ b/R/map_add_coordinate_uncertainty.R @@ -49,7 +49,7 @@ #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), -#' spatial_autocorr = "random", +#' spatial_pattern = "random", #' detection_probability = c(0.8, 0.9, 1), #' invert = FALSE, #' coords_uncertainty_meters = c(25, 30, 50), diff --git a/R/map_filter_observations.R b/R/map_filter_observations.R index 3075904..b8f5451 100644 --- a/R/map_filter_observations.R +++ b/R/map_filter_observations.R @@ -49,7 +49,7 @@ #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), -#' spatial_autocorr = "random", +#' spatial_pattern = "random", #' detection_probability = c(0.8, 0.9, 1), #' invert = FALSE, #' seed = 123) diff --git a/R/map_grid_designation.R b/R/map_grid_designation.R index 9a4fc9a..bb0dfab 100644 --- a/R/map_grid_designation.R +++ b/R/map_grid_designation.R @@ -56,7 +56,7 @@ #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), -#' spatial_autocorr = "random", +#' spatial_pattern = "random", #' detection_probability = c(0.8, 0.9, 1), #' invert = FALSE, #' coords_uncertainty_meters = c(25, 30, 50), diff --git a/R/map_sample_observations.R b/R/map_sample_observations.R index 0a285cd..a210c90 100644 --- a/R/map_sample_observations.R +++ b/R/map_sample_observations.R @@ -49,7 +49,7 @@ #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), -#' spatial_autocorr = "random", +#' spatial_pattern = "random", #' detection_probability = c(0.8, 0.9, 1), #' seed = 123) #' diff --git a/R/map_simulate_occurrences.R b/R/map_simulate_occurrences.R index 18cc60e..b1cdd09 100644 --- a/R/map_simulate_occurrences.R +++ b/R/map_simulate_occurrences.R @@ -51,7 +51,7 @@ #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), -#' spatial_autocorr = "random", +#' spatial_pattern = "random", #' seed = 123) #' #' # Simulate occurrences diff --git a/R/map_simulation_functions.R b/R/map_simulation_functions.R index 5d28cc6..29df3a6 100644 --- a/R/map_simulation_functions.R +++ b/R/map_simulation_functions.R @@ -49,7 +49,7 @@ #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), -#' spatial_autocorr = "random", +#' spatial_pattern = "random", #' seed = 123) #' #' # Simulate occurrences diff --git a/R/simulate_occurrences.R b/R/simulate_occurrences.R index 2677eab..7776087 100644 --- a/R/simulate_occurrences.R +++ b/R/simulate_occurrences.R @@ -16,7 +16,7 @@ #' function is provided, it defines the temporal pattern of number of #' occurrences. #' @param ... Additional arguments to be passed to `temporal_function`. -#' @param spatial_autocorr Specifies the spatial pattern of occurrences. It can +#' @param spatial_pattern Specifies the spatial pattern of occurrences. It can #' be a character string (`"random"` or `"clustered"`) or a numeric value ≥ 1 #' (1 means random distribution, larger values indicate more clustering). #' The default is `"random"`. `"clustered"` corresponds to a value of 10. @@ -65,7 +65,7 @@ #' ## Clustered spatial pattern with 4 time points #' occ_sf_100 <- simulate_occurrences( #' species_range = plgn, -#' spatial_autocorr = 100, +#' spatial_pattern = 100, #' n_time_points = 4, #' initial_average_occurrences = 100, #' seed = 123) @@ -82,7 +82,7 @@ simulate_occurrences <- function( species_range, initial_average_occurrences = 50, - spatial_autocorr = c("random", "clustered"), + spatial_pattern = c("random", "clustered"), n_time_points = 1, temporal_function = NA, ..., @@ -102,12 +102,12 @@ simulate_occurrences <- function( assertthat::is.number(initial_average_occurrences) & initial_average_occurrences >= 0) - if (!(assertthat::is.number(spatial_autocorr) && spatial_autocorr >= 1)) { - # Check if spatial_autocorr is random or clustered - spatial_autocorr <- tryCatch({ - match.arg(spatial_autocorr, c("random", "clustered")) + if (!(assertthat::is.number(spatial_pattern) && spatial_pattern >= 1)) { + # Check if spatial_pattern is random or clustered + spatial_pattern <- tryCatch({ + match.arg(spatial_pattern, c("random", "clustered")) }, error = function(e) { - stop(paste0("`spatial_autocorr` must be one of 'random', 'clustered',", + stop(paste0("`spatial_pattern` must be one of 'random', 'clustered',", " or a single number larger or equal to 1."), call. = FALSE) }) @@ -146,7 +146,7 @@ simulate_occurrences <- function( rs_pattern <- create_spatial_pattern( polygon = species_range, resolution = res, - spatial_pattern = spatial_autocorr, + spatial_pattern = spatial_pattern, seed = seed, n_sim = 1) diff --git a/man/create_spatial_pattern.Rd b/man/create_spatial_pattern.Rd index f71b037..1c3fde7 100644 --- a/man/create_spatial_pattern.Rd +++ b/man/create_spatial_pattern.Rd @@ -15,7 +15,8 @@ create_spatial_pattern( \arguments{ \item{polygon}{An sf object with POLYGON geometry.} -\item{resolution}{A numeric value defining the resolution of the raster cell.} +\item{resolution}{A numeric value defining the resolution of the raster +cells.} \item{spatial_pattern}{Specifies the desired spatial pattern. It can be a character string (\code{"random"} or \code{"clustered"}) or a numeric value ≥ 1 diff --git a/man/map_add_coordinate_uncertainty.Rd b/man/map_add_coordinate_uncertainty.Rd index a8d9d50..62b1ec9 100644 --- a/man/map_add_coordinate_uncertainty.Rd +++ b/man/map_add_coordinate_uncertainty.Rd @@ -52,7 +52,7 @@ species_dataset_df <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", detection_probability = c(0.8, 0.9, 1), invert = FALSE, coords_uncertainty_meters = c(25, 30, 50), diff --git a/man/map_filter_observations.Rd b/man/map_filter_observations.Rd index 05e4564..ad0545f 100644 --- a/man/map_filter_observations.Rd +++ b/man/map_filter_observations.Rd @@ -52,7 +52,7 @@ species_dataset_df <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", detection_probability = c(0.8, 0.9, 1), invert = FALSE, seed = 123) diff --git a/man/map_grid_designation.Rd b/man/map_grid_designation.Rd index 369886d..712659d 100644 --- a/man/map_grid_designation.Rd +++ b/man/map_grid_designation.Rd @@ -59,7 +59,7 @@ species_dataset_df <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", detection_probability = c(0.8, 0.9, 1), invert = FALSE, coords_uncertainty_meters = c(25, 30, 50), diff --git a/man/map_sample_observations.Rd b/man/map_sample_observations.Rd index 1c3eece..58748cf 100644 --- a/man/map_sample_observations.Rd +++ b/man/map_sample_observations.Rd @@ -52,7 +52,7 @@ species_dataset_df <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", detection_probability = c(0.8, 0.9, 1), seed = 123) diff --git a/man/map_simulate_occurrences.Rd b/man/map_simulate_occurrences.Rd index 570b315..791ef80 100644 --- a/man/map_simulate_occurrences.Rd +++ b/man/map_simulate_occurrences.Rd @@ -54,7 +54,7 @@ species_dataset_df <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", seed = 123) # Simulate occurrences diff --git a/man/map_simulation_functions.Rd b/man/map_simulation_functions.Rd index 71bf8b6..1197e11 100644 --- a/man/map_simulation_functions.Rd +++ b/man/map_simulation_functions.Rd @@ -50,7 +50,7 @@ species_dataset_df <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", seed = 123) # Simulate occurrences diff --git a/man/simulate_occurrences.Rd b/man/simulate_occurrences.Rd index b6a2e59..538d120 100644 --- a/man/simulate_occurrences.Rd +++ b/man/simulate_occurrences.Rd @@ -7,7 +7,7 @@ simulate_occurrences( species_range, initial_average_occurrences = 50, - spatial_autocorr = c("random", "clustered"), + spatial_pattern = c("random", "clustered"), n_time_points = 1, temporal_function = NA, ..., @@ -23,7 +23,7 @@ average number of occurrences to be simulated within the extent of \code{species_range} at the first time point. This value serves as the mean (lambda) of a Poisson distribution.} -\item{spatial_autocorr}{Specifies the spatial pattern of occurrences. It can +\item{spatial_pattern}{Specifies the spatial pattern of occurrences. It can be a character string (\code{"random"} or \code{"clustered"}) or a numeric value ≥ 1 (1 means random distribution, larger values indicate more clustering). The default is \code{"random"}. \code{"clustered"} corresponds to a value of 10. @@ -81,7 +81,7 @@ ggplot() + ## Clustered spatial pattern with 4 time points occ_sf_100 <- simulate_occurrences( species_range = plgn, - spatial_autocorr = 100, + spatial_pattern = 100, n_time_points = 4, initial_average_occurrences = 100, seed = 123) diff --git a/tests/testthat/test-map_add_coordinate_uncertainty.R b/tests/testthat/test-map_add_coordinate_uncertainty.R index 41eb797..50e633d 100644 --- a/tests/testthat/test-map_add_coordinate_uncertainty.R +++ b/tests/testthat/test-map_add_coordinate_uncertainty.R @@ -11,7 +11,7 @@ species_dataset_df1 <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", detection_probability = c(0.8, 0.9, 1), invert = FALSE, coords_uncertainty_meters = c(25, 30, 50), diff --git a/tests/testthat/test-map_filter_observations.R b/tests/testthat/test-map_filter_observations.R index bfd2b04..b96d7fd 100644 --- a/tests/testthat/test-map_filter_observations.R +++ b/tests/testthat/test-map_filter_observations.R @@ -11,7 +11,7 @@ species_dataset_df1 <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", detection_probability = c(0.8, 0.9, 1), invert = FALSE, seed = 123) diff --git a/tests/testthat/test-map_grid_designation.R b/tests/testthat/test-map_grid_designation.R index 319305e..6c686f4 100644 --- a/tests/testthat/test-map_grid_designation.R +++ b/tests/testthat/test-map_grid_designation.R @@ -18,7 +18,7 @@ species_dataset_df1 <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", detection_probability = c(0.8, 0.9, 1), invert = FALSE, coords_uncertainty_meters = c(25, 30, 50), diff --git a/tests/testthat/test-map_sample_observations.R b/tests/testthat/test-map_sample_observations.R index 8c82d7b..309e2f1 100644 --- a/tests/testthat/test-map_sample_observations.R +++ b/tests/testthat/test-map_sample_observations.R @@ -25,7 +25,7 @@ species_dataset_df1 <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", detection_probability = c(0.8, 0.9, 1), seed = 123) @@ -50,7 +50,7 @@ species_dataset_df3 <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", detection_probability = c(0.8, 0.9, 1), sampling_bias = "polygon", bias_area = rep(list(road_polygon), 3), diff --git a/tests/testthat/test-map_simulate_occurrences.R b/tests/testthat/test-map_simulate_occurrences.R index 8451cc4..27fb9c0 100644 --- a/tests/testthat/test-map_simulate_occurrences.R +++ b/tests/testthat/test-map_simulate_occurrences.R @@ -16,7 +16,7 @@ species_dataset_df1 <- tibble( n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), - spatial_autocorr = "random", + spatial_pattern = "random", seed = 123) # Dataframe with custom column names and named list for argument conversion diff --git a/tests/testthat/test-simulate_occurrences.R b/tests/testthat/test-simulate_occurrences.R index afb126b..d908cb8 100644 --- a/tests/testthat/test-simulate_occurrences.R +++ b/tests/testthat/test-simulate_occurrences.R @@ -33,14 +33,14 @@ test_that("simulate_occurrences returns reproducible results with a seed", { expect_equal(result1, result2) }) -test_that("simulate_occurrences handles different spatial_autocorr values", { +test_that("simulate_occurrences handles different spatial_pattern values", { result_random <- simulate_occurrences(plgn, initial_average_occurrences = 50, - spatial_autocorr = "random", + spatial_pattern = "random", n_time_points = 1) result_clustered <- simulate_occurrences(plgn, initial_average_occurrences = 50, - spatial_autocorr = "clustered", + spatial_pattern = "clustered", n_time_points = 1) expect_s3_class(result_random, "sf") @@ -78,10 +78,10 @@ test_that("simulate_occurrences raises an error for non-numeric abundance", { "`initial_average_occurrences` must be a single positive number.") }) -test_that("simulate_occurrences raises an error for invalid spatial_autocorr", { +test_that("simulate_occurrences raises an error for invalid spatial_pattern", { expect_error( - simulate_occurrences(plgn, spatial_autocorr = "invalid_value"), - paste("`spatial_autocorr` must be one of 'random', 'clustered', or a", + simulate_occurrences(plgn, spatial_pattern = "invalid_value"), + paste("`spatial_pattern` must be one of 'random', 'clustered', or a", "single number larger or equal to 1.") ) }) diff --git a/vignettes/articles/detection-process.Rmd b/vignettes/articles/detection-process.Rmd index 4ec087a..d85068c 100644 --- a/vignettes/articles/detection-process.Rmd +++ b/vignettes/articles/detection-process.Rmd @@ -58,7 +58,7 @@ occurrences_df <- simulate_occurrences( n_time_points = 6, temporal_function = simulate_random_walk, sd_step = 1, - spatial_autocorr = "random", + spatial_pattern = "random", seed = 123) ``` diff --git a/vignettes/articles/grid-designation-process.Rmd b/vignettes/articles/grid-designation-process.Rmd index 3456756..f5e9b2c 100644 --- a/vignettes/articles/grid-designation-process.Rmd +++ b/vignettes/articles/grid-designation-process.Rmd @@ -89,7 +89,7 @@ occurrences_df <- simulate_occurrences( n_time_points = 6, temporal_function = simulate_random_walk, sd_step = 1, - spatial_autocorr = "random", + spatial_pattern = "random", seed = 123) ``` diff --git a/vignettes/articles/multi_species_approach.Rmd b/vignettes/articles/multi_species_approach.Rmd index d8268d4..50bd904 100644 --- a/vignettes/articles/multi_species_approach.Rmd +++ b/vignettes/articles/multi_species_approach.Rmd @@ -111,7 +111,7 @@ multi_species_args <- tibble( n_time_points = rep(6, 6), temporal_function = c(simulate_random_walk, simulate_random_walk, rep(NA, 4)), sd_step = c(1, 1, rep(NA, 4)), - spatial_autocorr = c(rep("random", 3), rep("clustered", 3)), + spatial_pattern = c(rep("random", 3), rep("clustered", 3)), detection_probability = rep(c(0.8, 0.9, 1), 2), sampling_bias = c(rep("polygon", 3), rep("no_bias", 3)), bias_area = c(rep(list(road_polygon), 3), rep(NA, 3)), diff --git a/vignettes/articles/occurrence-process.Rmd b/vignettes/articles/occurrence-process.Rmd index b6a6bcb..89249cd 100644 --- a/vignettes/articles/occurrence-process.Rmd +++ b/vignettes/articles/occurrence-process.Rmd @@ -328,7 +328,7 @@ occurrences_df <- simulate_occurrences( n_time_points = 6, temporal_function = simulate_random_walk, sd_step = 1, - spatial_autocorr = "random", + spatial_pattern = "random", seed = 123) ``` From 37e093aeca38035c4726c6630603efdd9409535f Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 6 Aug 2024 16:44:42 +0200 Subject: [PATCH 23/57] better explanation readme --- README.Rmd | 22 +++++++++++++--------- README.md | 36 ++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/README.Rmd b/README.Rmd index 5102e91..17112b7 100644 --- a/README.Rmd +++ b/README.Rmd @@ -46,8 +46,8 @@ The name **gcube** stands for 'generate cube' since it can be used to generate b ## Example -This is a basic example which shows you the workflow for simulating a biodiversity data cube. -This is divided in three steps or processes: +This is a basic example which shows the workflow for simulating a biodiversity data cube. +It is divided in three steps or processes: 1. Occurrence process 2. Detection process @@ -66,12 +66,12 @@ library(dplyr) # data wrangling library(ggplot2) # visualisation with ggplot ``` -We create a random polygon as input. +We create a polygon as input. It represents the spatial extend of the species. ```{r polygon} #| fig.alt: > #| Spatial extend in which we will simulate species occurrences. -# Create a polygon to simulate occurrences +# Create a polygon to simulate occurrences within polygon <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7,9, 5, 2)))) # Visualise @@ -83,8 +83,8 @@ ggplot() + ### Occurrence process We generate occurrence points within the polygon using the `simulate_occurrences()` function. -These are the "real" occurrences of the species, whether we have observed them or not. -In the `simulate_occurrences()` function, the user can specify different levels of spatial clustering, and can define the trend change of the species over time. +In this function, the user can specify different levels of spatial clustering, and define the trend of number of occurrences over time. +The default is a random spatial pattern and a single time point with `rpois(1, 50)` occurrences. ```{r simulate-occurrences} #| fig.alt: > @@ -92,6 +92,9 @@ In the `simulate_occurrences()` function, the user can specify different levels # Simulate occurrences within polygon occurrences_df <- simulate_occurrences( species_range = polygon, + initial_average_occurrences = 50, + spatial_pattern = c("random", "clustered"), + n_time_points = 1, seed = 123) # Visualise @@ -103,9 +106,9 @@ ggplot() + ### Detection process -In this step we define the sampling process, based on the detection probability of the species and the sampling bias. +In the second step we define the sampling process, based on the detection probability of the species and the sampling bias. This is done using the `sample_observations()` function. -The default sampling bias is `"no_bias"`, but bias can also be inserted using a polygon or a grid. +The default sampling bias is `"no_bias"`, but bias can be added using a polygon or a grid as well. ```{r detect-occurrences} #| fig.alt: > @@ -114,6 +117,7 @@ The default sampling bias is `"no_bias"`, but bias can also be inserted using a detections_df_raw <- sample_observations( occurrences = occurrences_df, detection_probability = 0.5, + sampling_bias = c("no_bias", "polygon", "manual"), seed = 123) # Visualise @@ -157,7 +161,7 @@ ggplot() + ### Grid designation process -Finally, observations are designated to a grid to create an occurrence cube. +Finally, observations are designated to a grid with `grid_designation()` to create an occurrence cube. We create a grid over the spatial extend using `sf::st_make_grid()`. ```{r create-grid} diff --git a/README.md b/README.md index fe30920..3e848d7 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,8 @@ generate biodiversity data cubes from minimal input. ## Example -This is a basic example which shows you the workflow for simulating a -biodiversity data cube. This is divided in three steps or processes: +This is a basic example which shows the workflow for simulating a +biodiversity data cube. It is divided in three steps or processes: 1. Occurrence process 2. Detection process @@ -68,10 +68,11 @@ library(dplyr) # data wrangling library(ggplot2) # visualisation with ggplot ``` -We create a random polygon as input. +We create a polygon as input. It represents the spatial extend of the +species. ``` r -# Create a polygon to simulate occurrences +# Create a polygon to simulate occurrences within polygon <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7,9, 5, 2)))) # Visualise @@ -85,16 +86,18 @@ ggplot() + ### Occurrence process We generate occurrence points within the polygon using the -`simulate_occurrences()` function. These are the “real” occurrences of -the species, whether we have observed them or not. In the -`simulate_occurrences()` function, the user can specify different levels -of spatial clustering, and can define the trend change of the species -over time. +`simulate_occurrences()` function. In this function, the user can +specify different levels of spatial clustering, and define the trend of +number of occurrences over time. The default is a random spatial pattern +and a single time point with `rpois(1, 50)` occurrences. ``` r # Simulate occurrences within polygon occurrences_df <- simulate_occurrences( species_range = polygon, + initial_average_occurrences = 50, + spatial_pattern = c("random", "clustered"), + n_time_points = 1, seed = 123) #> [using unconditional Gaussian simulation] @@ -109,16 +112,17 @@ ggplot() + ### Detection process -In this step we define the sampling process, based on the detection -probability of the species and the sampling bias. This is done using the -`sample_observations()` function. The default sampling bias is -`"no_bias"`, but bias can also be inserted using a polygon or a grid. +In the second step we define the sampling process, based on the +detection probability of the species and the sampling bias. This is done +using the `sample_observations()` function. The default sampling bias is +`"no_bias"`, but bias can be added using a polygon or a grid as well. ``` r # Detect occurrences detections_df_raw <- sample_observations( occurrences = occurrences_df, detection_probability = 0.5, + sampling_bias = c("no_bias", "polygon", "manual"), seed = 123) # Visualise @@ -165,9 +169,9 @@ ggplot() + ### Grid designation process -Finally, observations are designated to a grid to create an occurrence -cube. We create a grid over the spatial extend using -`sf::st_make_grid()`. +Finally, observations are designated to a grid with `grid_designation()` +to create an occurrence cube. We create a grid over the spatial extend +using `sf::st_make_grid()`. ``` r # Define a grid over spatial extend From a516fe31fac7d57d7b6cde421162e1a54ee7cb7f Mon Sep 17 00:00:00 2001 From: wlangera Date: Tue, 6 Aug 2024 16:55:12 +0200 Subject: [PATCH 24/57] add multispecies approach --- README.Rmd | 14 ++++++++++++++ README.md | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/README.Rmd b/README.Rmd index 17112b7..37b7fc6 100644 --- a/README.Rmd +++ b/README.Rmd @@ -223,3 +223,17 @@ ggplot() + labs(x = "", y = "") + theme_minimal() ``` + +### Cubes for multiple species + +Each cube simulation function mentioned earlier has a corresponding mapping function. +These mapping functions are designed to handle operations for multiple species simultaneously by using the `purrr::pmap()` function. +Please consult the documentation for detailed information on how these mapping functions are implemented. + +| single species | multiple species | +|-----------------------------|---------------------------------| +| simulate_occurrences() | map_simulate_occurrences() | +| sample_observations() | map_sample_observations() | +| filter_observations() | map_filter_observations() | +| add_coordinate_uncertainty()| map_add_coordinate_uncertainty()| +| grid_designation() | map_grid_designation() | diff --git a/README.md b/README.md index 3e848d7..8d8568b 100644 --- a/README.md +++ b/README.md @@ -234,3 +234,19 @@ ggplot() + ``` Distribution of minimal coordinate uncertainty. + +### Cubes for multiple species + +Each cube simulation function mentioned earlier has a corresponding +mapping function. These mapping functions are designed to handle +operations for multiple species simultaneously by using the +`purrr::pmap()` function. Please consult the documentation for detailed +information on how these mapping functions are implemented. + +| single species | multiple species | +|------------------------------|----------------------------------| +| simulate_occurrences() | map_simulate_occurrences() | +| sample_observations() | map_sample_observations() | +| filter_observations() | map_filter_observations() | +| add_coordinate_uncertainty() | map_add_coordinate_uncertainty() | +| grid_designation() | map_grid_designation() | From 703f633bd088f873dfbd7bce567165fe67cc0886 Mon Sep 17 00:00:00 2001 From: wlangera Date: Wed, 14 Aug 2024 16:14:24 +0200 Subject: [PATCH 25/57] test error message --- tests/testthat/test-add_coordinate_uncertainty.R | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/testthat/test-add_coordinate_uncertainty.R b/tests/testthat/test-add_coordinate_uncertainty.R index aea1e32..f80395f 100644 --- a/tests/testthat/test-add_coordinate_uncertainty.R +++ b/tests/testthat/test-add_coordinate_uncertainty.R @@ -8,12 +8,6 @@ observations_sf <- ) %>% st_as_sf(coords = c("long", "lat"), crs = 3035) -# Create dataframe without geometry -non_sf_data <- data.frame( - lat = runif(n_observations, 3110000, 3112000), - long = runif(n_observations, 3841000, 3842000) -) - ## Unit tests @@ -29,8 +23,8 @@ test_that("Function adds a column with a vector of uncertainty values", { }) test_that("Function throws an error if observations is not an sf object", { - expect_error(add_coordinate_uncertainty(non_sf_data, 1000), - "`observations` must be an sf object with POINT geometry.") + expect_error(add_coordinate_uncertainty("non_sf_data", 1000), + "`observations` must be an sf object or a dataframe.") }) test_that("Function throws an error if coord. uncert. is not numeric", { From c8638cab3e23dbba93eee769707e62f7353a575d Mon Sep 17 00:00:00 2001 From: wlangera Date: Wed, 14 Aug 2024 16:14:40 +0200 Subject: [PATCH 26/57] clean examples --- R/add_coordinate_uncertainty.R | 58 ++++++++++++--------------- R/apply_manual_sampling_bias.R | 49 +++++++++------------- R/apply_polygon_sampling_bias.R | 10 ++--- R/create_spatial_pattern.R | 33 ++------------- R/filter_observations.R | 37 +++++------------ R/sample_observations.R | 8 ++-- R/sample_occurrences_from_raster.R | 3 -- R/simulate_occurrences.R | 19 ++++----- R/simulate_random_walk.R | 1 - R/simulate_timeseries.R | 57 +++----------------------- man/add_coordinate_uncertainty.Rd | 51 ++++++++++------------- man/apply_manual_sampling_bias.Rd | 49 +++++++++------------- man/apply_polygon_sampling_bias.Rd | 10 ++--- man/create_spatial_pattern.Rd | 33 ++------------- man/filter_observations.Rd | 37 +++++------------ man/sample_observations.Rd | 8 ++-- man/sample_occurrences_from_raster.Rd | 3 -- man/simulate_occurrences.Rd | 19 ++++----- man/simulate_random_walk.Rd | 1 - man/simulate_timeseries.Rd | 57 +++----------------------- 20 files changed, 157 insertions(+), 386 deletions(-) diff --git a/R/add_coordinate_uncertainty.R b/R/add_coordinate_uncertainty.R index 4c58816..8071096 100644 --- a/R/add_coordinate_uncertainty.R +++ b/R/add_coordinate_uncertainty.R @@ -1,20 +1,20 @@ #' Add coordinate uncertainty to observations #' -#' This function adds a column to the input sf object containing the coordinate -#' uncertainty for each observation, measured in meters. +#' This function adds a column to the input dataframe or sf object containing +#' the coordinate uncertainty for each observation, measured in meters. #' -#' @param observations An sf object with POINT geometry representing the -#' observations. This object contains the spatial points to which the coordinate -#' uncertainty will be added. +#' @param observations An sf object with POINT geometry or a simple +#' dataframe representing the observations. This object contains the observation +#' points to which the coordinate uncertainty will be added. #' @param coords_uncertainty_meters A numeric value or a vector of numeric #' values representing the coordinate uncertainty (in meters) associated with #' each observation. If a single numeric value is provided, it will be applied #' to all observations. If a numeric vector is provided, it must be the same #' length as the number of observations. #' -#' @returns The input sf object with POINT geometry, with an additional column -#' named `coordinateUncertaintyInMeters` that contains the coordinate -#' uncertainty values in meters. +#' @returns The input data frame or an sf object with POINT geometry, with an +#' additional column named `coordinateUncertaintyInMeters` that contains the +#' coordinate uncertainty values in meters. #' #' @export #' @@ -24,31 +24,24 @@ #' @family main #' #' @examples +#' # Create dataframe with sampling status column +#' observations_data <- data.frame( +#' time_point = 1, +#' sampling_prob = seq(0.5, 1, 0.1) +#' ) #' -#' library(sf) -#' library(dplyr) -#' -#' set.seed(123) -#' -#' # Create four random points -#' n_points <- 4 -#' xlim <- c(3841000, 3842000) -#' ylim <- c(3110000, 3112000) -#' observations_sf <- data.frame( -#' lat = runif(n_points, ylim[1], ylim[2]), -#' long = runif(n_points, xlim[1], xlim[2])) %>% -#' st_as_sf(coords = c("long", "lat"), crs = 3035) -#' -#' # provide a fixed uncertainty for all points -#' add_coordinate_uncertainty( -#' observations_sf, -#' coords_uncertainty_meters = 1000 -#' ) +#' # provide a fixed uncertainty for all points +#' add_coordinate_uncertainty( +#' observations_data, +#' coords_uncertainty_meters = 1000 +#' ) #' #' # add variability in uncertainty. For example, using gamma distribution +#' uncertainty_vec <- seq(50, 100, 10) +#' #' add_coordinate_uncertainty( -#' observations_sf, -#' coords_uncertainty_meters = rgamma(n_points, shape = 5, rate = 0.1) +#' observations_data, +#' coords_uncertainty_meters = uncertainty_vec #' ) add_coordinate_uncertainty <- function( @@ -57,10 +50,9 @@ add_coordinate_uncertainty <- function( ### Start checks # 1. Check input type and length # Check if observations is an sf object - stopifnot("`observations` must be an sf object with POINT geometry." = - inherits(observations, "sf") && - sf::st_geometry_type(observations, - by_geometry = FALSE) == "POINT") + stopifnot("`observations` must be an sf object or a dataframe." = + inherits(observations, "sf") || + inherits(observations, "data.frame")) # Check if coords_uncertainty_meters is numeric stopifnot("`coords_uncertainty_meters` must be numeric vector." = diff --git a/R/apply_manual_sampling_bias.R b/R/apply_manual_sampling_bias.R index c9c070a..10b3eb3 100644 --- a/R/apply_manual_sampling_bias.R +++ b/R/apply_manual_sampling_bias.R @@ -28,43 +28,34 @@ #' library(dplyr) #' library(ggplot2) #' -#' # Set seed for reproducibility -#' set.seed(123) +#' # Create polygon +#' plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) #' -#' # Simulate some occurrence data with coordinates and time points -#' num_points <- 10 -#' occurrences <- data.frame( -#' lon = runif(num_points, min = -180, max = 180), -#' lat = runif(num_points, min = -90, max = 90), -#' time_point = 0 -#' ) +#' # Get occurrence points +#' occurrences_sf <- simulate_occurrences(plgn) #' -#' # Convert the occurrence data to an sf object -#' occurrences_sf <- st_as_sf(occurrences, coords = c("lon", "lat")) +#' # Set seed for reproducibility +#' set.seed(123) #' -#' # Create raster grid -#' grid <- st_make_grid(occurrences_sf) %>% +#' # Create grid with bias weights +#' grid <- st_make_grid( +#' plgn, +#' n = c(10, 10), +#' square = TRUE) %>% #' st_sf() +#' grid$bias_weight <- runif(nrow(grid), min = 0, max = 1) #' -#' # Bias weights between 0 and 1 -#' grid1 <- grid %>% -#' mutate(bias_weight = runif(nrow(grid), min = 0, max = 1)) -#' -#' apply_manual_sampling_bias(occurrences_sf, grid1) -#' -#' # Bias weights larger than 1 -#' grid2 <- grid %>% -#' mutate(bias_weight = rpois(nrow(grid), 5)) -#' -#' occurrence_bias_sf <- apply_manual_sampling_bias(occurrences_sf, grid2) -#' occurrence_bias_sf +#' # Calculate occurrence bias +#' occurrence_bias <- apply_manual_sampling_bias(occurrences_sf, grid) +#' occurrence_bias #' #' # Visualise where the bias is #' ggplot() + -#' geom_sf(data = grid2) + -#' geom_sf_text(data = grid2, aes(label = bias_weight)) + -#' geom_sf(data = occurrence_bias_sf, aes(colour = bias_weight)) + -#' scale_color_gradient(trans = "reverse") +#' geom_sf(data = plgn) + +#' geom_sf(data = grid, alpha = 0) + +#' geom_sf(data = occurrence_bias, aes(colour = bias_weight)) + +#' geom_sf_text(data = grid, aes(label = round(bias_weight, 2))) + +#' theme_minimal() apply_manual_sampling_bias <- function(occurrences_sf, bias_weights) { ### Start checks diff --git a/R/apply_polygon_sampling_bias.R b/R/apply_polygon_sampling_bias.R index 0bf01c5..1cf9ea8 100644 --- a/R/apply_polygon_sampling_bias.R +++ b/R/apply_polygon_sampling_bias.R @@ -43,7 +43,7 @@ #' occurrences <- data.frame( #' lon = runif(num_points, min = -180, max = 180), #' lat = runif(num_points, min = -90, max = 90), -#' time_point = 0 +#' time_point = 1 #' ) #' #' # Convert the occurrence data to an sf object @@ -52,7 +52,7 @@ #' # Create bias_area polygon overlapping at least two of the points #' selected_observations <- st_union(occurrences_sf[2:3,]) #' bias_area <- st_convex_hull(selected_observations) %>% -#' st_buffer(dist = 100) %>% +#' st_buffer(dist = 50) %>% #' st_as_sf() #' #' occurrence_bias_sf <- apply_polygon_sampling_bias( @@ -63,11 +63,11 @@ #' #' # Visualise where the bias is #' occurrence_bias_sf %>% -#' mutate(bias_weight_f = as.factor(round(bias_weight, 3))) %>% +#' mutate(bias_weight = as.factor(round(bias_weight, 3))) %>% #' ggplot() + #' geom_sf(data = bias_area) + -#' geom_sf(aes(colour = bias_weight_f)) + -#' ggtitle("Sampling Bias via Polygon") +#' geom_sf(aes(colour = bias_weight)) + +#' theme_minimal() apply_polygon_sampling_bias <- function( occurrences_sf, diff --git a/R/create_spatial_pattern.R b/R/create_spatial_pattern.R index 060bcef..8edb783 100644 --- a/R/create_spatial_pattern.R +++ b/R/create_spatial_pattern.R @@ -47,11 +47,8 @@ #' #' # Create polygon #' plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) -#' ggplot() + -#' geom_sf(data = plgn) + -#' theme_minimal() #' -#' # Random spatial pattern +#' # 1. Random spatial pattern #' rs_pattern_random <- create_spatial_pattern( #' polygon = plgn, #' resolution = 0.1, @@ -63,7 +60,7 @@ #' scale_fill_continuous(type = "viridis") + #' theme_minimal() #' -#' ## Clustered spatial pattern +#' # 2. Clustered spatial pattern #' rs_pattern_clustered <- create_spatial_pattern( #' polygon = plgn, #' resolution = 0.1, @@ -75,31 +72,7 @@ #' scale_fill_continuous(type = "viridis") + #' theme_minimal() #' -#' ## User defined spatial pattern -#' # Small scale clustering -#' rs_pattern_small <- create_spatial_pattern( -#' polygon = plgn, -#' resolution = 0.1, -#' spatial_pattern = 5, -#' seed = 123) -#' -#' ggplot() + -#' geom_spatraster(data = rs_pattern_small) + -#' scale_fill_continuous(type = "viridis") + -#' theme_minimal() -#' -#' # Medium scale clustering (= the built-in clustered pattern) -#' rs_pattern_medium <- create_spatial_pattern( -#' polygon = plgn, -#' resolution = 0.1, -#' spatial_pattern = 10, -#' seed = 123) -#' -#' ggplot() + -#' geom_spatraster(data = rs_pattern_medium) + -#' scale_fill_continuous(type = "viridis") + -#' theme_minimal() -#' +#' # 3. User defined spatial pattern #' # Large scale clustering #' rs_pattern_large <- create_spatial_pattern( #' polygon = plgn, diff --git a/R/filter_observations.R b/R/filter_observations.R index 8acdc9d..55c9cfe 100644 --- a/R/filter_observations.R +++ b/R/filter_observations.R @@ -22,37 +22,18 @@ #' @family main #' #' @examples -#' # Load packages -#' library(sf) -#' library(dplyr) -#' -#' # Set seed for reproducibility -#' set.seed(123) -#' -#' # Simulate some occurrence data with coordinates and time points -#' num_points <- 10 -#' occurrences <- data.frame( -#' lon = runif(num_points, min = -180, max = 180), -#' lat = runif(num_points, min = -90, max = 90), -#' time_point = 0 -#' ) -#' -#' # Convert the occurrence data to an sf object -#' occurrences_sf <- st_as_sf(occurrences, coords = c("lon", "lat")) -#' -#' # Sample observations without sampling bias -#' observations_total_sf <- sample_observations( -#' occurrences_sf, -#' detection_probability = 0.8, -#' sampling_bias = "no_bias", -#' seed = 123 +#' # Create dataframe with sampling status column +#' occurrences_data <- data.frame( +#' time_point = 1, +#' sampling_prob = seq(0.5, 1, 0.1), +#' sampling_status = rep(c("undetected", "detected"), each = 3) #' ) #' -#' # Filter detected observations -#' filter_observations(observations_total_sf) +#' # Keep detected occurrences +#' filter_observations(occurrences_data) #' -#' # Filter undetected observations -#' filter_observations(observations_total_sf, invert = TRUE) +#' # Keep undetected occurrences +#' filter_observations(occurrences_data, invert = TRUE) filter_observations <- function(observations_total, invert = FALSE) { ### Start checks diff --git a/R/sample_observations.R b/R/sample_observations.R index deb84e1..9e6a97e 100644 --- a/R/sample_observations.R +++ b/R/sample_observations.R @@ -87,7 +87,7 @@ #' # Convert the occurrence data to an sf object #' occurrences_sf <- st_as_sf(occurrences, coords = c("lon", "lat")) #' -#' # Sample observations without sampling bias +#' # 1. Sample observations without sampling bias #' sample_observations( #' occurrences_sf, #' detection_probability = 0.8, @@ -95,11 +95,11 @@ #' seed = 123 #' ) #' -#' # Sample observations with sampling bias in a polygon +#' # 2. Sample observations with sampling bias in a polygon #' # Create bias_area polygon overlapping two of the points #' selected_observations <- st_union(occurrences_sf[2:3,]) #' bias_area <- st_convex_hull(selected_observations) %>% -#' st_buffer(dist = 100) %>% +#' st_buffer(dist = 50) %>% #' st_as_sf() #' #' sample_observations( @@ -111,7 +111,7 @@ #' seed = 123 #' ) #' -#' # Sample observations with sampling bias given manually in a grid +#' # 3. Sample observations with sampling bias given manually in a grid #' # Create raster grid with bias weights between 0 and 1 #' grid <- st_make_grid(occurrences_sf) %>% #' st_sf() %>% diff --git a/R/sample_occurrences_from_raster.R b/R/sample_occurrences_from_raster.R index 63b316f..ae1342d 100644 --- a/R/sample_occurrences_from_raster.R +++ b/R/sample_occurrences_from_raster.R @@ -27,9 +27,6 @@ #' #' # Create polygon #' plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) -#' ggplot() + -#' geom_sf(data = plgn) + -#' theme_minimal() #' #' ## Medium scale clustering #' # Create the random field diff --git a/R/simulate_occurrences.R b/R/simulate_occurrences.R index 7776087..181751a 100644 --- a/R/simulate_occurrences.R +++ b/R/simulate_occurrences.R @@ -42,11 +42,8 @@ #' #' # Create polygon #' plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) -#' ggplot() + -#' geom_sf(data = plgn) + -#' theme_minimal() #' -#' ## Random spatial pattern with 4 time points +#' # 1. Random spatial pattern with 4 time points #' occ_sf <- simulate_occurrences( #' species_range = plgn, #' n_time_points = 4, @@ -58,15 +55,15 @@ #' geom_sf(data = plgn, fill = NA) + #' facet_wrap("time_point") + #' labs( -#' title = "Occurrences with random\nspatial and temporal pattern", +#' title = "Occurrences with random spatial and temporal pattern", #' subtitle = "4 time steps") + -#' theme_bw() +#' theme_minimal() #' -#' ## Clustered spatial pattern with 4 time points +#' # 2. Highly clustered spatial pattern with 6 time points #' occ_sf_100 <- simulate_occurrences( #' species_range = plgn, #' spatial_pattern = 100, -#' n_time_points = 4, +#' n_time_points = 6, #' initial_average_occurrences = 100, #' seed = 123) #' @@ -75,9 +72,9 @@ #' geom_sf(data = plgn, fill = NA) + #' facet_wrap("time_point") + #' labs( -#' title = "Occurrences with structured\nspatial and temporal pattern", -#' subtitle = "4 time steps") + -#' theme_bw() +#' title = "Occurrences with structured spatial and temporal pattern", +#' subtitle = "6 time steps") + +#' theme_minimal() simulate_occurrences <- function( species_range, diff --git a/R/simulate_random_walk.R b/R/simulate_random_walk.R index a3fe110..6695093 100644 --- a/R/simulate_random_walk.R +++ b/R/simulate_random_walk.R @@ -24,7 +24,6 @@ #' @family occurrence #' #' @examples -#' #' simulate_random_walk( #' initial_average_occurrences = 50, #' n_time_points = 10, diff --git a/R/simulate_timeseries.R b/R/simulate_timeseries.R index 78bcd75..8d78275 100644 --- a/R/simulate_timeseries.R +++ b/R/simulate_timeseries.R @@ -27,9 +27,7 @@ #' @family occurrence #' #' @examples -#' library(ggplot2) -#' -#' ## 1. Use the function simulate_random_walk() +#' # 1. Use the function simulate_random_walk() #' simulate_timeseries( #' initial_average_occurrences = 50, #' n_time_points = 10, @@ -38,52 +36,7 @@ #' seed = 123 #' ) #' -#' ## 2. Visualising multiple draws -#' # Set seed for reproducibility -#' set.seed(123) -#' -#' # Draw n_sim abundances from Poisson distribution using random walk -#' n_sim <- 10 -#' n_time_points <- 50 -#' sd_step <- 1 -#' list_abundances <- vector("list", length = n_sim) -#' -#' # Loop n_sim times over simulate_timeseries() -#' for (i in seq_len(n_sim)) { -#' abundances <- simulate_timeseries( -#' initial_average_occurrences = 50, -#' n_time_points = n_time_points, -#' temporal_function = simulate_random_walk, -#' sd_step = sd_step -#' ) -#' -#' list_abundances[[i]] <- data.frame( -#' time = seq_along(abundances), -#' abundance = abundances, -#' sim = i -#' ) -#' } -#' -#' # Combine list of dataframes -#' data_abundances <- do.call(rbind.data.frame, list_abundances) -#' -#' # Plot the simulated abundances over time using ggplot2 -#' ggplot(data_abundances, aes(x = time, y = abundance, colour = factor(sim))) + -#' geom_line() + -#' labs( -#' x = "Time", y = "Species abundance", -#' title = paste( -#' n_sim, "simulated abundances using random walk", -#' "with sd =", sd_step -#' ) -#' ) + -#' scale_y_continuous(limits = c(0, NA)) + -#' scale_x_continuous(breaks = seq(0, n_time_points, 5)) + -#' theme_minimal() + -#' theme(legend.position = "") -#' -#' ## 3. Using your own function -#' # You can also specify your own trend function, e.g. this linear function +#' # 2. Using your own custom function, e.g. this linear function #' my_own_linear_function <- function( #' initial_average_occurrences = initial_average_occurrences, #' n_time_points = n_time_points, @@ -108,7 +61,8 @@ #' # Set seed for reproducibility #' set.seed(123) #' -#' # Draw n_sim abundances from Poisson distribution using our own function +#' # Draw n_sim number of occurrences from Poisson distribution using +#' # the custom function #' n_sim <- 10 #' n_time_points <- 50 #' slope <- 1 @@ -134,12 +88,13 @@ #' data_abundances <- do.call(rbind.data.frame, list_abundances) #' #' # Plot the simulated abundances over time using ggplot2 +#' library(ggplot) #' ggplot(data_abundances, aes(x = time, y = abundance, colour = factor(sim))) + #' geom_line() + #' labs( #' x = "Time", y = "Species abundance", #' title = paste( -#' n_sim, "simulated abundances using our own linear function", +#' n_sim, "simulated trends using custom linear function", #' "with slope", slope #' ) #' ) + diff --git a/man/add_coordinate_uncertainty.Rd b/man/add_coordinate_uncertainty.Rd index 92b561f..764d380 100644 --- a/man/add_coordinate_uncertainty.Rd +++ b/man/add_coordinate_uncertainty.Rd @@ -7,9 +7,9 @@ add_coordinate_uncertainty(observations, coords_uncertainty_meters = 25) } \arguments{ -\item{observations}{An sf object with POINT geometry representing the -observations. This object contains the spatial points to which the coordinate -uncertainty will be added.} +\item{observations}{An sf object with POINT geometry or a simple +dataframe representing the observations. This object contains the observation +points to which the coordinate uncertainty will be added.} \item{coords_uncertainty_meters}{A numeric value or a vector of numeric values representing the coordinate uncertainty (in meters) associated with @@ -18,40 +18,33 @@ to all observations. If a numeric vector is provided, it must be the same length as the number of observations.} } \value{ -The input sf object with POINT geometry, with an additional column -named \code{coordinateUncertaintyInMeters} that contains the coordinate -uncertainty values in meters. +The input data frame or an sf object with POINT geometry, with an +additional column named \code{coordinateUncertaintyInMeters} that contains the +coordinate uncertainty values in meters. } \description{ -This function adds a column to the input sf object containing the coordinate -uncertainty for each observation, measured in meters. +This function adds a column to the input dataframe or sf object containing +the coordinate uncertainty for each observation, measured in meters. } \examples{ +# Create dataframe with sampling status column +observations_data <- data.frame( + time_point = 1, + sampling_prob = seq(0.5, 1, 0.1) + ) -library(sf) -library(dplyr) - -set.seed(123) - -# Create four random points -n_points <- 4 -xlim <- c(3841000, 3842000) -ylim <- c(3110000, 3112000) -observations_sf <- data.frame( - lat = runif(n_points, ylim[1], ylim[2]), - long = runif(n_points, xlim[1], xlim[2])) \%>\% - st_as_sf(coords = c("long", "lat"), crs = 3035) - - # provide a fixed uncertainty for all points - add_coordinate_uncertainty( - observations_sf, - coords_uncertainty_meters = 1000 - ) +# provide a fixed uncertainty for all points +add_coordinate_uncertainty( + observations_data, + coords_uncertainty_meters = 1000 + ) # add variability in uncertainty. For example, using gamma distribution +uncertainty_vec <- seq(50, 100, 10) + add_coordinate_uncertainty( - observations_sf, - coords_uncertainty_meters = rgamma(n_points, shape = 5, rate = 0.1) + observations_data, + coords_uncertainty_meters = uncertainty_vec ) } \seealso{ diff --git a/man/apply_manual_sampling_bias.Rd b/man/apply_manual_sampling_bias.Rd index 9c44b3b..317c081 100644 --- a/man/apply_manual_sampling_bias.Rd +++ b/man/apply_manual_sampling_bias.Rd @@ -31,43 +31,34 @@ library(sf) library(dplyr) library(ggplot2) -# Set seed for reproducibility -set.seed(123) +# Create polygon +plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) -# Simulate some occurrence data with coordinates and time points -num_points <- 10 -occurrences <- data.frame( - lon = runif(num_points, min = -180, max = 180), - lat = runif(num_points, min = -90, max = 90), - time_point = 0 -) +# Get occurrence points +occurrences_sf <- simulate_occurrences(plgn) -# Convert the occurrence data to an sf object -occurrences_sf <- st_as_sf(occurrences, coords = c("lon", "lat")) +# Set seed for reproducibility +set.seed(123) -# Create raster grid -grid <- st_make_grid(occurrences_sf) \%>\% +# Create grid with bias weights +grid <- st_make_grid( + plgn, + n = c(10, 10), + square = TRUE) \%>\% st_sf() +grid$bias_weight <- runif(nrow(grid), min = 0, max = 1) -# Bias weights between 0 and 1 -grid1 <- grid \%>\% - mutate(bias_weight = runif(nrow(grid), min = 0, max = 1)) - -apply_manual_sampling_bias(occurrences_sf, grid1) - -# Bias weights larger than 1 -grid2 <- grid \%>\% - mutate(bias_weight = rpois(nrow(grid), 5)) - -occurrence_bias_sf <- apply_manual_sampling_bias(occurrences_sf, grid2) -occurrence_bias_sf +# Calculate occurrence bias +occurrence_bias <- apply_manual_sampling_bias(occurrences_sf, grid) +occurrence_bias # Visualise where the bias is ggplot() + - geom_sf(data = grid2) + - geom_sf_text(data = grid2, aes(label = bias_weight)) + - geom_sf(data = occurrence_bias_sf, aes(colour = bias_weight)) + - scale_color_gradient(trans = "reverse") + geom_sf(data = plgn) + + geom_sf(data = grid, alpha = 0) + + geom_sf(data = occurrence_bias, aes(colour = bias_weight)) + + geom_sf_text(data = grid, aes(label = round(bias_weight, 2))) + + theme_minimal() } \seealso{ Other detection: diff --git a/man/apply_polygon_sampling_bias.Rd b/man/apply_polygon_sampling_bias.Rd index 34235f6..2dc1eba 100644 --- a/man/apply_polygon_sampling_bias.Rd +++ b/man/apply_polygon_sampling_bias.Rd @@ -46,7 +46,7 @@ num_points <- 10 occurrences <- data.frame( lon = runif(num_points, min = -180, max = 180), lat = runif(num_points, min = -90, max = 90), - time_point = 0 + time_point = 1 ) # Convert the occurrence data to an sf object @@ -55,7 +55,7 @@ occurrences_sf <- st_as_sf(occurrences, coords = c("lon", "lat")) # Create bias_area polygon overlapping at least two of the points selected_observations <- st_union(occurrences_sf[2:3,]) bias_area <- st_convex_hull(selected_observations) \%>\% - st_buffer(dist = 100) \%>\% + st_buffer(dist = 50) \%>\% st_as_sf() occurrence_bias_sf <- apply_polygon_sampling_bias( @@ -66,11 +66,11 @@ occurrence_bias_sf # Visualise where the bias is occurrence_bias_sf \%>\% - mutate(bias_weight_f = as.factor(round(bias_weight, 3))) \%>\% + mutate(bias_weight = as.factor(round(bias_weight, 3))) \%>\% ggplot() + geom_sf(data = bias_area) + - geom_sf(aes(colour = bias_weight_f)) + - ggtitle("Sampling Bias via Polygon") + geom_sf(aes(colour = bias_weight)) + + theme_minimal() } \seealso{ Other detection: diff --git a/man/create_spatial_pattern.Rd b/man/create_spatial_pattern.Rd index 1c3fde7..d1fa16c 100644 --- a/man/create_spatial_pattern.Rd +++ b/man/create_spatial_pattern.Rd @@ -52,11 +52,8 @@ library(tidyterra) # Create polygon plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) -ggplot() + - geom_sf(data = plgn) + - theme_minimal() -# Random spatial pattern +# 1. Random spatial pattern rs_pattern_random <- create_spatial_pattern( polygon = plgn, resolution = 0.1, @@ -68,7 +65,7 @@ ggplot() + scale_fill_continuous(type = "viridis") + theme_minimal() -## Clustered spatial pattern +# 2. Clustered spatial pattern rs_pattern_clustered <- create_spatial_pattern( polygon = plgn, resolution = 0.1, @@ -80,31 +77,7 @@ ggplot() + scale_fill_continuous(type = "viridis") + theme_minimal() -## User defined spatial pattern -# Small scale clustering -rs_pattern_small <- create_spatial_pattern( - polygon = plgn, - resolution = 0.1, - spatial_pattern = 5, - seed = 123) - -ggplot() + - geom_spatraster(data = rs_pattern_small) + - scale_fill_continuous(type = "viridis") + - theme_minimal() - -# Medium scale clustering (= the built-in clustered pattern) -rs_pattern_medium <- create_spatial_pattern( - polygon = plgn, - resolution = 0.1, - spatial_pattern = 10, - seed = 123) - -ggplot() + - geom_spatraster(data = rs_pattern_medium) + - scale_fill_continuous(type = "viridis") + - theme_minimal() - +# 3. User defined spatial pattern # Large scale clustering rs_pattern_large <- create_spatial_pattern( polygon = plgn, diff --git a/man/filter_observations.Rd b/man/filter_observations.Rd index a355c88..c385c65 100644 --- a/man/filter_observations.Rd +++ b/man/filter_observations.Rd @@ -26,37 +26,18 @@ This function filters observations from all occurrences based on the function. } \examples{ -# Load packages -library(sf) -library(dplyr) - -# Set seed for reproducibility -set.seed(123) - -# Simulate some occurrence data with coordinates and time points -num_points <- 10 -occurrences <- data.frame( - lon = runif(num_points, min = -180, max = 180), - lat = runif(num_points, min = -90, max = 90), - time_point = 0 - ) - -# Convert the occurrence data to an sf object -occurrences_sf <- st_as_sf(occurrences, coords = c("lon", "lat")) - -# Sample observations without sampling bias -observations_total_sf <- sample_observations( - occurrences_sf, - detection_probability = 0.8, - sampling_bias = "no_bias", - seed = 123 +# Create dataframe with sampling status column +occurrences_data <- data.frame( + time_point = 1, + sampling_prob = seq(0.5, 1, 0.1), + sampling_status = rep(c("undetected", "detected"), each = 3) ) -# Filter detected observations -filter_observations(observations_total_sf) +# Keep detected occurrences +filter_observations(occurrences_data) -# Filter undetected observations -filter_observations(observations_total_sf, invert = TRUE) +# Keep undetected occurrences +filter_observations(occurrences_data, invert = TRUE) } \seealso{ Other main: diff --git a/man/sample_observations.Rd b/man/sample_observations.Rd index 17eaa6e..35468dd 100644 --- a/man/sample_observations.Rd +++ b/man/sample_observations.Rd @@ -94,7 +94,7 @@ occurrences <- data.frame( # Convert the occurrence data to an sf object occurrences_sf <- st_as_sf(occurrences, coords = c("lon", "lat")) -# Sample observations without sampling bias +# 1. Sample observations without sampling bias sample_observations( occurrences_sf, detection_probability = 0.8, @@ -102,11 +102,11 @@ sample_observations( seed = 123 ) -# Sample observations with sampling bias in a polygon +# 2. Sample observations with sampling bias in a polygon # Create bias_area polygon overlapping two of the points selected_observations <- st_union(occurrences_sf[2:3,]) bias_area <- st_convex_hull(selected_observations) \%>\% - st_buffer(dist = 100) \%>\% + st_buffer(dist = 50) \%>\% st_as_sf() sample_observations( @@ -118,7 +118,7 @@ sample_observations( seed = 123 ) -# Sample observations with sampling bias given manually in a grid +# 3. Sample observations with sampling bias given manually in a grid # Create raster grid with bias weights between 0 and 1 grid <- st_make_grid(occurrences_sf) \%>\% st_sf() \%>\% diff --git a/man/sample_occurrences_from_raster.Rd b/man/sample_occurrences_from_raster.Rd index d60ec55..d83c0e6 100644 --- a/man/sample_occurrences_from_raster.Rd +++ b/man/sample_occurrences_from_raster.Rd @@ -30,9 +30,6 @@ library(tidyterra) # Create polygon plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) -ggplot() + - geom_sf(data = plgn) + - theme_minimal() ## Medium scale clustering # Create the random field diff --git a/man/simulate_occurrences.Rd b/man/simulate_occurrences.Rd index 538d120..8a230eb 100644 --- a/man/simulate_occurrences.Rd +++ b/man/simulate_occurrences.Rd @@ -58,11 +58,8 @@ library(ggplot2) # Create polygon plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) -ggplot() + - geom_sf(data = plgn) + - theme_minimal() -## Random spatial pattern with 4 time points +# 1. Random spatial pattern with 4 time points occ_sf <- simulate_occurrences( species_range = plgn, n_time_points = 4, @@ -74,15 +71,15 @@ ggplot() + geom_sf(data = plgn, fill = NA) + facet_wrap("time_point") + labs( - title = "Occurrences with random\nspatial and temporal pattern", + title = "Occurrences with random spatial and temporal pattern", subtitle = "4 time steps") + - theme_bw() + theme_minimal() -## Clustered spatial pattern with 4 time points +# 2. Highly clustered spatial pattern with 6 time points occ_sf_100 <- simulate_occurrences( species_range = plgn, spatial_pattern = 100, - n_time_points = 4, + n_time_points = 6, initial_average_occurrences = 100, seed = 123) @@ -91,9 +88,9 @@ ggplot() + geom_sf(data = plgn, fill = NA) + facet_wrap("time_point") + labs( - title = "Occurrences with structured\nspatial and temporal pattern", - subtitle = "4 time steps") + - theme_bw() + title = "Occurrences with structured spatial and temporal pattern", + subtitle = "6 time steps") + + theme_minimal() } \seealso{ Other main: diff --git a/man/simulate_random_walk.Rd b/man/simulate_random_walk.Rd index d28f9a3..0b7ab07 100644 --- a/man/simulate_random_walk.Rd +++ b/man/simulate_random_walk.Rd @@ -33,7 +33,6 @@ This function simulates a timeseries for the average number of occurrences of a species using a random walk over time. } \examples{ - simulate_random_walk( initial_average_occurrences = 50, n_time_points = 10, diff --git a/man/simulate_timeseries.Rd b/man/simulate_timeseries.Rd index b602516..fc94f26 100644 --- a/man/simulate_timeseries.Rd +++ b/man/simulate_timeseries.Rd @@ -39,9 +39,7 @@ This function simulates a timeseries for the number of occurrences of a species. } \examples{ -library(ggplot2) - -## 1. Use the function simulate_random_walk() +# 1. Use the function simulate_random_walk() simulate_timeseries( initial_average_occurrences = 50, n_time_points = 10, @@ -50,52 +48,7 @@ simulate_timeseries( seed = 123 ) -## 2. Visualising multiple draws -# Set seed for reproducibility -set.seed(123) - -# Draw n_sim abundances from Poisson distribution using random walk -n_sim <- 10 -n_time_points <- 50 -sd_step <- 1 -list_abundances <- vector("list", length = n_sim) - -# Loop n_sim times over simulate_timeseries() -for (i in seq_len(n_sim)) { - abundances <- simulate_timeseries( - initial_average_occurrences = 50, - n_time_points = n_time_points, - temporal_function = simulate_random_walk, - sd_step = sd_step - ) - - list_abundances[[i]] <- data.frame( - time = seq_along(abundances), - abundance = abundances, - sim = i - ) -} - -# Combine list of dataframes -data_abundances <- do.call(rbind.data.frame, list_abundances) - -# Plot the simulated abundances over time using ggplot2 -ggplot(data_abundances, aes(x = time, y = abundance, colour = factor(sim))) + - geom_line() + - labs( - x = "Time", y = "Species abundance", - title = paste( - n_sim, "simulated abundances using random walk", - "with sd =", sd_step - ) - ) + - scale_y_continuous(limits = c(0, NA)) + - scale_x_continuous(breaks = seq(0, n_time_points, 5)) + - theme_minimal() + - theme(legend.position = "") - -## 3. Using your own function -# You can also specify your own trend function, e.g. this linear function +# 2. Using your own custom function, e.g. this linear function my_own_linear_function <- function( initial_average_occurrences = initial_average_occurrences, n_time_points = n_time_points, @@ -120,7 +73,8 @@ my_own_linear_function <- function( # Set seed for reproducibility set.seed(123) -# Draw n_sim abundances from Poisson distribution using our own function +# Draw n_sim number of occurrences from Poisson distribution using +# the custom function n_sim <- 10 n_time_points <- 50 slope <- 1 @@ -146,12 +100,13 @@ for (i in seq_len(n_sim)) { data_abundances <- do.call(rbind.data.frame, list_abundances) # Plot the simulated abundances over time using ggplot2 +library(ggplot) ggplot(data_abundances, aes(x = time, y = abundance, colour = factor(sim))) + geom_line() + labs( x = "Time", y = "Species abundance", title = paste( - n_sim, "simulated abundances using our own linear function", + n_sim, "simulated trends using custom linear function", "with slope", slope ) ) + From 883780e66f174d92fac233c16c8d26fe4c7ed93f Mon Sep 17 00:00:00 2001 From: wlangera Date: Wed, 14 Aug 2024 16:18:19 +0200 Subject: [PATCH 27/57] list examples --- R/generate_taxonomy.R | 4 ++-- man/generate_taxonomy.Rd | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/generate_taxonomy.R b/R/generate_taxonomy.R index 9322768..2faee50 100644 --- a/R/generate_taxonomy.R +++ b/R/generate_taxonomy.R @@ -37,14 +37,14 @@ #' @family multispecies #' #' @examples -#' # Create simple taxonomic hierarchy +#' # 1. Create simple taxonomic hierarchy #' generate_taxonomy( #' num_species = 5, #' num_genera = 3, #' num_families = 2, #' seed = 123) #' -#' # Add taxonomic hierarchy to a dataframe +#' # 2. Add taxonomic hierarchy to a dataframe #' existing_df <- data.frame( #' count = c(1, 2, 5, 4, 8, 9, 3), #' det_prob = c(0.9, 0.9, 0.9, 0.8, 0.5, 0.2, 0.2) diff --git a/man/generate_taxonomy.Rd b/man/generate_taxonomy.Rd index fce04cf..73cf54c 100644 --- a/man/generate_taxonomy.Rd +++ b/man/generate_taxonomy.Rd @@ -55,14 +55,14 @@ lower-level taxa (e.g., species) to be assigned to the same higher-level taxon (e.g., genus). } \examples{ -# Create simple taxonomic hierarchy +# 1. Create simple taxonomic hierarchy generate_taxonomy( num_species = 5, num_genera = 3, num_families = 2, seed = 123) -# Add taxonomic hierarchy to a dataframe +# 2. Add taxonomic hierarchy to a dataframe existing_df <- data.frame( count = c(1, 2, 5, 4, 8, 9, 3), det_prob = c(0.9, 0.9, 0.9, 0.8, 0.5, 0.2, 0.2) From 9c512f9cc972e1513a8c8465303053c7556a9985 Mon Sep 17 00:00:00 2001 From: wlangera Date: Wed, 14 Aug 2024 16:52:36 +0200 Subject: [PATCH 28/57] fix wrong package --- R/simulate_timeseries.R | 2 +- man/simulate_timeseries.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/simulate_timeseries.R b/R/simulate_timeseries.R index 8d78275..e4a7941 100644 --- a/R/simulate_timeseries.R +++ b/R/simulate_timeseries.R @@ -88,7 +88,7 @@ #' data_abundances <- do.call(rbind.data.frame, list_abundances) #' #' # Plot the simulated abundances over time using ggplot2 -#' library(ggplot) +#' library(ggplot2) #' ggplot(data_abundances, aes(x = time, y = abundance, colour = factor(sim))) + #' geom_line() + #' labs( diff --git a/man/simulate_timeseries.Rd b/man/simulate_timeseries.Rd index fc94f26..19b288c 100644 --- a/man/simulate_timeseries.Rd +++ b/man/simulate_timeseries.Rd @@ -100,7 +100,7 @@ for (i in seq_len(n_sim)) { data_abundances <- do.call(rbind.data.frame, list_abundances) # Plot the simulated abundances over time using ggplot2 -library(ggplot) +library(ggplot2) ggplot(data_abundances, aes(x = time, y = abundance, colour = factor(sim))) + geom_line() + labs( From b839116cf71e14db212cbc85fd6524c39ce4b6b3 Mon Sep 17 00:00:00 2001 From: wlangera Date: Wed, 14 Aug 2024 17:15:33 +0200 Subject: [PATCH 29/57] clarify text --- vignettes/articles/detection-process.Rmd | 2 ++ vignettes/articles/grid-designation-process.Rmd | 2 ++ vignettes/articles/multi_species_approach.Rmd | 1 + vignettes/articles/occurrence-process.Rmd | 3 ++- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/vignettes/articles/detection-process.Rmd b/vignettes/articles/detection-process.Rmd index d85068c..e154adf 100644 --- a/vignettes/articles/detection-process.Rmd +++ b/vignettes/articles/detection-process.Rmd @@ -32,7 +32,9 @@ library(ggplot2) # data visualisation The functions are set up such that a single polygon as input is enough to go through this workflow using default arguments. The user can change these arguments to allow for more flexibility. + As input, we create a polygon in which we simulate occurrences. +It represents the spatial extend of the species. ```{r} polygon <- st_polygon(list(cbind(c(500, 1000, 1000, 600, 200, 100, 500), diff --git a/vignettes/articles/grid-designation-process.Rmd b/vignettes/articles/grid-designation-process.Rmd index f5e9b2c..50e915d 100644 --- a/vignettes/articles/grid-designation-process.Rmd +++ b/vignettes/articles/grid-designation-process.Rmd @@ -32,8 +32,10 @@ library(ggExtra) # enhance data visualisation # Input The functions are set up such that a single polygon as input is enough to go through this workflow using default arguments. + The user can change these arguments to allow for more flexibility. As input, we create a polygon in which we simulate occurrences. +It represents the spatial extend of the species. ```{r} polygon <- st_polygon(list(cbind(c(500, 1000, 1000, 600, 200, 100, 500), diff --git a/vignettes/articles/multi_species_approach.Rmd b/vignettes/articles/multi_species_approach.Rmd index 50bd904..1106ca4 100644 --- a/vignettes/articles/multi_species_approach.Rmd +++ b/vignettes/articles/multi_species_approach.Rmd @@ -30,6 +30,7 @@ library(ggplot2) # data visualisation # Spatial extend As input, we create a polygon in which we simulate occurrences. +It represents the spatial extend of the species. ```{r} polygon <- st_polygon(list(cbind(c(500, 1000, 1000, 600, 200, 100, 500), diff --git a/vignettes/articles/occurrence-process.Rmd b/vignettes/articles/occurrence-process.Rmd index 89249cd..d1203ae 100644 --- a/vignettes/articles/occurrence-process.Rmd +++ b/vignettes/articles/occurrence-process.Rmd @@ -36,6 +36,7 @@ The user can change these arguments to allow for more flexibility. In this tutorial we will demonstrate the different options. As input, we create a polygon in which we want to simulate occurrences. +It represents the spatial extend of the species. ```{r} polygon <- st_polygon(list(cbind(c(500, 1000, 1000, 600, 200, 100, 500), @@ -66,7 +67,7 @@ The options for user defined arguments are demonstrated in the next subsections. ## Changing number of occurrences over time Say we want to have 100 occurrences in our plot over 10 years. -You can change the trend in number of occurrences over time. +You can change the trend in the average number of occurrences over time. We visualise this with the supporting functions used in `simulate_occurrences()`. The number of occurrences are always drawn from a Poisson distribution. From 15545e65064491fc9025d27fdcc22c7e4a31ef35 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 12:56:15 +0200 Subject: [PATCH 30/57] rename sampling probability column --- R/create_spatial_pattern.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/R/create_spatial_pattern.R b/R/create_spatial_pattern.R index 8edb783..8b64998 100644 --- a/R/create_spatial_pattern.R +++ b/R/create_spatial_pattern.R @@ -24,7 +24,8 @@ #' @seealso [gstat::vgm()] and its `range` argument #' #' @returns An object of class SpatRaster with a spatial pattern for the area of -#' the given polygon. +#' the given polygon with `n_sim` layers `sampling_p'n_sim'` containing the +#' sampling probabilities from the raster grid for each simulation. #' #' @export #' @@ -176,7 +177,8 @@ create_spatial_pattern <- function( dplyr::starts_with("sim"), ~vegan::decostand(.x, "range") ) - ) + ) %>% + dplyr::rename_with(~ gsub("sim", "sampling_p", .x)) # Return final raster return(terra::rast(dfxy_std, crs = terra::crs(poly_vect))) From fdbb15d26faf120bcb8f2836fb355ad42dfad184 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 12:56:40 +0200 Subject: [PATCH 31/57] use purrr and random in cell --- R/sample_occurrences_from_raster.R | 45 +++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/R/sample_occurrences_from_raster.R b/R/sample_occurrences_from_raster.R index ae1342d..68b4aa6 100644 --- a/R/sample_occurrences_from_raster.R +++ b/R/sample_occurrences_from_raster.R @@ -9,13 +9,19 @@ #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), no seed is used. #' -#' @returns An sf object with POINT geometry. +#' @returns An sf object with POINT geometry containing the locations of the +#' simulated occurrences, a `time_point` column indicating the associated +#' time point for each occurrence and columns used as weights for sampling. +#' If the raster is created with `create_spatial_pattern()`, the column +#' `sampling_p1` is used. #' #' @export #' #' @import sf #' @import assertthat -#' @importFrom terra spatSample global +#' @importFrom terra spatSample global res +#' @importFrom purrr map_dfr map +#' @importFrom stats runif #' #' @family occurrence #' @@ -90,36 +96,49 @@ sample_occurrences_from_raster <- function( length(seed) == 1) ### End checks - # centre the values of the raster (mean = 0) + # Center the values of the raster (mean = 0) rs_mean <- terra::global(raster, "mean", na.rm = TRUE)[, 1] rs2 <- raster - rs_mean - # increase contrast between high and low values + # Increase contrast between high and low values a <- 30 # a = 1 -> logistic a > 1 => steeper sigmoid (higher contrast) rs3 <- 1 / (1 + exp(-a * rs2)) - # For each time step sample points from the raster - # Should be recoded: with lapply? or map? - - occ_pf <- NULL + # For each time step, sample points from the raster + # Get raster resolution to determine cell size + cell_size <- terra::res(rs3) # Set seed if provided if (!is.na(seed)) { withr::local_seed(seed) } - for (t in seq_along(time_series)) { + occ_pf <- purrr::map_dfr(seq_along(time_series), function(t) { + # Sample points within the raster occ_p <- terra::spatSample( x = rs3, size = time_series[t], method = "weights", replace = TRUE, as.points = TRUE ) + + # Convert to sf object occ_sf <- sf::st_as_sf(occ_p) + + # Random shift within raster cells + # Assuming coordinates are in two dimensions (x and y) + occ_sf$geometry <- sf::st_sfc(purrr::map(occ_sf$geometry, function(pt) { + shift_x <- stats::runif(1, -0.5 * cell_size[1], 0.5 * cell_size[1]) + shift_y <- stats::runif(1, -0.5 * cell_size[2], 0.5 * cell_size[2]) + + sf::st_point(c(sf::st_coordinates(pt)[1] + shift_x, + sf::st_coordinates(pt)[2] + shift_y)) + }), + crs = sf::st_crs(occ_sf)) + + # Add time_point column occ_sf$time_point <- t - occ_pf <- rbind(occ_pf, occ_sf) - } - # points need to be shifted randomly (uniform within the raster cell size) - # For the moment the points are all at the center of the raster cells + return(occ_sf) + }) return(occ_pf) } From 69fb0f0f292a502e6abe77ac2c7efa096d9dec3f Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 12:56:52 +0200 Subject: [PATCH 32/57] update simulation column documentation --- R/simulate_occurrences.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/R/simulate_occurrences.R b/R/simulate_occurrences.R index 181751a..4cf7a99 100644 --- a/R/simulate_occurrences.R +++ b/R/simulate_occurrences.R @@ -25,8 +25,10 @@ #' generation to ensure reproducibility. If `NA` (default), no seed is used. #' #' @returns An sf object with POINT geometry containing the locations of the -#' simulated occurrences and a `time_point` column indicating the associated -#' time point for each occurrence. +#' simulated occurrences, a `time_point` column indicating the associated +#' time point for each occurrence and a `sampling_p1` column indicating the +#' sampling probability associated with the spatial pattern (see +#' `create_spatial_pattern()`). #' #' @export #' From 7e811a8417b4d884706426a1e98aef5b9b357a64 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 13:32:41 +0200 Subject: [PATCH 33/57] avoid superseded map_dfr --- R/sample_occurrences_from_raster.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/R/sample_occurrences_from_raster.R b/R/sample_occurrences_from_raster.R index 68b4aa6..1fada73 100644 --- a/R/sample_occurrences_from_raster.R +++ b/R/sample_occurrences_from_raster.R @@ -20,7 +20,7 @@ #' @import sf #' @import assertthat #' @importFrom terra spatSample global res -#' @importFrom purrr map_dfr map +#' @importFrom purrr map #' @importFrom stats runif #' #' @family occurrence @@ -113,7 +113,7 @@ sample_occurrences_from_raster <- function( withr::local_seed(seed) } - occ_pf <- purrr::map_dfr(seq_along(time_series), function(t) { + occ_pf_list <- lapply(seq_along(time_series), function(t) { # Sample points within the raster occ_p <- terra::spatSample( x = rs3, size = time_series[t], method = "weights", @@ -139,6 +139,8 @@ sample_occurrences_from_raster <- function( return(occ_sf) }) + occ_pf <- do.call(rbind.data.frame, occ_pf_list) %>% + dplyr::select("time_point", dplyr::everything()) return(occ_pf) } From 0f94b10cd52d89eb17f856a68439cb37573d5c97 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 13:36:50 +0200 Subject: [PATCH 34/57] update package documentation --- NAMESPACE | 2 ++ NEWS.md | 2 ++ man/create_spatial_pattern.Rd | 3 ++- man/sample_occurrences_from_raster.Rd | 6 +++++- man/simulate_occurrences.Rd | 6 ++++-- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 42a1acd..e59794b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -27,6 +27,7 @@ importFrom(gstat,gstat) importFrom(gstat,vgm) importFrom(methods,formalArgs) importFrom(mnormt,rmnorm) +importFrom(purrr,map) importFrom(purrr,pmap) importFrom(purrr,quietly) importFrom(rlang,.data) @@ -39,6 +40,7 @@ importFrom(stats,setNames) importFrom(terra,global) importFrom(terra,rast) importFrom(terra,rasterize) +importFrom(terra,res) importFrom(terra,spatSample) importFrom(terra,vect) importFrom(tidyr,unnest) diff --git a/NEWS.md b/NEWS.md index c1bd5d6..d04627d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,8 @@ # gcube 0.4.0 * Consolidate documentation across all functions, README, and vignettes. +* Update `sample_occurrences_from_raster()` with better coding style and randomise points in raster cells. +* Fix issues (#76). # gcube 0.3.0 diff --git a/man/create_spatial_pattern.Rd b/man/create_spatial_pattern.Rd index d1fa16c..46af4c8 100644 --- a/man/create_spatial_pattern.Rd +++ b/man/create_spatial_pattern.Rd @@ -32,7 +32,8 @@ the raster. Default is 1.} } \value{ An object of class SpatRaster with a spatial pattern for the area of -the given polygon. +the given polygon with \code{n_sim} layers \verb{sampling_p'n_sim'} containing the +sampling probabilities from the raster grid for each simulation. } \description{ This function creates a raster with a spatial pattern for the area of a diff --git a/man/sample_occurrences_from_raster.Rd b/man/sample_occurrences_from_raster.Rd index d83c0e6..7630cc3 100644 --- a/man/sample_occurrences_from_raster.Rd +++ b/man/sample_occurrences_from_raster.Rd @@ -15,7 +15,11 @@ sample_occurrences_from_raster(raster, time_series, seed = NA) generation to ensure reproducibility. If \code{NA} (default), no seed is used.} } \value{ -An sf object with POINT geometry. +An sf object with POINT geometry containing the locations of the +simulated occurrences, a \code{time_point} column indicating the associated +time point for each occurrence and columns used as weights for sampling. +If the raster is created with \code{create_spatial_pattern()}, the column +\code{sampling_p1} is used. } \description{ This function draws point occurrences from a spatial random field represented diff --git a/man/simulate_occurrences.Rd b/man/simulate_occurrences.Rd index 8a230eb..27ea626 100644 --- a/man/simulate_occurrences.Rd +++ b/man/simulate_occurrences.Rd @@ -44,8 +44,10 @@ generation to ensure reproducibility. If \code{NA} (default), no seed is used.} } \value{ An sf object with POINT geometry containing the locations of the -simulated occurrences and a \code{time_point} column indicating the associated -time point for each occurrence. +simulated occurrences, a \code{time_point} column indicating the associated +time point for each occurrence and a \code{sampling_p1} column indicating the +sampling probability associated with the spatial pattern (see +\code{create_spatial_pattern()}). } \description{ This function simulates occurrences of a species within a specified spatial From 0ad4d98146c253a95c81057ed636aaa34a420bb7 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 13:45:22 +0200 Subject: [PATCH 35/57] update readme figures --- man/figures/readme-detect-occurrences-1.png | Bin 29776 -> 29827 bytes man/figures/readme-grid-designation-1.png | Bin 39555 -> 39713 bytes man/figures/readme-simulate-occurrences-1.png | Bin 25773 -> 25790 bytes .../readme-uncertainty-occurrences-1.png | Bin 33778 -> 35932 bytes .../readme-visualise-designation-1.png | Bin 36700 -> 36732 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/man/figures/readme-detect-occurrences-1.png b/man/figures/readme-detect-occurrences-1.png index fb361659143b8345e446f7326a4e06b54b7b23c5..aa6097335c2a14e050cf047ecfc25053ce4d017e 100644 GIT binary patch literal 29827 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 WR8BLg(2t4!?d#LWEB<=^hyMZ`o>{#B literal 29776 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%0D0D?6#^e%bu@n0g=(6YTj z=g++iH^drSjbi!Hhgjq8!q2dU4CSt72V#v!dQL9W(j(STwf!%wdb~Bq9ExaWwKz7z zFfcBbDIM7ciw&1J@IKOm z$3-1Z<)cntYB#f&z=}&`C=K4F~1^1()aUVqDqsL`W_stPEat zZ*7-@xJe8DdNsP@gY7vGV=(#w5gw3r7Jlkz%|RAtDF%k)j%QEzL!qe_YQFWk=ZAaIgwY89r z+=z&A9wrOVb$H&rvU^au;y&G4cOd;!RsGS_@E6-IHkxal#)}d&9@Laj!rvS_*VDdR zG{IN>{7?oaH(`T=&UuB%OA^>WPo$s4XRoy#J$$aWefMGja7F>P7jG_aG^bbtg)E?8 z7v9EAq{WnNzKQvxw?+>4H8^MF!814K71tT+@vl5ggkp{?vkb#H?(~zz{r$|e!rp4z z#~&xFew5n^YYF)->t2Dqpk?Nvd!jJ^<}2cw$}=kIzK=KS${Y$)h+GRoe9t&5pS{GE zwffv(`|dwLh?t?9m|W`i!u1!K=2})D#4`{=EByNLL`F=R00{B8_9^ZwC8wYA%)^-# z2SU_%XbG)6644lH;<$72B>ceAEuZ61!{~2(>ub>MDI6)C&VTNqpqDS`(dG3ionO_xT_-xc}s>kQr;1}*-P+Rf-MPjcQCOi9m@`M=t`(y*qk zZcQ&$?yUrSr78pr`t??vQ9wd~0EL>=I)I`~hDi+I0KqUP3{u)y6-6tP5%!^N1kx-P^7OVL8aO+5&TUEID>^j4=L%jV~bVue8$L-3`e7t@LB~ zv*?f!{#DxhDTy8oDGZ+txt^r-;sLy@Y5tSLn`zBGUr^(azQ2ob2Wf~)yb<|XpJaico~^?^rw}tr?Q-JHOkOf3Wp>?x5KhxeKLHP>7&BXgH=mifU?r!-Mco~;u5>!hOi@BT~VA_vjH?7dZ7OSa*cR2VnalqTf3m9KLD`c4?m}@dOvkW6UQaqGsFuf5wnA(rBV$0ODpX`Xe*JkC!zpZ!!;5eb>a&6t3of6*pl0xFy?v#- zH13}C+a$W)hiiKou(`Mqgw`1|(M44pxf61=A~FQEmKI;<2cqhju8$v4L$C0??dzr}_Sbx%~Rp+zqt&OC;njjiZAJYc~{9 zj0YO37#%=T9LFz->bC2^LR$ki-xI{7svo?DWm7x*2ajI^XiN<^tzG`6ulFlwC(-p? z8v^c~$EbfL&GJU=?VjT8_1#~&1Vi|S6R(@Ny2=kg5{x}{Sl{fzo9|p}!0;}AQg4OP_Jk2-= zaF%f^vgG**d{<9Gy9xY1^U^Lla*1y*H545<>^zu_Hs7ucQ&$d1^0jOVNUwsw!=2lw z95?BW=J>c`)K3BRaWop3B&D+d0Gd<>a`t?oE!}mwo{Bd;jDrR3DFXu5!AU>PCF)$} z)oob10u|T!b>+&1OC0Wt!myvMu#~t8KxAd&O>|&SIS}R>QmTDXTZuA7ywc_OC&8rG zZW#81za~YaIXOtFVFItN3TU7hPt601=oYb-t}zY;eX$wzKt*#D7ol$qB|8PKE%Cq8 zH3$$-c}^htn$oTwbz1yQlJ0ttc7}7875M^HS-Hes5L3a9{A_gjQb=xqKfM#Z=Hv^1 zLieXT>H205t-}7XOwz?cKudV?Bdrz6yYl=|7wn?M`l=!u`Idnp6V2M#I#oyMj&{-Y zG0s_$Gkq$mdA5LRN6{6H+IV`?Jt(nfN;Vy@7)(32AqJq-c%V0ZcKr}rnPEZJY*aFa zA%k#zl4oxPjgRg72CCzA08MdxuEF5Zo0PkR82=0uV%X!q41}p%L=4mbJSB;~KST#) z*pNe^JTV3U=xECDu}=eIyze2PO(4@br~4YuXmqSU1T^CefPT4x;0W3fi(r$>OXJ}I zklqiGI@MO>P_V0kh*$s!k=*~^WAZVc1q77YP1lFJ{HjqjO~io5%1SyGTH?kX zmZwoCjv*31jU9tzL{lp8M^W9Qis>sO$iIn#?L_qrZHT2M=V){qMg2J{ukf!Ce~NCp zAif!0!FX7rf4tfLNG5t|QRfIWQ`x6Q@zfOv!XA##l_}eUD{)o)A=>K{wfX)E=BYMf zdQOdy+e3o{QCEz|HPQ7SZeQCKBFlTOWSBzI9S1ALVMkJTf)mtZIi4?$p*dSld>{qe zL5y^8B3W6YiFD%}1eKy>Qd4S-c#cuex5Jhk@%ikKe(-Z2nhfP#~>7qDSF_ zI)F}x1PnyX8Qss)k3|PRQE-V*NZ+c$#*}(;IQ0Sdd{rTtA7GlLv5Q#Ft}xMzw13O8 z*^Swi3d~tP`_0MFo?v9pOlPv&$fH1m(niENdBj^Qp|ub zxfvr98DMEeOc1?QMOH0(}-+LPU0@^u%6Ti{2 z8vW*KhteKWC|6b5!0z**sN{%jFTQm6j;bLn^1Azp+XyygT4LVPz9h!a94{&mISOK( znH@A675l?aJWV(`HH*JhJ;A!D7~pv^x2D=NKI7){u|~_bv&^zIU^BHSz-lbpLYV87 zHce6?mcML^VWyPm z-K3(zk&rsCyuWVQJyKY&gl|x#EZ#j6j`~izA~!&T##jISGH+Q$IiLb7VxUa(uM{|r z%i5ck1D5CVvIR=iu_A4GX*nz6Y7azeWtocc|90_jqLixs|FoktZNPbB?R&w;!Nk>S z-s@;zsiil7bI7*t``sF!o*F6rT+NnSI`q$BqzAVf>@IaU@jFPf9*y~1{j_qm1dMz#twsyu6tC82nvPp>)Mz}DWF*`Fi!JKnv z^@LxR^i&WC5<073aU|47ky((yD?U}gt(_CKcFMV}&PVZMykM!c4@vQg85HaanO^JX zH8V)MC~uVfUPLzeR4Cz(&NZ)XddOhpX*dwOnPiXto9{^mxv+MofN~(* zL5Kmz*$j-k|J#4Pc|58CE6mUag-dTTl zNl8wbDq`ea76$XB$o4+|S*h0v% zmNO}bYb&IJ@EA9&K@Dt@RpLZgzR5%%;34fDvL;s z$T%~<9kh6{nK4|JQD1blB~+gJlM>iS!o2BGhms+~<^S;}KigXF@4J;8Z8|&owPERX zUh%x;9y2yhgYZ1iv_&>G{4#32(9?ab zJ)WL5*v63sOCvUSAdR88aWeub%k}DiZwgje^+un#;fV4<6Zr8Pip(L7z-nu^@fX?G zoiH2lcK^<)HM+3v8b;s83<#q_K2J6nr^&&$gni1HZt6j$@UU#jPN88|0e!&)U(0r} zBQiU=fgQQswXHiM-;5Lxu!~OZKLY>7m-KRdR9CA)}u^e54>?E?!>m1|E}j zX+F33ti+hf#|g;pd_qvsr|gc5;V*|U1c!clk@GxNr{y*6z0@DWKC1)c;1*{ZrH|)_ z+q1jB{8Tj~&Jd1^7hf42c903i@8DusgxOp}>!bK#o|+o<2KYF98XVJkTL-3A_dghQ iwY^vV&rPos>SbuBz<1v|-%y?mdho!}p9<}~fBh%h;4%aN diff --git a/man/figures/readme-grid-designation-1.png b/man/figures/readme-grid-designation-1.png index 2b75371d672a836005ec85f41db8eac0caa33800..0ca6c076afcfec7f57fd30ab2206485cf48da407 100644 GIT binary patch literal 39713 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 literal 39555 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 J^n({}{1+E%ho=Al diff --git a/man/figures/readme-simulate-occurrences-1.png b/man/figures/readme-simulate-occurrences-1.png index a160581edf7e6be5be5d92473672795f9e4829c7..433199ebd9f3b1f9db2b788c91172e323421951d 100644 GIT binary patch literal 25790 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�@dW=YJM4S$cIzajVv{)P=z-%n6(_Buqsg7I5#u&D6&2lRLJ z64Bp`DGGjJE?+{p+7JVlL+On6wXX2D3o4^Cw}sw)ZbTwLV=1a0MQ4}l1jm!HlP;7{ zt~R8dH1v=bi^>@F>%&2gN$_oAK)_d2J1^n?h5|MqU^k}q6#$@R{kR0A8HN40Lu*sP zf1uj_3Dz$KiVdH$etrJ|h!s#--P-9#{9wbJ-CTscOB?O(bs22ySby#)7()%zpBv}0 zsA{7(A}0v$yv-t_7;QfhyDvVNi~EXQC3}XVyk2v>!u# zB(J~2L&v_WDIM~ILpNHtEyO>e639YFLJAA;SBOmmh}CADnY8qxz&nUZMCx>e`~n~~ z2n?32B@t!;nE+IA%zIcxE%M8Ts>u5(A~l1x5mx8%M`Su@DD$9raOelCR7-+u*u%ZE z!2z`4a>f*e`S_1OssyrPqzy>n)<8DOj6s47n6o`N(NragQC=jX7YCl#J%uNGgDv++^#*{Hb zDA^u=10W~`T6ZD+fqj)s;_2)+eU3=5O3>dyKe%s!)*Jx^0uWLlN=UU1WF7mlrgS9P zI4H2&g2?#=fW9IiXGT~l(x3@wFt96~mR=mV7-T{d|`G*dw&RKyr1*Syih>Ce+k7$ky>ez>NL6E0mt3f=_4r|h#nvU-~$)&;E715jp z%s~t%K%S=R0iih1bxu;6i)}}fDw;#b?r08`C3g@Ls;rVCAjCJpkn3sWJAUq{nobqY z#lhS|#DpV%n-o=z_6t)i6JU${wB}qIIn&P_f;HtHJl?}E;-*8#4GuAg2YURiP1m#5 zfHI6h87`ox$3{k}v_}DH~tx#$&|51D7g;w`IAi7 zvewvH=wxtvqo7<_1^w8>SjtrkB6TK!zX0%uB&I7P4DC5rfc2$7Q{3;X?y9Psj|F<* zJP$s>O{aYfwvkr$LL81cC=Wivz-|~|u$LTv64(ZC$Z9Ef<~I9|8ZWNO`COnE4Ua)b zmbULYdsPQ-$9YPtFYuoRi@BXn%Zs3?W{&n^P!VMY7%~#C9QJP-VOj0Q(w_t}0b3a~ zU0MT3%&05D+@s)@)zGIcX-7a~y-{F1nPAdkWw0etugd8y&^rgTIAAhaTZxSn==Gp| zYCDK!{|^*MZ)PsQ^Dn~_P{+w4CY^5 z7s6ntYi=V_p&dc#LAmQK+oa;1Eq1&(@DT79i41vaguE^kD48MPlf4kyPXUe5e%HBJ z_;}%(8JwfUpRJi+`~E#__v@$)HB_^`1tBonIUVYi3{zqbnDMKA*Pk)>qD1Oi zcQfX=3#S*}`|%wZL-!HihdyYlyNfrUB(Zn%gJIxEP4OHou$}?6l=;cAG6y1cIx8o( zB?LO3aB7^LS6rWLbJ`CsALm?b301_#>Tki5$7f;Bp;&;%8eBFy2^XFA4OG`WrnJg4 z$STltoQbX=g+0ZKQ)oii$d=$?jx!Ig3GTOtDk{!9WItWLSqmTH@cWv10B{V@_*RY_ zS&R-yYM+)m-5ef-PMn`A4>sBr+J@W^a(R|~AH1MQo?1M{VXvOtPgFbj-lg^<>ukrz zgl9@vp*hjDkBeHheZ_&R@h3+Jt8JJ)?mq2hZ@r6H)S*p%Jvo+P)0MFsW5b#AcwY;+ z4Cq2Wiil;fpVU0=b#Q)CPstAgy{@H_8Dy{G3ATzBiMiG>h4!}Q`t(`Y9(BRY@q`eM z%|x|NDpl`B?Y-c%CN0TW9ySwQuXIB=$rRv)1 z($M;E;Z8RdF|(00{9McNEXHu86_KNWx{WPAebeN92VUkm?KABUEZ_Tc=&W<{>{H zL1x|`dh%&QRnC~D@o*iM1x*ooWTl0lqFibx^41Rr|7;K42B_-c zIXdN(#S94;D;>b{+&D$^^Oaa7UC>)B9r)WOjm&jddNeju&J0!^8XBI4tCg{wv)8Ix zn*?vHX8S(^Glh7?f&K(g`uqai15RX1()k#0mYnJ@`>|&J&GyoPv{=wxNif`HgxH`~ z(dC!|I)QWDLP&$2hS|#4pEqp957Ye8$>A#d)=}3o!r*jiYjnq|K5M47WyaCg1Aqat zg<6FjiSS%rpfE(eGc)v$-}ChL{YqWN2#ba67u|8fef(r>$tn^77zcm`65;mey?hNL z084 zuu2ZvMyvU9?@ODap@k3sz{k9uxbdL17+bkD!Eh&$qr;!tsZ+uv5uEsf_K*mMZo9?L zsl#(Sh;XWem^0NWNrLD2@<54d=xQZ?&HzYr)z0#f{UxSy8d%Z0oqzpr*n2s##QB4oL{R2lAP)rE;choauzK3JUOXXR| zc8Ezexk0VVS(@%}nbPXT2W*Lg4Wq`9hv`aK5Z~A!?T`K1;A4@>_akJrB`44$hw>n} z1o4d~CO;Jq8a8_Arm_SF7JwaS9Qq#4TcNsJq|1Rn?b4oxw;byBJ;De};9JF7t_`!d zqJM7$ZG|#64qZM^!5uQ>b_cXtuTxrK%kUbo*jwtrcFG!H zwD1k^raDDGit@$RQv#7ZXw77iO!Zi&C|}@Zb(8F5Tww|Rm@ZE;)k4zFto?k*k$A}< z4=)T{C}6%0i0Ik{zl)@KLO<2&wa&zjX`;IT{fms9;OfZX`!fT|-&qjhxC0T*uc2uU zUQcXDl_+jCh||~bO=Y%#HW-VNQ&uwG{II8w^yiFn5`nML2%5o2PvrQgf5hwf+@>p) z#syDZfTMo=`uOKHFyJRO0OT%GTAj7Tv1n1qy?7UYXhJ(P$Uc0u8qss@-ng>JiKWS8j zzG-kA#Q?B|jUO{4*jIF=%IDK5w$QKu!Lu`y@m9FqaZDhye8f#_k;7c{_W?9;Ujm#1 z(0m*mgu7=Oi5Yx9nZ}a4Ch>hG_YA*-I>U1!!DhfG(bJiO(P{FbR|(}L<};z3iW}}o zR`PQ|sWl1*x2{oaWBIJOXZw+K42@Fd)95rf!wr#uv2vj6~3at{z7?L|b1omT-WW0lOtnLprynp#Toee$N^<$)(sr zSqUnDhO)!A(C)T$Q6y8+%kXp6=^j7rk$ZeAgJKIk5qK;w!|yN*ntJ4B-(WA`-H4>2 z&I)2u|HGCf_OOJoMVI=JrB(Mt8}+dYgRR87A4${UKkCsCU^a0w*1p-CMLi3g9~ZJs zO-bL%WJ?TGwPp`$A-FylhE}CKFwa^zz>GkZA>U~U;g4O3Y7XMH%7-O~jsa6w$%}TKKjrlt)t8NB^yoGNDjrW^>s-ixv z`1?Xu{(Xuq>>-ev*wbUjc~dOCUtgOkE*Q2oK5TAJXERLPjVpi9n}flzC0hI^gIS>Z zuu~LU*jDgiy%=H7FGwUbs44J=Y@r0pBcNE|cu1!)UXBrVh5{#|2&N{67Pw#|QE`2a z*l0Ys0X`7;_0Ln_EEYY(&9>Qx`RzSLnYRGn2fGn)$*Lsw+Zpd>kfPJY@y{^Faj#C} znAgrV;HgXS&-123?L^I_@&viCvR>cAkY z3nM^n{Vd5kQHyGzR?At#%7M-`xLoC+V=Aw5 z&KQ#fcwLCRGv?!P2L|=p)GU85gRJ)Br1iJ?BqsFZ_-Wf_016>m?L)C4-|K|^zZ`1~ zT;Mb2t0mU~Qm!TO{CqqM`Wk#faIGrZNWz&aWXGLa_-92dR*)2`At~Ia)7F!i?ZP+& z`{x;d9}>sQ-v@w`*KiS>!4_ni+7P2E>aXQ%!YTP11W|-JNTIDCl6XpYPP~i$HOf^e zexOAKdJP14cqN~Jd^bbS0uYLm^ zb9+H{Dn$SXY~V0GQY^f9U>yZI1edu|@LPLx3o`+ajA9Fxi zP(9cP>OT+gwJS^TEPnY4nZL9)S8U22t)N9%#p!&qA723=)C(%YGV#=D*8^ft*Y^gd z-;kJt{NUu#4S;9qG(pOMl66;ZhD0Ao>tqJolWzg~0RZ?I3|$y=Ti2*c98dwNTAM}H z;TxS=F#zsouw8_%2N&@r#-asEvb{pdhejuaqPI}y8k(2{dk(csg@~=f&mT~wGWoYo z*;Y3s2Dx*;`v`>SG9|1A+E);u)_1LP9qB+1#mk9mOvhIgzY&B+&M?TmY`pB z$uBM3cDprpQQHst_>e-*L$=9*r4ulEz~iZGKaM9tL#bO5z&&t@?`G|;bwhfLJ- z+SY5CLeu9zZ&PUtP2{q+6T3e{#WlC*XY0D2IFZ(F^?Y~p{ z%k|!bnJZ(NB<9W#3e91XMeiG@-P%ze@Fr|}0WWWJwSDhHIxTp-tEIbkMzmJt!+6iu z>m<`q04N1Wz*`}0X9X))~f_ran$RxM|tenbz z%k@^h%`W+`+H;jLyRqTp%EirjbJZ+AZ3iL&blP?X`B1UREi~c@Bg^?CcACY0-`^JN z_Q$oow)Zk>-xz0*&b}UN2Uoqi+E(ujBFFwgHKb3^PFABpK&8_xCOG9uTbQmkPCyL~ z@RYC{qr>US408VB>OBfS=V#+r@*B~O$G|!gL30NZOaO_aBP2-f*cHZL!K4GBRv~)%aG{6yf8oB6RO-2 zK|hOJ^t`KUS@?>;am{GB)Pj}`%9}x@zKmqGtYyQxGxG8?CETv|C<}N4MF7Eszc9ec z4bZ*K0%7Y6z5&*l2iPOv`bw^p4xbZ%L-^jU_Tt}V!##utL9~*(}WjgIA;mWQ(Xw(k(z7vJEX}$wI z(*B(R+yXuB?jd@{1)c$;7UaQdbI2s??P(MWKjQ+Qrl6x(j<3#)iF!N14!qiYhoj|s z$U;Aeq#X}<195k$2t*x3+3mj1w6vzVUAP=17CaI|xhmM{bS;*I)BJ-- ziAMnKIy$XWq>lr@i3f@4EhOXd6!fz++lKi=@eqU#zJopA3j*$mpus&&KAn%105gTe zq>5lB0OmCu+H{5X>=mhg3($c(zOWY);DCS@4mjZ6G#|(Bj{t`4m`-bdGIUgA#yUU; z4pY!oQsmgrp8?G22pV)l1u%mEGsQ8LR#iMi6VjR16`ZU#v;<$m&-m!$LleKP@7_w> z0k>=UE>u1ChiC1JvZ~hO!X^>=-p=Y};hA*WQz71G0ohgxs!G^tA>A{B%?vf&Z@=pd z*GoP<%GGZFQyKFTKjN08wtzv_7DOySseXycbWD?X682=#mqdE+s6oH|rBJ$QT}Gt! zaEI?a+|Cei7I@P$pE+HIGsll({g=>tX>d=QzwDre7|?Hn({Hlom;l}(E zM(wa=T?6-8p$21IyEf}j8RdeaXoGA-Lggb)N7 zQ840M)b@tlb=ZIMUrL7^%9+sR1G5-p$e^MH>mK|Gz;_7tTu7Ajw%HkKAz8FpU80}} z;b%W&b`lfr4~r}$XK?wOO~e@zupRd9LF>n@G36RNK_*8_xYoDmH7E*QJrI8N_Gq@= z1HiOWjEVF#fKbLRby;fbF6i~34{wpm0`3F)^P;3wZEl^Zhn9A!1g<4u;T!fIo$r}` z+a!aX0nV|Y9{vW5uMCP~-*Z@)i^5n0i#6C4XRQ#LMjIW>lISe|Wk4wa{vrX)e-{vb zMRueeO7GV_nrle zT>=84q?QqDd$gz=)&hD@A~(r+*nGF0Q|{6gCH7$RmHEy4K&Q|N66tiGgZpV92%%2I zlzFzw6{g1PHVeN5K}9YUW}hSwDYA2;Ua<{?qQ<4=md5UN^u05G9)N%R%b zrmrtNhhGK|fN{~sKl`+u&;mk}Py8KI?pOxU`!CQFeML0zT{42}fB-D|_9sL!x145F zQHnVD-#4B4-KE&iyqT6j@QfD9m(Y&w>5;q}G;|B#oHJF^s%+Za*# z|K1-~cgo!>b~@j$%d0RsAG~O`UHQ?Nm&UZ^MS=I!g9fx#ml{VepYWNe{b$vK>qf&# zW8AH@L%VU8@6PXkIWVAEYJA0|>E;T|G-GwIe2dF)^R<%O;cZ%z7IL(@3ow{ zb{RIiezjrL{d+NY_eEZ-TvJl|o>|-F#OrUF6L)Q_sdnlf`i;oR-|T#T9Y0pc9OdoC zzrNpbJv_G0-CgfQyRN(Zu~>~eiPm1BM}2eEgSu9X%swvPG~S+k&472Lo0+;f&^dh% ze!Y4_@i?{Mk?~$TvqYk`vzJf)+CpPJcUqmp8iUTu2h?`@PVXq%T>fE#=ui`)7FlQU z;q8Jvb>EqeeZ0lYY|CRQZ++9$s=r=__d|$fd6iaK`;+@#+IhWyx@t(N>HC$hAtj%y z`e%}xX4w3CB=^3xoE#w@{eJ|govyTs+smEy;PWSEwsy_RI+LZ?iz`h}^ArzW>)n=B zGcn@aquiop;#aUK&vpOA)9^U2r za*6hM8D>gMHTwJGrG~7JZ@rn19y%U1Xx!;Il=OAA|BAke=#9rx?EK~~aPhn7aBOLA z;Gri?cFvQZxS(L;$cfGh?fFymTJlsQe9TQRHjtmbFMJ7ZTHa<&$ncGsSb@E$Ur{j8 zyg~chb{%!zr{2rv%6eZ0ioXs=InUT`T2<&xENR-q+irUA^PKYR@JyebH-~;w&wYP0 zu25s_pi3fgM`WedebolyjGSTbs0`xIcDcy~;js`+v zb_L4T8BMMw%GlX`et}BMKsRwti*=_bWq-JPpHNuwlC%ta&mewC@0sBkx754#uirqR zo9afdv`dtF&Y#ej*Hl=8ZcTOUVSOI!H*=2_p?9Ex>pof}qZb?5W^ z?y1dn%ae(>zh@8J9D0*t&m10l{s3ZJK>O^GuuHT{Zx7wh%Np1|a`N}9RV*8A?&-Is zb;ul3)(;r{eZcwAj%78$N|k>(d}%)Y=W?6hvWaTJ-^^>P$$vUxlhohyu2ovyxZ!n& zU1o*T>I#E%)qQ4GG{*$b+k2qczwtKr!}RxLZu7c2`@Ffvd7P-t$K>QTUy_%bNBaNl dk4gFWoq3h1zSryE|KdY#+m@ZAtj!1i_&=VTnXv!> literal 25773 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%0A>kctlTljj{q)P@1oe_E9W0jVVCl_!e5-0j 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 Date: Mon, 9 Sep 2024 13:48:37 +0200 Subject: [PATCH 36/57] reduce lines --- NEWS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index d04627d..8ec6480 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,9 @@ # gcube 0.4.0 * Consolidate documentation across all functions, README, and vignettes. -* Update `sample_occurrences_from_raster()` with better coding style and randomise points in raster cells. +* Update `sample_occurrences_from_raster()` + - Use `lapply()` instead of for-loop + - Randomise points in raster cells. * Fix issues (#76). # gcube 0.3.0 From 2b85b3a5b821209f884e50222c71bc5ae1b5f340 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 14:40:19 +0200 Subject: [PATCH 37/57] examples take some time --- checklist.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/checklist.yml b/checklist.yml index 48c8e34..37c56d0 100644 --- a/checklist.yml +++ b/checklist.yml @@ -10,6 +10,10 @@ allowed: Maintainer: 'Ward Langeraert ' New submission + - motivation: Mapping functions are expected to take some time. + value: |- + checking examples ... NOTE + Examples with CPU (user + system) or elapsed time > 5s required: - CITATION - DESCRIPTION From 1323ded28dd59249e538be51b0f2a6752bb62a30 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 14:59:02 +0200 Subject: [PATCH 38/57] shorter examples run time --- R/map_add_coordinate_uncertainty.R | 2 +- R/map_grid_designation.R | 2 +- R/map_simulate_occurrences.R | 2 +- checklist.yml | 4 ---- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/R/map_add_coordinate_uncertainty.R b/R/map_add_coordinate_uncertainty.R index acb7c90..50fd913 100644 --- a/R/map_add_coordinate_uncertainty.R +++ b/R/map_add_coordinate_uncertainty.R @@ -45,7 +45,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' species_range = rep(list(plgn), 3), -#' initial_average_occurrences = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 200), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), diff --git a/R/map_grid_designation.R b/R/map_grid_designation.R index bb0dfab..0ffb216 100644 --- a/R/map_grid_designation.R +++ b/R/map_grid_designation.R @@ -52,7 +52,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' species_range = rep(list(plgn), 3), -#' initial_average_occurrences = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 200), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), diff --git a/R/map_simulate_occurrences.R b/R/map_simulate_occurrences.R index b1cdd09..05f42f1 100644 --- a/R/map_simulate_occurrences.R +++ b/R/map_simulate_occurrences.R @@ -47,7 +47,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' species_range = rep(list(plgn), 3), -#' initial_average_occurrences = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 200), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), diff --git a/checklist.yml b/checklist.yml index 37c56d0..48c8e34 100644 --- a/checklist.yml +++ b/checklist.yml @@ -10,10 +10,6 @@ allowed: Maintainer: 'Ward Langeraert ' New submission - - motivation: Mapping functions are expected to take some time. - value: |- - checking examples ... NOTE - Examples with CPU (user + system) or elapsed time > 5s required: - CITATION - DESCRIPTION From 3c5c62b90a56c2ce89b1fb7b994f368df3f12ec8 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 15:20:30 +0200 Subject: [PATCH 39/57] shorter example runtime --- R/map_add_coordinate_uncertainty.R | 6 ------ R/map_filter_observations.R | 9 +-------- R/map_grid_designation.R | 10 ---------- R/map_sample_observations.R | 8 +------- R/map_simulate_occurrences.R | 12 ------------ man/map_add_coordinate_uncertainty.Rd | 8 +------- man/map_filter_observations.Rd | 9 +-------- man/map_grid_designation.Rd | 12 +----------- man/map_sample_observations.Rd | 8 +------- man/map_simulate_occurrences.Rd | 14 +------------- 10 files changed, 7 insertions(+), 89 deletions(-) diff --git a/R/map_add_coordinate_uncertainty.R b/R/map_add_coordinate_uncertainty.R index 50fd913..3422c8e 100644 --- a/R/map_add_coordinate_uncertainty.R +++ b/R/map_add_coordinate_uncertainty.R @@ -68,12 +68,6 @@ #' obs_uncertainty_nested <- map_add_coordinate_uncertainty(df = filter_obs1) #' obs_uncertainty_nested #' -#' # Unnest output and create sf object again -#' obs_uncertainty_unnested <- map_add_coordinate_uncertainty(df = filter_obs1, -#' nested = FALSE) -#' obs_uncertainty_unnested %>% -#' st_sf() -#' #' #' ## Example with deviating column names #' # Specify dataframe for 3 species with custom function arguments diff --git a/R/map_filter_observations.R b/R/map_filter_observations.R index b8f5451..e647ddd 100644 --- a/R/map_filter_observations.R +++ b/R/map_filter_observations.R @@ -45,7 +45,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' species_range = rep(list(plgn), 3), -#' initial_average_occurrences = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 200), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), @@ -64,13 +64,6 @@ #' filter_obs_nested <- map_filter_observations(df = samp_obs1) #' filter_obs_nested #' -#' # Unnest output and create sf object again -#' filter_obs_unnested <- map_filter_observations(df = samp_obs1, -#' nested = FALSE) -#' filter_obs_unnested %>% -#' st_sf() -#' -#' #' ## Example with deviating column names #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df2 <- species_dataset_df %>% diff --git a/R/map_grid_designation.R b/R/map_grid_designation.R index 0ffb216..74d009a 100644 --- a/R/map_grid_designation.R +++ b/R/map_grid_designation.R @@ -79,16 +79,6 @@ #' occ_cube_nested <- map_grid_designation(df = obs_uncertainty1) #' occ_cube_nested #' -#' # From filtered observations -#' map_grid_designation(df = filter_obs1) -#' -#' # Unnest output and create sf object again -#' occ_cube_unnested <- map_grid_designation(df = obs_uncertainty1, -#' nested = FALSE) -#' occ_cube_unnested %>% -#' st_sf() -#' -#' #' ## Example with deviating column names #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df2 <- species_dataset_df %>% diff --git a/R/map_sample_observations.R b/R/map_sample_observations.R index a210c90..766ca8f 100644 --- a/R/map_sample_observations.R +++ b/R/map_sample_observations.R @@ -45,7 +45,7 @@ #' species_dataset_df <- tibble( #' taxonID = c("species1", "species2", "species3"), #' species_range = rep(list(plgn), 3), -#' initial_average_occurrences = c(50, 100, 500), +#' initial_average_occurrences = c(50, 100, 200), #' n_time_points = rep(6, 3), #' temporal_function = c(simulate_random_walk, simulate_random_walk, NA), #' sd_step = c(1, 1, NA), @@ -60,12 +60,6 @@ #' samp_obs_nested <- map_sample_observations(df = sim_occ1) #' samp_obs_nested #' -#' # Unnest output and create sf object again -#' samp_obs_unnested <- map_sample_observations(df = sim_occ1, -#' nested = FALSE) -#' samp_obs_unnested %>% -#' st_sf() -#' #' ## Example with deviating column names #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df2 <- species_dataset_df %>% diff --git a/R/map_simulate_occurrences.R b/R/map_simulate_occurrences.R index 05f42f1..47d1202 100644 --- a/R/map_simulate_occurrences.R +++ b/R/map_simulate_occurrences.R @@ -58,13 +58,6 @@ #' sim_occ_nested <- map_simulate_occurrences(df = species_dataset_df) #' sim_occ_nested #' -#' # Unnest output and create sf object again -#' sim_occ_unnested <- map_simulate_occurrences(df = species_dataset_df, -#' nested = FALSE) -#' sim_occ_unnested %>% -#' st_sf() -#' -#' #' ## Example with deviating column names #' # Specify dataframe for 3 species with custom function arguments #' species_dataset_df2 <- species_dataset_df %>% @@ -81,11 +74,6 @@ #' map_simulate_occurrences( #' df = species_dataset_df2, #' arg_list = arg_conv_list) -#' -#' map_simulate_occurrences( -#' df = species_dataset_df2, -#' nested = FALSE, -#' arg_list = arg_conv_list) map_simulate_occurrences <- function( df, diff --git a/man/map_add_coordinate_uncertainty.Rd b/man/map_add_coordinate_uncertainty.Rd index 62b1ec9..3d6b618 100644 --- a/man/map_add_coordinate_uncertainty.Rd +++ b/man/map_add_coordinate_uncertainty.Rd @@ -48,7 +48,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), species_range = rep(list(plgn), 3), - initial_average_occurrences = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 200), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), @@ -71,12 +71,6 @@ filter_obs1 <- map_filter_observations(df = samp_obs1) obs_uncertainty_nested <- map_add_coordinate_uncertainty(df = filter_obs1) obs_uncertainty_nested -# Unnest output and create sf object again -obs_uncertainty_unnested <- map_add_coordinate_uncertainty(df = filter_obs1, - nested = FALSE) -obs_uncertainty_unnested \%>\% - st_sf() - ## Example with deviating column names # Specify dataframe for 3 species with custom function arguments diff --git a/man/map_filter_observations.Rd b/man/map_filter_observations.Rd index ad0545f..289a3da 100644 --- a/man/map_filter_observations.Rd +++ b/man/map_filter_observations.Rd @@ -48,7 +48,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), species_range = rep(list(plgn), 3), - initial_average_occurrences = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 200), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), @@ -67,13 +67,6 @@ samp_obs1 <- map_sample_observations(df = sim_occ1) filter_obs_nested <- map_filter_observations(df = samp_obs1) filter_obs_nested -# Unnest output and create sf object again -filter_obs_unnested <- map_filter_observations(df = samp_obs1, - nested = FALSE) -filter_obs_unnested \%>\% - st_sf() - - ## Example with deviating column names # Specify dataframe for 3 species with custom function arguments species_dataset_df2 <- species_dataset_df \%>\% diff --git a/man/map_grid_designation.Rd b/man/map_grid_designation.Rd index 712659d..b10211c 100644 --- a/man/map_grid_designation.Rd +++ b/man/map_grid_designation.Rd @@ -55,7 +55,7 @@ cube_grid <- st_make_grid( species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), species_range = rep(list(plgn), 3), - initial_average_occurrences = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 200), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), @@ -82,16 +82,6 @@ obs_uncertainty1 <- map_add_coordinate_uncertainty(df = filter_obs1) occ_cube_nested <- map_grid_designation(df = obs_uncertainty1) occ_cube_nested -# From filtered observations -map_grid_designation(df = filter_obs1) - -# Unnest output and create sf object again -occ_cube_unnested <- map_grid_designation(df = obs_uncertainty1, - nested = FALSE) -occ_cube_unnested \%>\% - st_sf() - - ## Example with deviating column names # Specify dataframe for 3 species with custom function arguments species_dataset_df2 <- species_dataset_df \%>\% diff --git a/man/map_sample_observations.Rd b/man/map_sample_observations.Rd index 58748cf..d9369a4 100644 --- a/man/map_sample_observations.Rd +++ b/man/map_sample_observations.Rd @@ -48,7 +48,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), species_range = rep(list(plgn), 3), - initial_average_occurrences = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 200), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), @@ -63,12 +63,6 @@ sim_occ1 <- map_simulate_occurrences(df = species_dataset_df) samp_obs_nested <- map_sample_observations(df = sim_occ1) samp_obs_nested -# Unnest output and create sf object again -samp_obs_unnested <- map_sample_observations(df = sim_occ1, - nested = FALSE) -samp_obs_unnested \%>\% - st_sf() - ## Example with deviating column names # Specify dataframe for 3 species with custom function arguments species_dataset_df2 <- species_dataset_df \%>\% diff --git a/man/map_simulate_occurrences.Rd b/man/map_simulate_occurrences.Rd index 791ef80..b728d5f 100644 --- a/man/map_simulate_occurrences.Rd +++ b/man/map_simulate_occurrences.Rd @@ -50,7 +50,7 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) species_dataset_df <- tibble( taxonID = c("species1", "species2", "species3"), species_range = rep(list(plgn), 3), - initial_average_occurrences = c(50, 100, 500), + initial_average_occurrences = c(50, 100, 200), n_time_points = rep(6, 3), temporal_function = c(simulate_random_walk, simulate_random_walk, NA), sd_step = c(1, 1, NA), @@ -61,13 +61,6 @@ species_dataset_df <- tibble( sim_occ_nested <- map_simulate_occurrences(df = species_dataset_df) sim_occ_nested -# Unnest output and create sf object again -sim_occ_unnested <- map_simulate_occurrences(df = species_dataset_df, - nested = FALSE) -sim_occ_unnested \%>\% - st_sf() - - ## Example with deviating column names # Specify dataframe for 3 species with custom function arguments species_dataset_df2 <- species_dataset_df \%>\% @@ -84,11 +77,6 @@ arg_conv_list <- list( map_simulate_occurrences( df = species_dataset_df2, arg_list = arg_conv_list) - -map_simulate_occurrences( - df = species_dataset_df2, - nested = FALSE, - arg_list = arg_conv_list) } \seealso{ Other multispecies: From 3a04a57e5ed39bb9e2cdbb16823dcbc11729b97b Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 16:18:41 +0200 Subject: [PATCH 40/57] reset seed on exit --- DESCRIPTION | 3 +-- NAMESPACE | 1 - R/create_spatial_pattern.R | 7 +++++-- R/generate_taxonomy.R | 7 +++++-- R/sample_from_binormal_circle.R | 6 +++++- R/sample_from_uniform_circle.R | 6 +++++- R/sample_observations.R | 7 +++++-- R/sample_occurrences_from_raster.R | 6 +++++- R/simulate_random_walk.R | 7 +++++-- R/simulate_timeseries.R | 6 +++++- codemeta.json | 14 +------------- 11 files changed, 42 insertions(+), 28 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 64343e1..d101f80 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -50,8 +50,7 @@ Imports: stats, terra, tidyr, - vegan, - withr + vegan Suggests: ggExtra, ggplot2, diff --git a/NAMESPACE b/NAMESPACE index e59794b..bbe44c8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -45,4 +45,3 @@ importFrom(terra,spatSample) importFrom(terra,vect) importFrom(tidyr,unnest) importFrom(vegan,decostand) -importFrom(withr,local_seed) diff --git a/R/create_spatial_pattern.R b/R/create_spatial_pattern.R index 8b64998..2e5490b 100644 --- a/R/create_spatial_pattern.R +++ b/R/create_spatial_pattern.R @@ -35,7 +35,6 @@ #' @importFrom stats predict #' @importFrom terra vect rast rasterize #' @importFrom gstat vgm gstat -#' @importFrom withr local_seed #' @importFrom vegan decostand #' #' @family occurrence @@ -149,7 +148,11 @@ create_spatial_pattern <- function( # Set seed if provided if (!is.na(seed)) { - withr::local_seed(seed) + if (exists(".Random.seed", envir = .GlobalEnv)) { + rng_state_old <- get(".Random.seed", envir = .GlobalEnv) + on.exit(assign(".Random.seed", rng_state_old, envir = .GlobalEnv)) + } + set.seed(seed) } # Use gstat object with vgm model to create spatial pattern diff --git a/R/generate_taxonomy.R b/R/generate_taxonomy.R index 2faee50..b34fcae 100644 --- a/R/generate_taxonomy.R +++ b/R/generate_taxonomy.R @@ -32,7 +32,6 @@ #' #' @import dplyr #' @import assertthat -#' @importFrom withr local_seed #' #' @family multispecies #' @@ -129,7 +128,11 @@ generate_taxonomy <- function( # Set seed if provided if (!is.na(seed)) { - withr::local_seed(seed) + if (exists(".Random.seed", envir = .GlobalEnv)) { + rng_state_old <- get(".Random.seed", envir = .GlobalEnv) + on.exit(assign(".Random.seed", rng_state_old, envir = .GlobalEnv)) + } + set.seed(seed) } # Assign species to genera diff --git a/R/sample_from_binormal_circle.R b/R/sample_from_binormal_circle.R index ca0d61c..bac1889 100644 --- a/R/sample_from_binormal_circle.R +++ b/R/sample_from_binormal_circle.R @@ -86,7 +86,11 @@ sample_from_binormal_circle <- function( # Set seed if provided if (!is.na(seed)) { - withr::local_seed(seed) + if (exists(".Random.seed", envir = .GlobalEnv)) { + rng_state_old <- get(".Random.seed", envir = .GlobalEnv) + on.exit(assign(".Random.seed", rng_state_old, envir = .GlobalEnv)) + } + set.seed(seed) } # Set uncertainty to zero if column not present in data diff --git a/R/sample_from_uniform_circle.R b/R/sample_from_uniform_circle.R index 991b3c3..c1787cb 100644 --- a/R/sample_from_uniform_circle.R +++ b/R/sample_from_uniform_circle.R @@ -65,7 +65,11 @@ sample_from_uniform_circle <- function( # Set seed if provided if (!is.na(seed)) { - withr::local_seed(seed) + if (exists(".Random.seed", envir = .GlobalEnv)) { + rng_state_old <- get(".Random.seed", envir = .GlobalEnv) + on.exit(assign(".Random.seed", rng_state_old, envir = .GlobalEnv)) + } + set.seed(seed) } # Set uncertainty to zero if column not present in data diff --git a/R/sample_observations.R b/R/sample_observations.R index 9e6a97e..71e07ec 100644 --- a/R/sample_observations.R +++ b/R/sample_observations.R @@ -63,7 +63,6 @@ #' @import dplyr #' @import assertthat #' @importFrom stats rbinom -#' @importFrom withr local_seed #' @importFrom rlang .data #' #' @family main @@ -164,7 +163,11 @@ sample_observations <- function( # Set seed if provided if (!is.na(seed)) { - withr::local_seed(seed) + if (exists(".Random.seed", envir = .GlobalEnv)) { + rng_state_old <- get(".Random.seed", envir = .GlobalEnv) + on.exit(assign(".Random.seed", rng_state_old, envir = .GlobalEnv)) + } + set.seed(seed) } # Add detection probability diff --git a/R/sample_occurrences_from_raster.R b/R/sample_occurrences_from_raster.R index 1fada73..3d4d23b 100644 --- a/R/sample_occurrences_from_raster.R +++ b/R/sample_occurrences_from_raster.R @@ -110,7 +110,11 @@ sample_occurrences_from_raster <- function( # Set seed if provided if (!is.na(seed)) { - withr::local_seed(seed) + if (exists(".Random.seed", envir = .GlobalEnv)) { + rng_state_old <- get(".Random.seed", envir = .GlobalEnv) + on.exit(assign(".Random.seed", rng_state_old, envir = .GlobalEnv)) + } + set.seed(seed) } occ_pf_list <- lapply(seq_along(time_series), function(t) { diff --git a/R/simulate_random_walk.R b/R/simulate_random_walk.R index 6695093..4f62e5a 100644 --- a/R/simulate_random_walk.R +++ b/R/simulate_random_walk.R @@ -19,7 +19,6 @@ #' #' @import assertthat #' @importFrom stats rnorm -#' @importFrom withr local_seed #' #' @family occurrence #' @@ -62,7 +61,11 @@ simulate_random_walk <- function( # Set seed if provided if (!is.na(seed)) { - withr::local_seed(seed) + if (exists(".Random.seed", envir = .GlobalEnv)) { + rng_state_old <- get(".Random.seed", envir = .GlobalEnv) + on.exit(assign(".Random.seed", rng_state_old, envir = .GlobalEnv)) + } + set.seed(seed) } # Initialize an empty vector to store average abundance values diff --git a/R/simulate_timeseries.R b/R/simulate_timeseries.R index e4a7941..c8ef775 100644 --- a/R/simulate_timeseries.R +++ b/R/simulate_timeseries.R @@ -135,7 +135,11 @@ simulate_timeseries <- function( # Set seed if provided if (!is.na(seed)) { - withr::local_seed(seed) + if (exists(".Random.seed", envir = .GlobalEnv)) { + rng_state_old <- get(".Random.seed", envir = .GlobalEnv) + on.exit(assign(".Random.seed", rng_state_old, envir = .GlobalEnv)) + } + set.seed(seed) } # Check type of temporal_function diff --git a/codemeta.json b/codemeta.json index abf6436..28f1885 100644 --- a/codemeta.json +++ b/codemeta.json @@ -332,21 +332,9 @@ }, "sameAs": "https://CRAN.R-project.org/package=vegan" }, - "13": { - "@type": "SoftwareApplication", - "identifier": "withr", - "name": "withr", - "provider": { - "@id": "https://cran.r-project.org", - "@type": "Organization", - "name": "Comprehensive R Archive Network (CRAN)", - "url": "https://cran.r-project.org" - }, - "sameAs": "https://CRAN.R-project.org/package=withr" - }, "SystemRequirements": null }, - "fileSize": "549.627KB", + "fileSize": "550.823KB", "citation": [ { "@type": "SoftwareSourceCode", From f0e4fe6366143a2eb2833f338ad71ae7e8752fda Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 16:35:43 +0200 Subject: [PATCH 41/57] update seed documentation --- R/create_spatial_pattern.R | 4 +++- R/generate_taxonomy.R | 4 +++- R/grid_designation.R | 4 +++- R/sample_from_binormal_circle.R | 4 +++- R/sample_from_uniform_circle.R | 4 +++- R/sample_observations.R | 4 +++- R/sample_occurrences_from_raster.R | 4 +++- R/simulate_occurrences.R | 4 +++- R/simulate_random_walk.R | 4 +++- R/simulate_timeseries.R | 4 +++- man/create_spatial_pattern.Rd | 4 +++- man/generate_taxonomy.Rd | 4 +++- man/grid_designation.Rd | 4 +++- man/sample_from_binormal_circle.Rd | 4 +++- man/sample_from_uniform_circle.Rd | 4 +++- man/sample_observations.Rd | 4 +++- man/sample_occurrences_from_raster.Rd | 4 +++- man/simulate_occurrences.Rd | 4 +++- man/simulate_random_walk.Rd | 4 +++- man/simulate_timeseries.Rd | 4 +++- 20 files changed, 60 insertions(+), 20 deletions(-) diff --git a/R/create_spatial_pattern.R b/R/create_spatial_pattern.R index 2e5490b..b72a6a6 100644 --- a/R/create_spatial_pattern.R +++ b/R/create_spatial_pattern.R @@ -12,7 +12,9 @@ #' The default is `"random"`. `"clustered"` corresponds to a value of 10. #' See Details. #' @param seed A positive numeric value setting the seed for random number -#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' generation to ensure reproducibility. If `NA` (default), then `set.seed()` +#' is not called at all. If not `NA`, then the random number generator state is +#' reset (to the state before calling this function) upon exiting this function. #' @param n_sim Number of simulations. Each simulation is a different layer in #' the raster. Default is 1. #' diff --git a/R/generate_taxonomy.R b/R/generate_taxonomy.R index b34fcae..06d9656 100644 --- a/R/generate_taxonomy.R +++ b/R/generate_taxonomy.R @@ -15,7 +15,9 @@ #' @param num_phyla Number of phyla to generate. Defaults to 1. #' @param num_kingdoms Number of kingdoms to generate. Defaults to 1. #' @param seed A positive numeric value setting the seed for random number -#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' generation to ensure reproducibility. If `NA` (default), then `set.seed()` +#' is not called at all. If not `NA`, then the random number generator state is +#' reset (to the state before calling this function) upon exiting this function. #' #' @details The function works by randomly assigning species to genera, genera #' to families, families to orders, orders to classes, classes to phyla, and diff --git a/R/grid_designation.R b/R/grid_designation.R index dccd79d..e7f78bf 100644 --- a/R/grid_designation.R +++ b/R/grid_designation.R @@ -13,7 +13,9 @@ #' `"row_names"` (the default), a new column `id` is created where the row names #' represent the unique IDs. #' @param seed A positive numeric value setting the seed for random number -#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' generation to ensure reproducibility. If `NA` (default), then `set.seed()` +#' is not called at all. If not `NA`, then the random number generator state is +#' reset (to the state before calling this function) upon exiting this function. #' @param aggregate Logical. If `TRUE` (default), returns data cube in #' aggregated form (grid with the number of observations per grid cell). #' Otherwise, returns sampled points within the uncertainty circle. diff --git a/R/sample_from_binormal_circle.R b/R/sample_from_binormal_circle.R index bac1889..7fb594c 100644 --- a/R/sample_from_binormal_circle.R +++ b/R/sample_from_binormal_circle.R @@ -13,7 +13,9 @@ #' circle. Default is 0.95. #' See Details. #' @param seed A positive numeric value setting the seed for random number -#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' generation to ensure reproducibility. If `NA` (default), then `set.seed()` +#' is not called at all. If not `NA`, then the random number generator state is +#' reset (to the state before calling this function) upon exiting this function. #' #' @details A new observation point is sampled from a bivariate Normal #' distribution with means equal to the X and Y coordinates of its original diff --git a/R/sample_from_uniform_circle.R b/R/sample_from_uniform_circle.R index c1787cb..b9d6634 100644 --- a/R/sample_from_uniform_circle.R +++ b/R/sample_from_uniform_circle.R @@ -8,7 +8,9 @@ #' the function will assume no uncertainty (zero meters) around the observation #' points. #' @param seed A positive numeric value setting the seed for random number -#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' generation to ensure reproducibility. If `NA` (default), then `set.seed()` +#' is not called at all. If not `NA`, then the random number generator state is +#' reset (to the state before calling this function) upon exiting this function. #' #' @returns An sf object with POINT geometry containing the locations of the #' sampled occurrences and a `coordinateUncertaintyInMeters` column containing diff --git a/R/sample_observations.R b/R/sample_observations.R index 71e07ec..621f802 100644 --- a/R/sample_observations.R +++ b/R/sample_observations.R @@ -42,7 +42,9 @@ #' Weights can be numeric values between 0 and 1 or positive integers, which #' will be rescaled to values between 0 and 1. #' @param seed A positive numeric value setting the seed for random number -#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' generation to ensure reproducibility. If `NA` (default), then `set.seed()` +#' is not called at all. If not `NA`, then the random number generator state is +#' reset (to the state before calling this function) upon exiting this function. #' #' @returns An sf object with POINT geometry containing the locations of the #' occurrence with detection status. The object includes the following columns: diff --git a/R/sample_occurrences_from_raster.R b/R/sample_occurrences_from_raster.R index 3d4d23b..86f02b4 100644 --- a/R/sample_occurrences_from_raster.R +++ b/R/sample_occurrences_from_raster.R @@ -7,7 +7,9 @@ #' @param raster A SpatRaster object (see [terra::rast()]). #' @param time_series A vector with the number of occurrences per time point. #' @param seed A positive numeric value setting the seed for random number -#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' generation to ensure reproducibility. If `NA` (default), then `set.seed()` +#' is not called at all. If not `NA`, then the random number generator state is +#' reset (to the state before calling this function) upon exiting this function. #' #' @returns An sf object with POINT geometry containing the locations of the #' simulated occurrences, a `time_point` column indicating the associated diff --git a/R/simulate_occurrences.R b/R/simulate_occurrences.R index 4cf7a99..596ff07 100644 --- a/R/simulate_occurrences.R +++ b/R/simulate_occurrences.R @@ -22,7 +22,9 @@ #' The default is `"random"`. `"clustered"` corresponds to a value of 10. #' See `create_spatial_pattern()`. #' @param seed A positive numeric value setting the seed for random number -#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' generation to ensure reproducibility. If `NA` (default), then `set.seed()` +#' is not called at all. If not `NA`, then the random number generator state is +#' reset (to the state before calling this function) upon exiting this function. #' #' @returns An sf object with POINT geometry containing the locations of the #' simulated occurrences, a `time_point` column indicating the associated diff --git a/R/simulate_random_walk.R b/R/simulate_random_walk.R index 4f62e5a..a32ebb0 100644 --- a/R/simulate_random_walk.R +++ b/R/simulate_random_walk.R @@ -10,7 +10,9 @@ #' @param sd_step A positive numeric value indicating the standard deviation of #' the random steps. #' @param seed A positive numeric value setting the seed for random number -#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' generation to ensure reproducibility. If `NA` (default), then `set.seed()` +#' is not called at all. If not `NA`, then the random number generator state is +#' reset (to the state before calling this function) upon exiting this function. #' #' @returns A vector of integers of length `n_time_points` with the average #' number of occurrences. diff --git a/R/simulate_timeseries.R b/R/simulate_timeseries.R index c8ef775..695fdf4 100644 --- a/R/simulate_timeseries.R +++ b/R/simulate_timeseries.R @@ -14,7 +14,9 @@ #' occurrences. #' @param ... Additional arguments to be passed to `temporal_function`. #' @param seed A positive numeric value setting the seed for random number -#' generation to ensure reproducibility. If `NA` (default), no seed is used. +#' generation to ensure reproducibility. If `NA` (default), then `set.seed()` +#' is not called at all. If not `NA`, then the random number generator state is +#' reset (to the state before calling this function) upon exiting this function. #' #' @returns A vector of integers of length `n_time_points` with the number of #' occurrences. diff --git a/man/create_spatial_pattern.Rd b/man/create_spatial_pattern.Rd index 46af4c8..c71c481 100644 --- a/man/create_spatial_pattern.Rd +++ b/man/create_spatial_pattern.Rd @@ -25,7 +25,9 @@ The default is \code{"random"}. \code{"clustered"} corresponds to a value of 10. See Details.} \item{seed}{A positive numeric value setting the seed for random number -generation to ensure reproducibility. If \code{NA} (default), no seed is used.} +generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} +is not called at all. If not \code{NA}, then the random number generator state is +reset (to the state before calling this function) upon exiting this function.} \item{n_sim}{Number of simulations. Each simulation is a different layer in the raster. Default is 1.} diff --git a/man/generate_taxonomy.Rd b/man/generate_taxonomy.Rd index 73cf54c..e67432b 100644 --- a/man/generate_taxonomy.Rd +++ b/man/generate_taxonomy.Rd @@ -34,7 +34,9 @@ output.} \item{num_kingdoms}{Number of kingdoms to generate. Defaults to 1.} \item{seed}{A positive numeric value setting the seed for random number -generation to ensure reproducibility. If \code{NA} (default), no seed is used.} +generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} +is not called at all. If not \code{NA}, then the random number generator state is +reset (to the state before calling this function) upon exiting this function.} } \value{ A data frame with the taxonomic classification of each species. If diff --git a/man/grid_designation.Rd b/man/grid_designation.Rd index f7eb330..522f3dc 100644 --- a/man/grid_designation.Rd +++ b/man/grid_designation.Rd @@ -28,7 +28,9 @@ observations should be designated.} represent the unique IDs.} \item{seed}{A positive numeric value setting the seed for random number -generation to ensure reproducibility. If \code{NA} (default), no seed is used.} +generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} +is not called at all. If not \code{NA}, then the random number generator state is +reset (to the state before calling this function) upon exiting this function.} \item{aggregate}{Logical. If \code{TRUE} (default), returns data cube in aggregated form (grid with the number of observations per grid cell). diff --git a/man/sample_from_binormal_circle.Rd b/man/sample_from_binormal_circle.Rd index b401c33..8e0c675 100644 --- a/man/sample_from_binormal_circle.Rd +++ b/man/sample_from_binormal_circle.Rd @@ -18,7 +18,9 @@ circle. Default is 0.95. See Details.} \item{seed}{A positive numeric value setting the seed for random number -generation to ensure reproducibility. If \code{NA} (default), no seed is used.} +generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} +is not called at all. If not \code{NA}, then the random number generator state is +reset (to the state before calling this function) upon exiting this function.} } \value{ An sf object with POINT geometry containing the locations of the diff --git a/man/sample_from_uniform_circle.Rd b/man/sample_from_uniform_circle.Rd index 9159f2d..56d0b51 100644 --- a/man/sample_from_uniform_circle.Rd +++ b/man/sample_from_uniform_circle.Rd @@ -13,7 +13,9 @@ the function will assume no uncertainty (zero meters) around the observation points.} \item{seed}{A positive numeric value setting the seed for random number -generation to ensure reproducibility. If \code{NA} (default), no seed is used.} +generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} +is not called at all. If not \code{NA}, then the random number generator state is +reset (to the state before calling this function) upon exiting this function.} } \value{ An sf object with POINT geometry containing the locations of the diff --git a/man/sample_observations.Rd b/man/sample_observations.Rd index 35468dd..9ceb285 100644 --- a/man/sample_observations.Rd +++ b/man/sample_observations.Rd @@ -54,7 +54,9 @@ Weights can be numeric values between 0 and 1 or positive integers, which will be rescaled to values between 0 and 1.} \item{seed}{A positive numeric value setting the seed for random number -generation to ensure reproducibility. If \code{NA} (default), no seed is used.} +generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} +is not called at all. If not \code{NA}, then the random number generator state is +reset (to the state before calling this function) upon exiting this function.} } \value{ An sf object with POINT geometry containing the locations of the diff --git a/man/sample_occurrences_from_raster.Rd b/man/sample_occurrences_from_raster.Rd index 7630cc3..ebc111a 100644 --- a/man/sample_occurrences_from_raster.Rd +++ b/man/sample_occurrences_from_raster.Rd @@ -12,7 +12,9 @@ sample_occurrences_from_raster(raster, time_series, seed = NA) \item{time_series}{A vector with the number of occurrences per time point.} \item{seed}{A positive numeric value setting the seed for random number -generation to ensure reproducibility. If \code{NA} (default), no seed is used.} +generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} +is not called at all. If not \code{NA}, then the random number generator state is +reset (to the state before calling this function) upon exiting this function.} } \value{ An sf object with POINT geometry containing the locations of the diff --git a/man/simulate_occurrences.Rd b/man/simulate_occurrences.Rd index 27ea626..0b3a6f8 100644 --- a/man/simulate_occurrences.Rd +++ b/man/simulate_occurrences.Rd @@ -40,7 +40,9 @@ occurrences.} \item{...}{Additional arguments to be passed to \code{temporal_function}.} \item{seed}{A positive numeric value setting the seed for random number -generation to ensure reproducibility. If \code{NA} (default), no seed is used.} +generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} +is not called at all. If not \code{NA}, then the random number generator state is +reset (to the state before calling this function) upon exiting this function.} } \value{ An sf object with POINT geometry containing the locations of the diff --git a/man/simulate_random_walk.Rd b/man/simulate_random_walk.Rd index 0b7ab07..1067d62 100644 --- a/man/simulate_random_walk.Rd +++ b/man/simulate_random_walk.Rd @@ -22,7 +22,9 @@ to simulate.} the random steps.} \item{seed}{A positive numeric value setting the seed for random number -generation to ensure reproducibility. If \code{NA} (default), no seed is used.} +generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} +is not called at all. If not \code{NA}, then the random number generator state is +reset (to the state before calling this function) upon exiting this function.} } \value{ A vector of integers of length \code{n_time_points} with the average diff --git a/man/simulate_timeseries.Rd b/man/simulate_timeseries.Rd index 19b288c..5daf8e9 100644 --- a/man/simulate_timeseries.Rd +++ b/man/simulate_timeseries.Rd @@ -28,7 +28,9 @@ occurrences.} \item{...}{Additional arguments to be passed to \code{temporal_function}.} \item{seed}{A positive numeric value setting the seed for random number -generation to ensure reproducibility. If \code{NA} (default), no seed is used.} +generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} +is not called at all. If not \code{NA}, then the random number generator state is +reset (to the state before calling this function) upon exiting this function.} } \value{ A vector of integers of length \code{n_time_points} with the number of From 919a2e25a2def4b06747ba52271ac23988e62cfb Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 16:42:39 +0200 Subject: [PATCH 42/57] update news with solved issue --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 8ec6480..71f29d1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,7 +4,7 @@ * Update `sample_occurrences_from_raster()` - Use `lapply()` instead of for-loop - Randomise points in raster cells. -* Fix issues (#76). +* Fix issues (#37, #76). # gcube 0.3.0 From 5516472b6e6dc21642cdf837e3b3b3130fe44848 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 16:49:29 +0200 Subject: [PATCH 43/57] do not use set.seed here --- tests/testthat/test-sample_from_binormal_circle.R | 3 +-- tests/testthat/test-sample_from_uniform_circle.R | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-sample_from_binormal_circle.R b/tests/testthat/test-sample_from_binormal_circle.R index bc7c77b..1dd7993 100644 --- a/tests/testthat/test-sample_from_binormal_circle.R +++ b/tests/testthat/test-sample_from_binormal_circle.R @@ -13,8 +13,7 @@ observations_sf1 <- data.frame( sf::st_as_sf(coords = c("long", "lat"), crs = 3035) ## dataset with coordinateUncertaintyInMeters -set.seed(123) -coordinate_uncertainty <- rgamma(n_points, shape = 5, rate = 0.1) +coordinate_uncertainty <- c(24.32870, 53.96961, 28.16026, 43.24885) observations_sf2 <- observations_sf1 %>% dplyr::mutate(coordinateUncertaintyInMeters = coordinate_uncertainty) diff --git a/tests/testthat/test-sample_from_uniform_circle.R b/tests/testthat/test-sample_from_uniform_circle.R index c6f996a..aa0099c 100644 --- a/tests/testthat/test-sample_from_uniform_circle.R +++ b/tests/testthat/test-sample_from_uniform_circle.R @@ -13,8 +13,7 @@ observations_sf1 <- data.frame( sf::st_as_sf(coords = c("long", "lat"), crs = 3035) ## dataset with coordinateUncertaintyInMeters -set.seed(123) -coordinate_uncertainty <- rgamma(n_points, shape = 5, rate = 0.1) +coordinate_uncertainty <- c(24.32870, 53.96961, 28.16026, 43.24885) observations_sf2 <- observations_sf1 %>% dplyr::mutate(coordinateUncertaintyInMeters = coordinate_uncertainty) From d8ecbee1da327c2b65afbdd4eac0465fb6ca884e Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 17:01:02 +0200 Subject: [PATCH 44/57] remove set seed from tests and examples --- R/apply_manual_sampling_bias.R | 3 --- R/apply_polygon_sampling_bias.R | 3 --- R/grid_designation.R | 2 -- R/sample_from_binormal_circle.R | 2 -- R/sample_from_uniform_circle.R | 2 -- R/sample_observations.R | 3 --- R/simulate_timeseries.R | 3 --- man/apply_manual_sampling_bias.Rd | 3 --- man/apply_polygon_sampling_bias.Rd | 3 --- man/grid_designation.Rd | 2 -- man/sample_from_binormal_circle.Rd | 2 -- man/sample_from_uniform_circle.Rd | 2 -- man/sample_observations.Rd | 3 --- man/simulate_timeseries.Rd | 3 --- tests/testthat/test-apply_polygon_sampling_bias.R | 5 ++--- tests/testthat/test-grid_designation.R | 7 ++----- 16 files changed, 4 insertions(+), 44 deletions(-) diff --git a/R/apply_manual_sampling_bias.R b/R/apply_manual_sampling_bias.R index 10b3eb3..4cd26a2 100644 --- a/R/apply_manual_sampling_bias.R +++ b/R/apply_manual_sampling_bias.R @@ -34,9 +34,6 @@ #' # Get occurrence points #' occurrences_sf <- simulate_occurrences(plgn) #' -#' # Set seed for reproducibility -#' set.seed(123) -#' #' # Create grid with bias weights #' grid <- st_make_grid( #' plgn, diff --git a/R/apply_polygon_sampling_bias.R b/R/apply_polygon_sampling_bias.R index 1cf9ea8..7c0af0b 100644 --- a/R/apply_polygon_sampling_bias.R +++ b/R/apply_polygon_sampling_bias.R @@ -35,9 +35,6 @@ #' library(dplyr) #' library(ggplot2) #' -#' # Set seed for reproducibility -#' set.seed(123) -#' #' # Simulate some occurrence data with coordinates and time points #' num_points <- 10 #' occurrences <- data.frame( diff --git a/R/grid_designation.R b/R/grid_designation.R index e7f78bf..3741141 100644 --- a/R/grid_designation.R +++ b/R/grid_designation.R @@ -53,8 +53,6 @@ #' library(sf) #' library(dplyr) #' -#' set.seed(123) -#' #' # Create four random points #' n_points <- 4 #' xlim <- c(3841000, 3842000) diff --git a/R/sample_from_binormal_circle.R b/R/sample_from_binormal_circle.R index 7fb594c..c388214 100644 --- a/R/sample_from_binormal_circle.R +++ b/R/sample_from_binormal_circle.R @@ -42,8 +42,6 @@ #' library(sf) #' library(dplyr) #' -#' set.seed(123) -#' #' # Create four random points #' n_points <- 4 #' xlim <- c(3841000, 3842000) diff --git a/R/sample_from_uniform_circle.R b/R/sample_from_uniform_circle.R index b9d6634..6d7f8e7 100644 --- a/R/sample_from_uniform_circle.R +++ b/R/sample_from_uniform_circle.R @@ -28,8 +28,6 @@ #' @examples #' library(sf) #' -#' set.seed(123) -#' #' # Create four random points #' n_points <- 4 #' xlim <- c(3841000, 3842000) diff --git a/R/sample_observations.R b/R/sample_observations.R index 621f802..de8875e 100644 --- a/R/sample_observations.R +++ b/R/sample_observations.R @@ -74,9 +74,6 @@ #' library(sf) #' library(dplyr) #' -#' # Set seed for reproducibility -#' set.seed(123) -#' #' # Simulate some occurrence data with coordinates and time points #' num_points <- 10 #' occurrences <- data.frame( diff --git a/R/simulate_timeseries.R b/R/simulate_timeseries.R index 695fdf4..f11c248 100644 --- a/R/simulate_timeseries.R +++ b/R/simulate_timeseries.R @@ -60,9 +60,6 @@ #' return(lambdas) #' } #' -#' # Set seed for reproducibility -#' set.seed(123) -#' #' # Draw n_sim number of occurrences from Poisson distribution using #' # the custom function #' n_sim <- 10 diff --git a/man/apply_manual_sampling_bias.Rd b/man/apply_manual_sampling_bias.Rd index 317c081..5da0d9f 100644 --- a/man/apply_manual_sampling_bias.Rd +++ b/man/apply_manual_sampling_bias.Rd @@ -37,9 +37,6 @@ plgn <- st_polygon(list(cbind(c(5, 10, 8, 2, 3, 5), c(2, 1, 7, 9, 5, 2)))) # Get occurrence points occurrences_sf <- simulate_occurrences(plgn) -# Set seed for reproducibility -set.seed(123) - # Create grid with bias weights grid <- st_make_grid( plgn, diff --git a/man/apply_polygon_sampling_bias.Rd b/man/apply_polygon_sampling_bias.Rd index 2dc1eba..203342b 100644 --- a/man/apply_polygon_sampling_bias.Rd +++ b/man/apply_polygon_sampling_bias.Rd @@ -38,9 +38,6 @@ library(sf) library(dplyr) library(ggplot2) -# Set seed for reproducibility -set.seed(123) - # Simulate some occurrence data with coordinates and time points num_points <- 10 occurrences <- data.frame( diff --git a/man/grid_designation.Rd b/man/grid_designation.Rd index 522f3dc..ec401b5 100644 --- a/man/grid_designation.Rd +++ b/man/grid_designation.Rd @@ -67,8 +67,6 @@ aggregated data cube. library(sf) library(dplyr) -set.seed(123) - # Create four random points n_points <- 4 xlim <- c(3841000, 3842000) diff --git a/man/sample_from_binormal_circle.Rd b/man/sample_from_binormal_circle.Rd index 8e0c675..d90b33d 100644 --- a/man/sample_from_binormal_circle.Rd +++ b/man/sample_from_binormal_circle.Rd @@ -44,8 +44,6 @@ circle. library(sf) library(dplyr) -set.seed(123) - # Create four random points n_points <- 4 xlim <- c(3841000, 3842000) diff --git a/man/sample_from_uniform_circle.Rd b/man/sample_from_uniform_circle.Rd index 56d0b51..076e622 100644 --- a/man/sample_from_uniform_circle.Rd +++ b/man/sample_from_uniform_circle.Rd @@ -29,8 +29,6 @@ uncertainty circle around each observation assuming a Uniform distribution. \examples{ library(sf) -set.seed(123) - # Create four random points n_points <- 4 xlim <- c(3841000, 3842000) diff --git a/man/sample_observations.Rd b/man/sample_observations.Rd index 9ceb285..6ae6f4a 100644 --- a/man/sample_observations.Rd +++ b/man/sample_observations.Rd @@ -82,9 +82,6 @@ probability and sampling bias by implementing a Bernoulli trial. library(sf) library(dplyr) -# Set seed for reproducibility -set.seed(123) - # Simulate some occurrence data with coordinates and time points num_points <- 10 occurrences <- data.frame( diff --git a/man/simulate_timeseries.Rd b/man/simulate_timeseries.Rd index 5daf8e9..a0dd5d2 100644 --- a/man/simulate_timeseries.Rd +++ b/man/simulate_timeseries.Rd @@ -72,9 +72,6 @@ my_own_linear_function <- function( return(lambdas) } -# Set seed for reproducibility -set.seed(123) - # Draw n_sim number of occurrences from Poisson distribution using # the custom function n_sim <- 10 diff --git a/tests/testthat/test-apply_polygon_sampling_bias.R b/tests/testthat/test-apply_polygon_sampling_bias.R index 155afd5..dff6b6e 100644 --- a/tests/testthat/test-apply_polygon_sampling_bias.R +++ b/tests/testthat/test-apply_polygon_sampling_bias.R @@ -4,10 +4,9 @@ n_points <- 4 xlim <- c(3841000, 3842000) ylim <- c(3110000, 3112000) -set.seed(123) occurrences_sf <- data.frame( - lat = runif(n_points, ylim[1], ylim[2]), - long = runif(n_points, xlim[1], xlim[2]) + lat = c(3110575, 3111577, 3110818, 3111766), + long = c(3841940, 3841046, 3841528, 3841892) ) %>% sf::st_as_sf(coords = c("long", "lat"), crs = 3035) diff --git a/tests/testthat/test-grid_designation.R b/tests/testthat/test-grid_designation.R index 967fd1f..8316ac3 100644 --- a/tests/testthat/test-grid_designation.R +++ b/tests/testthat/test-grid_designation.R @@ -4,13 +4,10 @@ n_points <- 4 xlim <- c(3841000, 3842000) ylim <- c(3110000, 3112000) -## set seed for reproducible observations object -set.seed(9) - ## dataset without coordinateUncertaintyInMeters observations_sf1 <- data.frame( - lat = runif(n_points, ylim[1], ylim[2]), - long = runif(n_points, xlim[1], xlim[2]), + lat = c(3110575, 3111577, 3110818, 3111766), + long = c(3841940, 3841046, 3841528, 3841892), time_point = 1 ) %>% sf::st_as_sf(coords = c("long", "lat"), crs = 3035) From d30432ebff96885a5a65715ea438ecc2759ea4ff Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 17:09:54 +0200 Subject: [PATCH 45/57] create time point column if not present --- R/grid_designation.R | 7 ++++--- R/sample_from_binormal_circle.R | 17 ++++++++++++++--- R/sample_from_uniform_circle.R | 17 ++++++++++++++--- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/R/grid_designation.R b/R/grid_designation.R index 3741141..6e1e57e 100644 --- a/R/grid_designation.R +++ b/R/grid_designation.R @@ -4,9 +4,10 @@ #' aggregated data cube. #' #' @param observations An sf object with POINT geometry and a `time_point` and -#' `coordinateUncertaintyInMeters` column. If the latter column is not present, -#' the function will assume no uncertainty (zero meters) around the observation -#' points. +#' `coordinateUncertaintyInMeters` column. If the former column is not present, +#' the function will assume a single time point. If the latter column is not +#' present, the function will assume no uncertainty (zero meters) around the +#' observation points. #' @param grid An sf object with POLYGON geometry (usually a grid) to which #' observations should be designated. #' @param id_col The column name containing unique IDs for each grid cell. If diff --git a/R/sample_from_binormal_circle.R b/R/sample_from_binormal_circle.R index c388214..2e8d510 100644 --- a/R/sample_from_binormal_circle.R +++ b/R/sample_from_binormal_circle.R @@ -5,9 +5,10 @@ #' distribution. #' #' @param observations An sf object with POINT geometry and a `time_point` and -#' `coordinateUncertaintyInMeters` column. If the latter column is not present, -#' the function will assume no uncertainty (zero meters) around the observation -#' points. +#' `coordinateUncertaintyInMeters` column. If the former column is not present, +#' the function will assume a single time point. If the latter column is not +#' present, the function will assume no uncertainty (zero meters) around the +#' observation points. #' @param p_norm A numeric value between 0 and 1. The proportion of all possible #' samples from a bivariate Normal distribution that fall within the uncertainty #' circle. Default is 0.95. @@ -93,6 +94,16 @@ sample_from_binormal_circle <- function( set.seed(seed) } + # Create time_point column if column not present in data + if (!"time_point" %in% names(observations)) { + observations$time_point <- 1 + warning(paste( + "No column `time_point` present!", + "Assuming only a single time point.", + sep = "\n" + )) + } + # Set uncertainty to zero if column not present in data if (!"coordinateUncertaintyInMeters" %in% names(observations)) { observations$coordinateUncertaintyInMeters <- 0 diff --git a/R/sample_from_uniform_circle.R b/R/sample_from_uniform_circle.R index 6d7f8e7..424f53c 100644 --- a/R/sample_from_uniform_circle.R +++ b/R/sample_from_uniform_circle.R @@ -4,9 +4,10 @@ #' uncertainty circle around each observation assuming a Uniform distribution. #' #' @param observations An sf object with POINT geometry and a `time_point` and -#' `coordinateUncertaintyInMeters` column. If the latter column is not present, -#' the function will assume no uncertainty (zero meters) around the observation -#' points. +#' `coordinateUncertaintyInMeters` column. If the former column is not present, +#' the function will assume a single time point. If the latter column is not +#' present, the function will assume no uncertainty (zero meters) around the +#' observation points. #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), then `set.seed()` #' is not called at all. If not `NA`, then the random number generator state is @@ -72,6 +73,16 @@ sample_from_uniform_circle <- function( set.seed(seed) } + # Create time_point column if column not present in data + if (!"time_point" %in% names(observations)) { + observations$time_point <- 1 + warning(paste( + "No column `time_point` present!", + "Assuming only a single time point.", + sep = "\n" + )) + } + # Set uncertainty to zero if column not present in data if (!"coordinateUncertaintyInMeters" %in% names(observations)) { observations$coordinateUncertaintyInMeters <- 0 From bce8b85fecedcbe7a79b6eea7fe722763904e0f7 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 17:15:25 +0200 Subject: [PATCH 46/57] add test for warning time point col missing --- tests/testthat/test-sample_from_binormal_circle.R | 14 ++++++++++++++ tests/testthat/test-sample_from_uniform_circle.R | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/tests/testthat/test-sample_from_binormal_circle.R b/tests/testthat/test-sample_from_binormal_circle.R index 1dd7993..79202db 100644 --- a/tests/testthat/test-sample_from_binormal_circle.R +++ b/tests/testthat/test-sample_from_binormal_circle.R @@ -21,6 +21,9 @@ observations_sf2 <- observations_sf1 %>% observations_sf3 <- observations_sf2 %>% sf::st_drop_geometry() +## dataset without time points +observations_sf4 <- observations_sf2[-1] + # Unit tests ## expect errors test_that("arguments are of the right class", { @@ -90,6 +93,17 @@ test_that("warning if coordinateUncertaintyInMeters column is not present", { fixed = TRUE) }) +test_that("warning if time_point column is not present", { + expect_warning( + sample_from_uniform_circle(observations_sf4), + regexp = paste( + "No column `time_point` present!", + "Assuming only a single time point.", + sep = "\n" + ), + fixed = TRUE) +}) + ## expected outputs test_that("output class is correct", { suppressWarnings({ diff --git a/tests/testthat/test-sample_from_uniform_circle.R b/tests/testthat/test-sample_from_uniform_circle.R index aa0099c..2145d56 100644 --- a/tests/testthat/test-sample_from_uniform_circle.R +++ b/tests/testthat/test-sample_from_uniform_circle.R @@ -21,6 +21,9 @@ observations_sf2 <- observations_sf1 %>% observations_sf3 <- observations_sf2 %>% sf::st_drop_geometry() +## dataset without time points +observations_sf4 <- observations_sf2[-1] + # Unit tests ## expect errors test_that("arguments are of the right class", { @@ -66,6 +69,17 @@ test_that("warning if coordinateUncertaintyInMeters column is not present", { fixed = TRUE) }) +test_that("warning if time_point column is not present", { + expect_warning( + sample_from_uniform_circle(observations_sf4), + regexp = paste( + "No column `time_point` present!", + "Assuming only a single time point.", + sep = "\n" + ), + fixed = TRUE) +}) + ## expected outputs test_that("output class is correct", { suppressWarnings({ From fcfaa24756c24f999e3cc3de722da4092675b8a0 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 17:16:44 +0200 Subject: [PATCH 47/57] update documentation --- man/grid_designation.Rd | 7 ++++--- man/sample_from_binormal_circle.Rd | 7 ++++--- man/sample_from_uniform_circle.Rd | 7 ++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/man/grid_designation.Rd b/man/grid_designation.Rd index ec401b5..8f001ba 100644 --- a/man/grid_designation.Rd +++ b/man/grid_designation.Rd @@ -16,9 +16,10 @@ grid_designation( } \arguments{ \item{observations}{An sf object with POINT geometry and a \code{time_point} and -\code{coordinateUncertaintyInMeters} column. If the latter column is not present, -the function will assume no uncertainty (zero meters) around the observation -points.} +\code{coordinateUncertaintyInMeters} column. If the former column is not present, +the function will assume a single time point. If the latter column is not +present, the function will assume no uncertainty (zero meters) around the +observation points.} \item{grid}{An sf object with POLYGON geometry (usually a grid) to which observations should be designated.} diff --git a/man/sample_from_binormal_circle.Rd b/man/sample_from_binormal_circle.Rd index d90b33d..9cd89f3 100644 --- a/man/sample_from_binormal_circle.Rd +++ b/man/sample_from_binormal_circle.Rd @@ -8,9 +8,10 @@ sample_from_binormal_circle(observations, p_norm = 0.95, seed = NA) } \arguments{ \item{observations}{An sf object with POINT geometry and a \code{time_point} and -\code{coordinateUncertaintyInMeters} column. If the latter column is not present, -the function will assume no uncertainty (zero meters) around the observation -points.} +\code{coordinateUncertaintyInMeters} column. If the former column is not present, +the function will assume a single time point. If the latter column is not +present, the function will assume no uncertainty (zero meters) around the +observation points.} \item{p_norm}{A numeric value between 0 and 1. The proportion of all possible samples from a bivariate Normal distribution that fall within the uncertainty diff --git a/man/sample_from_uniform_circle.Rd b/man/sample_from_uniform_circle.Rd index 076e622..a036513 100644 --- a/man/sample_from_uniform_circle.Rd +++ b/man/sample_from_uniform_circle.Rd @@ -8,9 +8,10 @@ sample_from_uniform_circle(observations, seed = NA) } \arguments{ \item{observations}{An sf object with POINT geometry and a \code{time_point} and -\code{coordinateUncertaintyInMeters} column. If the latter column is not present, -the function will assume no uncertainty (zero meters) around the observation -points.} +\code{coordinateUncertaintyInMeters} column. If the former column is not present, +the function will assume a single time point. If the latter column is not +present, the function will assume no uncertainty (zero meters) around the +observation points.} \item{seed}{A positive numeric value setting the seed for random number generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} From 0170eceb2992449eb6077e8e30694e36ceee5564 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 17:17:25 +0200 Subject: [PATCH 48/57] update news of issue fix --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 71f29d1..e53d1bb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,7 +4,7 @@ * Update `sample_occurrences_from_raster()` - Use `lapply()` instead of for-loop - Randomise points in raster cells. -* Fix issues (#37, #76). +* Fix issues (#37, #70, #76). # gcube 0.3.0 From 2abf34dc329e49e40cca622c1e9d4a1a1977c533 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 17:37:01 +0200 Subject: [PATCH 49/57] update tests --- tests/testthat/test-sample_from_binormal_circle.R | 4 ++-- tests/testthat/test-sample_from_uniform_circle.R | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-sample_from_binormal_circle.R b/tests/testthat/test-sample_from_binormal_circle.R index 79202db..a10d27d 100644 --- a/tests/testthat/test-sample_from_binormal_circle.R +++ b/tests/testthat/test-sample_from_binormal_circle.R @@ -6,8 +6,8 @@ ylim <- c(3110000, 3112000) ## dataset without coordinateUncertaintyInMeters observations_sf1 <- data.frame( - lat = runif(n_points, ylim[1], ylim[2]), - long = runif(n_points, xlim[1], xlim[2]), + lat = c(3110575, 3111577, 3110818, 3111766), + long = c(3841940, 3841046, 3841528, 3841892), time_point = 1 ) %>% sf::st_as_sf(coords = c("long", "lat"), crs = 3035) diff --git a/tests/testthat/test-sample_from_uniform_circle.R b/tests/testthat/test-sample_from_uniform_circle.R index 2145d56..43d2983 100644 --- a/tests/testthat/test-sample_from_uniform_circle.R +++ b/tests/testthat/test-sample_from_uniform_circle.R @@ -6,8 +6,8 @@ ylim <- c(3110000, 3112000) ## dataset without coordinateUncertaintyInMeters observations_sf1 <- data.frame( - lat = runif(n_points, ylim[1], ylim[2]), - long = runif(n_points, xlim[1], xlim[2]), + lat = c(3110575, 3111577, 3110818, 3111766), + long = c(3841940, 3841046, 3841528, 3841892), time_point = 1 ) %>% sf::st_as_sf(coords = c("long", "lat"), crs = 3035) From 47c8de1970125785bef52ec3d57b3f40bd389e56 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 17:49:06 +0200 Subject: [PATCH 50/57] add note on absences --- vignettes/articles/grid-designation-process.Rmd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vignettes/articles/grid-designation-process.Rmd b/vignettes/articles/grid-designation-process.Rmd index 50e915d..aa93fbb 100644 --- a/vignettes/articles/grid-designation-process.Rmd +++ b/vignettes/articles/grid-designation-process.Rmd @@ -350,6 +350,8 @@ sampled_points <- grid_designation( ``` Lets visualise were the samples were taken for time point 1. +Note that no distinction is made between zeroes and `NA` values! +Every absence gets a zero value. ```{r} #| fig.alt: > From bd679557d45889c2de5ad7d1c974f1e462048140 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 18:15:17 +0200 Subject: [PATCH 51/57] update tests --- tests/testthat/test-grid_designation.R | 2 +- tests/testthat/test-sample_from_binormal_circle.R | 2 +- tests/testthat/test-sample_from_uniform_circle.R | 2 +- tests/testthat/test-simulate_random_walk.R | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-grid_designation.R b/tests/testthat/test-grid_designation.R index 8316ac3..cdf2150 100644 --- a/tests/testthat/test-grid_designation.R +++ b/tests/testthat/test-grid_designation.R @@ -13,7 +13,7 @@ observations_sf1 <- data.frame( sf::st_as_sf(coords = c("long", "lat"), crs = 3035) ## dataset with coordinateUncertaintyInMeters -coordinate_uncertainty <- rgamma(n_points, shape = 5, rate = 0.1) +coordinate_uncertainty <- c(24.32870, 53.96961, 28.16026, 43.24885) observations_sf2 <- observations_sf1 %>% dplyr::mutate(coordinateUncertaintyInMeters = coordinate_uncertainty) diff --git a/tests/testthat/test-sample_from_binormal_circle.R b/tests/testthat/test-sample_from_binormal_circle.R index a10d27d..a7e7f7b 100644 --- a/tests/testthat/test-sample_from_binormal_circle.R +++ b/tests/testthat/test-sample_from_binormal_circle.R @@ -95,7 +95,7 @@ test_that("warning if coordinateUncertaintyInMeters column is not present", { test_that("warning if time_point column is not present", { expect_warning( - sample_from_uniform_circle(observations_sf4), + sample_from_binormal_circle(observations_sf4), regexp = paste( "No column `time_point` present!", "Assuming only a single time point.", diff --git a/tests/testthat/test-sample_from_uniform_circle.R b/tests/testthat/test-sample_from_uniform_circle.R index 43d2983..4b27719 100644 --- a/tests/testthat/test-sample_from_uniform_circle.R +++ b/tests/testthat/test-sample_from_uniform_circle.R @@ -153,7 +153,7 @@ test_smaller_distances <- function(observations, seed = NA) { test_dists_df <- observations %>% sf::st_drop_geometry() %>% dplyr::mutate(dist = sample_dists, - test = dist < .data$coordinateUncertaintyInMeters) + test = dist <= .data$coordinateUncertaintyInMeters) return(all(test_dists_df$test)) } diff --git a/tests/testthat/test-simulate_random_walk.R b/tests/testthat/test-simulate_random_walk.R index b7e798a..9d23b46 100644 --- a/tests/testthat/test-simulate_random_walk.R +++ b/tests/testthat/test-simulate_random_walk.R @@ -24,7 +24,8 @@ test_that("simulate_random_walk handles zero and neg. occurrences properly", { result <- simulate_random_walk( initial_average_occurrences = 1, n_time_points = 10, - sd_step = 5) + sd_step = 5, + seed = 123) expect_equal(result[1], 1) expect_true(all(result >= 0)) }) From a63530bbae94f0e399615f852efa2ce727fe02e4 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 18:31:36 +0200 Subject: [PATCH 52/57] no seed remove --- tests/testthat/test-sample_from_uniform_circle.R | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/testthat/test-sample_from_uniform_circle.R b/tests/testthat/test-sample_from_uniform_circle.R index 4b27719..56ceca0 100644 --- a/tests/testthat/test-sample_from_uniform_circle.R +++ b/tests/testthat/test-sample_from_uniform_circle.R @@ -187,9 +187,6 @@ test_that("distance to new point falls within coordinate uncertainty", { }) # in case of provided initial coordinateUncertaintyInMeters column - ## no seed - expect_true(test_smaller_distances(observations_sf2)) - ## different seeds expect_true(test_smaller_distances(observations_sf2, seed = 123)) expect_true(test_smaller_distances(observations_sf2, seed = 456)) }) From de1dbac07cd1ffa35576b8eb67ddf2e23536847c Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Mon, 9 Sep 2024 18:56:33 +0200 Subject: [PATCH 53/57] normal can go outside range --- tests/testthat/test-grid_designation.R | 3 ++- tests/testthat/test-sample_from_binormal_circle.R | 2 -- tests/testthat/test-sample_from_uniform_circle.R | 3 +++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-grid_designation.R b/tests/testthat/test-grid_designation.R index cdf2150..8147f2e 100644 --- a/tests/testthat/test-grid_designation.R +++ b/tests/testthat/test-grid_designation.R @@ -322,7 +322,8 @@ test_that("number of observations be the same as output if aggregate = FALSE", { }) expect_equal(grid_designation(observations_sf2, grid = grid_df1, randomisation = "normal", - aggregate = FALSE) %>% + aggregate = FALSE, + seed = 123) %>% nrow(), nrow(observations_sf2)) }) diff --git a/tests/testthat/test-sample_from_binormal_circle.R b/tests/testthat/test-sample_from_binormal_circle.R index a7e7f7b..0c623ae 100644 --- a/tests/testthat/test-sample_from_binormal_circle.R +++ b/tests/testthat/test-sample_from_binormal_circle.R @@ -211,8 +211,6 @@ test_that("distance to new point falls within coordinate uncertainty", { }) # in case of provided initial coordinateUncertaintyInMeters column - ## no seed - expect_true(test_smaller_distances(observations_sf2)) ## different seeds expect_true(test_smaller_distances(observations_sf2, seed = 123)) expect_true(test_smaller_distances(observations_sf2, seed = 456)) diff --git a/tests/testthat/test-sample_from_uniform_circle.R b/tests/testthat/test-sample_from_uniform_circle.R index 56ceca0..4b27719 100644 --- a/tests/testthat/test-sample_from_uniform_circle.R +++ b/tests/testthat/test-sample_from_uniform_circle.R @@ -187,6 +187,9 @@ test_that("distance to new point falls within coordinate uncertainty", { }) # in case of provided initial coordinateUncertaintyInMeters column + ## no seed + expect_true(test_smaller_distances(observations_sf2)) + ## different seeds expect_true(test_smaller_distances(observations_sf2, seed = 123)) expect_true(test_smaller_distances(observations_sf2, seed = 456)) }) From 2b54e7d2100f9e546057f9b2720f1a5c3fa51d04 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Tue, 10 Sep 2024 09:26:03 +0200 Subject: [PATCH 54/57] update codemeta --- codemeta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codemeta.json b/codemeta.json index 28f1885..4be69c7 100644 --- a/codemeta.json +++ b/codemeta.json @@ -334,7 +334,7 @@ }, "SystemRequirements": null }, - "fileSize": "550.823KB", + "fileSize": "555.949KB", "citation": [ { "@type": "SoftwareSourceCode", From 68b4d18fc2f5bd2ef6c3eb64aa60ca77b638dcfe Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Tue, 10 Sep 2024 09:59:07 +0200 Subject: [PATCH 55/57] change id column to cell_code column name --- R/grid_designation.R | 8 ++++---- man/grid_designation.Rd | 2 +- tests/testthat/test-grid_designation.R | 20 +++++++++---------- tests/testthat/test-map_grid_designation.R | 2 +- .../articles/grid-designation-process.Rmd | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/R/grid_designation.R b/R/grid_designation.R index 6e1e57e..fb7504d 100644 --- a/R/grid_designation.R +++ b/R/grid_designation.R @@ -11,7 +11,7 @@ #' @param grid An sf object with POLYGON geometry (usually a grid) to which #' observations should be designated. #' @param id_col The column name containing unique IDs for each grid cell. If -#' `"row_names"` (the default), a new column `id` is created where the row names +#' `"row_names"` (the default), a new column `cell_code` is created where the row names #' represent the unique IDs. #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), then `set.seed()` @@ -136,7 +136,7 @@ grid_designation <- function( warning( paste0( "Column name '", id_col, "' not present in provided grid!\n", - "Creating ids based on row names." + "Creating 'cell_code' column with ids based on row names." ) ) id_col <- "row_names" @@ -144,7 +144,7 @@ grid_designation <- function( warning( paste0( "Column '", id_col, "' does not contain unique ids for grid cells!", - "\nCreating new ids based on row names." + "\nCreating 'cell_code' column with ids based on row names." ) ) id_col <- "row_names" @@ -162,7 +162,7 @@ grid_designation <- function( # We assign each occurrence to a grid cell # Each grid cell needs a unique id if (id_col == "row_names") { - id_col <- "id" + id_col <- "cell_code" grid[[id_col]] <- rownames(grid) } sf::st_agr(new_points) <- "constant" diff --git a/man/grid_designation.Rd b/man/grid_designation.Rd index 8f001ba..27ead8e 100644 --- a/man/grid_designation.Rd +++ b/man/grid_designation.Rd @@ -25,7 +25,7 @@ observation points.} observations should be designated.} \item{id_col}{The column name containing unique IDs for each grid cell. If -\code{"row_names"} (the default), a new column \code{id} is created where the row names +\code{"row_names"} (the default), a new column \code{cell_code} is created where the row names represent the unique IDs.} \item{seed}{A positive numeric value setting the seed for random number diff --git a/tests/testthat/test-grid_designation.R b/tests/testthat/test-grid_designation.R index 8147f2e..c3f695c 100644 --- a/tests/testthat/test-grid_designation.R +++ b/tests/testthat/test-grid_designation.R @@ -49,7 +49,7 @@ test_that("unique ids if id column is provided", { id_col = "id"), regexp = paste0( "Column 'id' does not contain unique ids for grid cells!\n", - "Creating new ids based on row names." + "Creating 'cell_code' column with ids based on row names." ), fixed = TRUE) }) @@ -62,7 +62,7 @@ test_that("provided id column present in provided grid", { id_col = "identifier"), regexp = paste0( "Column name 'identifier' not present in provided grid!\n", - "Creating ids based on row names." + "Creating 'cell_code' column with ids based on row names." ), fixed = TRUE) }) @@ -102,10 +102,10 @@ test_that("correct column names present", { # aggregate = TRUE, randomisation = "uniform" suppressWarnings({ expect_contains(names(grid_designation(observations_sf1, grid = grid_df1)), - c("id", "n", "min_coord_uncertainty", "geometry")) + c("cell_code", "n", "min_coord_uncertainty", "geometry")) }) expect_contains(names(grid_designation(observations_sf2, grid = grid_df1)), - c("id", "n", "min_coord_uncertainty", "geometry")) + c("cell_code", "n", "min_coord_uncertainty", "geometry")) expect_contains( names(grid_designation( observations_sf2, @@ -118,11 +118,11 @@ test_that("correct column names present", { suppressWarnings({ expect_contains(names(grid_designation(observations_sf1, grid = grid_df1, randomisation = "normal")), - c("id", "n", "min_coord_uncertainty", "geometry")) + c("cell_code", "n", "min_coord_uncertainty", "geometry")) }) expect_contains(names(grid_designation(observations_sf2, grid = grid_df1, randomisation = "normal")), - c("id", "n", "min_coord_uncertainty", "geometry")) + c("cell_code", "n", "min_coord_uncertainty", "geometry")) expect_contains( names(grid_designation( observations_sf2, @@ -136,11 +136,11 @@ test_that("correct column names present", { suppressWarnings({ expect_contains(names(grid_designation(observations_sf1, grid = grid_df1, aggregate = FALSE)), - c("id", "coordinateUncertaintyInMeters", "geometry")) + c("cell_code", "coordinateUncertaintyInMeters", "geometry")) }) expect_contains(names(grid_designation(observations_sf2, grid = grid_df1, aggregate = FALSE)), - c("id", "coordinateUncertaintyInMeters", "geometry")) + c("cell_code", "coordinateUncertaintyInMeters", "geometry")) expect_contains( names(grid_designation( observations_sf2, @@ -155,12 +155,12 @@ test_that("correct column names present", { expect_contains(names(grid_designation(observations_sf1, grid = grid_df1, aggregate = FALSE, randomisation = "normal")), - c("id", "coordinateUncertaintyInMeters", "geometry")) + c("cell_code", "coordinateUncertaintyInMeters", "geometry")) }) expect_contains(names(grid_designation(observations_sf2, grid = grid_df1, aggregate = FALSE, randomisation = "normal")), - c("id", "coordinateUncertaintyInMeters", "geometry")) + c("cell_code", "coordinateUncertaintyInMeters", "geometry")) expect_contains( names(grid_designation( observations_sf2, diff --git a/tests/testthat/test-map_grid_designation.R b/tests/testthat/test-map_grid_designation.R index 6c686f4..d2f299d 100644 --- a/tests/testthat/test-map_grid_designation.R +++ b/tests/testthat/test-map_grid_designation.R @@ -125,7 +125,7 @@ test_that("map_grid_designation works with pipes", { expect_equal( sort(colnames(occ_cube_piped)), sort(c("species", "species_range", "grid", "seed", "occurrences", - "observations_total", "observations", "time_point", "id", "n", + "observations_total", "observations", "time_point", "cell_code", "n", "min_coord_uncertainty", "geometry"))) }) diff --git a/vignettes/articles/grid-designation-process.Rmd b/vignettes/articles/grid-designation-process.Rmd index aa93fbb..33c53c0 100644 --- a/vignettes/articles/grid-designation-process.Rmd +++ b/vignettes/articles/grid-designation-process.Rmd @@ -332,7 +332,7 @@ occurrence_cube_df <- grid_designation( seed = 123) ``` -For each grid cell (column `id`) at each time point (column `time_point`), we get the number of observations (column `n`, sampled within uncertainty circle) and the minimal coordinate uncertainty (column `min_coord_uncertainty`). +For each grid cell (column `cell_code`) at each time point (column `time_point`), we get the number of observations (column `n`, sampled within uncertainty circle) and the minimal coordinate uncertainty (column `min_coord_uncertainty`). The latter is 25 for each grid cell since each observation had the same coordinate uncertainty. ```{r} From c64187a8f30cf935448bc4b1911ccd8b5ca42396 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Tue, 10 Sep 2024 10:06:36 +0200 Subject: [PATCH 56/57] line length 80 characters --- R/grid_designation.R | 4 ++-- man/grid_designation.Rd | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/grid_designation.R b/R/grid_designation.R index fb7504d..7294830 100644 --- a/R/grid_designation.R +++ b/R/grid_designation.R @@ -11,8 +11,8 @@ #' @param grid An sf object with POLYGON geometry (usually a grid) to which #' observations should be designated. #' @param id_col The column name containing unique IDs for each grid cell. If -#' `"row_names"` (the default), a new column `cell_code` is created where the row names -#' represent the unique IDs. +#' `"row_names"` (the default), a new column `cell_code` is created where the +#' row names represent the unique IDs. #' @param seed A positive numeric value setting the seed for random number #' generation to ensure reproducibility. If `NA` (default), then `set.seed()` #' is not called at all. If not `NA`, then the random number generator state is diff --git a/man/grid_designation.Rd b/man/grid_designation.Rd index 27ead8e..5ac735f 100644 --- a/man/grid_designation.Rd +++ b/man/grid_designation.Rd @@ -25,8 +25,8 @@ observation points.} observations should be designated.} \item{id_col}{The column name containing unique IDs for each grid cell. If -\code{"row_names"} (the default), a new column \code{cell_code} is created where the row names -represent the unique IDs.} +\code{"row_names"} (the default), a new column \code{cell_code} is created where the +row names represent the unique IDs.} \item{seed}{A positive numeric value setting the seed for random number generation to ensure reproducibility. If \code{NA} (default), then \code{set.seed()} From cd906f84e1ae6b0b27eb23012ed8693649a26384 Mon Sep 17 00:00:00 2001 From: Ward Langeraert Date: Tue, 10 Sep 2024 10:06:57 +0200 Subject: [PATCH 57/57] update codemeta --- codemeta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codemeta.json b/codemeta.json index 4be69c7..3f4a34e 100644 --- a/codemeta.json +++ b/codemeta.json @@ -334,7 +334,7 @@ }, "SystemRequirements": null }, - "fileSize": "555.949KB", + "fileSize": "555.681KB", "citation": [ { "@type": "SoftwareSourceCode",