From 89ffbde1ab68742af5ec1476356bb7a591c32898 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Mon, 26 Feb 2024 17:29:52 +0000 Subject: [PATCH 01/45] minor fixes to vignette --- vignettes/BRCindicators.Rmd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vignettes/BRCindicators.Rmd b/vignettes/BRCindicators.Rmd index 2af1694..a59aeab 100644 --- a/vignettes/BRCindicators.Rmd +++ b/vignettes/BRCindicators.Rmd @@ -115,7 +115,6 @@ occ_mod_function <- function(taxa_name){ output_dir = '~/Testing_indicator_pipe', seed = 123) } - # I then run this in parallel system.time({ para_out <- sfClusterApplyLB(unique(myData$taxa), occ_mod_function) @@ -134,7 +133,8 @@ Installing the package is easy and can be done in a couple of lines ```{r eval = FALSE} library(devtools) -install_github(repo = 'biologicalrecordscentre/BRCindicators') + +install_github('biologicalrecordscentre/BRCindicators') ``` # Summarising sparta output for an indicator @@ -288,7 +288,7 @@ head(bma_indicator) We can use the plotting function in BRCindicators to plot the results of this analysis, which in this case are not all that interesting! ```{r BMAplot} -plot_indicator(indicator = bma_indicator[,'Index'], +plot_indicator(indicator = bma_indicator[,'Index.M'], CIs = bma_indicator[,c(3,4)]) ``` From 3bbeadd317b1007b4a5124e744c3298b449d4bdb Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Tue, 27 Feb 2024 13:22:10 +0000 Subject: [PATCH 02/45] Changed testthat to 3e, update to CompositeTrend.r function --- DESCRIPTION | 5 +++-- R/CompositeTrend.r | 8 ++++---- samp_post.rdata | Bin 0 -> 1441 bytes tests/testthat.R | 10 +++++++++- tests/testthat/test-CompositeTrend.R | 3 +++ 5 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 samp_post.rdata create mode 100644 tests/testthat/test-CompositeTrend.R diff --git a/DESCRIPTION b/DESCRIPTION index a30e56a..72979d7 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -30,9 +30,10 @@ BugReports: https://github.com/BiologicalRecordsCentre/BRCindicators/issues RoxygenNote: 7.1.1 Encoding: UTF-8 Suggests: - testthat, + testthat (>= 3.0.0), covr, knitr, snowfall, rmarkdown, - jagsUI, + jagsUI +Config/testthat/edition: 3 diff --git a/R/CompositeTrend.r b/R/CompositeTrend.r index ded097c..4619c7c 100644 --- a/R/CompositeTrend.r +++ b/R/CompositeTrend.r @@ -65,22 +65,22 @@ CompositeTrend <- function(indata, output_path, trend_choice = "arithmetic_logit t_table <- temp_table[,(1:(ncol(temp_table)-2))] # convert shape of the table # arithmean on the occ scale # - logit_temp_table <- t_table + logit_temp_table <- subset(t_table, select = -c(spp, iter)) logit_temp_table <- as.data.frame(car::logit(as.matrix(logit_temp_table))) # geomean on the occ scale # - log_temp_table <- t_table + log_temp_table <- subset(t_table, select = -c(spp, iter)) log_temp_table <- log(log_temp_table) # geometric mean raw occupancy # if(trend_choice == "geometric_raw_occ"){ - composite_trend_temp <- apply(t_table, 2, geomean) + composite_trend_temp <- apply(subset(t_table, select = -c(spp, iter)), 2, geomean) composite_trend <- rbind(composite_trend, composite_trend_temp) } # arithmetic mean raw occupancy # if(trend_choice == "arithmetic_raw_occ"){ - composite_trend_temp <- apply(t_table, 2, mean) + composite_trend_temp <- apply(subset(t_table, select = -c(spp, iter)), 2, mean) composite_trend <- rbind(composite_trend, composite_trend_temp) } diff --git a/samp_post.rdata b/samp_post.rdata new file mode 100644 index 0000000000000000000000000000000000000000..9ee5bfb2eef1354797f7db0896314ab7ccd2578b GIT binary patch literal 1441 zcmV;S1z!3eiwFP!0000029;HNIFxA=AGyU!YTI;qvb1XL+Q|;dEzenHJWF9+wvb$6 zY;A+VB!#l>)}xDdCHsiBgj8FyKitXxDZExuOB@)6jw7S(pk=D^?%pu5+BeSbSa|Au@CaamI=CS z1K^S(3aNVIAT(YXR@&SHy$*taJcx!4=OdBh`7eQGP`D;NC=-OKe0Bdd=b(?w{M7l? zH4s_+U`8r@2UP*?`ngsqAdbsAaXZBX#49xYkGJrFGisY=)^HD=4F+-2PkS!l=QlX! z@jNy_LoAE_$xlikI_eSQH^GE{eqH7``FE&3>_y<9pW*Gc=WBBQ22ry0kyykZTs?ZF zKlB!GBPL7R^0>gc66a{h`4&Xkm1c=a`oP+CZEWUz8nDeXU-Qv#*R*5PMvS9oNIPOj z3x5G$oWWz?GzQ?4hqRXRePCG7z4D>KdSENODSwW6l|}u!$+Ue2+U&b$4Eij9UsTmL z?e78I88v06@V`G&hB`JB6XCg2>t*v3Q}85%uN1s47sR_mQX{)mp(4$RET^;yUa0xl z(XTIsfsbN~)K^6=;ENu#RF%Ac1HBAlP0QsjsBzHsw#R-j&x9QJiCYOm!9@B&waP%i@akjJu0UJqI}6TG0*us;{dIAV0(e|VJ3v(51CNau z%KHWaf%o?P)l{s*Gb+V82IKE4oDR0BFIm7B6tF`peRjZOFuZmbc{dW?;4qHya}HX& z?DhNtx}8~uiimf=?F`Wo>&lfYvfdp22)GyCz6?S>md5Y=%&s~PT9ac=KEOOXlNlLK zm`9D(F>|dlXY6;CzMIAYxO;W)X0jyi71TESOO1>De-x_|k342V`EOImA8zwZtqc7w zh6*Y8aD6okRK%{Ca1a4Yea zEAn|D-Rg!W_AQHe_69Z88Y)u!73NQ>?C06gvbF_Fx51^&9a z4T{LuK`V`ZCf0i-sx>6}fCId+8QaD~{xm-9GS|g;MQOnv?T91PO(NRhoV5F?tZKnN ziuUGEbdVP#&YhESI2TQ&F;SBjvYaP4Zn7IXx# z+fN=odl1^< literal 0 HcmV?d00001 diff --git a/tests/testthat.R b/tests/testthat.R index 9efae38..220933d 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,4 +1,12 @@ +# This file is part of the standard setup for testthat. +# It is recommended that you do not modify it. +# +# Where should you do additional test configuration? +# Learn more about the roles of various files in: +# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview +# * https://testthat.r-lib.org/articles/special-files.html + library(testthat) library(BRCindicators) -test_check("BRCindicators") \ No newline at end of file +test_check("BRCindicators") diff --git a/tests/testthat/test-CompositeTrend.R b/tests/testthat/test-CompositeTrend.R new file mode 100644 index 0000000..8849056 --- /dev/null +++ b/tests/testthat/test-CompositeTrend.R @@ -0,0 +1,3 @@ +test_that("multiplication works", { + expect_equal(2 * 2, 4) +}) From 46b7b8386206860824057d0aee1b01260c7502fc Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 28 Feb 2024 09:56:27 +0000 Subject: [PATCH 03/45] Fixes to CompositeTrend function, removing numeric indices used for iteration and speices columns and removing misleading error message. --- .gitignore | 5 +- R/CompositeTrend.r | 134 ++++++++++++++------------- samp_post.rdata | Bin 1441 -> 1478 bytes tests/testthat/test-CompositeTrend.R | 2 +- 4 files changed, 76 insertions(+), 65 deletions(-) diff --git a/.gitignore b/.gitignore index d5a4fa2..716f34d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,7 @@ Other_files/ vignettes/BRCindicators_cache/ vignettes/BRCindicators_files/ misc/ -tests/testthat/Rplots.pdf \ No newline at end of file +tests/testthat/Rplots.pdf +test_delTestGroup_arithmetic_logit_occ_composite_trend_iterations.csv +test_delTestGroup_arithmetic_logit_occ_composite_trend_summary.csv +test_del/TestGroup_arithmetic_logit_occ_composite_trend.png diff --git a/R/CompositeTrend.r b/R/CompositeTrend.r index 4619c7c..8edd494 100644 --- a/R/CompositeTrend.r +++ b/R/CompositeTrend.r @@ -1,112 +1,123 @@ #' CompositeTrend function -#' -#' @description This function can be used to produce composite metrics of change -#' (indicators), whilst propagating uncertainty in the individual -#' species trend estimates through to the final composite trend +#' +#' @description This function can be used to produce composite metrics of change +#' (indicators), whilst propagating uncertainty in the individual +#' species trend estimates through to the final composite trend #' metric. This function takes in a dataframe of sampled annual -#' occupancy estimates across multiple species and returns a +#' occupancy estimates across multiple species and returns a #' a single composite trend metric with uncertainty. This approach #' is only suitable for species without missing years. #' #' @param indata The file path to an .rdata file containing a dataframe -#' (called samp_post - GP to change this) which contains -#' year columns (prefixed with "X", e.g "X1985"), a species -#' column ("spp"), and an iteration identifier ("iter"). The -#' year columns contain the annual occupancy estimates for +#' (called samp_post - GP to change this) which contains +#' year columns (prefixed with "X", e.g "X1985"), a species +#' column ("spp"), and an iteration identifier ("iter"). The +#' year columns contain the annual occupancy estimates for #' the species-year-iteration combination in question. #' @param output_path The location where the outputs should be saved. -#' @param trend_choice The approach used to combine the individual species -#' estimates into a single composite trend. See details. +#' @param trend_choice The approach used to combine the individual species +#' estimates into a single composite trend. See details. #' @param group_name The name of the species group we are running, used for #' naming output files. #' @param save_iterations Do we want to save the composite trend estimates -#' for each individual iteration, these are generally used for -#' estimating temporal trends with uncertainty. -#' @param TrendScale Traditionally some indicators are scaled so the first -#' year is set to a given number, 100 in the case of the UK -#' biodiversity indicators. This value can be chosen here, with no -#' scaling as the default. +#' for each individual iteration, these are generally used for +#' estimating temporal trends with uncertainty. +#' @param TrendScale Traditionally some indicators are scaled so the first +#' year is set to a given number, 100 in the case of the UK +#' biodiversity indicators. This value can be chosen here, with no +#' scaling as the default. #' @param plot_output plot the resulting composite indicator: TRUE or FALSE. # @details There are a number of model to choose from: #' \itemize{ -#' \item{\code{"arithmetic_logit_occ"}}{ - The raw occupancy values are +#' \item{\code{"arithmetic_logit_occ"}}{ - The raw occupancy values are #' converted to the log odds scale (using the logit function). The #' arithmetic mean across species is used to create a composite trend for each #' iteration. These means are then converted back to the odds scale (exp) } -#' \item{\code{"geometric_raw_occ"}}{ - Take the geometric mean across species +#' \item{\code{"geometric_raw_occ"}}{ - Take the geometric mean across species #' raw occupancy estimates } #' \item{\code{"arithmetic_raw_occ"}}{ - potentially drop this, as not used.} #' } -#' @return A summary file. This .csv is saved in the output_path location and -#' contains the annual composite indicator estimate (summarized across -#' the iterations as wither the mean or median). The unique number of +#' @return A summary file. This .csv is saved in the output_path location and +#' contains the annual composite indicator estimate (summarized across +#' the iterations as wither the mean or median). The unique number of #' species contributing to the indicator is shown in the "spp_num" column. -#' Various forms of uncertainty (estimated across the iterations) for the -#' annual composite trend are presented, including the upper and lower -#' 95% credible intervals, SD of the mean and standard error of the mean. +#' Various forms of uncertainty (estimated across the iterations) for the +#' annual composite trend are presented, including the upper and lower +#' 95% credible intervals, SD of the mean and standard error of the mean. #' @import ggplot2 #' @import reshape2 #' @import car #' @export CompositeTrend <- function(indata, output_path, trend_choice = "arithmetic_logit_occ", group_name, - save_iterations = "yes", TrendScale = NULL, plot_output = TRUE){ - + save_iterations = "yes", TrendScale = NULL, plot_output = TRUE) { load(indata) - + number_of_spp <- length(unique(as.character(samp_post$spp))) # How many species contribute to the indicator? - + # loop through iterations - later convert to array and apply across array, should be quicker # composite_trend <- NULL - for (j in 1:length(unique(samp_post$iter))){ + for (j in 1:length(unique(samp_post$iter))) { print(j) temp_table <- NULL - temp_table <- samp_post[samp_post$iter == j,] - t_table <- temp_table[,(1:(ncol(temp_table)-2))] # convert shape of the table + temp_table <- samp_post[samp_post$iter == j, ] + + t_table <- subset(temp_table, select = -c(spp, iter)) # convert shape of the table + + # Check if any column is non-numeric + non_numeric_check <- !sapply(t_table, is.numeric) + if (any(non_numeric_check)) { + non_numeric_cols <- names(t_table)[non_numeric_check] - # arithmean on the occ scale # - logit_temp_table <- subset(t_table, select = -c(spp, iter)) + # Stop the operation and show the non-numeric columns found + stop(paste0("Column(s) contains non-numeric fields: ", paste(non_numeric_cols, collapse = ", "))) + } + + # arithmean on the occ scale + # Subset the table to exclude certain columns + logit_temp_table <- t_table + + # If all columns are numeric, proceed with logit transformation logit_temp_table <- as.data.frame(car::logit(as.matrix(logit_temp_table))) - + # geomean on the occ scale # - log_temp_table <- subset(t_table, select = -c(spp, iter)) + log_temp_table <- t_table log_temp_table <- log(log_temp_table) - + # geometric mean raw occupancy # - if(trend_choice == "geometric_raw_occ"){ - composite_trend_temp <- apply(subset(t_table, select = -c(spp, iter)), 2, geomean) + if (trend_choice == "geometric_raw_occ") { + composite_trend_temp <- apply(t_table, 2, geomean) composite_trend <- rbind(composite_trend, composite_trend_temp) } - + # arithmetic mean raw occupancy # - if(trend_choice == "arithmetic_raw_occ"){ - composite_trend_temp <- apply(subset(t_table, select = -c(spp, iter)), 2, mean) + if (trend_choice == "arithmetic_raw_occ") { + composite_trend_temp <- apply(t_table, 2, mean) composite_trend <- rbind(composite_trend, composite_trend_temp) } - + # arithmetic log odds (logit) occupancy back converted to occupancy scale with inv.logit - if(trend_choice == "arithmetic_logit_occ"){ + if (trend_choice == "arithmetic_logit_occ") { composite_trend_temp <- apply(logit_temp_table, 2, mean) composite_trend <- rbind(composite_trend, composite_trend_temp) } - } - + # if the trend is based on logit, back convert to odds (rather than occupancy) following Steve Freeman's comments # - if(trend_choice == "arithmetic_logit_occ"){ + if (trend_choice == "arithmetic_logit_occ") { composite_trend <- exp(composite_trend) } - + # Are we scaling the indicator? - Scale to 100 for UK biodiversity indicators - if(!is.null(TrendScale)){ - multiplier <- TrendScale/mean(composite_trend[,1]) # identify multiplier + if (!is.null(TrendScale)) { + multiplier <- TrendScale / mean(composite_trend[, 1]) # identify multiplier composite_trend <- composite_trend * multiplier # scale logit arithmetic mean so mean value in year 1 = the input value for TrendScale # } - - if(save_iterations == "yes"){ - write.csv(composite_trend, file = paste(output_path, group_name, "_", trend_choice, "_composite_trend_iterations.csv", sep = "") , row.names = FALSE) + + if (save_iterations == "yes") { + write.csv(composite_trend, file = paste(output_path, group_name, "_", trend_choice, "_composite_trend_iterations.csv", sep = ""), row.names = FALSE) } - + # save the summarised iterations # composite_trend_summary <- data.frame( year = as.numeric(gsub("X", "", colnames(composite_trend))), @@ -117,14 +128,13 @@ CompositeTrend <- function(indata, output_path, trend_choice = "arithmetic_logit sd_occupancy = apply(composite_trend, 2, sd), sem_occupancy = apply(composite_trend, 2, sem) ) - + # add species number column # composite_trend_summary$spp_num <- number_of_spp write.csv(composite_trend_summary, file = paste(output_path, group_name, "_", trend_choice, "_composite_trend_summary.csv", sep = ""), row.names = FALSE) - - if(plot_output == TRUE){ - - ggplot(composite_trend_summary, aes_string(x = "year", y = "mean_occupancy")) + + + if (plot_output == TRUE) { + ggplot(composite_trend_summary, aes_string(x = "year", y = "mean_occupancy")) + theme_bw() + geom_ribbon(data = composite_trend_summary, aes_string(group = 1, ymin = "lower_5_perc_CI_occ", ymax = "upper_95_perc_CI_occ"), alpha = 0.2) + geom_line(size = 1, col = "black") + @@ -133,10 +143,8 @@ CompositeTrend <- function(indata, output_path, trend_choice = "arithmetic_logit ylab("Index") + xlab("Year") + scale_y_continuous(limits = c(0, max(composite_trend_summary$upper_95_perc_CI_occ))) - - ggsave(paste(group_name, "_", trend_choice, "_composite_trend.png", sep = ""), plot = last_plot(), path = output_path, width=6, height=4, units="in", dpi = 300) - + + ggsave(paste(group_name, "_", trend_choice, "_composite_trend.png", sep = ""), plot = last_plot(), path = output_path, width = 6, height = 4, units = "in", dpi = 300) } return(composite_trend_summary) } - diff --git a/samp_post.rdata b/samp_post.rdata index 9ee5bfb2eef1354797f7db0896314ab7ccd2578b..e25774ab99eefc30d25f78f7d7f05839924fa69a 100644 GIT binary patch literal 1478 zcmV;%1v&a3iwFP!000002Gv$;IMitr9~s1!)VAsJBwDq0?PQ0 zYa7>@q)^t~dUVmQWFOI%kZMbITsBK8L@pWEiI~P86Lg?ZUO!b zCNBOI70TEz#eUiQMGI|_M3a?hM2RN1&^`c>r_+LIbOw%8-p|fL+Xv9R4$>HIWNC%2 zv_el>u~k~3F9j**MfYILABuC0iD);K3NbBEp~@P$Fg4lM<;i6vR^6)68XyBtIt9=wPAzZ z%c~$X{*gk?eG6qiPTHBK2_T9}JANy{07T1Gy^l5WfjeT6M5(z8PY3+C$){ZA@w01e zvU;7@Lro-y`Pt9%AUxt6;Wgd>J^ZTFG3p;se#n);#dC(XTb!%N_y>gXW``pYe_++f z<({CMzzdluWM}b!dpXKRm-`(E(@QC_aoWJyb#-+5ToQB{rM}|hxouN6bsN!+ib3^| z9gX~Xd{Iho*A0CDK6OxSG2b1A`r21K&{+pv#Mi~o(66Gf-!>UGPXpVkeOjm682EW* z>?v<&XiupqI*I%KOc-ogpHG5kwoR9ej!(kl6u!Lw+Ds7b4onPfQ-YEtTdIuwCV0NY z-I952G4y{DnWwxmbRJ)Lzp<>~-D~J%kt!N5wLyiorkfSUqv3SmG54qyAm|%U9!}2( zZnEh1w`kuLLU)FBhbjmrCKbpbEg*_GT7IFr09qDV-n@l40v|tl7S2B_;VR{$zoMvi z!4iribZUJaKc4hGbo6Da?W@dLzz-<4F;ss51M19G{{_4`8an|D_d@gAPiX=V{Q{A{S8gV$KbT2XA`LS-*J7YG0z(2Fz9PJTo zCEavDKKCb^URTAqrIF5Drze_0NrJab37+3`?a`KH!x!f9pZ+@BXff3fPuhNprScqr zzqV?<9P)L*RHdf@?|V3`DKOsC8lIbvZtF$<)IMl4(nNcON&e2wh$GO9Bbj5KG`v$`C6ku&Cb9sgu@1D+?46)A|{r}{kMq!#9T7dtTD0pF=2?R7Jun1{DJqBrWX z%;E9U{nVi|2cWB`wr5<)1RA0fa^B*74f+OCEl;^XkE=?+6XbLM!H%yC&mNwAU6;+i zu0&6|$AOP#{Gsh~Vjbfn6{ISkpNB7PuJS3*DrFe~0dwvjWYU7>);f|YhWe7iHc7!i zQZSShC=vqY8%cpIDd)}xDdCHsiBgj8FyKitXxDZExuOB@)6jw7S(pk=D^?%pu5+BeSbSa|Au@CaamI=CS z1K^S(3aNVIAT(YXR@&SHy$*taJcx!4=OdBh`7eQGP`D;NC=-OKe0Bdd=b(?w{M7l? zH4s_+U`8r@2UP*?`ngsqAdbsAaXZBX#49xYkGJrFGisY=)^HD=4F+-2PkS!l=QlX! z@jNy_LoAE_$xlikI_eSQH^GE{eqH7``FE&3>_y<9pW*Gc=WBBQ22ry0kyykZTs?ZF zKlB!GBPL7R^0>gc66a{h`4&Xkm1c=a`oP+CZEWUz8nDeXU-Qv#*R*5PMvS9oNIPOj z3x5G$oWWz?GzQ?4hqRXRePCG7z4D>KdSENODSwW6l|}u!$+Ue2+U&b$4Eij9UsTmL z?e78I88v06@V`G&hB`JB6XCg2>t*v3Q}85%uN1s47sR_mQX{)mp(4$RET^;yUa0xl z(XTIsfsbN~)K^6=;ENu#RF%Ac1HBAlP0QsjsBzHsw#R-j&x9QJiCYOm!9@B&waP%i@akjJu0UJqI}6TG0*us;{dIAV0(e|VJ3v(51CNau z%KHWaf%o?P)l{s*Gb+V82IKE4oDR0BFIm7B6tF`peRjZOFuZmbc{dW?;4qHya}HX& z?DhNtx}8~uiimf=?F`Wo>&lfYvfdp22)GyCz6?S>md5Y=%&s~PT9ac=KEOOXlNlLK zm`9D(F>|dlXY6;CzMIAYxO;W)X0jyi71TESOO1>De-x_|k342V`EOImA8zwZtqc7w zh6*Y8aD6okRK%{Ca1a4Yea zEAn|D-Rg!W_AQHe_69Z88Y)u!73NQ>?C06gvbF_Fx51^&9a z4T{LuK`V`ZCf0i-sx>6}fCId+8QaD~{xm-9GS|g;MQOnv?T91PO(NRhoV5F?tZKnN ziuUGEbdVP#&YhESI2TQ&F;SBjvYaP4Zn7IXx# z+fN=odl1^< diff --git a/tests/testthat/test-CompositeTrend.R b/tests/testthat/test-CompositeTrend.R index 8849056..1863c58 100644 --- a/tests/testthat/test-CompositeTrend.R +++ b/tests/testthat/test-CompositeTrend.R @@ -1,3 +1,3 @@ -test_that("multiplication works", { +test_that("Returns correct error message", { expect_equal(2 * 2, 4) }) From 54c3ea84686328df9c4cb7d4fe47ef6af3c83595 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 28 Feb 2024 17:39:38 +0000 Subject: [PATCH 04/45] Test and helper file for CompositeTrend function --- DESCRIPTION | 2 +- R/CompositeTrend.r | 28 +++--- man/CompositeTrend.Rd | 43 ++++----- samp_post.rdata | Bin 1478 -> 0 bytes tests/testthat/helper-CompositeTrend.R | 128 +++++++++++++++++++++++++ tests/testthat/test-CompositeTrend.R | 123 +++++++++++++++++++++++- 6 files changed, 284 insertions(+), 40 deletions(-) delete mode 100644 samp_post.rdata create mode 100644 tests/testthat/helper-CompositeTrend.R diff --git a/DESCRIPTION b/DESCRIPTION index 72979d7..7c2aa1a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -27,7 +27,7 @@ VignetteBuilder: knitr License: GPL-3 URL: https://github.com/BiologicalRecordsCentre/BRCindicators BugReports: https://github.com/BiologicalRecordsCentre/BRCindicators/issues -RoxygenNote: 7.1.1 +RoxygenNote: 7.2.3 Encoding: UTF-8 Suggests: testthat (>= 3.0.0), diff --git a/R/CompositeTrend.r b/R/CompositeTrend.r index 8edd494..e33fb00 100644 --- a/R/CompositeTrend.r +++ b/R/CompositeTrend.r @@ -8,9 +8,8 @@ #' a single composite trend metric with uncertainty. This approach #' is only suitable for species without missing years. #' -#' @param indata The file path to an .rdata file containing a dataframe -#' (called samp_post - GP to change this) which contains -#' year columns (prefixed with "X", e.g "X1985"), a species +#' @param indata The file path to an csv file containing a dataframe +#' which contains year columns (prefixed with "X", e.g "X1985"), a species #' column ("spp"), and an iteration identifier ("iter"). The #' year columns contain the annual occupancy estimates for #' the species-year-iteration combination in question. @@ -51,18 +50,21 @@ CompositeTrend <- function(indata, output_path, trend_choice = "arithmetic_logit_occ", group_name, save_iterations = "yes", TrendScale = NULL, plot_output = TRUE) { - load(indata) + + # read in a csv file + samp_post = read.csv(indata)[, -1] number_of_spp <- length(unique(as.character(samp_post$spp))) # How many species contribute to the indicator? # loop through iterations - later convert to array and apply across array, should be quicker # composite_trend <- NULL - for (j in 1:length(unique(samp_post$iter))) { - print(j) + for (i in 1:length(unique(samp_post$iter))) { + print(paste0("Running iteration: ", unique(samp_post$iter)[i])) + temp_table <- NULL - temp_table <- samp_post[samp_post$iter == j, ] + temp_table <- samp_post[samp_post$iter == i,] - t_table <- subset(temp_table, select = -c(spp, iter)) # convert shape of the table + t_table <- subset(temp_table, select = -c(spp, iter)) # Check if any column is non-numeric non_numeric_check <- !sapply(t_table, is.numeric) @@ -74,17 +76,13 @@ CompositeTrend <- function(indata, output_path, trend_choice = "arithmetic_logit } # arithmean on the occ scale - # Subset the table to exclude certain columns - logit_temp_table <- t_table - - # If all columns are numeric, proceed with logit transformation - logit_temp_table <- as.data.frame(car::logit(as.matrix(logit_temp_table))) + logit_temp_table <- as.data.frame(car::logit(as.matrix(t_table))) - # geomean on the occ scale # + # geomean on the occ scale log_temp_table <- t_table log_temp_table <- log(log_temp_table) - # geometric mean raw occupancy # + # geometric mean raw occupancy if (trend_choice == "geometric_raw_occ") { composite_trend_temp <- apply(t_table, 2, geomean) composite_trend <- rbind(composite_trend, composite_trend_temp) diff --git a/man/CompositeTrend.Rd b/man/CompositeTrend.Rd index 22402b9..813c9af 100644 --- a/man/CompositeTrend.Rd +++ b/man/CompositeTrend.Rd @@ -15,56 +15,55 @@ CompositeTrend( ) } \arguments{ -\item{indata}{The file path to an .rdata file containing a dataframe -(called samp_post - GP to change this) which contains -year columns (prefixed with "X", e.g "X1985"), a species -column ("spp"), and an iteration identifier ("iter"). The -year columns contain the annual occupancy estimates for - the species-year-iteration combination in question.} +\item{indata}{The file path to an csv file containing a dataframe +which contains year columns (prefixed with "X", e.g "X1985"), a species +column ("spp"), and an iteration identifier ("iter"). The +year columns contain the annual occupancy estimates for +the species-year-iteration combination in question.} \item{output_path}{The location where the outputs should be saved.} -\item{trend_choice}{The approach used to combine the individual species +\item{trend_choice}{The approach used to combine the individual species estimates into a single composite trend. See details.} \item{group_name}{The name of the species group we are running, used for naming output files.} \item{save_iterations}{Do we want to save the composite trend estimates -for each individual iteration, these are generally used for +for each individual iteration, these are generally used for estimating temporal trends with uncertainty.} -\item{TrendScale}{Traditionally some indicators are scaled so the first -year is set to a given number, 100 in the case of the UK +\item{TrendScale}{Traditionally some indicators are scaled so the first +year is set to a given number, 100 in the case of the UK biodiversity indicators. This value can be chosen here, with no scaling as the default.} \item{plot_output}{plot the resulting composite indicator: TRUE or FALSE. \itemize{ - \item{\code{"arithmetic_logit_occ"}}{ - The raw occupancy values are + \item{\code{"arithmetic_logit_occ"}}{ - The raw occupancy values are converted to the log odds scale (using the logit function). The arithmetic mean across species is used to create a composite trend for each iteration. These means are then converted back to the odds scale (exp) } - \item{\code{"geometric_raw_occ"}}{ - Take the geometric mean across species + \item{\code{"geometric_raw_occ"}}{ - Take the geometric mean across species raw occupancy estimates } \item{\code{"arithmetic_raw_occ"}}{ - potentially drop this, as not used.} }} } \value{ -A summary file. This .csv is saved in the output_path location and - contains the annual composite indicator estimate (summarized across - the iterations as wither the mean or median). The unique number of +A summary file. This .csv is saved in the output_path location and + contains the annual composite indicator estimate (summarized across + the iterations as wither the mean or median). The unique number of species contributing to the indicator is shown in the "spp_num" column. - Various forms of uncertainty (estimated across the iterations) for the - annual composite trend are presented, including the upper and lower - 95% credible intervals, SD of the mean and standard error of the mean. + Various forms of uncertainty (estimated across the iterations) for the + annual composite trend are presented, including the upper and lower + 95% credible intervals, SD of the mean and standard error of the mean. } \description{ -This function can be used to produce composite metrics of change -(indicators), whilst propagating uncertainty in the individual -species trend estimates through to the final composite trend +This function can be used to produce composite metrics of change +(indicators), whilst propagating uncertainty in the individual +species trend estimates through to the final composite trend metric. This function takes in a dataframe of sampled annual -occupancy estimates across multiple species and returns a +occupancy estimates across multiple species and returns a a single composite trend metric with uncertainty. This approach is only suitable for species without missing years. } diff --git a/samp_post.rdata b/samp_post.rdata deleted file mode 100644 index e25774ab99eefc30d25f78f7d7f05839924fa69a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1478 zcmV;%1v&a3iwFP!000002Gv$;IMitr9~s1!)VAsJBwDq0?PQ0 zYa7>@q)^t~dUVmQWFOI%kZMbITsBK8L@pWEiI~P86Lg?ZUO!b zCNBOI70TEz#eUiQMGI|_M3a?hM2RN1&^`c>r_+LIbOw%8-p|fL+Xv9R4$>HIWNC%2 zv_el>u~k~3F9j**MfYILABuC0iD);K3NbBEp~@P$Fg4lM<;i6vR^6)68XyBtIt9=wPAzZ z%c~$X{*gk?eG6qiPTHBK2_T9}JANy{07T1Gy^l5WfjeT6M5(z8PY3+C$){ZA@w01e zvU;7@Lro-y`Pt9%AUxt6;Wgd>J^ZTFG3p;se#n);#dC(XTb!%N_y>gXW``pYe_++f z<({CMzzdluWM}b!dpXKRm-`(E(@QC_aoWJyb#-+5ToQB{rM}|hxouN6bsN!+ib3^| z9gX~Xd{Iho*A0CDK6OxSG2b1A`r21K&{+pv#Mi~o(66Gf-!>UGPXpVkeOjm682EW* z>?v<&XiupqI*I%KOc-ogpHG5kwoR9ej!(kl6u!Lw+Ds7b4onPfQ-YEtTdIuwCV0NY z-I952G4y{DnWwxmbRJ)Lzp<>~-D~J%kt!N5wLyiorkfSUqv3SmG54qyAm|%U9!}2( zZnEh1w`kuLLU)FBhbjmrCKbpbEg*_GT7IFr09qDV-n@l40v|tl7S2B_;VR{$zoMvi z!4iribZUJaKc4hGbo6Da?W@dLzz-<4F;ss51M19G{{_4`8an|D_d@gAPiX=V{Q{A{S8gV$KbT2XA`LS-*J7YG0z(2Fz9PJTo zCEavDKKCb^URTAqrIF5Drze_0NrJab37+3`?a`KH!x!f9pZ+@BXff3fPuhNprScqr zzqV?<9P)L*RHdf@?|V3`DKOsC8lIbvZtF$<)IMl4(nNcON&e2wh$GO9Bbj5KG`v$`C6ku&Cb9sgu@1D+?46)A|{r}{kMq!#9T7dtTD0pF=2?R7Jun1{DJqBrWX z%;E9U{nVi|2cWB`wr5<)1RA0fa^B*74f+OCEl;^XkE=?+6XbLM!H%yC&mNwAU6;+i zu0&6|$AOP#{Gsh~Vjbfn6{ISkpNB7PuJS3*DrFe~0dwvjWYU7>);f|YhWe7iHc7!i zQZSShC=vqY8%cpIDd Date: Wed, 13 Mar 2024 16:15:32 +0000 Subject: [PATCH 05/45] New tests and helper functions for functions bma(), CompositeTrend(), and bootstrap_indicator(), and small changes to functions --- DESCRIPTION | 3 +- NAMESPACE | 1 + R/BRCindicators-package.R | 7 + R/bayesian_meta_analysis.R | 462 ++++++++---------- R/bootstrap_indicator.R | 36 +- R/scale_indicator.R | 59 +++ .../testthat/helper-bayesian_meta_analysis.R | 226 +++++++++ tests/testthat/helper-bootstrap_indicator.R | 56 +++ tests/testthat/test-CompositeTrend.R | 4 +- tests/testthat/test-bayesian_meta_analysis.R | 49 ++ tests/testthat/test-bootstrap_indicator.R | 54 ++ tests/testthat/testbma.R | 203 -------- 12 files changed, 686 insertions(+), 474 deletions(-) create mode 100644 R/BRCindicators-package.R create mode 100644 R/scale_indicator.R create mode 100644 tests/testthat/helper-bayesian_meta_analysis.R create mode 100644 tests/testthat/helper-bootstrap_indicator.R create mode 100644 tests/testthat/test-bayesian_meta_analysis.R create mode 100644 tests/testthat/test-bootstrap_indicator.R delete mode 100644 tests/testthat/testbma.R diff --git a/DESCRIPTION b/DESCRIPTION index 7c2aa1a..1830419 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -22,7 +22,8 @@ Imports: car, RColorBrewer, mgcv, - boot + boot, + mockery VignetteBuilder: knitr License: GPL-3 URL: https://github.com/BiologicalRecordsCentre/BRCindicators diff --git a/NAMESPACE b/NAMESPACE index b96470d..ac4a7c8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -29,4 +29,5 @@ importFrom(boot,inv.logit) importFrom(car,logit) importFrom(coda,as.mcmc) importFrom(coda,mcmc.list) +importFrom(mockery,stub) importFrom(reshape2,dcast) diff --git a/R/BRCindicators-package.R b/R/BRCindicators-package.R new file mode 100644 index 0000000..5a5adce --- /dev/null +++ b/R/BRCindicators-package.R @@ -0,0 +1,7 @@ +#' @keywords internal +"_PACKAGE" + +## usethis namespace: start +#' @importFrom mockery stub +## usethis namespace: end +NULL diff --git a/R/bayesian_meta_analysis.R b/R/bayesian_meta_analysis.R index 9e1ab59..d6c8b70 100644 --- a/R/bayesian_meta_analysis.R +++ b/R/bayesian_meta_analysis.R @@ -1,7 +1,7 @@ #' Bayesian Meta-analysis #' #' @description Use a Bayesian meta-analysis to create an indicator from species index values, optionally incorporating standard error. -#' @param data a data.frame with 3-4 columns in this order: `species`, `year`, `index`, `se` (standard error). The `se` column is optional +#' @param data a data.frame with 3-4 columns: `species`, `year`, `index`, `se` (standard error). The `se` column is optional #' NB: Index values are assumed to be on the unbounded (logarithmic scale) #' @param plot Logical, should a trace plot be plotted to diagnose the model output? #' @param model The type of model to be used. See details. @@ -58,262 +58,214 @@ bma <- function (data, - plot = TRUE, - model = 'smooth', - parallel = FALSE, - n.cores = parallel::detectCores()-1, - incl.model = TRUE, - n.iter = 1e4, - n.thin = 5, - m.scale = 'loge', - num.knots = 12, - seFromData = FALSE, - Y1perfect = TRUE, - rescale_indices = NULL, - rescaleYr = 1, - baseline = 100, - errorY1 = FALSE, - save.sppars = TRUE, - incl.2deriv = FALSE, - CI = 95, - seed = NULL){ - - set.seed(seed = seed) - - # Check if jagsUI is installed - if (!requireNamespace("jagsUI", quietly = TRUE)) { - stop("Package 'jagsUI' is needed for the 'bma' function to work. Please insatll this from CRAN. You will also be required to install JAGS, which you can download from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", - call. = FALSE) - } - - if (!identical(colnames(data)[1:3], c("species", "year", "index"))) { - stop('data column names should be: "species", "year", "index"') - } - - if(colnames(data)[4] != "se" | ncol(data) < 4) {# add a set of NAs - data$se <- NA - if(seFromData) { - stop("Error: Standard errors have not been provided") - } - } - - switch(tolower(model), - smooth = {}, # the default - smooth_jabes = {# this is the version SF ran for the paper - model = "smooth" - seFromData = FALSE - Y1perfect = TRUE}, - smooth_det2 = {# this is the version NI tested for ISEC - model = "smooth" - seFromData = TRUE - Y1perfect = FALSE}, - smooth_det_sigtheta = {# this is the version NI ran for Scottish indicators - model = "smooth" - seFromData=FALSE - Y1perfect = FALSE}, - smooth_det = stop("smooth_det model has been deprecated"), - random_walk = stop("Random walk model has been deprecated"), - uniform = stop("Uniform model has been deprecated"), - uniform_noeta = stop("Uniform model has been deprecated"), - fngr = stop("This model option has been deprecated"), - smooth_stoch = stop("This model option has been deprecated"), - fngr2 = stop("This model option has been deprecated"), - smooth_stoch2 = stop("This model option has been deprecated"), - stop(paste("Model type not known. Check the help file for details")) - ) - - # do a quick check for whether the index values have been transformed - if(min(data$index, na.rm = T) >= 0) - print("Warning: No negative index values detected. Are you sure you transformed the data?") - - # check whether the data contain any infinite values - if(any(is.infinite(data$index))) - stop('Dataset contains Infinite values. Fix this before proceeding') - - # This is not my preferrred behaviour - if(!m.scale %in% c('loge', 'log10', 'logit')) stop("m.scale must be 'loge', 'log10', or 'logit'") - - # pick the correct model - model_code <- get_bmaBUGScode(option = model, - incl.2deriv=incl.2deriv, - seFromData = seFromData, - Y1perfect = Y1perfect) - - # save it to a temp file - bugs_path <- tempfile() - writeLines(text = model_code, con = bugs_path) - - # include an option here to standardise the data to some value in year 1 - - # we assume that the index values are already on the unbounded (log) scale (we checked for negative values above) - index <- acast(data, species ~ year, value.var = "index") - - if(!is.null(rescale_indices)){ - # the user has specified that each species' time series should be scaled to start at a common value. - # since the data are on the unbounded (log or logit) scale, we standardise them by addition/subtraction, rather than multiplication (as in rescale_species()) - # recall that rescale_indices has to be an integer - # without mising data this would be easy, but we have to identify the first year in each species' timeseries - index <- t(apply(index, 1, function(x) rescale_indices + x - x[min(which(!is.na(x)))])) - } - - - # Setup BUGS data - bugs_data <- list(nsp = nrow(index), - nyears = ncol(index), - estimate = index - ) - + plot = TRUE, + model = 'smooth', + parallel = FALSE, + n.cores = parallel::detectCores()-1, + incl.model = TRUE, + n.iter = 1e4, + n.thin = 5, + m.scale = 'loge', + num.knots = 12, + seFromData = FALSE, + Y1perfect = TRUE, + rescale_indices = NULL, + rescaleYr = 1, + baseline = 100, + errorY1 = FALSE, + save.sppars = TRUE, + incl.2deriv = FALSE, + CI = 95, + seed = NULL){ + +set.seed(seed = seed) + +# Check if jagsUI is installed +if (!requireNamespace("jagsUI", quietly = TRUE)) { +stop("Package 'jagsUI' is needed for the 'bma' function to work. Please insatll this from CRAN. You will also be required to install JAGS, which you can download from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", + call. = FALSE) +} + +# Check to see that column names are present +if (!all(c("species", "year", "index") %in% colnames(data))){ + +stop('data column names should include "species", "year" and "index"') + +} + +if (!("se" %in% colnames(data))){ + if(seFromData) { - se <- acast(data, species ~ year, value.var = "se") - bugs_data$sigma.obs <- se - bugs_data$max_se = ifelse(test = all(is.na(se)), - yes = 10, - no = max(se, na.rm = TRUE)) - } - - if(model %in% c('smooth', 'smooth_stoch', 'smooth_det', 'smooth_stoch2')){ - ZX <- makeZX(num.knots = num.knots, - covariate = seq(min(data$year), - max(data$year))) - bugs_data[['Z']] <- ZX[['Z']] - bugs_data[['X']] <- ZX[['X']] - bugs_data[['num.knots']] <- num.knots - } - - - if(model %in% c('smooth', 'smooth_stoch2', 'FNgr2')){ - # using row.names should ensure the same order in the bugs data - #FY <- sapply(row.names(index), FUN = function(x){ - # min(data$year[!is.na(data$index) & data$species == x]) - #}) - #bugs_data[['FY']] <- FY - min(FY) + 1 # set lowest value to 1 - bugs_data[['FY']] <- apply(index, 1, function(x) min(which(!is.na(x)))) # simpler alternative - } - - # Setup parameters to monitor. NB Most of the model options have been deprecated, so much of this code is redundant - params = c("tau.spi") - if(model == 'smooth') params <- c(params, "Mprime") - if(model %in% c('smooth', 'smooth_stoch', 'smooth_det', 'FNgr', - 'smooth_stoch2', 'FNgr2')) params <- c(params, "logLambda", "spgrowth", "M") - if(model %in% c('smooth_stoch', 'smooth_det', 'FNgr')) params <- c(params, "tau.sg") - if(model %in% c('smooth', 'smooth_stoch', 'smooth_det','smooth_stoch2')) params <- c(params, "beta", "taub") - if(incl.2deriv) params <- c(params, "t2dash") - if(!seFromData) params <- c(params, "theta") - if(save.sppars) { - params <- c(params, "spindex") - } else { - params <- params[!params %in% c("spgrowth", "sigma.obs")] - } - if(incl.2deriv) params <- c(params, "t2dash") - - set.seed(seed = seed) - model.out <- jagsUI::jags(data = bugs_data, - inits = NULL, - param = params, - parallel = parallel, - n.cores = n.cores, - model.file = bugs_path, - store.data = TRUE, - n.chains = 3, - n.thin = n.thin, - n.iter = n.iter, - n.burnin = floor(n.iter/2)) - - if (plot==TRUE) { - array_sim <- model.out$samples - comb.samples <- mcmc.list(lapply(1:3, FUN = function(x, - array_sim) { - year_ests <- colnames(array_sim[[x]])[grepl("^Mprime\\[", # changed from I - colnames(array_sim[[x]]))] - ar_temp <- array_sim[[x]][,c(head(year_ests, 1), tail(year_ests,1))] - colnames(ar_temp) <- c("First year", "Last year") - as.mcmc(ar_temp) - }, array_sim = array_sim)) - plot(comb.samples) - } - - - MSI1 <- MSI2 <- NULL - if("Mprime" %in% params) { - MSI1 <- scale_indicator(logI = model.out$sims.list$Mprime, FirstYr = min(data$year), - CI=CI, m.scale=m.scale, - rescaleYr=rescaleYr, errorY1=errorY1, baseline=baseline) - } - if("M" %in% params) { - MSI2 <- scale_indicator(logI = model.out$sims.list$M, FirstYr = min(data$year), - CI=CI, m.scale=m.scale, - rescaleYr=rescaleYr, errorY1=errorY1, baseline=baseline) - } - - MSI <- merge(MSI1, MSI2, by="Year") # this will fail for smooth_det - - # rename the output columns - names(MSI) <- gsub(names(MSI), pattern="x", replacement = "Mprime") - names(MSI) <- gsub(names(MSI), pattern="y", replacement = "M") - names(MSI) <- gsub(names(MSI), pattern="mean", replacement = "Index") - - if(incl.model) attr(MSI, 'model') <- model.out - - return(MSI) + stop("Error: Standard errors have not been provided") +} + +data$se <- NA +} + +# reconfigure function parameters based on model choice +# Print statements when parameters are reconfigured +switch(gsub(" ", "_", tolower(model)), + smooth = {}, # the default + + smooth_jabes = {# this is the version SF ran for the paper + cat("'smooth_jabes' model chosen. The following parameters have been set:", "model = 'smooth'", "seFromData = FALSE", "Y1perfect = TRUE", sep = "\n") + model = "smooth" + seFromData = FALSE + Y1perfect = TRUE}, + + smooth_det2 = {# this is the version NI tested for ISEC + cat("'smooth_det2' model chosen. The following parameters have been set:", "model = 'smooth'", "seFromData = TRUE", "Y1perfect = FALSE", sep = "\n") + model = "smooth" + seFromData = TRUE + Y1perfect = FALSE}, + + smooth_det_sigtheta = {# this is the version NI ran for Scottish indicators + cat("'smooth_det_sigtheta' model chosen. The following parameters have been set:", "model = 'smooth'", "seFromData = FALSE", "Y1perfect = FALSE", sep = "\n") + model = "smooth" + seFromData=FALSE + Y1perfect = FALSE}, + + smooth_det = stop("smooth_det model has been deprecated"), + random_walk = stop("Random walk model has been deprecated"), + uniform = stop("Uniform model has been deprecated"), + uniform_noeta = stop("Uniform model has been deprecated"), + fngr = stop("This model option has been deprecated"), + smooth_stoch = stop("This model option has been deprecated"), + fngr2 = stop("This model option has been deprecated"), + smooth_stoch2 = stop("This model option has been deprecated"), + stop(paste("Model type not known. Check the help file for details")) + ) + +# do a quick check for whether the index values have been transformed +if(min(data$index, na.rm = T) >= 0){ + print("Warning: No negative index values detected. Are you sure you transformed the data?")} + +# check whether the data contain any infinite values +if(any(is.infinite(data$index))){ + stop('Dataset contains Infinite values. Fix this before proceeding')} + +# This is not my preferrred behaviour +if(!m.scale %in% c('loge', 'log10', 'logit')){ + stop("m.scale must be 'loge', 'log10', or 'logit'")} + +# pick the correct model +model_code <- get_bmaBUGScode(option = model, + incl.2deriv=incl.2deriv, + seFromData = seFromData, + Y1perfect = Y1perfect) + +# save it to a temp file +bugs_path <- tempfile() +writeLines(text = model_code, con = bugs_path) + +# include an option here to standardise the data to some value in year 1 + +# we assume that the index values are already on the unbounded (log) scale (we checked for negative values above) +index <- acast(data, species ~ year, value.var = "index") + +if(!is.null(rescale_indices)){ +# the user has specified that each species' time series should be scaled to start at a common value. +# since the data are on the unbounded (log or logit) scale, we standardise them by addition/subtraction, rather than multiplication (as in rescale_species()) +# recall that rescale_indices has to be an integer +# without mising data this would be easy, but we have to identify the first year in each species' timeseries +index <- t(apply(index, 1, function(x) rescale_indices + x - x[min(which(!is.na(x)))])) +} + +# Setup BUGS data +bugs_data <- list(nsp = nrow(index), + nyears = ncol(index), + estimate = index + ) + +if(seFromData) { +se <- acast(data, species ~ year, value.var = "se") +bugs_data$sigma.obs <- se +bugs_data$max_se = ifelse(test = all(is.na(se)), + yes = 10, + no = max(se, na.rm = TRUE)) +} + +if(model %in% c('smooth', 'smooth_stoch', 'smooth_det', 'smooth_stoch2')){ +ZX <- makeZX(num.knots = num.knots, + covariate = seq(min(data$year), + max(data$year))) +bugs_data[['Z']] <- ZX[['Z']] +bugs_data[['X']] <- ZX[['X']] +bugs_data[['num.knots']] <- num.knots +} + +if(model %in% c('smooth', 'smooth_stoch2', 'FNgr2')){ +# using row.names should ensure the same order in the bugs data +#FY <- sapply(row.names(index), FUN = function(x){ +# min(data$year[!is.na(data$index) & data$species == x]) +#}) +#bugs_data[['FY']] <- FY - min(FY) + 1 # set lowest value to 1 +bugs_data[['FY']] <- apply(index, 1, function(x) min(which(!is.na(x)))) # simpler alternative +} + +# Setup parameters to monitor. NB Most of the model options have been deprecated, so much of this code is redundant +params = c("tau.spi") +if(model == 'smooth'){ params <- c(params, "Mprime")} +if(model %in% c('smooth', 'smooth_stoch', 'smooth_det', 'FNgr', 'smooth_stoch2', 'FNgr2')){ + params <- c(params, "logLambda", "spgrowth", "M")} +if(model %in% c('smooth_stoch', 'smooth_det', 'FNgr')){params <- c(params, "tau.sg")} +if(model %in% c('smooth', 'smooth_stoch', 'smooth_det','smooth_stoch2')){params <- c(params, "beta", "taub")} +if(incl.2deriv){ params <- c(params, "t2dash")} +if(!seFromData){ params <- c(params, "theta")} +if(save.sppars) { +params <- c(params, "spindex") +} else { +params <- params[!params %in% c("spgrowth", "sigma.obs")] } +if(incl.2deriv){ params <- c(params, "t2dash")} +set.seed(seed = seed) +model.out <- jagsUI::jags(data = bugs_data, + inits = NULL, + param = params, + parallel = parallel, + n.cores = n.cores, + model.file = bugs_path, + store.data = TRUE, + n.chains = 3, + n.thin = n.thin, + n.iter = n.iter, + n.burnin = floor(n.iter/2)) + +if (plot==TRUE) { +array_sim <- model.out$samples +comb.samples <- mcmc.list(lapply(1:3, FUN = function(x, + array_sim) { + year_ests <- colnames(array_sim[[x]])[grepl("^Mprime\\[", # changed from I + colnames(array_sim[[x]]))] + ar_temp <- array_sim[[x]][,c(head(year_ests, 1), tail(year_ests,1))] + colnames(ar_temp) <- c("First year", "Last year") + as.mcmc(ar_temp) +}, array_sim = array_sim)) + +plot(comb.samples) +} -scale_indicator <- function(logI, - FirstYr, - CI = 95, - m.scale = "loge", - rescaleYr = 1, - errorY1 = FALSE, - baseline = 100){ # SCALING FUNCTION - - # first check the value of rescale year - # if its a large value fix to the final year. - if(rescaleYr > nrow(logI)) rescaleYr <- ncol(logI) - if(rescaleYr < 1) stop('rescaleYr must be an integer of 1 or higher') - - # which is the baseline year? rescale year = 0 indicates no scaling - # calculate the difference of each index value from the baseline year (default is year 1) - # this makes the baseline equal to zero, so it will be exactly one after back transformation - if(errorY1){ # we retain error in the first year - logI_rescaled <- t(apply(logI, 1, function(x) x - mean(logI[,rescaleYr]))) - } else { # scale every iteration of the posterior to have the same value in the reference year - logI_rescaled <- t(apply(logI, 1, function(x) x - x[rescaleYr])) - } - - # convert the CI into quantiles - # first check that CI is sensible - if((CI > 100) | (CI <= 0)) stop("Credible intervals must be between 0 and 100") - CI2q <- function(CI) { - q <- (1 - CI/100)/2 - return(c(q, 1-q)) - } - q <- CI2q(CI) - - # summarise quantiles of the posterior distribution - pd <- data.frame(mean = apply(logI_rescaled, 2, mean), - lowerCI = apply(logI_rescaled, 2, quantile, probs = q[1]), - upperCI = apply(logI_rescaled, 2, quantile, probs = q[2]), - row.names = paste0('Mprime', 1:ncol(logI_rescaled))) - if(q[1] == 0.025 & q[2] == 0.975) names(pd)[2:3] <- c("q2.5", "q97.5") - - # convert the logI back to the measurement scale - unb2b <- function(x, m.scale){ - switch(m.scale, - loge = x <- exp(x), - log10 = x <- 10^x, - logit = x <- exp(x), # Counter-intuitively, since we want geometric mean odds - #logit = {x <- boot::inv.logit(as.matrix(x))}, # this would give occupancy, which is hard to interpret - warning(paste(m.scale, 'unknown, no back-transformation applied'))) - return(x) - } - # pd <- unb2b(pd[grepl("^Mprime[[:digit:]]+",dimnames(pd)[[1]]),], m.scale) # PROBLEM HERE - pd <- unb2b(pd, m.scale) - # the indicator metric is now back on the measurement scale - - # rescale to baseline value in the first year - pd <- pd * baseline - - pd$Year <- as.numeric(gsub("[A-z]", repl="", dimnames(pd)[[1]])) - pd$Year <- as.numeric(pd$Year) + FirstYr - 1 - return(pd) +MSI1 <- MSI2 <- NULL +if("Mprime" %in% params) { +MSI1 <- scale_indicator(logI = model.out$sims.list$Mprime, FirstYr = min(data$year), + CI=CI, m.scale=m.scale, + rescaleYr=rescaleYr, errorY1=errorY1, baseline=baseline) } +if("M" %in% params) { +MSI2 <- scale_indicator(logI = model.out$sims.list$M, FirstYr = min(data$year), + CI=CI, m.scale=m.scale, + rescaleYr=rescaleYr, errorY1=errorY1, baseline=baseline) +} + +MSI <- merge(MSI1, MSI2, by="Year") # this will fail for smooth_det + +# rename the output columns +names(MSI) <- gsub(names(MSI), pattern="x", replacement = "Mprime") +names(MSI) <- gsub(names(MSI), pattern="y", replacement = "M") +names(MSI) <- gsub(names(MSI), pattern="mean", replacement = "Index") + +if(incl.model) attr(MSI, 'model') <- model.out + +return(MSI) +} \ No newline at end of file diff --git a/R/bootstrap_indicator.R b/R/bootstrap_indicator.R index afe4938..8b8a57b 100644 --- a/R/bootstrap_indicator.R +++ b/R/bootstrap_indicator.R @@ -16,25 +16,36 @@ bootstrap_indicator <- function(Data, iterations = 10000, CI_limits = c(0.025, 0.975), verbose = TRUE){ - geomean <- function(x) exp(mean(log(x), na.rm = T)) + + if(!is.matrix(Data)){ + stop("the Data parameter must be a matrix object.") + } + + if(!all(is.numeric(Data))){ + stop("Matrix values must all be numeric.") + } ### bootstrap to estimate 95% CI around the geometric mean ### nSpecies <- ncol(Data) bootstrap_values <- matrix(data = NA, nrow = nrow(Data), ncol = iterations) bootstrap_data <- as.data.frame(Data) - BS <- function(x){ # function to do bootstrapping, x is data - - # Randomly sample species - samp <- sample(x, size = ncol(x), replace = TRUE) - gm <- apply(samp, 1, geomean) - - } + pb <- txtProgressBar(min = 0, max = iterations, style = 3) # Run bootstrapping - if(verbose) cat('Running bootstrapping for', iterations, 'iterations...') - CIData <- replicate(n = iterations, BS(bootstrap_data)) - if(verbose) cat('done\n') + CIData <- sapply(1:iterations, simplify = TRUE, function(iteration) { + + if(verbose){setTxtProgressBar(pb, iteration)} + + # Randomly sample species + samp <- sample(bootstrap_data, size = ncol(bootstrap_data), replace = TRUE) + + apply(samp, 1, function(x){ + exp(mean(log(x), na.rm = T)) + }) +}) + + close(pb) # Extract the 2.5 97.5% CI around the geometric mean (from the bootstrapped resamples) CIs <- as.data.frame(t(apply(X = CIData, MARGIN = 1, FUN = quantile, @@ -43,5 +54,4 @@ bootstrap_indicator <- function(Data, iterations = 10000, CI_limits = c(0.025, paste('quant_', gsub('0\\.', '', as.character(CI_limits[2])), sep = '')) return(as.matrix(CIs)) -} - \ No newline at end of file +} \ No newline at end of file diff --git a/R/scale_indicator.R b/R/scale_indicator.R new file mode 100644 index 0000000..a0344b1 --- /dev/null +++ b/R/scale_indicator.R @@ -0,0 +1,59 @@ +scale_indicator <- function(logI, + FirstYr, + CI = 95, + m.scale = "loge", + rescaleYr = 1, + errorY1 = FALSE, + baseline = 100){ # SCALING FUNCTION + +# first check the value of rescale year +# if its a large value fix to the final year. +if(rescaleYr > nrow(logI)) rescaleYr <- ncol(logI) +if(rescaleYr < 1) stop('rescaleYr must be an integer of 1 or higher') + +# which is the baseline year? rescale year = 0 indicates no scaling +# calculate the difference of each index value from the baseline year (default is year 1) +# this makes the baseline equal to zero, so it will be exactly one after back transformation +if(errorY1){ # we retain error in the first year +logI_rescaled <- t(apply(logI, 1, function(x) x - mean(logI[,rescaleYr]))) +} else { # scale every iteration of the posterior to have the same value in the reference year +logI_rescaled <- t(apply(logI, 1, function(x) x - x[rescaleYr])) +} + +# convert the CI into quantiles +# first check that CI is sensible +if((CI > 100) | (CI <= 0)) stop("Credible intervals must be between 0 and 100") +CI2q <- function(CI) { +q <- (1 - CI/100)/2 +return(c(q, 1-q)) +} +q <- CI2q(CI) + +# summarise quantiles of the posterior distribution +pd <- data.frame(mean = apply(logI_rescaled, 2, mean), + lowerCI = apply(logI_rescaled, 2, quantile, probs = q[1]), + upperCI = apply(logI_rescaled, 2, quantile, probs = q[2]), + row.names = paste0('Mprime', 1:ncol(logI_rescaled))) +if(q[1] == 0.025 & q[2] == 0.975) names(pd)[2:3] <- c("q2.5", "q97.5") + +# convert the logI back to the measurement scale +unb2b <- function(x, m.scale){ +switch(m.scale, + loge = x <- exp(x), + log10 = x <- 10^x, + logit = x <- exp(x), # Counter-intuitively, since we want geometric mean odds + #logit = {x <- boot::inv.logit(as.matrix(x))}, # this would give occupancy, which is hard to interpret + warning(paste(m.scale, 'unknown, no back-transformation applied'))) +return(x) +} +# pd <- unb2b(pd[grepl("^Mprime[[:digit:]]+",dimnames(pd)[[1]]),], m.scale) # PROBLEM HERE +pd <- unb2b(pd, m.scale) +# the indicator metric is now back on the measurement scale + +# rescale to baseline value in the first year +pd <- pd * baseline + +pd$Year <- as.numeric(gsub("[A-z]", repl="", dimnames(pd)[[1]])) +pd$Year <- as.numeric(pd$Year) + FirstYr - 1 +return(pd) +} \ No newline at end of file diff --git a/tests/testthat/helper-bayesian_meta_analysis.R b/tests/testthat/helper-bayesian_meta_analysis.R new file mode 100644 index 0000000..5575d71 --- /dev/null +++ b/tests/testthat/helper-bayesian_meta_analysis.R @@ -0,0 +1,226 @@ +# Fake data taken from vignette +bayesian_meta_analysis_fake_data <- data.frame(species = rep(letters, each = 50), + year = rep(1:50, length(letters)), + index = runif(n = 50 * length(letters), min = 0, max = 1), + se = runif(n = 50 * length(letters), min = 0.01, max = .1)) + + +bma_objects <- function (data, + plot = TRUE, + model = 'smooth', + parallel = FALSE, + n.cores = parallel::detectCores()-1, + incl.model = TRUE, + n.iter = 1e4, + n.thin = 5, + m.scale = 'loge', + num.knots = 12, + seFromData = FALSE, + Y1perfect = TRUE, + rescale_indices = NULL, + rescaleYr = 1, + baseline = 100, + errorY1 = FALSE, + save.sppars = TRUE, + incl.2deriv = FALSE, + CI = 95, + seed = NULL){ + +# To capture objects +output = list() + +set.seed(seed = seed) + +# Check if jagsUI is installed +if (!requireNamespace("jagsUI", quietly = TRUE)) { +stop("Package 'jagsUI' is needed for the 'bma' function to work. Please insatll this from CRAN. You will also be required to install JAGS, which you can download from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", + call. = FALSE) +} + +# Check to see that column names are present +if (!all(c("species", "year", "index") %in% colnames(data))){ + +stop('data column names should include "species", "year" and "index"') + +} + +if (!("se" %in% colnames(data))){ + + if(seFromData) { + stop("Error: Standard errors have not been provided") +} + +data$se <- NA +} + +# reconfigure function parameters based on model choice +# Print statements when parameters are reconfigured +switch(gsub(" ", "_", tolower(model)), + smooth = {}, # the default + + smooth_jabes = {# this is the version SF ran for the paper + cat("'smooth_jabes' model chosen. The following parameters have been set:", "model = 'smooth'", "seFromData = FALSE", "Y1perfect = TRUE", sep = "\n") + model = "smooth" + seFromData = FALSE + Y1perfect = TRUE}, + + smooth_det2 = {# this is the version NI tested for ISEC + cat("'smooth_det2' model chosen. The following parameters have been set:", "model = 'smooth'", "seFromData = TRUE", "Y1perfect = FALSE", sep = "\n") + model = "smooth" + seFromData = TRUE + Y1perfect = FALSE}, + + smooth_det_sigtheta = {# this is the version NI ran for Scottish indicators + cat("'smooth_det_sigtheta' model chosen. The following parameters have been set:", "model = 'smooth'", "seFromData = FALSE", "Y1perfect = FALSE", sep = "\n") + model = "smooth" + seFromData=FALSE + Y1perfect = FALSE}, + + smooth_det = stop("smooth_det model has been deprecated"), + random_walk = stop("Random walk model has been deprecated"), + uniform = stop("Uniform model has been deprecated"), + uniform_noeta = stop("Uniform model has been deprecated"), + fngr = stop("This model option has been deprecated"), + smooth_stoch = stop("This model option has been deprecated"), + fngr2 = stop("This model option has been deprecated"), + smooth_stoch2 = stop("This model option has been deprecated"), + stop(paste("Model type not known. Check the help file for details")) + ) + +# do a quick check for whether the index values have been transformed +if(min(data$index, na.rm = T) >= 0){ + print("Warning: No negative index values detected. Are you sure you transformed the data?")} + +# check whether the data contain any infinite values +if(any(is.infinite(data$index))){ + stop('Dataset contains Infinite values. Fix this before proceeding')} + +# This is not my preferrred behaviour +if(!m.scale %in% c('loge', 'log10', 'logit')){ + stop("m.scale must be 'loge', 'log10', or 'logit'")} + +# pick the correct model +model_code <- get_bmaBUGScode(option = model, + incl.2deriv=incl.2deriv, + seFromData = seFromData, + Y1perfect = Y1perfect) + +# save it to a temp file +bugs_path <- tempfile() +writeLines(text = model_code, con = bugs_path) + +# include an option here to standardise the data to some value in year 1 + +# we assume that the index values are already on the unbounded (log) scale (we checked for negative values above) +index <- acast(data, species ~ year, value.var = "index") + +if(!is.null(rescale_indices)){ +# the user has specified that each species' time series should be scaled to start at a common value. +# since the data are on the unbounded (log or logit) scale, we standardise them by addition/subtraction, rather than multiplication (as in rescale_species()) +# recall that rescale_indices has to be an integer +# without mising data this would be easy, but we have to identify the first year in each species' timeseries +index <- t(apply(index, 1, function(x) rescale_indices + x - x[min(which(!is.na(x)))])) +} + +# Setup BUGS data +bugs_data <- list(nsp = nrow(index), + nyears = ncol(index), + estimate = index + ) + +if(seFromData) { +se <- acast(data, species ~ year, value.var = "se") +bugs_data$sigma.obs <- se +bugs_data$max_se = ifelse(test = all(is.na(se)), + yes = 10, + no = max(se, na.rm = TRUE)) +} + +if(model %in% c('smooth', 'smooth_stoch', 'smooth_det', 'smooth_stoch2')){ +ZX <- makeZX(num.knots = num.knots, + covariate = seq(min(data$year), + max(data$year))) +bugs_data[['Z']] <- ZX[['Z']] +bugs_data[['X']] <- ZX[['X']] +bugs_data[['num.knots']] <- num.knots +} + +if(model %in% c('smooth', 'smooth_stoch2', 'FNgr2')){ +# using row.names should ensure the same order in the bugs data +#FY <- sapply(row.names(index), FUN = function(x){ +# min(data$year[!is.na(data$index) & data$species == x]) +#}) +#bugs_data[['FY']] <- FY - min(FY) + 1 # set lowest value to 1 +bugs_data[['FY']] <- apply(index, 1, function(x) min(which(!is.na(x)))) # simpler alternative +} + +# Setup parameters to monitor. NB Most of the model options have been deprecated, so much of this code is redundant +params = c("tau.spi") +if(model == 'smooth'){ params <- c(params, "Mprime")} +if(model %in% c('smooth', 'smooth_stoch', 'smooth_det', 'FNgr', 'smooth_stoch2', 'FNgr2')){ + params <- c(params, "logLambda", "spgrowth", "M")} +if(model %in% c('smooth_stoch', 'smooth_det', 'FNgr')){params <- c(params, "tau.sg")} +if(model %in% c('smooth', 'smooth_stoch', 'smooth_det','smooth_stoch2')){params <- c(params, "beta", "taub")} +if(incl.2deriv){ params <- c(params, "t2dash")} +if(!seFromData){ params <- c(params, "theta")} +if(save.sppars) { +params <- c(params, "spindex") +} else { +params <- params[!params %in% c("spgrowth", "sigma.obs")] +} +if(incl.2deriv){ params <- c(params, "t2dash")} + +set.seed(seed = seed) +model.out <- jagsUI::jags(data = bugs_data, + inits = NULL, + param = params, + parallel = parallel, + n.cores = n.cores, + model.file = bugs_path, + store.data = TRUE, + n.chains = 3, + n.thin = n.thin, + n.iter = n.iter, + n.burnin = floor(n.iter/2)) + +if (plot==TRUE) { +array_sim <- model.out$samples +comb.samples <- mcmc.list(lapply(1:3, FUN = function(x, + array_sim) { + year_ests <- colnames(array_sim[[x]])[grepl("^Mprime\\[", # changed from I + colnames(array_sim[[x]]))] + ar_temp <- array_sim[[x]][,c(head(year_ests, 1), tail(year_ests,1))] + colnames(ar_temp) <- c("First year", "Last year") + as.mcmc(ar_temp) +}, array_sim = array_sim)) + +output$comb.samples = comb.samples + +plot(comb.samples) +} + +MSI1 <- MSI2 <- NULL +if("Mprime" %in% params) { +MSI1 <- scale_indicator(logI = model.out$sims.list$Mprime, FirstYr = min(data$year), + CI=CI, m.scale=m.scale, + rescaleYr=rescaleYr, errorY1=errorY1, baseline=baseline) +} +if("M" %in% params) { +MSI2 <- scale_indicator(logI = model.out$sims.list$M, FirstYr = min(data$year), + CI=CI, m.scale=m.scale, + rescaleYr=rescaleYr, errorY1=errorY1, baseline=baseline) +} + +MSI <- merge(MSI1, MSI2, by="Year") # this will fail for smooth_det + +# rename the output columns +names(MSI) <- gsub(names(MSI), pattern="x", replacement = "Mprime") +names(MSI) <- gsub(names(MSI), pattern="y", replacement = "M") +names(MSI) <- gsub(names(MSI), pattern="mean", replacement = "Index") + +if(incl.model) attr(MSI, 'model') <- model.out + +output$MSI = MSI + +return(output) +} \ No newline at end of file diff --git a/tests/testthat/helper-bootstrap_indicator.R b/tests/testthat/helper-bootstrap_indicator.R new file mode 100644 index 0000000..0259629 --- /dev/null +++ b/tests/testthat/helper-bootstrap_indicator.R @@ -0,0 +1,56 @@ +bootstrap_indicator_fake_data <- matrix(runif(50 * length(letters), max = 100), + nrow = 50, + ncol = length(letters)) + +# Assign the same column names +colnames(bootstrap_indicator_fake_data) <- letters + + +bootstrap_indicator_objects <- function(Data, iterations = 10000, CI_limits = c(0.025, 0.975), + verbose = TRUE){ + + # To capture function objects + output = list() + + if(!is.matrix(Data)){ + stop("the Data parameter must be a matrix object.") + } + + if(!all(is.numeric(Data))){ + stop("Matrix values must all be numeric.") + } + + ### bootstrap to estimate 95% CI around the geometric mean ### + nSpecies <- ncol(Data) + bootstrap_values <- matrix(data = NA, nrow = nrow(Data), ncol = iterations) + bootstrap_data <- as.data.frame(Data) + + pb <- txtProgressBar(min = 0, max = iterations, style = 3) + + # Run bootstrapping + CIData <- sapply(1:iterations, simplify = TRUE, function(iteration) { + + if(verbose){setTxtProgressBar(pb, iteration)} + + # Randomly sample species + samp <- sample(bootstrap_data, size = ncol(bootstrap_data), replace = TRUE) + + apply(samp, 1, function(x){ + exp(mean(log(x), na.rm = T)) + }) +}) + +output$CIData = CIData + + close(pb) + + # Extract the 2.5 97.5% CI around the geometric mean (from the bootstrapped resamples) + CIs <- as.data.frame(t(apply(X = CIData, MARGIN = 1, FUN = quantile, + probs = CI_limits, na.rm = TRUE))) + names(CIs) <- c(paste('quant_', gsub('0\\.', '', as.character(CI_limits[1])), sep = ''), + paste('quant_', gsub('0\\.', '', as.character(CI_limits[2])), sep = '')) + + output$CI_matrix = as.matrix(CIs) + + return(output) +} \ No newline at end of file diff --git a/tests/testthat/test-CompositeTrend.R b/tests/testthat/test-CompositeTrend.R index b56ba74..ac50ebd 100644 --- a/tests/testthat/test-CompositeTrend.R +++ b/tests/testthat/test-CompositeTrend.R @@ -16,8 +16,8 @@ test_that("Does function return correct error message when the user provides add group_name = "TestGroup", save_iterations = "yes", TrendScale = 100, - plot_output = FALSE, regexp = "Column(s) contains non-numeric fields: character_col_1, character_col_2" - )) + plot_output = FALSE), regexp = "Column\\(s\\) contains non-numeric fields") + }) test_that("Does the rbind of NULL value with a row work without error", { diff --git a/tests/testthat/test-bayesian_meta_analysis.R b/tests/testthat/test-bayesian_meta_analysis.R new file mode 100644 index 0000000..f0c0d3d --- /dev/null +++ b/tests/testthat/test-bayesian_meta_analysis.R @@ -0,0 +1,49 @@ +# Test that function responds correctly when 'jagsUI' is not available +test_that("Does the function stop when 'jagsUI' is not available?", { + + # Mock `requireNamespace` to return FALSE + mockery::stub(bma, "requireNamespace", FALSE) + + expect_error(bma(bayesian_meta_analysis_fake_data), regexp = "Package 'jagsUI' is needed") + +}) + +test_that("Does it correctly detect missing columns in the input data?", { + +expect_error(bma(subset(bayesian_meta_analysis_fake_data, select = -c(index))), + regexp = "data column names should include") +}) + +test_that("Does it correctly detect a missing standard error column?", { + +expect_error(bma(subset(bayesian_meta_analysis_fake_data, select = -c(se)), seFromData = TRUE), + regexp = "Standard errors have not been provided") +}) + +test_that("Does it correctly return an error if a deprecated model, in this case smooth_det, is chosen?", { + +expect_error(bma(bayesian_meta_analysis_fake_data, model = "smooth_det"), + regexp = "model has been deprecated") +}) + +test_that("Does the function output, assuming 'jagsUI' is available, return a dataframe?", { + + # skip the test if jagsUI is not installed + if ((!requireNamespace("jagsUI", quietly = TRUE))) { + skip("jagsUI software not available") + } + + expect_equal(class(suppressWarnings(bma(bayesian_meta_analysis_fake_data, plot = FALSE))), "data.frame") + +}) + +test_that("Does the function return a Markov Chain Monte Carlo (MCMC) output object?", { + + # skip the test if jagsUI is not installed + if ((!requireNamespace("jagsUI", quietly = TRUE))) { + skip("jagsUI software not available") + } + + expect_equal(class(suppressWarnings(bma_objects(bayesian_meta_analysis_fake_data, plot = TRUE)$comb.samples)), "mcmc.list") + +}) diff --git a/tests/testthat/test-bootstrap_indicator.R b/tests/testthat/test-bootstrap_indicator.R new file mode 100644 index 0000000..9a6d2a6 --- /dev/null +++ b/tests/testthat/test-bootstrap_indicator.R @@ -0,0 +1,54 @@ +test_that("Does function fail when given a non-matrix object?", { + +df = as.data.frame(bootstrap_indicator_fake_data) + +expect_error(bootstrap_indicator(df), "the Data parameter must be a matrix object") + +}) + +test_that("Does function fail when given non-numeric values", { + +letters_matrix <- matrix(sample(letters, 50 * length(letters), replace = TRUE), + nrow = 50, + ncol = length(letters)) + +# Assign the same column names +colnames(letters_matrix) <- letters + +expect_error(bootstrap_indicator(letters_matrix), "Matrix values must all be numeric") + +}) + +test_that("is the CI object ouput from sapply a matrix", { + +expect_equal(class(bootstrap_indicator_objects(bootstrap_indicator_fake_data)$CIData), c("matrix", "array")) + +}) + +test_that("is the CI object ouput from sapply a matrix", { + +expect_equal(class(bootstrap_indicator(bootstrap_indicator_fake_data)), c("matrix", "array")) + +}) + +test_that("is the CI object ouput from sapply a matrix", { + +CI_limits = c(0.025, 0.975) + +expect_equal(names(bootstrap_indicator(bootstrap_indicator_fake_data, CI_limits = CI_limits)), c("quant_025", "quant_975")) + +}) + +test_that("is the CI object ouput from sapply a matrix", { + +expect_equal(class(bootstrap_indicator(bootstrap_indicator_fake_data)), c("matrix", "array")) + +}) + +test_that("Does the CI matrix output have the same column names as the CI_limits", { + +CI_limits = c(0.025, 0.975) + +expect_equal(colnames(bootstrap_indicator(bootstrap_indicator_fake_data, CI_limits = CI_limits)), c("quant_025", "quant_975")) + +}) \ No newline at end of file diff --git a/tests/testthat/testbma.R b/tests/testthat/testbma.R deleted file mode 100644 index b43f1c6..0000000 --- a/tests/testthat/testbma.R +++ /dev/null @@ -1,203 +0,0 @@ -context("Test bma") - -set.seed(123) - -# Create data for testing -data <- data.frame(species = rep(letters, each = 50), - year = rep(1:50, length(letters)), - index = rnorm(n = 50 * length(letters), mean = 0, sd = 1), # on the unbounded scale - se = runif(n = 50 * length(letters), min = 0.01, max = .1)) -temp <- tempfile() - -test_that("simple run", { - - # Run the Bayesian meta-analysis - sink(temp) - bma_indicator <- bma(data, - model = "smooth", - m.scale = "logit", - n.iter = 100, - seed = 123) - sink() - # test a data frame is returned.. - expect_is(bma_indicator, 'data.frame') - # it has the right elements ... - expect_equal(names(bma_indicator), - c("Year", "Index.Mprime", "lowerCI.Mprime", "upperCI.Mprime", - "Index.M", "lowerCI.M", "upperCI.M")) - # and the values are the same - # expect_equal(bma_indicator$Index.Mprime, - # c(100, 93.6269997707243, 90.7642414846384, 86.4298924338845, - # 84.9660369922485, 83.7795156080376, 81.7308696892823, 81.6835339747922, - # 83.785498716095, 86.5077332003976, 87.6172556814392, 89.2808101569097, - # 89.4325835702012, 89.3560597071043, 88.9755823882497, 88.9353548004614, - # 88.3891728744399, 86.7979384446049, 85.2610259639761, 84.2409033288102, - # 82.3361826455479, 80.4730747181269, 80.4001032086602, 80.9139796855676, - # 81.8654713301714, 82.9416418952172, 83.3709956238925, 82.2126310793402, - # 81.6051372670485, 81.030762956591, 81.3100983064533, 80.8560131088383, - # 79.1559257580868, 77.846150838896, 76.7975166999173, 76.5336153994869, - # 77.4264611588755, 77.4785340958339, 78.1311758607365, 79.160262525242, - # 81.8550651741761, 84.0829271927207, 84.9058311033919, 86.3448876097604, - # 87.3739969177288, 87.9062457477897, 88.7744721743016, 89.4518976996496, - # 89.2268047378459, 87.8301566665547), tolerance=1e-3) - # expect_equal(bma_indicator$Index.M, - # c(100, 93.7151859934056, 88.875900433918, 85.20094872192, 82.4765853212565, - # 80.5376857944807, 79.2493211841401, 78.4870867450422, 78.1287961186731, - # 78.0491399314556, 78.1351896369235, 78.3057925867868, 78.5114547241235, - # 78.7318919099216, 78.9516941612148, 79.1455137994674, 79.2777836955814, - # 79.3071750299046, 79.2089527032148, 78.9822255414714, 78.6497989645542, - # 78.2481502176824, 77.8030174303428, 77.3257693887762, 76.814065732173, - # 76.2659923256175, 75.6984596395346, 75.1477823779312, 74.66740771411, - # 74.3057098446791, 74.0905727971162, 74.0299753705505, 74.11530034614, - # 74.3394964938591, 74.704155847949, 75.219667467046, 75.9015707542142, - # 76.7580360559506, 77.7869948130706, 78.9754033565609, 80.2991540237979, - # 81.7234877505562, 83.2018612557898, 84.6750349752032, 86.076547229449, - # 87.3386690915678, 88.3936477532576, 89.1746925343515, 89.6136824473334, - # 89.6413004538255), tolerance=1e-3) - # expect_equal(bma_indicator$upperCI.Mprime, - # c(100, 100.912670097298, 97.9127659677016, 95.8499701178894, - # 94.671316063692, 89.967762734754, 93.9463008307346, 91.8714213799306, - # 93.5154825557029, 97.2103850587094, 100.604000942243, 98.284833857316, - # 99.5794344914379, 100.058004589596, 96.023948265488, 93.9152620486179, - # 92.8653040381579, 93.6033909233236, 91.3659011269288, 92.2840208179575, - # 91.3889899929472, 95.6265216726726, 100.784686621884, 98.3937767294999, - # 99.0346891492975, 98.5975993343305, 93.7589428156902, 94.0423242183192, - # 90.0355125646069, 89.3574031716457, 88.5411398430671, 88.6128338294964, - # 86.9089757554644, 85.9470043459705, 85.2854508468944, 87.5774486565996, - # 86.1864430155758, 87.6080843176338, 88.2546335849038, 90.2893916629357, - # 91.5802927032084, 93.6644725019474, 97.7541237492462, 100.40716888185, - # 101.115243416602, 101.47662882825, 104.027283069311, 107.464620760871, - # 111.656833538891, 116.888378869377), tolerance=1e-3) - -}) - -test_that("degraded data", { - - data2 <- data - # Add NAs to one year - data2[5,c(3:4)] <- NA - # Remove a year of data - data2 <- data2[1:(nrow(data2)-1),] - - sink(temp) - set.seed(123) - bma_indicator <- bma(data2, - model = "smooth", - m.scale = "logit", - n.iter = 100, - seed = 123) - sink() - - expect_is(bma_indicator, 'data.frame') - -}) - - -test_that("model options", { - - # bma_indicator_det <- bma(data, - # model = "smooth_det", - # m.scale = "logit", - # n.iter = 100, - # seed = 123) - # - # # test a data frame is returned.. - # expect_is(bma_indicator_det, 'data.frame') - # # it has the right elements ... - # expect_equal(names(bma_indicator_det), - # c("Year", "Index.Mprime", "lowerCI.Mprime", "upperCI.Mprime", - # "Index.M", "lowerCI.M", "upperCI.M")) - - sink(temp) - bma_indicator_smooth_det2 <- bma(data, - model = "smooth_det2", - m.scale = "logit", - n.iter = 100, - seed = 123) - sink() - - # test a data frame is returned.. - expect_is(bma_indicator_smooth_det2, 'data.frame') - # it has hte right elements ... - expect_equal(names(bma_indicator_smooth_det2), - c("Year", "Index.Mprime", "lowerCI.Mprime", "upperCI.Mprime", - "Index.M", "lowerCI.M", "upperCI.M")) - # expect_equal(bma_indicator_smooth_det2$Index.M, - # c(100, 95.93909090935, 92.7672496713464, 90.3009486772026, 88.3910564873888, - # 86.9135156978769, 85.762962294484, 84.8485307419185, 84.0907145192179, - # 83.419528497586, 82.7801742540728, 82.1397126378509, 81.4865697868186, - # 80.8283760531123, 80.1769336841978, 79.5395128209959, 78.918912999682, - # 78.3155809591785, 77.7374190089791, 77.2025558362917, 76.7390450389058, - # 76.3828846793717, 76.173517902885, 76.1536897880308, 76.3699821598276, - # 76.8573692438152, 77.6172261446705, 78.6144721564787, 79.7775852704348, - # 81.0389454212986, 82.367518378779, 83.7723498196311, 85.3012594012114, - # 87.0137904449635, 88.9705565936254, 91.2341498812796, 93.8627067415033, - # 96.8850398226239, 100.290874956475, 104.023304761472, 107.984525783209, - # 112.054621420101, 116.091278710721, 119.928865688976, 123.379833426503, - # 126.238820288326, 128.289739888977, 129.315919102855, 129.111127561621, - # 127.494892855453)) - - sink(temp) - bma_indicatorsmooth_det_sigtheta <- bma(data, - model = "smooth_det_sigtheta", - m.scale = "logit", - n.iter = 100, - seed = 123) - sink() - - # test a data frame is returned.. - expect_is(bma_indicatorsmooth_det_sigtheta, 'data.frame') - # it has the right elements ... - expect_equal(names(bma_indicatorsmooth_det_sigtheta), - c("Year", "Index.Mprime", "lowerCI.Mprime", "upperCI.Mprime", - "Index.M", "lowerCI.M", "upperCI.M")) - # expect_equal(bma_indicatorsmooth_det_sigtheta$Index.M, - # c(100, 95.1066424835543, 91.3277245037226, 88.4678784823851, - # 86.3749875308697, 84.9289578315946, 84.0282642314814, 83.5732818349891, - # 83.4603232794704, 83.5779762787929, 83.8203922311029, 84.1037927653868, - # 84.3668218409477, 84.5704470720489, 84.6949304070813, 84.7380799523458, - # 84.7153660145554, 84.6562107839785, 84.5854974146199, 84.5177530126733, - # 84.4571762815342, 84.3988976106347, 84.3321081506402, 84.2405725547339, - # 84.1028316496622, 83.8984447989557, 83.6163659513033, 83.2555068082075, - # 82.8247269865824, 82.3445098974237, 81.8479674471805, 81.3803882658224, - # 80.9967396195252, 80.7479373177327, 80.6761690900314, 80.8158186574453, - # 81.1918701375337, 81.8126218286924, 82.6678619340919, 83.7277504340206, - # 84.9432933084802, 86.248127439241, 87.5568964352754, 88.7643412067508, - # 89.7566668063039, 90.4240705821149, 90.6646901541542, 90.3880341473998, - # 89.5124407395943, 87.9667954932412)) - -}) - -test_that("different parameters", { - - sink(temp) - # test reading se and indexing - bma_indicator_params1 <- bma(data, - model = "smooth", - m.scale = "logit", - n.iter = 100, - seFromData = TRUE, - rescale_indices = 2, - rescaleYr = 5, - baseline = 10, - incl.2deriv = TRUE, - seed = 123) - - sink() - - expect_equal(bma_indicator_params1$Index.Mprime[5], 10) - expect_equal(bma_indicator_params1$lowerCI.Mprime[5], 10) - expect_equal(bma_indicator_params1$upperCI.Mprime[5], 10) - - sink(temp) - # test reading se - bma_indicator_params2 <- bma(data, - model = "smooth", - m.scale = "logit", - n.iter = 100, - Y1perfect = FALSE, - seed = 123) - sink() - expect_is(bma_indicator_params2, 'data.frame') - -}) \ No newline at end of file From 90a11cb8b1a49f34e177ea778e29cff171749e04 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 13 Mar 2024 17:39:22 +0000 Subject: [PATCH 06/45] setup of CI --- .github/workflows/r.yml | 52 ----------------------------------------- .travis.yml | 44 ---------------------------------- README.md | 4 ---- codecov.yml | 12 ---------- 4 files changed, 112 deletions(-) delete mode 100644 .github/workflows/r.yml delete mode 100644 .travis.yml delete mode 100644 codecov.yml diff --git a/.github/workflows/r.yml b/.github/workflows/r.yml deleted file mode 100644 index 5f43690..0000000 --- a/.github/workflows/r.yml +++ /dev/null @@ -1,52 +0,0 @@ -# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples -# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help -on: - push: - branches: [main, master] - pull_request: - branches: [main, master] - -name: R-CMD-check - -jobs: - R-CMD-check: - runs-on: ${{ matrix.config.os }} - - name: ${{ matrix.config.os }} (${{ matrix.config.r }}) - - strategy: - fail-fast: false - matrix: - config: - - {os: macos-latest, r: 'release'} - - {os: windows-latest, r: 'release'} - - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - - {os: ubuntu-latest, r: 'release'} - - {os: ubuntu-latest, r: 'oldrel-1'} - - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - R_KEEP_PKG_SOURCE: yes - - steps: - - uses: actions/checkout@v3 - - - uses: r-lib/actions/setup-pandoc@v2 - - - uses: r-lib/actions/setup-r@v2 - with: - r-version: ${{ matrix.config.r }} - http-user-agent: ${{ matrix.config.http-user-agent }} - use-public-rspm: true - - - uses: r-lib/actions/setup-r-dependencies@v2 - with: - extra-packages: any::rcmdcheck - needs: check - - - uses: r-lib/actions/check-r-package@v2 - with: - args: 'c("--no-manual", "--no-build-vignettes")' - build_args: 'c("--no-build-vignettes", "--no-manual", "--no-resave-data")' - error-on: '"error"' - upload-snapshots: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2fcd018..0000000 --- a/.travis.yml +++ /dev/null @@ -1,44 +0,0 @@ -language: r -cache: packages -sudo: required - -apt_packages: - - r-cran-rjags - - libv8-dev - -r_packages: - - rjags - - covr - -r_github_packages: - - BiologicalRecordsCentre/sparta - -## After success update the code coverage -after_success: - - Rscript -e 'library(covr);codecov()' - -## Email notification if the package pass status changes -notifications: - email: - recipients: - - tomaug@ceh.ac.uk - on_success: change - on_failure: change - -# Warnings dont fail build -warnings_are_errors: false - -## Set up the matrix of different runs -r_build_args: --no-build-vignettes --no-manual --no-resave-data -r_check_args: --no-build-vignettes --no-manual - -env: - matrix: - - r: release - not_cran: true - - r: release - not_cran: false - - r: devel - not_cran: true - - r: devel - not_cran: false diff --git a/README.md b/README.md index 1c056d8..25f52be 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ # BRCindicators - - [![Travis build status](https://travis-ci.com/BiologicalRecordsCentre/BRCindicators.svg?branch=master)](https://travis-ci.com/BiologicalRecordsCentre/BRCindicators) - [![Codecov test coverage](https://codecov.io/gh/biologicalrecordscentre/BRCindicators/branch/master/graph/badge.svg)](https://codecov.io/gh/biologicalrecordscentre/BRCindicators?branch=master) - The functions in BRCindicators work with yearly estimates of species abundance or occurrence and aggregate them into an scaled indicator value with bootstrapped confidence intervals diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 8f36b6c..0000000 --- a/codecov.yml +++ /dev/null @@ -1,12 +0,0 @@ -comment: false - -coverage: - status: - project: - default: - target: auto - threshold: 1% - patch: - default: - target: auto - threshold: 1% From 8c1045cab1145002d79bd864a3bb67712d965028 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 13 Mar 2024 17:42:32 +0000 Subject: [PATCH 07/45] CI setup --- .github/.gitignore | 1 + .github/workflows/R-CMD-check.yaml | 50 ++++++++++++++++++++++++++++ .github/workflows/test-coverage.yaml | 50 ++++++++++++++++++++++++++++ README.md | 3 ++ 4 files changed, 104 insertions(+) create mode 100644 .github/.gitignore create mode 100644 .github/workflows/R-CMD-check.yaml create mode 100644 .github/workflows/test-coverage.yaml diff --git a/.github/.gitignore b/.github/.gitignore new file mode 100644 index 0000000..2d19fc7 --- /dev/null +++ b/.github/.gitignore @@ -0,0 +1 @@ +*.html diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml new file mode 100644 index 0000000..14159b7 --- /dev/null +++ b/.github/workflows/R-CMD-check.yaml @@ -0,0 +1,50 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +name: R-CMD-check + +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: macos-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + - {os: ubuntu-latest, r: 'oldrel-1'} + + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + + - uses: r-lib/actions/check-r-package@v2 + with: + upload-snapshots: true + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml new file mode 100644 index 0000000..21b8a93 --- /dev/null +++ b/.github/workflows/test-coverage.yaml @@ -0,0 +1,50 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +name: test-coverage + +jobs: + test-coverage: + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::covr + needs: coverage + + - name: Test coverage + run: | + covr::codecov( + quiet = FALSE, + clean = FALSE, + install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") + ) + shell: Rscript {0} + + - name: Show testthat output + if: always() + run: | + ## -------------------------------------------------------------------- + find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + + - name: Upload test results + if: failure() + uses: actions/upload-artifact@v4 + with: + name: coverage-test-failures + path: ${{ runner.temp }}/package diff --git a/README.md b/README.md index 25f52be..f2384ae 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # BRCindicators + +[![R-CMD-check](https://github.com/DylanCarbone/BRCindicators/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/DylanCarbone/BRCindicators/actions/workflows/R-CMD-check.yaml) + The functions in BRCindicators work with yearly estimates of species abundance or occurrence and aggregate them into an scaled indicator value with bootstrapped confidence intervals From 2664fa8145af939a42b08b74ef2a4e8948c8b5a4 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 13 Mar 2024 17:55:50 +0000 Subject: [PATCH 08/45] documentation --- man/BRCindicators-package.Rd | 33 +++++++++++++++++++++++++++++++++ man/bma.Rd | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 man/BRCindicators-package.Rd diff --git a/man/BRCindicators-package.Rd b/man/BRCindicators-package.Rd new file mode 100644 index 0000000..2371d85 --- /dev/null +++ b/man/BRCindicators-package.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/BRCindicators-package.R +\docType{package} +\name{BRCindicators-package} +\alias{BRCindicators} +\alias{BRCindicators-package} +\title{BRCindicators: Creating multispecies biodiversity indicators} +\description{ +This package contains a number of functions used to create biodiversity indicators. +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://github.com/BiologicalRecordsCentre/BRCindicators} + \item Report bugs at \url{https://github.com/BiologicalRecordsCentre/BRCindicators/issues} +} + +} +\author{ +\strong{Maintainer}: Tom August \email{tomaug@ceh.ac.uk} + +Authors: +\itemize{ + \item Gary Powney + \item Charlie Outhwaite + \item Jack Hatfield + \item Mark Logie + \item Stephen Freeman + \item Nick Isaac +} + +} +\keyword{internal} diff --git a/man/bma.Rd b/man/bma.Rd index ae2c78d..263e3cc 100644 --- a/man/bma.Rd +++ b/man/bma.Rd @@ -28,7 +28,7 @@ bma( ) } \arguments{ -\item{data}{a data.frame with 3-4 columns in this order: `species`, `year`, `index`, `se` (standard error). The `se` column is optional +\item{data}{a data.frame with 3-4 columns: `species`, `year`, `index`, `se` (standard error). The `se` column is optional NB: Index values are assumed to be on the unbounded (logarithmic scale)} \item{plot}{Logical, should a trace plot be plotted to diagnose the model output?} From ce8267b099047ce1a001de5134ab037dbdfb597b Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 13 Mar 2024 18:06:42 +0000 Subject: [PATCH 09/45] CI enabled on branch --- .github/workflows/R-CMD-check.yaml | 4 ++-- .github/workflows/test-coverage.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 14159b7..8597766 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -2,9 +2,9 @@ # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: [main, master] + branches: [main, master, CI] pull_request: - branches: [main, master] + branches: [main, master, CI] name: R-CMD-check diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 21b8a93..b88eb58 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -2,9 +2,9 @@ # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: [main, master] + branches: [main, master, CI] pull_request: - branches: [main, master] + branches: [main, master, CI] name: test-coverage From 163971ae381c40e04dc9d5bc19687ac4af196bcd Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 13 Mar 2024 18:20:34 +0000 Subject: [PATCH 10/45] fix of duplicate test --- tests/testthat/test-bootstrap_indicator.R | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/testthat/test-bootstrap_indicator.R b/tests/testthat/test-bootstrap_indicator.R index 9a6d2a6..2084b8f 100644 --- a/tests/testthat/test-bootstrap_indicator.R +++ b/tests/testthat/test-bootstrap_indicator.R @@ -33,14 +33,6 @@ expect_equal(class(bootstrap_indicator(bootstrap_indicator_fake_data)), c("matri test_that("is the CI object ouput from sapply a matrix", { -CI_limits = c(0.025, 0.975) - -expect_equal(names(bootstrap_indicator(bootstrap_indicator_fake_data, CI_limits = CI_limits)), c("quant_025", "quant_975")) - -}) - -test_that("is the CI object ouput from sapply a matrix", { - expect_equal(class(bootstrap_indicator(bootstrap_indicator_fake_data)), c("matrix", "array")) }) From b91df5b65d7103f2846ba46a2550873c3c9045d7 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 13 Mar 2024 18:38:10 +0000 Subject: [PATCH 11/45] test of remote sparta installation --- .github/workflows/R-CMD-check.yaml | 8 +++++++- vignettes/BRCindicators.Rmd | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 8597766..92921d5 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -39,6 +39,12 @@ jobs: http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true + - name: Install remotes and GitHub package + run: | + install.packages('remotes') + remotes::install_github('biologicalrecordscentre/sparta') + shell: Rscript {0} + - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::rcmdcheck @@ -47,4 +53,4 @@ jobs: - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true - build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf") diff --git a/vignettes/BRCindicators.Rmd b/vignettes/BRCindicators.Rmd index a59aeab..bd163f3 100644 --- a/vignettes/BRCindicators.Rmd +++ b/vignettes/BRCindicators.Rmd @@ -39,7 +39,8 @@ If you already have yearly estimates of abundance or occurrence for your species ```{r set_up, eval=FALSE} library(devtools) -install_github('biologicalrecordscentre/sparta') +remotes::install_github('biologicalrecordscentre/sparta') +#install_github('biologicalrecordscentre/sparta') ``` Let's assume you have some raw data already, we can under take occupancy modelling like this From ef2a5718016b7a229607cbb7533aeef3ae20cb89 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Fri, 22 Mar 2024 10:45:04 +0000 Subject: [PATCH 12/45] Fix of spelling mistake --- R/bayesian_meta_analysis.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/bayesian_meta_analysis.R b/R/bayesian_meta_analysis.R index d6c8b70..e8b8685 100644 --- a/R/bayesian_meta_analysis.R +++ b/R/bayesian_meta_analysis.R @@ -82,7 +82,7 @@ set.seed(seed = seed) # Check if jagsUI is installed if (!requireNamespace("jagsUI", quietly = TRUE)) { -stop("Package 'jagsUI' is needed for the 'bma' function to work. Please insatll this from CRAN. You will also be required to install JAGS, which you can download from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", +stop("Package 'jagsUI' is needed for the 'bma' function to work. Please install this from CRAN. You will also be required to install JAGS, which you can download from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", call. = FALSE) } From 50a8a550dab5c590b41b9bb0a405f87c0942aba2 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Fri, 22 Mar 2024 10:45:04 +0000 Subject: [PATCH 13/45] Fix of spelling mistake --- R/bayesian_meta_analysis.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/bayesian_meta_analysis.R b/R/bayesian_meta_analysis.R index d6c8b70..e8b8685 100644 --- a/R/bayesian_meta_analysis.R +++ b/R/bayesian_meta_analysis.R @@ -82,7 +82,7 @@ set.seed(seed = seed) # Check if jagsUI is installed if (!requireNamespace("jagsUI", quietly = TRUE)) { -stop("Package 'jagsUI' is needed for the 'bma' function to work. Please insatll this from CRAN. You will also be required to install JAGS, which you can download from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", +stop("Package 'jagsUI' is needed for the 'bma' function to work. Please install this from CRAN. You will also be required to install JAGS, which you can download from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", call. = FALSE) } From f37db43452548450c994c45756fe417baac431c5 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 13 Mar 2024 18:20:34 +0000 Subject: [PATCH 14/45] fix of duplicate test --- tests/testthat/test-bootstrap_indicator.R | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/testthat/test-bootstrap_indicator.R b/tests/testthat/test-bootstrap_indicator.R index 9a6d2a6..2084b8f 100644 --- a/tests/testthat/test-bootstrap_indicator.R +++ b/tests/testthat/test-bootstrap_indicator.R @@ -33,14 +33,6 @@ expect_equal(class(bootstrap_indicator(bootstrap_indicator_fake_data)), c("matri test_that("is the CI object ouput from sapply a matrix", { -CI_limits = c(0.025, 0.975) - -expect_equal(names(bootstrap_indicator(bootstrap_indicator_fake_data, CI_limits = CI_limits)), c("quant_025", "quant_975")) - -}) - -test_that("is the CI object ouput from sapply a matrix", { - expect_equal(class(bootstrap_indicator(bootstrap_indicator_fake_data)), c("matrix", "array")) }) From 344489f90037ff79cdb2a17d1305bc2a5fc2fa75 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Fri, 22 Mar 2024 11:48:49 +0000 Subject: [PATCH 15/45] change to gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 716f34d..7fbfdfe 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,3 @@ vignettes/BRCindicators_cache/ vignettes/BRCindicators_files/ misc/ tests/testthat/Rplots.pdf -test_delTestGroup_arithmetic_logit_occ_composite_trend_iterations.csv -test_delTestGroup_arithmetic_logit_occ_composite_trend_summary.csv -test_del/TestGroup_arithmetic_logit_occ_composite_trend.png From d29ec4c885b0ea2cf545c142d24cadc786e13742 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Fri, 22 Mar 2024 11:59:52 +0000 Subject: [PATCH 16/45] revert "test of remote sparta installation" This reverts commit b91df5b65d7103f2846ba46a2550873c3c9045d7. --- .github/workflows/R-CMD-check.yaml | 8 +------- vignettes/BRCindicators.Rmd | 3 +-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 92921d5..8597766 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -39,12 +39,6 @@ jobs: http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true - - name: Install remotes and GitHub package - run: | - install.packages('remotes') - remotes::install_github('biologicalrecordscentre/sparta') - shell: Rscript {0} - - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::rcmdcheck @@ -53,4 +47,4 @@ jobs: - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true - build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf") + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' diff --git a/vignettes/BRCindicators.Rmd b/vignettes/BRCindicators.Rmd index bd163f3..a59aeab 100644 --- a/vignettes/BRCindicators.Rmd +++ b/vignettes/BRCindicators.Rmd @@ -39,8 +39,7 @@ If you already have yearly estimates of abundance or occurrence for your species ```{r set_up, eval=FALSE} library(devtools) -remotes::install_github('biologicalrecordscentre/sparta') -#install_github('biologicalrecordscentre/sparta') +install_github('biologicalrecordscentre/sparta') ``` Let's assume you have some raw data already, we can under take occupancy modelling like this From a10bab3af23a84a4a22731b138e7116e4f7f1b6a Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Tue, 2 Apr 2024 11:47:49 +0100 Subject: [PATCH 17/45] small changes to lambda indicator function --- R/lambda_indicator.r | 8 ++++---- ...lambda_indicator.R => helper-lambda_indicator.r} | 13 +------------ tests/testthat/test-lambda_indicator.R | 8 ++++++++ 3 files changed, 13 insertions(+), 16 deletions(-) rename tests/testthat/{testlambda_indicator.R => helper-lambda_indicator.r} (64%) create mode 100644 tests/testthat/test-lambda_indicator.R diff --git a/R/lambda_indicator.r b/R/lambda_indicator.r index 8a6d3ca..91f0e6a 100644 --- a/R/lambda_indicator.r +++ b/R/lambda_indicator.r @@ -2,17 +2,17 @@ #' #' @description This function takes in the output from a sparta occupancy model #' or a three dimensional array. The first year is set to an index value for all -#' species and then using the average change for one year to the next, across -#' species is used to calculate the indicator. A species only contributes to the +#' species and the average change across years for each species is used to +#' calculate the indicator. A species only contributes to the #' dataset in years where the standard deviation across iterations #' meets the sd threshold. Missing years in the middle a a species dataset are -#' filled in using interpolation. +#' filled in using interpolation. #' #' @param input Either a string giving the path to occupancy model output files #' produced by sparta, or a three dimensional array [species, year, iteration]. #' @param index The index value for the first year, defaults to 100. #' @param threshold_sd The threshold standard deviation for a species-year value -#' to be included. If the standard deviation is above this value it is removed. +#' to be included. If the standard deviation is above this value it is removed. #' @param threshold_Rhat The threshold Rhat value for a species-year value #' to be included. If the Rhat is above this value it is removed. This rule will #' only be used when \code{input} is a path to sparta output. diff --git a/tests/testthat/testlambda_indicator.R b/tests/testthat/helper-lambda_indicator.r similarity index 64% rename from tests/testthat/testlambda_indicator.R rename to tests/testthat/helper-lambda_indicator.r index bb8ee65..4154c83 100644 --- a/tests/testthat/testlambda_indicator.R +++ b/tests/testthat/helper-lambda_indicator.r @@ -1,5 +1,3 @@ -context("Test lambda_indicator") - # number of species nsp = 50 @@ -20,13 +18,4 @@ myArray <- array(data = rnorm(n = nsp*nyr*iter, # Ensure values are bounded by 0 and 1 myArray[myArray > 1] <- 1 -myArray[myArray < 0] <- 0 - -test_that("runs without error", { - - # Run the lambda_interpolation method on this data - myIndicator <- lambda_indicator(myArray) - expect_is(myIndicator, 'list') - expect_equal(length(myIndicator), 5) - -}) \ No newline at end of file +myArray[myArray < 0] <- 0 \ No newline at end of file diff --git a/tests/testthat/test-lambda_indicator.R b/tests/testthat/test-lambda_indicator.R new file mode 100644 index 0000000..e1f419d --- /dev/null +++ b/tests/testthat/test-lambda_indicator.R @@ -0,0 +1,8 @@ +test_that("runs without error", { + + # Run the lambda_interpolation method on this data + myIndicator <- lambda_indicator(myArray) + expect_equal(class(myIndicator), 'list') + expect_equal(length(myIndicator), 5) + +}) From 6f672ec658799669eee6b9d8e2075c3f6f313604 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Tue, 2 Apr 2024 14:46:36 +0100 Subject: [PATCH 18/45] Attempt to install sparta via remotes --- .github/workflows/R-CMD-check.yaml | 4 ++-- .github/workflows/test-coverage.yaml | 4 ++-- DESCRIPTION | 18 +++++++++++------- man/BRCindicators-package.Rd | 14 -------------- man/lambda_indicator.Rd | 4 ++-- vignettes/BRCindicators.Rmd | 2 +- 6 files changed, 18 insertions(+), 28 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 8597766..d42d6bc 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -2,9 +2,9 @@ # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: [main, master, CI] + branches: [master, pull_requests] pull_request: - branches: [main, master, CI] + branches: [master, pull_requests] name: R-CMD-check diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index b88eb58..fb7b0c4 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -2,9 +2,9 @@ # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: [main, master, CI] + branches: [master, pull_requests] pull_request: - branches: [main, master, CI] + branches: [master, pull_requests] name: test-coverage diff --git a/DESCRIPTION b/DESCRIPTION index 1830419..d85619c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -3,13 +3,14 @@ Type: Package Title: Creating multispecies biodiversity indicators Version: 1.3.7 Date: 2021-05-24 -Authors@R: c(person("Tom", "August", role = c("aut", "cre"), email = "tomaug@ceh.ac.uk"), - person("Gary", "Powney", role = c("aut")), - person("Charlie", "Outhwaite", role = c("aut")), - person("Jack", "Hatfield", role = c("aut")), - person("Mark", "Logie", role = c("aut")), +Authors@R: c(person("Tom", "August", role = c("aut", "cre"), email = "tomaug@ceh.ac.uk", comment = c(ORCID = "0000-0003-1116-3385")), + person("Dylan", "Carbone", role = c("aut", "cre"), email = "dylcar@ceh.ac.uk"), comment = c(ORCID = "0009-0003-5290-786X")), + person("Gary", "Powney", role = c("aut"), comment = c(ORCID = "0000-0003-3313-7786")), + person("Charlie", "Outhwaite", role = c("aut"), comment = c(ORCID = "0000-0001-9997-6780")), + person("Jack", "Hatfield", role = c("aut"), comment = c(ORCID = "0000-0002-6361-0629")), + person("Mark", "Logie", role = c("aut"), comment = c(ORCID = "0000-0003-0840-9575")), person("Stephen", "Freeman", role = c("aut")), - person("Nick", "Isaac", role = c("aut"))) + person("Nick", "Isaac", role = c("aut"), comment = c(ORCID = "https://orcid.org/0000-0002-4869-8052")) Maintainer: Tom August Description: This package contains a number of functions used to create biodiversity indicators. @@ -36,5 +37,8 @@ Suggests: knitr, snowfall, rmarkdown, - jagsUI + jagsUI, +remotes: + BiologicalRecordsCentre/sparta Config/testthat/edition: 3 +SystemRequirements: JAGS (https://sourceforge.net/projects/mcmc-jags/files/JAGS/) \ No newline at end of file diff --git a/man/BRCindicators-package.Rd b/man/BRCindicators-package.Rd index 2371d85..df03db6 100644 --- a/man/BRCindicators-package.Rd +++ b/man/BRCindicators-package.Rd @@ -15,19 +15,5 @@ Useful links: \item Report bugs at \url{https://github.com/BiologicalRecordsCentre/BRCindicators/issues} } -} -\author{ -\strong{Maintainer}: Tom August \email{tomaug@ceh.ac.uk} - -Authors: -\itemize{ - \item Gary Powney - \item Charlie Outhwaite - \item Jack Hatfield - \item Mark Logie - \item Stephen Freeman - \item Nick Isaac -} - } \keyword{internal} diff --git a/man/lambda_indicator.Rd b/man/lambda_indicator.Rd index 24c8d54..3f9dc4c 100644 --- a/man/lambda_indicator.Rd +++ b/man/lambda_indicator.Rd @@ -62,8 +62,8 @@ is fixed to the index value. \description{ This function takes in the output from a sparta occupancy model or a three dimensional array. The first year is set to an index value for all -species and then using the average change for one year to the next, across -species is used to calculate the indicator. A species only contributes to the +species and the average change across years for each species is used to +calculate the indicator. A species only contributes to the dataset in years where the standard deviation across iterations meets the sd threshold. Missing years in the middle a a species dataset are filled in using interpolation. diff --git a/vignettes/BRCindicators.Rmd b/vignettes/BRCindicators.Rmd index a59aeab..6501b5c 100644 --- a/vignettes/BRCindicators.Rmd +++ b/vignettes/BRCindicators.Rmd @@ -39,7 +39,7 @@ If you already have yearly estimates of abundance or occurrence for your species ```{r set_up, eval=FALSE} library(devtools) -install_github('biologicalrecordscentre/sparta') +#install_github('biologicalrecordscentre/sparta') ``` Let's assume you have some raw data already, we can under take occupancy modelling like this From c77347410e1a71a19f14dceca4a74a789e570889 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Tue, 2 Apr 2024 14:55:08 +0100 Subject: [PATCH 19/45] minor fix to description --- DESCRIPTION | 65 +++++++++++++++++++++--------------- man/BRCindicators-package.Rd | 14 ++++++++ 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index d85619c..cfa7800 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,44 +1,55 @@ -Package: BRCindicators Type: Package +Package: BRCindicators Title: Creating multispecies biodiversity indicators Version: 1.3.7 Date: 2021-05-24 -Authors@R: c(person("Tom", "August", role = c("aut", "cre"), email = "tomaug@ceh.ac.uk", comment = c(ORCID = "0000-0003-1116-3385")), - person("Dylan", "Carbone", role = c("aut", "cre"), email = "dylcar@ceh.ac.uk"), comment = c(ORCID = "0009-0003-5290-786X")), - person("Gary", "Powney", role = c("aut"), comment = c(ORCID = "0000-0003-3313-7786")), - person("Charlie", "Outhwaite", role = c("aut"), comment = c(ORCID = "0000-0001-9997-6780")), - person("Jack", "Hatfield", role = c("aut"), comment = c(ORCID = "0000-0002-6361-0629")), - person("Mark", "Logie", role = c("aut"), comment = c(ORCID = "0000-0003-0840-9575")), - person("Stephen", "Freeman", role = c("aut")), - person("Nick", "Isaac", role = c("aut"), comment = c(ORCID = "https://orcid.org/0000-0002-4869-8052")) +Authors@R: c( + person("Tom", "August", , "tomaug@ceh.ac.uk", role = c("aut", "cre"), + comment = c(ORCID = "0000-0003-1116-3385")), + person("Dylan", "Carbone", , "dylcar@ceh.ac.uk", role = c("aut", "cre"), + comment = c(ORCID = "0009-0003-5290-786X")), + person("Gary", "Powney", role = "aut", + comment = c(ORCID = "0000-0003-3313-7786")), + person("Charlie", "Outhwaite", role = "aut", + comment = c(ORCID = "0000-0001-9997-6780")), + person("Jack", "Hatfield", role = "aut", + comment = c(ORCID = "0000-0002-6361-0629")), + person("Mark", "Logie", role = "aut", + comment = c(ORCID = "0000-0003-0840-9575")), + person("Stephen", "Freeman", role = "aut"), + person("Nick", "Isaac", role = "aut", + comment = c(ORCID = "https://orcid.org/0000-0002-4869-8052")) + ) Maintainer: Tom August Description: This package contains a number of functions used to create biodiversity indicators. +License: GPL-3 +URL: https://github.com/BiologicalRecordsCentre/BRCindicators +BugReports: + https://github.com/BiologicalRecordsCentre/BRCindicators/issues Depends: R (>= 3.5.0) Imports: - ggplot2, - reshape2, - coda, + boot, car, - RColorBrewer, + coda, + ggplot2, mgcv, - boot, - mockery -VignetteBuilder: knitr -License: GPL-3 -URL: https://github.com/BiologicalRecordsCentre/BRCindicators -BugReports: https://github.com/BiologicalRecordsCentre/BRCindicators/issues -RoxygenNote: 7.2.3 -Encoding: UTF-8 + mockery, + RColorBrewer, + reshape2 Suggests: - testthat (>= 3.0.0), covr, + jagsUI, knitr, - snowfall, rmarkdown, - jagsUI, -remotes: - BiologicalRecordsCentre/sparta + snowfall, + testthat (>= 3.0.0) +VignetteBuilder: + knitr Config/testthat/edition: 3 -SystemRequirements: JAGS (https://sourceforge.net/projects/mcmc-jags/files/JAGS/) \ No newline at end of file +Encoding: UTF-8 +remotes:BiologicalRecordsCentre/sparta +RoxygenNote: 7.2.3 +SystemRequirements: JAGS + (https://sourceforge.net/projects/mcmc-jags/files/JAGS/) diff --git a/man/BRCindicators-package.Rd b/man/BRCindicators-package.Rd index df03db6..755435d 100644 --- a/man/BRCindicators-package.Rd +++ b/man/BRCindicators-package.Rd @@ -15,5 +15,19 @@ Useful links: \item Report bugs at \url{https://github.com/BiologicalRecordsCentre/BRCindicators/issues} } +} +\author{ +\strong{Maintainer}: Tom August \email{tomaug@ceh.ac.uk} (\href{https://orcid.org/0000-0003-1116-3385}{ORCID}) + +Authors: +\itemize{ + \item Gary Powney (\href{https://orcid.org/0000-0003-3313-7786}{ORCID}) + \item Charlie Outhwaite (\href{https://orcid.org/0000-0001-9997-6780}{ORCID}) + \item Jack Hatfield (\href{https://orcid.org/0000-0002-6361-0629}{ORCID}) + \item Mark Logie (\href{https://orcid.org/0000-0003-0840-9575}{ORCID}) + \item Stephen Freeman + \item Nick Isaac (\href{https://orcid.org/0000-0002-4869-8052}{ORCID}) +} + } \keyword{internal} From 2e4f2fbafcb2fc4a448f8c22e07131295970d401 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Tue, 2 Apr 2024 15:03:29 +0100 Subject: [PATCH 20/45] Small error fix --- DESCRIPTION | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index cfa7800..44ed4f6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -4,10 +4,10 @@ Title: Creating multispecies biodiversity indicators Version: 1.3.7 Date: 2021-05-24 Authors@R: c( - person("Tom", "August", , "tomaug@ceh.ac.uk", role = c("aut", "cre"), - comment = c(ORCID = "0000-0003-1116-3385")), person("Dylan", "Carbone", , "dylcar@ceh.ac.uk", role = c("aut", "cre"), comment = c(ORCID = "0009-0003-5290-786X")), + person("Tom", "August", , "tomaug@ceh.ac.uk", role = "aut", + comment = c(ORCID = "0000-0003-1116-3385")), person("Gary", "Powney", role = "aut", comment = c(ORCID = "0000-0003-3313-7786")), person("Charlie", "Outhwaite", role = "aut", @@ -49,7 +49,7 @@ VignetteBuilder: knitr Config/testthat/edition: 3 Encoding: UTF-8 -remotes:BiologicalRecordsCentre/sparta +remotes: BiologicalRecordsCentre/sparta RoxygenNote: 7.2.3 SystemRequirements: JAGS (https://sourceforge.net/projects/mcmc-jags/files/JAGS/) From 6f97faf1bf98c42c13149c1c30afdc4d4550023c Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Tue, 2 Apr 2024 16:01:51 +0100 Subject: [PATCH 21/45] test of pkgdepends installation --- DESCRIPTION | 1 + man/BRCindicators-package.Rd | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 44ed4f6..44d63ab 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -44,6 +44,7 @@ Suggests: knitr, rmarkdown, snowfall, + sparta, testthat (>= 3.0.0) VignetteBuilder: knitr diff --git a/man/BRCindicators-package.Rd b/man/BRCindicators-package.Rd index 755435d..fba9171 100644 --- a/man/BRCindicators-package.Rd +++ b/man/BRCindicators-package.Rd @@ -17,10 +17,11 @@ Useful links: } \author{ -\strong{Maintainer}: Tom August \email{tomaug@ceh.ac.uk} (\href{https://orcid.org/0000-0003-1116-3385}{ORCID}) +\strong{Maintainer}: Dylan Carbone \email{dylcar@ceh.ac.uk} (\href{https://orcid.org/0009-0003-5290-786X}{ORCID}) Authors: \itemize{ + \item Tom August \email{tomaug@ceh.ac.uk} (\href{https://orcid.org/0000-0003-1116-3385}{ORCID}) \item Gary Powney (\href{https://orcid.org/0000-0003-3313-7786}{ORCID}) \item Charlie Outhwaite (\href{https://orcid.org/0000-0001-9997-6780}{ORCID}) \item Jack Hatfield (\href{https://orcid.org/0000-0002-6361-0629}{ORCID}) From f8ca3dde1e0ec02d936893a05bc1587e99589bd4 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Tue, 2 Apr 2024 16:40:01 +0100 Subject: [PATCH 22/45] test of ignoring vignette compilation --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index d42d6bc..04a4706 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -47,4 +47,4 @@ jobs: - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true - build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' + build_args: 'c("--no-build-vignettes","--no-manual","--compact-vignettes=gs+qpdf")' From 04f5368a22466b47fab1e67117f3cbdc397bc612 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Tue, 2 Apr 2024 16:57:35 +0100 Subject: [PATCH 23/45] split apart misc functions for easier navigation --- R/indicator_assessment.r | 29 ++ R/list_to_array.r | 32 ++ R/makeZX.R | 28 -- R/misc_functions.r | 542 ------------------------ R/misc_functions/bootstrap_posteriors.r | 27 ++ R/misc_functions/cap_index.r | 13 + R/misc_functions/entering_multiplier.r | 15 + R/misc_functions/geomean.r | 1 + R/misc_functions/getData.r | 58 +++ R/misc_functions/lambda_calc.r | 30 ++ R/misc_functions/leaving_multiplier.r | 14 + R/misc_functions/list_geomean.r | 32 ++ R/misc_functions/list_quantiles_CI.r | 55 +++ R/misc_functions/read_posterior.r | 43 ++ R/misc_functions/remove_bad_years.r | 14 + R/misc_functions/subset_years.r | 27 ++ R/remove_bad_species.r | 45 ++ R/species_assessment.r | 121 ++++++ 18 files changed, 556 insertions(+), 570 deletions(-) create mode 100644 R/indicator_assessment.r create mode 100644 R/list_to_array.r delete mode 100644 R/makeZX.R create mode 100644 R/misc_functions/bootstrap_posteriors.r create mode 100644 R/misc_functions/cap_index.r create mode 100644 R/misc_functions/entering_multiplier.r create mode 100644 R/misc_functions/geomean.r create mode 100644 R/misc_functions/getData.r create mode 100644 R/misc_functions/lambda_calc.r create mode 100644 R/misc_functions/leaving_multiplier.r create mode 100644 R/misc_functions/list_geomean.r create mode 100644 R/misc_functions/list_quantiles_CI.r create mode 100644 R/misc_functions/read_posterior.r create mode 100644 R/misc_functions/remove_bad_years.r create mode 100644 R/misc_functions/subset_years.r create mode 100644 R/remove_bad_species.r create mode 100644 R/species_assessment.r diff --git a/R/indicator_assessment.r b/R/indicator_assessment.r new file mode 100644 index 0000000..874d300 --- /dev/null +++ b/R/indicator_assessment.r @@ -0,0 +1,29 @@ +indicator_assessment <- function(summary_table, + start_year = NULL, + end_year = NULL){ + + if(is.null(start_year)) start_year <- min(summary_table$year) + if(is.null(end_year)) end_year <- max(summary_table$year) + + # Get the indicator value at the start + start_ind <- summary_table$indicator[summary_table$year == start_year] + + # Get the confidence intervals for the end of the period + end_CIs <- summary_table[summary_table$year == end_year, c('lower', 'upper')] + + # assess + if(start_ind < end_CIs$upper & start_ind > end_CIs$lower){ + assessment <- 'stable' + } else if(start_ind < end_CIs$lower){ + assessment <- 'increasing' + } else if(start_ind > end_CIs$upper){ + assessment <- 'decreasing' + } + + # Return the assessment + assessment <- data.frame(start_index = start_ind, + end_lower = end_CIs$lower, + end_upper = end_CIs$upper, + assessment = assessment) + +} \ No newline at end of file diff --git a/R/list_to_array.r b/R/list_to_array.r new file mode 100644 index 0000000..e3ec565 --- /dev/null +++ b/R/list_to_array.r @@ -0,0 +1,32 @@ +list_to_array <- function(Occ){ + + # Calculate dimensions + nsp <- length(Occ) + maxyr <- max(unlist(lapply(Occ, FUN = function(x) as.numeric(row.names(x))))) + minyr <- min(unlist(lapply(Occ, FUN = function(x) as.numeric(row.names(x))))) + nyr <- (maxyr - minyr) + 1 + + # Throw an error if the number of iterations is not the same + if(min(unlist(lapply(Occ, ncol))) != max(unlist(lapply(Occ, ncol)))){ + stop('Input data must have the same number of iterations') + } else { + iter <- colnames(Occ[[1]]) + } + + # Build the array + array_holder <- array(data = NA, + dim = c(nsp, nyr, length(iter)), + dimnames = list(species = names(Occ), + years = as.character(minyr:maxyr), + iterations = iter)) + + # Fill the array + for(i in 1:length(Occ)){ + + array_holder[names(Occ[i]), row.names(Occ[[i]]), ] <- Occ[[i]] + + } + + return(array_holder) + +} \ No newline at end of file diff --git a/R/makeZX.R b/R/makeZX.R deleted file mode 100644 index 04dd51f..0000000 --- a/R/makeZX.R +++ /dev/null @@ -1,28 +0,0 @@ -########################################################### -#### You're going to need to create a matrix 'Z' from #### -#### the series of 'years' (something like (1,2,3.....20) # -#### and teh number of 'knots' ("degrees of freedom") ##### -#### an integer probably around 1/3rd of the length ##### -#### of the series. This function does that ############### -########################################################### - - -makeZX <- function(num.knots, covariate){ - - n <- length(covariate) - X <- cbind(rep(1, n), covariate) - - knots <- quantile(unique(covariate), - seq(0, 1, length = (num.knots + 2))[-c(1, (num.knots + 2))]) - - Z_K <- (abs(outer(covariate, knots, "-")))^3 - OMEGA_all <- (abs(outer(knots, knots, "-")))^3 - - svd.OMEGA_all <- svd(OMEGA_all) - sqrt.OMEGA_all <- t(svd.OMEGA_all$v %*% - (t(svd.OMEGA_all$u) * sqrt(svd.OMEGA_all$d))) - Z <- t(solve(sqrt.OMEGA_all, t(Z_K))) - - return(list(Z = Z, X = X)) - -} diff --git a/R/misc_functions.r b/R/misc_functions.r index 74a5258..874d300 100644 --- a/R/misc_functions.r +++ b/R/misc_functions.r @@ -1,545 +1,3 @@ -geomean <- function(x) exp(mean(log(x), na.rm = T)) - -# create a function to read in the data we want from these .rdata files -read_posterior <- function(file, sample_size = NULL, region = NULL){ - - load(file) - - # some old outputs dont have min year in which case make it == 1 - min_year <- ifelse(is.null(out$min_year), 1, out$min_year) - - # Extract the values for each iteration - if(!is.null(region)){ - iteration_values <- t(out$BUGSoutput$sims.list[paste0("psi.fs.r_",region)][[1]]) - } else { - iteration_values <- t(out$BUGSoutput$sims.list$psi.fs) - warning('No region specified therefore defaulting to the full dataset (psi.fs)') - } - - if(!is.null(sample_size)){ - # Sample is needed - iteration_values <- iteration_values[, sample(ncol(iteration_values), - size = sample_size, - replace = FALSE)] - } - - # Name the rows by year - row.names(iteration_values) <- min_year:(min_year + nrow(iteration_values) - 1) - - # Name the columns by iteration - colnames(iteration_values) <- paste('i', 1:ncol(iteration_values), sep = '') - - # Add rhat & sd - if(!is.null(region)){ - sum_dat <- out$BUGSoutput$summary[grep(paste0("psi.fs.r_", region), row.names(out$BUGSoutput$summary)), c('sd', 'Rhat')] - } else { - sum_dat <- out$BUGSoutput$summary[grep("psi.fs\\[", row.names(out$BUGSoutput$summary)), c('sd', 'Rhat')] - } - -# sum_dat <- out$BUGSoutput$summary[grepl("psi.fs",row.names(out$BUGSoutput$summary)), -# c('sd', 'Rhat')] - - iteration_values <- cbind(iteration_values, sum_dat) - - return(iteration_values) -} - -remove_bad_years <- function(species_name, subset_table, list_summaries){ - - # Good years for this species - good_years <- names(subset_table[species_name, subset_table[species_name, ] == TRUE]) - - # Data for this species - data <- list_summaries[[species_name]] - - # Now subset - new_data <- data[good_years, ] - - return(new_data) - -} - -# Create a function that will calculate the geomean from the list -# of species for a given year -list_geomean <- function(data_list, year, ignore_species = NULL){ - - all_values <- NULL - - species <- names(data_list) - - if(!is.null(ignore_species)){ - - species <- species[!species %in% ignore_species] - - } - - count <- 0 - - for(sp in species){ - # if the species has a value for this year add its data - if(as.character(year) %in% row.names(data_list[[sp]])){ - all_values <- c(all_values, median(data_list[[sp]][as.character(year),])) - count <- count + 1 - } - } - - if(!is.null(all_values)){ - year_mean <- geomean(all_values) - attr(year_mean, 'n_species') <- count - return(year_mean) - } else { - return(NA) - } -} - -# Create a function to apply multiplier to years after a species leaves -# year - is the first of the years to be adjusted ie the first year in -# which the species is absent -leaving_multiplier <- function(data_list, year, multiplier){ - - for(sp in names(data_list)){ - - data_list[[sp]][as.numeric(row.names(data_list[[sp]])) >= year,] <- data_list[[sp]][as.numeric(row.names(data_list[[sp]])) >= year,] * multiplier - - } - - return(data_list) - -} - -# Create a function that rescales a set of data so that the first year geomean -# is equal to the geomean given -entering_multiplier <- function(data_list, gm){ - - for(sp in names(data_list)){ - - multiplier <- gm / median(data_list[[sp]][1,]) - - data_list[[sp]] <- data_list[[sp]] * multiplier - - } - - return(data_list) - -} - -# A function to take in a list of tables and cap at the max and min index values -cap_index <- function(data_list, max = 10000, min = 1){ - - for(sp in names(data_list)){ - - data_list[[sp]][data_list[[sp]] > max] <- max - data_list[[sp]][data_list[[sp]] < min] <- min - - } - - return(data_list) - -} - -# A function to bootstrap across a rescaled list to get confidence intervals -bootstrap_posteriors <- function(rescaled_list, iterations = 10000, years){ - - pb <- txtProgressBar() - cat(paste('Bootstrapping -', iterations, 'iterations', '\n')) - - rep <- function(rescaled_list, progress, years){ - - setTxtProgressBar(pb, progress) - - sampled <- sample(rescaled_list, length(rescaled_list), replace = TRUE) - - return(sapply(years, FUN = function(x) list_geomean(sampled, year = x))) - - } - - bootstraps <- sapply(seq(from = 0, to = 1, length.out = iterations), - FUN = function(x) rep(rescaled_list, x, years)) - bootstrap_CIs <- t(apply(bootstraps, 1, quantile, probs = c(0.025, 0.975), na.rm = TRUE)) - - colnames(bootstrap_CIs) <- paste('bootstrap', colnames(bootstrap_CIs), sep = '_') - - cat('\n') - - return(bootstrap_CIs) - -} - -# A function to get the quantiles for each year -list_quantiles_CI <- function(rescaled_list, years, quantile_min = 0.025, quantile_max = 0.975){ - - cat('Calculating quantiles and confidence intervals...') - - yr_val <- function(year, rescaled_list){ - - year_vals <- lapply(rescaled_list, FUN = function(x){ - if(as.character(year) %in% row.names(x)){ - return(x[as.character(year),]) - } else { - return(NULL) - } - }) - - # Calculate the quantiles of all the posterior points from this year - data_quantiles <- quantile(unlist(year_vals), probs = c(quantile_min, quantile_max), na.rm = TRUE) - names(data_quantiles) <- paste('quantile', - gsub('%', '', names(data_quantiles)), - sep = '_') - - # rbind all the species from this year together, giving us a table, - # each row a species, each column an interation - if(is.list(year_vals)){ - - yr_iterations <- do.call(rbind, year_vals) - - } else { #if only one species is present - - yr_iterations <- t(as.data.frame(year_vals)) - - } - - # apply geomean across columns, giving one geomean for each iteration - yr_iteration_geomeans <- apply(X = yr_iterations, MARGIN = 2, FUN = geomean) - - # take the 95% CIs from these geomeans - geomean_quantiles <- quantile(yr_iteration_geomeans, probs = c(0.025, 0.975), na.rm = TRUE) - names(geomean_quantiles) <- paste('geomean_CI', - gsub('%', '', names(geomean_quantiles)), - sep = '_') - # combine this data in a easy to handle format - range_data <- cbind(t(as.data.frame(geomean_quantiles)), t(as.data.frame(data_quantiles))) - row.names(range_data) <- year - return(range_data) - - } - - range_summaries <- do.call(rbind, lapply(years, FUN = function(x) yr_val(year = x, rescaled_list = rescaled_list))) - - cat('done\n') - - return(range_summaries) - -} - -remove_bad_species <- function(Occ, threshold_sd, threshold_yrs, threshold_Rhat){ - - # If we have sd and Rhat from the input file use those - if('sd' %in% dimnames(Occ)[[3]] & 'Rhat' %in% dimnames(Occ)[[3]]){ - - reliable <- Occ[ , ,'sd', drop = FALSE] < threshold_sd & Occ[ , ,'Rhat', drop = FALSE] < threshold_Rhat - - # drop the third dimension (we want to keep the 3rd dimension even if it has - # length 1, so this is a little hacky) - reliable <- apply(reliable, c(1,2), mean) - reliable[reliable == 0] <- NA - - # set the posteriors where sd or Rhat not met to NA - # remove the sd and Rhat columns - Occ <- Occ[ , , !dimnames(Occ)[[3]] %in% c('sd','Rhat'), drop = FALSE] - - # else if it is an array calc sd on the fly - } else { - - # Calculate standard deviations - sds <- apply(Occ, c(1,2), sd, na.rm = TRUE) - - # first define reliable estimates as those with standard deviations lower than the threshold - reliable <- sds < threshold_sd - - # convert the FALSE elements to NA (for the maths to work below) - reliable[!reliable] <- NA - - } - - OccRel <- sapply(1:dim(Occ)[3], function(j) Occ[ , ,j] * reliable, - simplify='array') - - # OccRel is now identical to Occ except that unreliable estimates have been changed to NA - # now strip out species with fewer reliable years than the desired number - OccRel <- OccRel[rowSums(reliable, na.rm=T) >= threshold_yrs, , , drop = FALSE] - - if(dim(OccRel)[1] == 0) stop('None of your species meet the thresholds') - - # Save the good years table - for the species that pass - # the thresholds - as an attribute of the data returned - attr(OccRel, 'good_years') <- reliable[rowSums(reliable, na.rm = T) >= threshold_yrs, ] - return(OccRel) - -} - -lambda_calc <- function(x){ - - # simpler than before. It assumes data are on the Log scale - # takes just a vector of occupancy scores, not a 3D array - LogLambda <- rep(NA, length(x)) - - # All values should be NA then we start after the first year - # with data. The first year with data is set to 0 - yearOne <- min(which(!is.na(x))) - LogLambda[yearOne] <- 0 - - for (t in (yearOne + 1):length(x)){ - - relyrs <- (!is.na(x[1:(t - 1)])) - - if (any(relyrs)){ - - mrry <- max(which(relyrs)) # most recent reliable year - LogLambda[(mrry + 1):t] <- (x[t] - x[mrry])/(t - mrry) - - } else { - - LogLambda[t] <- NA - - } - } - - return(LogLambda) - -} - -getData <- function(input, sample_size = NULL, region = NULL){ - - if(!is.null(sample_size)) if(!is.numeric(sample_size)) stop('sample_size must be numeric') - - - if(class(input) == 'character'){ - # get files from the input directory - files <- list.files(path = paste(input), ignore.case = TRUE, pattern = '\\.rdata$') # list of the files to loop through - - # sense check these file names - if(length(files) == 0) stop('No .rdata files found in ', input) - if(length(files) < length(list.files(path = input))) warning('Not all files in ', input, ' are .rdata files, other file types have been ignored') - - cat('Loading data...') - # Use lapply to run this function on all files - org <- getwd() - setwd(input) - Occ <- sapply(files, read_posterior, - simplify = 'array', sample_size = sample_size, region = region) - setwd(org) - cat('done\n') - - # This does not come back as an array if the individual species - # results do not have the same dimensions, this happens when the - # data come from different runs of the occupancy models - if(class(Occ) != 'array'){ - - Occ <- list_to_array(Occ) - - } else { - - # reorder dimensions into a more logical order - # Species - Year - Iteration - Occ <- aperm(Occ, c(3,1,2)) - - } - - return(Occ) - - } else if(class(input) == "array"){ - - if(!is.null(sample_size)){ - - input <- input[ , , sample(ncol(input), - size = sample_size, - replace = FALSE)] - - } - - return(input) - - } else { - - stop('Input should be either a file path or an array') - - } - -} - -list_to_array <- function(Occ){ - - # Calculate dimensions - nsp <- length(Occ) - maxyr <- max(unlist(lapply(Occ, FUN = function(x) as.numeric(row.names(x))))) - minyr <- min(unlist(lapply(Occ, FUN = function(x) as.numeric(row.names(x))))) - nyr <- (maxyr - minyr) + 1 - - # Throw an error if the number of iterations is not the same - if(min(unlist(lapply(Occ, ncol))) != max(unlist(lapply(Occ, ncol)))){ - stop('Input data must have the same number of iterations') - } else { - iter <- colnames(Occ[[1]]) - } - - # Build the array - array_holder <- array(data = NA, - dim = c(nsp, nyr, length(iter)), - dimnames = list(species = names(Occ), - years = as.character(minyr:maxyr), - iterations = iter)) - - # Fill the array - for(i in 1:length(Occ)){ - - array_holder[names(Occ[i]), row.names(Occ[[i]]), ] <- Occ[[i]] - - } - - return(array_holder) - -} - -subset_years <- function(Occ, year_range = NULL){ - - # The second dimension of Occ is years - year_names <- dimnames(Occ)[[2]] - - if(is.null(year_names)){ - - if(max(year_range) > dim(Occ)[2]){ - - stop(paste('Years are un-named in your data and the maximum year in your year range [', - max(year_range), - '] exceeds the number of years in the data [', - dim(Occ)[2], - ']', sep = '') - ) - - } - - Occ <- Occ[ , seq(from = min(year_range), to = max(year_range)), , drop = FALSE] - - } else { - - Occ <- Occ[ , as.character(seq(from = min(year_range), to = max(year_range))), , drop = FALSE] - - } - -} - -species_assessment <- function(dat, - method = "lambda", - start_year = NULL, - end_year = NULL, - species_stat = 'mean', - plot = FALSE) { - # Sense checks - if(!method %in% c("lambda", "bma")) stop("Method must be one of 'lambda' or 'bma'") - if(!species_stat %in% c('mean', 'median')) stop("species_stat must be either 'mean' or 'median'") - - if(method == "lambda") { - - # lambda method - LogLambda <- dat - - # If we are subsetting - if(!is.null(start_year) | !is.null(end_year)) { - - # If the assessment is only over a subset of the years do the subsetting first - if(is.null(dimnames(LogLambda)[[2]])) { - - if(is.null(start_year)) start_year <- 1 - if(is.null(end_year)) end_year <- dim(LogLambda)[2] - - if(end_year > dim(LogLambda)[2]) { - - stop(paste('Years are un-named in your data and the specified end_year [', - end_year, - '] exceeds the number of years in the data [', - dim(LogLambda)[2], - ']', sep = '')) - - } else { - - LogLambda <- LogLambda[ , start_year:end_year, ] - - } - - } else { - - if(is.null(start_year)) start_year <- as.character(min(as.numeric(dimnames(LogLambda)[[2]]))) - if(is.null(end_year)) end_year <- as.character(max(as.numeric(dimnames(LogLambda)[[2]]))) - - LogLambda <- LogLambda[ , as.character(start_year:end_year), ] - - } - - } - - # Calculate the average change across this time period - if(species_stat == 'mean') spLogLamda <- rowMeans(apply(LogLambda, c(1,2), mean, na.rm = T), na.rm = T) # one value per species - if(species_stat == 'median') spLogLamda <- apply(apply(LogLambda, c(1,2), mean, na.rm = T), 1, FUN = median, na.rm = T) - - # Remove NAs - spLogLamda <- spLogLamda[!is.na(spLogLamda)] - - # this last value is simple, but conflates uncertainty with interannual variation - # of one value per species, convert to a percentage change per year - sp_pcpy <- 100 * (exp(spLogLamda) - 1) - - # Assign to cats - sp_cat <- cut(sp_pcpy, - breaks = c(-Inf,-2.73,-1.14,1.16,2.81,Inf), - labels = c('strong decrease','decrease', 'no change', 'increase','strong increase'), - ordered = T) - - # build DD - sp_change <- data.frame(percent_change_year = sp_pcpy, category = sp_cat) - - # Plot is desired - if(plot) plot_trend_stack(species_change = sp_change$category) - - return(sp_change) - - } else { - - # bma method - bma_df <- dat - - # mean growth rate between years per species - spgrowth <- attr(bma_df, "model")$mean$spgrowth - - yr_df <- data.frame(startyr = seq(min(bma_df$year), (max(bma_df$year) - 1), 1), - endyr = seq(min(bma_df$year) + 1, max(bma_df$year), 1), - yr_col = seq(1, ncol(spgrowth), 1)) - - if(is.null(start_year)) start_year <- 1 - # convert years from CE to numeric (e.g., 1970 to 1) - else start_year <- yr_df[yr_df$startyr == start_year,]$yr_col - - if(is.null(end_year)) end_year <- ncol(spgrowth) - # convert years from CE to numeric (e.g., 2016 to 46) - else end_year <- yr_df[yr_df$endyr == end_year,]$yr_col - - # subset to focal years - spgrowth_sub <- spgrowth[ , start_year:end_year] - - # average growth rate across years per species - if(species_stat == 'mean') spgrowth_av <- rowMeans(spgrowth_sub) - if(species_stat == 'median') spgrowth_av <- apply(spgrowth_sub, 1, median, na.rm = TRUE) - - # back-transform from log-scale - spg_pcpy <- 100*(exp(spgrowth_av)-1) - - # assign to categories - spg_cat <- cut(spg_pcpy, - breaks = c(-Inf,-2.73,-1.14,1.16,2.81,Inf), - labels = c('Strong decrease','Decrease', 'No change', 'Increase','Strong increase'), - ordered = TRUE) - - # build dataframe - sp_change <- data.frame(percent_change_year = spg_pcpy, category = spg_cat) - - # Plot is desired - if(plot) plot_trend_stack(species_change = sp_change$category) - - return(sp_change) - - } - -} - indicator_assessment <- function(summary_table, start_year = NULL, end_year = NULL){ diff --git a/R/misc_functions/bootstrap_posteriors.r b/R/misc_functions/bootstrap_posteriors.r new file mode 100644 index 0000000..8d90998 --- /dev/null +++ b/R/misc_functions/bootstrap_posteriors.r @@ -0,0 +1,27 @@ +# A function to bootstrap across a rescaled list to get confidence intervals +bootstrap_posteriors <- function(rescaled_list, iterations = 10000, years){ + + pb <- txtProgressBar() + cat(paste('Bootstrapping -', iterations, 'iterations', '\n')) + + rep <- function(rescaled_list, progress, years){ + + setTxtProgressBar(pb, progress) + + sampled <- sample(rescaled_list, length(rescaled_list), replace = TRUE) + + return(sapply(years, FUN = function(x) list_geomean(sampled, year = x))) + + } + + bootstraps <- sapply(seq(from = 0, to = 1, length.out = iterations), + FUN = function(x) rep(rescaled_list, x, years)) + bootstrap_CIs <- t(apply(bootstraps, 1, quantile, probs = c(0.025, 0.975), na.rm = TRUE)) + + colnames(bootstrap_CIs) <- paste('bootstrap', colnames(bootstrap_CIs), sep = '_') + + cat('\n') + + return(bootstrap_CIs) + +} \ No newline at end of file diff --git a/R/misc_functions/cap_index.r b/R/misc_functions/cap_index.r new file mode 100644 index 0000000..c7c049f --- /dev/null +++ b/R/misc_functions/cap_index.r @@ -0,0 +1,13 @@ +# A function to take in a list of tables and cap at the max and min index values +cap_index <- function(data_list, max = 10000, min = 1){ + + for(sp in names(data_list)){ + + data_list[[sp]][data_list[[sp]] > max] <- max + data_list[[sp]][data_list[[sp]] < min] <- min + + } + + return(data_list) + +} \ No newline at end of file diff --git a/R/misc_functions/entering_multiplier.r b/R/misc_functions/entering_multiplier.r new file mode 100644 index 0000000..caccdd3 --- /dev/null +++ b/R/misc_functions/entering_multiplier.r @@ -0,0 +1,15 @@ +# Create a function that rescales a set of data so that the first year geomean +# is equal to the geomean given +entering_multiplier <- function(data_list, gm){ + + for(sp in names(data_list)){ + + multiplier <- gm / median(data_list[[sp]][1,]) + + data_list[[sp]] <- data_list[[sp]] * multiplier + + } + + return(data_list) + +} \ No newline at end of file diff --git a/R/misc_functions/geomean.r b/R/misc_functions/geomean.r new file mode 100644 index 0000000..956d816 --- /dev/null +++ b/R/misc_functions/geomean.r @@ -0,0 +1 @@ +geomean <- function(x) exp(mean(log(x), na.rm = T)) \ No newline at end of file diff --git a/R/misc_functions/getData.r b/R/misc_functions/getData.r new file mode 100644 index 0000000..5fcaed0 --- /dev/null +++ b/R/misc_functions/getData.r @@ -0,0 +1,58 @@ +getData <- function(input, sample_size = NULL, region = NULL){ + + if(!is.null(sample_size)) if(!is.numeric(sample_size)) stop('sample_size must be numeric') + + + if(class(input) == 'character'){ + # get files from the input directory + files <- list.files(path = paste(input), ignore.case = TRUE, pattern = '\\.rdata$') # list of the files to loop through + + # sense check these file names + if(length(files) == 0) stop('No .rdata files found in ', input) + if(length(files) < length(list.files(path = input))) warning('Not all files in ', input, ' are .rdata files, other file types have been ignored') + + cat('Loading data...') + # Use lapply to run this function on all files + org <- getwd() + setwd(input) + Occ <- sapply(files, read_posterior, + simplify = 'array', sample_size = sample_size, region = region) + setwd(org) + cat('done\n') + + # This does not come back as an array if the individual species + # results do not have the same dimensions, this happens when the + # data come from different runs of the occupancy models + if(class(Occ) != 'array'){ + + Occ <- list_to_array(Occ) + + } else { + + # reorder dimensions into a more logical order + # Species - Year - Iteration + Occ <- aperm(Occ, c(3,1,2)) + + } + + return(Occ) + + } else if(class(input) == "array"){ + + if(!is.null(sample_size)){ + + input <- input[ , , sample(ncol(input), + size = sample_size, + replace = FALSE)] + + } + + return(input) + + } else { + + stop('Input should be either a file path or an array') + + } + +} \ No newline at end of file diff --git a/R/misc_functions/lambda_calc.r b/R/misc_functions/lambda_calc.r new file mode 100644 index 0000000..5dc7c7b --- /dev/null +++ b/R/misc_functions/lambda_calc.r @@ -0,0 +1,30 @@ +lambda_calc <- function(x){ + + # simpler than before. It assumes data are on the Log scale + # takes just a vector of occupancy scores, not a 3D array + LogLambda <- rep(NA, length(x)) + + # All values should be NA then we start after the first year + # with data. The first year with data is set to 0 + yearOne <- min(which(!is.na(x))) + LogLambda[yearOne] <- 0 + + for (t in (yearOne + 1):length(x)){ + + relyrs <- (!is.na(x[1:(t - 1)])) + + if (any(relyrs)){ + + mrry <- max(which(relyrs)) # most recent reliable year + LogLambda[(mrry + 1):t] <- (x[t] - x[mrry])/(t - mrry) + + } else { + + LogLambda[t] <- NA + + } + } + + return(LogLambda) + +} \ No newline at end of file diff --git a/R/misc_functions/leaving_multiplier.r b/R/misc_functions/leaving_multiplier.r new file mode 100644 index 0000000..4d2bd99 --- /dev/null +++ b/R/misc_functions/leaving_multiplier.r @@ -0,0 +1,14 @@ +# Create a function to apply multiplier to years after a species leaves +# year - is the first of the years to be adjusted ie the first year in +# which the species is absent +leaving_multiplier <- function(data_list, year, multiplier){ + + for(sp in names(data_list)){ + + data_list[[sp]][as.numeric(row.names(data_list[[sp]])) >= year,] <- data_list[[sp]][as.numeric(row.names(data_list[[sp]])) >= year,] * multiplier + + } + + return(data_list) + +} \ No newline at end of file diff --git a/R/misc_functions/list_geomean.r b/R/misc_functions/list_geomean.r new file mode 100644 index 0000000..1a63109 --- /dev/null +++ b/R/misc_functions/list_geomean.r @@ -0,0 +1,32 @@ +# Create a function that will calculate the geomean from the list +# of species for a given year +list_geomean <- function(data_list, year, ignore_species = NULL){ + + all_values <- NULL + + species <- names(data_list) + + if(!is.null(ignore_species)){ + + species <- species[!species %in% ignore_species] + + } + + count <- 0 + + for(sp in species){ + # if the species has a value for this year add its data + if(as.character(year) %in% row.names(data_list[[sp]])){ + all_values <- c(all_values, median(data_list[[sp]][as.character(year),])) + count <- count + 1 + } + } + + if(!is.null(all_values)){ + year_mean <- geomean(all_values) + attr(year_mean, 'n_species') <- count + return(year_mean) + } else { + return(NA) + } +} \ No newline at end of file diff --git a/R/misc_functions/list_quantiles_CI.r b/R/misc_functions/list_quantiles_CI.r new file mode 100644 index 0000000..0418901 --- /dev/null +++ b/R/misc_functions/list_quantiles_CI.r @@ -0,0 +1,55 @@ +# A function to get the quantiles for each year +list_quantiles_CI <- function(rescaled_list, years, quantile_min = 0.025, quantile_max = 0.975){ + + cat('Calculating quantiles and confidence intervals...') + + yr_val <- function(year, rescaled_list){ + + year_vals <- lapply(rescaled_list, FUN = function(x){ + if(as.character(year) %in% row.names(x)){ + return(x[as.character(year),]) + } else { + return(NULL) + } + }) + + # Calculate the quantiles of all the posterior points from this year + data_quantiles <- quantile(unlist(year_vals), probs = c(quantile_min, quantile_max), na.rm = TRUE) + names(data_quantiles) <- paste('quantile', + gsub('%', '', names(data_quantiles)), + sep = '_') + + # rbind all the species from this year together, giving us a table, + # each row a species, each column an interation + if(is.list(year_vals)){ + + yr_iterations <- do.call(rbind, year_vals) + + } else { #if only one species is present + + yr_iterations <- t(as.data.frame(year_vals)) + + } + + # apply geomean across columns, giving one geomean for each iteration + yr_iteration_geomeans <- apply(X = yr_iterations, MARGIN = 2, FUN = geomean) + + # take the 95% CIs from these geomeans + geomean_quantiles <- quantile(yr_iteration_geomeans, probs = c(0.025, 0.975), na.rm = TRUE) + names(geomean_quantiles) <- paste('geomean_CI', + gsub('%', '', names(geomean_quantiles)), + sep = '_') + # combine this data in a easy to handle format + range_data <- cbind(t(as.data.frame(geomean_quantiles)), t(as.data.frame(data_quantiles))) + row.names(range_data) <- year + return(range_data) + + } + + range_summaries <- do.call(rbind, lapply(years, FUN = function(x) yr_val(year = x, rescaled_list = rescaled_list))) + + cat('done\n') + + return(range_summaries) + +} \ No newline at end of file diff --git a/R/misc_functions/read_posterior.r b/R/misc_functions/read_posterior.r new file mode 100644 index 0000000..994d68a --- /dev/null +++ b/R/misc_functions/read_posterior.r @@ -0,0 +1,43 @@ +# create a function to read in the data we want from these .rdata files +read_posterior <- function(file, sample_size = NULL, region = NULL){ + + load(file) + + # some old outputs dont have min year in which case make it == 1 + min_year <- ifelse(is.null(out$min_year), 1, out$min_year) + + # Extract the values for each iteration + if(!is.null(region)){ + iteration_values <- t(out$BUGSoutput$sims.list[paste0("psi.fs.r_",region)][[1]]) + } else { + iteration_values <- t(out$BUGSoutput$sims.list$psi.fs) + warning('No region specified therefore defaulting to the full dataset (psi.fs)') + } + + if(!is.null(sample_size)){ + # Sample is needed + iteration_values <- iteration_values[, sample(ncol(iteration_values), + size = sample_size, + replace = FALSE)] + } + + # Name the rows by year + row.names(iteration_values) <- min_year:(min_year + nrow(iteration_values) - 1) + + # Name the columns by iteration + colnames(iteration_values) <- paste('i', 1:ncol(iteration_values), sep = '') + + # Add rhat & sd + if(!is.null(region)){ + sum_dat <- out$BUGSoutput$summary[grep(paste0("psi.fs.r_", region), row.names(out$BUGSoutput$summary)), c('sd', 'Rhat')] + } else { + sum_dat <- out$BUGSoutput$summary[grep("psi.fs\\[", row.names(out$BUGSoutput$summary)), c('sd', 'Rhat')] + } + +# sum_dat <- out$BUGSoutput$summary[grepl("psi.fs",row.names(out$BUGSoutput$summary)), +# c('sd', 'Rhat')] + + iteration_values <- cbind(iteration_values, sum_dat) + + return(iteration_values) +} \ No newline at end of file diff --git a/R/misc_functions/remove_bad_years.r b/R/misc_functions/remove_bad_years.r new file mode 100644 index 0000000..5dca196 --- /dev/null +++ b/R/misc_functions/remove_bad_years.r @@ -0,0 +1,14 @@ +remove_bad_years <- function(species_name, subset_table, list_summaries){ + + # Good years for this species + good_years <- names(subset_table[species_name, subset_table[species_name, ] == TRUE]) + + # Data for this species + data <- list_summaries[[species_name]] + + # Now subset + new_data <- data[good_years, ] + + return(new_data) + +} \ No newline at end of file diff --git a/R/misc_functions/subset_years.r b/R/misc_functions/subset_years.r new file mode 100644 index 0000000..f3e1655 --- /dev/null +++ b/R/misc_functions/subset_years.r @@ -0,0 +1,27 @@ +subset_years <- function(Occ, year_range = NULL){ + + # The second dimension of Occ is years + year_names <- dimnames(Occ)[[2]] + + if(is.null(year_names)){ + + if(max(year_range) > dim(Occ)[2]){ + + stop(paste('Years are un-named in your data and the maximum year in your year range [', + max(year_range), + '] exceeds the number of years in the data [', + dim(Occ)[2], + ']', sep = '') + ) + + } + + Occ <- Occ[ , seq(from = min(year_range), to = max(year_range)), , drop = FALSE] + + } else { + + Occ <- Occ[ , as.character(seq(from = min(year_range), to = max(year_range))), , drop = FALSE] + + } + +} \ No newline at end of file diff --git a/R/remove_bad_species.r b/R/remove_bad_species.r new file mode 100644 index 0000000..924d57f --- /dev/null +++ b/R/remove_bad_species.r @@ -0,0 +1,45 @@ +remove_bad_species <- function(Occ, threshold_sd, threshold_yrs, threshold_Rhat){ + + # If we have sd and Rhat from the input file use those + if('sd' %in% dimnames(Occ)[[3]] & 'Rhat' %in% dimnames(Occ)[[3]]){ + + reliable <- Occ[ , ,'sd', drop = FALSE] < threshold_sd & Occ[ , ,'Rhat', drop = FALSE] < threshold_Rhat + + # drop the third dimension (we want to keep the 3rd dimension even if it has + # length 1, so this is a little hacky) + reliable <- apply(reliable, c(1,2), mean) + reliable[reliable == 0] <- NA + + # set the posteriors where sd or Rhat not met to NA + # remove the sd and Rhat columns + Occ <- Occ[ , , !dimnames(Occ)[[3]] %in% c('sd','Rhat'), drop = FALSE] + + # else if it is an array calc sd on the fly + } else { + + # Calculate standard deviations + sds <- apply(Occ, c(1,2), sd, na.rm = TRUE) + + # first define reliable estimates as those with standard deviations lower than the threshold + reliable <- sds < threshold_sd + + # convert the FALSE elements to NA (for the maths to work below) + reliable[!reliable] <- NA + + } + + OccRel <- sapply(1:dim(Occ)[3], function(j) Occ[ , ,j] * reliable, + simplify='array') + + # OccRel is now identical to Occ except that unreliable estimates have been changed to NA + # now strip out species with fewer reliable years than the desired number + OccRel <- OccRel[rowSums(reliable, na.rm=T) >= threshold_yrs, , , drop = FALSE] + + if(dim(OccRel)[1] == 0) stop('None of your species meet the thresholds') + + # Save the good years table - for the species that pass + # the thresholds - as an attribute of the data returned + attr(OccRel, 'good_years') <- reliable[rowSums(reliable, na.rm = T) >= threshold_yrs, ] + return(OccRel) + +} \ No newline at end of file diff --git a/R/species_assessment.r b/R/species_assessment.r new file mode 100644 index 0000000..b33da6e --- /dev/null +++ b/R/species_assessment.r @@ -0,0 +1,121 @@ +species_assessment <- function(dat, + method = "lambda", + start_year = NULL, + end_year = NULL, + species_stat = 'mean', + plot = FALSE) { + # Sense checks + if(!method %in% c("lambda", "bma")) stop("Method must be one of 'lambda' or 'bma'") + if(!species_stat %in% c('mean', 'median')) stop("species_stat must be either 'mean' or 'median'") + + if(method == "lambda") { + + # lambda method + LogLambda <- dat + + # If we are subsetting + if(!is.null(start_year) | !is.null(end_year)) { + + # If the assessment is only over a subset of the years do the subsetting first + if(is.null(dimnames(LogLambda)[[2]])) { + + if(is.null(start_year)) start_year <- 1 + if(is.null(end_year)) end_year <- dim(LogLambda)[2] + + if(end_year > dim(LogLambda)[2]) { + + stop(paste('Years are un-named in your data and the specified end_year [', + end_year, + '] exceeds the number of years in the data [', + dim(LogLambda)[2], + ']', sep = '')) + + } else { + + LogLambda <- LogLambda[ , start_year:end_year, ] + + } + + } else { + + if(is.null(start_year)) start_year <- as.character(min(as.numeric(dimnames(LogLambda)[[2]]))) + if(is.null(end_year)) end_year <- as.character(max(as.numeric(dimnames(LogLambda)[[2]]))) + + LogLambda <- LogLambda[ , as.character(start_year:end_year), ] + + } + + } + + # Calculate the average change across this time period + if(species_stat == 'mean') spLogLamda <- rowMeans(apply(LogLambda, c(1,2), mean, na.rm = T), na.rm = T) # one value per species + if(species_stat == 'median') spLogLamda <- apply(apply(LogLambda, c(1,2), mean, na.rm = T), 1, FUN = median, na.rm = T) + + # Remove NAs + spLogLamda <- spLogLamda[!is.na(spLogLamda)] + + # this last value is simple, but conflates uncertainty with interannual variation + # of one value per species, convert to a percentage change per year + sp_pcpy <- 100 * (exp(spLogLamda) - 1) + + # Assign to cats + sp_cat <- cut(sp_pcpy, + breaks = c(-Inf,-2.73,-1.14,1.16,2.81,Inf), + labels = c('strong decrease','decrease', 'no change', 'increase','strong increase'), + ordered = T) + + # build DD + sp_change <- data.frame(percent_change_year = sp_pcpy, category = sp_cat) + + # Plot is desired + if(plot) plot_trend_stack(species_change = sp_change$category) + + return(sp_change) + + } else { + + # bma method + bma_df <- dat + + # mean growth rate between years per species + spgrowth <- attr(bma_df, "model")$mean$spgrowth + + yr_df <- data.frame(startyr = seq(min(bma_df$year), (max(bma_df$year) - 1), 1), + endyr = seq(min(bma_df$year) + 1, max(bma_df$year), 1), + yr_col = seq(1, ncol(spgrowth), 1)) + + if(is.null(start_year)) start_year <- 1 + # convert years from CE to numeric (e.g., 1970 to 1) + else start_year <- yr_df[yr_df$startyr == start_year,]$yr_col + + if(is.null(end_year)) end_year <- ncol(spgrowth) + # convert years from CE to numeric (e.g., 2016 to 46) + else end_year <- yr_df[yr_df$endyr == end_year,]$yr_col + + # subset to focal years + spgrowth_sub <- spgrowth[ , start_year:end_year] + + # average growth rate across years per species + if(species_stat == 'mean') spgrowth_av <- rowMeans(spgrowth_sub) + if(species_stat == 'median') spgrowth_av <- apply(spgrowth_sub, 1, median, na.rm = TRUE) + + # back-transform from log-scale + spg_pcpy <- 100*(exp(spgrowth_av)-1) + + # assign to categories + spg_cat <- cut(spg_pcpy, + breaks = c(-Inf,-2.73,-1.14,1.16,2.81,Inf), + labels = c('Strong decrease','Decrease', 'No change', 'Increase','Strong increase'), + ordered = TRUE) + + # build dataframe + sp_change <- data.frame(percent_change_year = spg_pcpy, category = spg_cat) + + # Plot is desired + if(plot) plot_trend_stack(species_change = sp_change$category) + + return(sp_change) + + } + +} \ No newline at end of file From 6f750b0aa3faf1c852562e368a797b3cbab07fd5 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Tue, 2 Apr 2024 17:02:09 +0100 Subject: [PATCH 24/45] test to see if this prevents GHA compiling vignette --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 04a4706..d85115e 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -47,4 +47,4 @@ jobs: - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true - build_args: 'c("--no-build-vignettes","--no-manual","--compact-vignettes=gs+qpdf")' + build_args: 'c("--no-build-vignettes","--ignore-vignettes","--no-manual","--compact-vignettes=gs+qpdf")' \ No newline at end of file From 0a48c041084c080ae2deeefede7f3dcba5338b2b Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 3 Apr 2024 10:15:19 +0100 Subject: [PATCH 25/45] another test of continuous integration --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index d85115e..74d179b 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -47,4 +47,4 @@ jobs: - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true - build_args: 'c("--no-build-vignettes","--ignore-vignettes","--no-manual","--compact-vignettes=gs+qpdf")' \ No newline at end of file + build_args: 'c("--no-build-vignettes", "--ignore-vignettes", "--no-manual")' \ No newline at end of file From eafb881874e69648390c2622457af136f0bc9f6a Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 3 Apr 2024 11:56:02 +0100 Subject: [PATCH 26/45] This should fix CI --- .github/workflows/R-CMD-check.yaml | 3 ++- .github/workflows/test.r | 2 ++ DESCRIPTION | 2 +- vignettes/BRCindicators.Rmd | 3 +++ 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/test.r diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 74d179b..a4449c4 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -47,4 +47,5 @@ jobs: - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true - build_args: 'c("--no-build-vignettes", "--ignore-vignettes", "--no-manual")' \ No newline at end of file + build_args: 'c("--no-build-vignettes")' + args: = 'c("--no-vignettes")'' \ No newline at end of file diff --git a/.github/workflows/test.r b/.github/workflows/test.r new file mode 100644 index 0000000..d02dfe8 --- /dev/null +++ b/.github/workflows/test.r @@ -0,0 +1,2 @@ +library(rcmdcheck) +rcmdcheck(build_args = c("--no-build-vignettes"), args = "--no-vignettes") diff --git a/DESCRIPTION b/DESCRIPTION index 44d63ab..09fc665 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -18,7 +18,7 @@ Authors@R: c( comment = c(ORCID = "0000-0003-0840-9575")), person("Stephen", "Freeman", role = "aut"), person("Nick", "Isaac", role = "aut", - comment = c(ORCID = "https://orcid.org/0000-0002-4869-8052")) + comment = c(ORCID = "0000-0002-4869-8052")) ) Maintainer: Tom August Description: This package contains a number of functions used to create diff --git a/vignettes/BRCindicators.Rmd b/vignettes/BRCindicators.Rmd index 6501b5c..533e372 100644 --- a/vignettes/BRCindicators.Rmd +++ b/vignettes/BRCindicators.Rmd @@ -21,6 +21,9 @@ knitr::opts_chunk$set(echo = TRUE, ``` ```{r libraries} require(snowfall) + + +to produce an error ``` # Introduction From db27ee5805c9657b4be75debdc48c93b40f32c15 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 3 Apr 2024 11:59:56 +0100 Subject: [PATCH 27/45] delete test file --- .github/workflows/test.r | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .github/workflows/test.r diff --git a/.github/workflows/test.r b/.github/workflows/test.r deleted file mode 100644 index d02dfe8..0000000 --- a/.github/workflows/test.r +++ /dev/null @@ -1,2 +0,0 @@ -library(rcmdcheck) -rcmdcheck(build_args = c("--no-build-vignettes"), args = "--no-vignettes") From 6d518fc36533fecf3f9748372fbbdbcd66f007ba Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 3 Apr 2024 12:03:13 +0100 Subject: [PATCH 28/45] silly error fix --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index a4449c4..d5d74de 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -48,4 +48,4 @@ jobs: with: upload-snapshots: true build_args: 'c("--no-build-vignettes")' - args: = 'c("--no-vignettes")'' \ No newline at end of file + args: 'c("--no-vignettes")'' \ No newline at end of file From d3279f6228ef7e6a0aa46227db7dc5628bd0ac79 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 3 Apr 2024 12:12:49 +0100 Subject: [PATCH 29/45] fix of syntax error --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index d5d74de..5f0489d 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -48,4 +48,4 @@ jobs: with: upload-snapshots: true build_args: 'c("--no-build-vignettes")' - args: 'c("--no-vignettes")'' \ No newline at end of file + args: 'c("--no-vignettes")' \ No newline at end of file From 6fbb7fee9ce531f369233904671c3af928e98a28 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Mon, 8 Apr 2024 09:24:16 +0100 Subject: [PATCH 30/45] fix to description syntax --- .github/workflows/R-CMD-check.yaml | 4 ++-- DESCRIPTION | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 5f0489d..3c79fe6 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -47,5 +47,5 @@ jobs: - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true - build_args: 'c("--no-build-vignettes")' - args: 'c("--no-vignettes")' \ No newline at end of file + build_args: 'c("--no-build-vignettes", "--no-manual")' + args: 'c("--no-vignettes", "--no-manual")' \ No newline at end of file diff --git a/DESCRIPTION b/DESCRIPTION index 09fc665..a0218a5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -4,9 +4,9 @@ Title: Creating multispecies biodiversity indicators Version: 1.3.7 Date: 2021-05-24 Authors@R: c( - person("Dylan", "Carbone", , "dylcar@ceh.ac.uk", role = c("aut", "cre"), + person("Dylan", "Carbone", email = "dylcar@ceh.ac.uk", role = c("aut", "cre"), comment = c(ORCID = "0009-0003-5290-786X")), - person("Tom", "August", , "tomaug@ceh.ac.uk", role = "aut", + person("Tom", "August", email = "tomaug@ceh.ac.uk", role = "aut", comment = c(ORCID = "0000-0003-1116-3385")), person("Gary", "Powney", role = "aut", comment = c(ORCID = "0000-0003-3313-7786")), From 060ef9b2d8c99efd98d42794f94a67c007bdf1ea Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 09:45:30 +0100 Subject: [PATCH 31/45] Restored ZX; Moved helper functions to misc folder --- DESCRIPTION | 2 +- R/bayesian_meta_analysis.R | 1 - R/misc_functions.r | 29 ------------------- R/{ => misc_functions}/indicator_assessment.r | 0 R/{ => misc_functions}/makeZ.R | 0 R/misc_functions/makeZX.r | 28 ++++++++++++++++++ 6 files changed, 29 insertions(+), 31 deletions(-) delete mode 100644 R/misc_functions.r rename R/{ => misc_functions}/indicator_assessment.r (100%) rename R/{ => misc_functions}/makeZ.R (100%) create mode 100644 R/misc_functions/makeZX.r diff --git a/DESCRIPTION b/DESCRIPTION index a0218a5..a41d2c8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,7 +20,7 @@ Authors@R: c( person("Nick", "Isaac", role = "aut", comment = c(ORCID = "0000-0002-4869-8052")) ) -Maintainer: Tom August +Maintainer: Dylan Carbone Description: This package contains a number of functions used to create biodiversity indicators. License: GPL-3 diff --git a/R/bayesian_meta_analysis.R b/R/bayesian_meta_analysis.R index e8b8685..c9c5ecd 100644 --- a/R/bayesian_meta_analysis.R +++ b/R/bayesian_meta_analysis.R @@ -56,7 +56,6 @@ #' plot_indicator(indicator = bma_indicator[,'Index.Mprime'], #' CIs = bma_indicator[,c(3,4)]) - bma <- function (data, plot = TRUE, model = 'smooth', diff --git a/R/misc_functions.r b/R/misc_functions.r deleted file mode 100644 index 874d300..0000000 --- a/R/misc_functions.r +++ /dev/null @@ -1,29 +0,0 @@ -indicator_assessment <- function(summary_table, - start_year = NULL, - end_year = NULL){ - - if(is.null(start_year)) start_year <- min(summary_table$year) - if(is.null(end_year)) end_year <- max(summary_table$year) - - # Get the indicator value at the start - start_ind <- summary_table$indicator[summary_table$year == start_year] - - # Get the confidence intervals for the end of the period - end_CIs <- summary_table[summary_table$year == end_year, c('lower', 'upper')] - - # assess - if(start_ind < end_CIs$upper & start_ind > end_CIs$lower){ - assessment <- 'stable' - } else if(start_ind < end_CIs$lower){ - assessment <- 'increasing' - } else if(start_ind > end_CIs$upper){ - assessment <- 'decreasing' - } - - # Return the assessment - assessment <- data.frame(start_index = start_ind, - end_lower = end_CIs$lower, - end_upper = end_CIs$upper, - assessment = assessment) - -} \ No newline at end of file diff --git a/R/indicator_assessment.r b/R/misc_functions/indicator_assessment.r similarity index 100% rename from R/indicator_assessment.r rename to R/misc_functions/indicator_assessment.r diff --git a/R/makeZ.R b/R/misc_functions/makeZ.R similarity index 100% rename from R/makeZ.R rename to R/misc_functions/makeZ.R diff --git a/R/misc_functions/makeZX.r b/R/misc_functions/makeZX.r new file mode 100644 index 0000000..bc745e7 --- /dev/null +++ b/R/misc_functions/makeZX.r @@ -0,0 +1,28 @@ +########################################################### +#### You're going to need to create a matrix 'Z' from #### +#### the series of 'years' (something like (1,2,3.....20) # +#### and teh number of 'knots' ("degrees of freedom") ##### +#### an integer probably around 1/3rd of the length ##### +#### of the series. This function does that ############### +########################################################### + + +makeZX <- function(num.knots, covariate){ + + n <- length(covariate) + X <- cbind(rep(1, n), covariate) + + knots <- quantile(unique(covariate), + seq(0, 1, length = (num.knots + 2))[-c(1, (num.knots + 2))]) + + Z_K <- (abs(outer(covariate, knots, "-")))^3 + OMEGA_all <- (abs(outer(knots, knots, "-")))^3 + + svd.OMEGA_all <- svd(OMEGA_all) + sqrt.OMEGA_all <- t(svd.OMEGA_all$v %*% + (t(svd.OMEGA_all$u) * sqrt(svd.OMEGA_all$d))) + Z <- t(solve(sqrt.OMEGA_all, t(Z_K))) + + return(list(Z = Z, X = X)) + +} \ No newline at end of file From 384fa653e060b740c1fc7d1e2369e53785902f9e Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 10:28:42 +0100 Subject: [PATCH 32/45] Removal of misc folder fixes test run requiring misc functions --- .github/workflows/test-coverage.yaml | 50 ------------------- R/{misc_functions => }/bootstrap_posteriors.r | 0 R/{misc_functions => }/cap_index.r | 0 R/{misc_functions => }/entering_multiplier.r | 0 R/{misc_functions => }/geomean.r | 0 R/{misc_functions => }/getData.r | 0 R/{misc_functions => }/indicator_assessment.r | 0 R/{misc_functions => }/lambda_calc.r | 0 R/{misc_functions => }/leaving_multiplier.r | 0 R/{misc_functions => }/list_geomean.r | 0 R/{misc_functions => }/list_quantiles_CI.r | 0 R/{misc_functions => }/makeZ.R | 0 R/{misc_functions => }/makeZX.r | 0 R/{misc_functions => }/read_posterior.r | 0 R/{misc_functions => }/remove_bad_years.r | 0 R/{misc_functions => }/subset_years.r | 0 16 files changed, 50 deletions(-) delete mode 100644 .github/workflows/test-coverage.yaml rename R/{misc_functions => }/bootstrap_posteriors.r (100%) rename R/{misc_functions => }/cap_index.r (100%) rename R/{misc_functions => }/entering_multiplier.r (100%) rename R/{misc_functions => }/geomean.r (100%) rename R/{misc_functions => }/getData.r (100%) rename R/{misc_functions => }/indicator_assessment.r (100%) rename R/{misc_functions => }/lambda_calc.r (100%) rename R/{misc_functions => }/leaving_multiplier.r (100%) rename R/{misc_functions => }/list_geomean.r (100%) rename R/{misc_functions => }/list_quantiles_CI.r (100%) rename R/{misc_functions => }/makeZ.R (100%) rename R/{misc_functions => }/makeZX.r (100%) rename R/{misc_functions => }/read_posterior.r (100%) rename R/{misc_functions => }/remove_bad_years.r (100%) rename R/{misc_functions => }/subset_years.r (100%) diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml deleted file mode 100644 index fb7b0c4..0000000 --- a/.github/workflows/test-coverage.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples -# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help -on: - push: - branches: [master, pull_requests] - pull_request: - branches: [master, pull_requests] - -name: test-coverage - -jobs: - test-coverage: - runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - - steps: - - uses: actions/checkout@v4 - - - uses: r-lib/actions/setup-r@v2 - with: - use-public-rspm: true - - - uses: r-lib/actions/setup-r-dependencies@v2 - with: - extra-packages: any::covr - needs: coverage - - - name: Test coverage - run: | - covr::codecov( - quiet = FALSE, - clean = FALSE, - install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") - ) - shell: Rscript {0} - - - name: Show testthat output - if: always() - run: | - ## -------------------------------------------------------------------- - find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true - shell: bash - - - name: Upload test results - if: failure() - uses: actions/upload-artifact@v4 - with: - name: coverage-test-failures - path: ${{ runner.temp }}/package diff --git a/R/misc_functions/bootstrap_posteriors.r b/R/bootstrap_posteriors.r similarity index 100% rename from R/misc_functions/bootstrap_posteriors.r rename to R/bootstrap_posteriors.r diff --git a/R/misc_functions/cap_index.r b/R/cap_index.r similarity index 100% rename from R/misc_functions/cap_index.r rename to R/cap_index.r diff --git a/R/misc_functions/entering_multiplier.r b/R/entering_multiplier.r similarity index 100% rename from R/misc_functions/entering_multiplier.r rename to R/entering_multiplier.r diff --git a/R/misc_functions/geomean.r b/R/geomean.r similarity index 100% rename from R/misc_functions/geomean.r rename to R/geomean.r diff --git a/R/misc_functions/getData.r b/R/getData.r similarity index 100% rename from R/misc_functions/getData.r rename to R/getData.r diff --git a/R/misc_functions/indicator_assessment.r b/R/indicator_assessment.r similarity index 100% rename from R/misc_functions/indicator_assessment.r rename to R/indicator_assessment.r diff --git a/R/misc_functions/lambda_calc.r b/R/lambda_calc.r similarity index 100% rename from R/misc_functions/lambda_calc.r rename to R/lambda_calc.r diff --git a/R/misc_functions/leaving_multiplier.r b/R/leaving_multiplier.r similarity index 100% rename from R/misc_functions/leaving_multiplier.r rename to R/leaving_multiplier.r diff --git a/R/misc_functions/list_geomean.r b/R/list_geomean.r similarity index 100% rename from R/misc_functions/list_geomean.r rename to R/list_geomean.r diff --git a/R/misc_functions/list_quantiles_CI.r b/R/list_quantiles_CI.r similarity index 100% rename from R/misc_functions/list_quantiles_CI.r rename to R/list_quantiles_CI.r diff --git a/R/misc_functions/makeZ.R b/R/makeZ.R similarity index 100% rename from R/misc_functions/makeZ.R rename to R/makeZ.R diff --git a/R/misc_functions/makeZX.r b/R/makeZX.r similarity index 100% rename from R/misc_functions/makeZX.r rename to R/makeZX.r diff --git a/R/misc_functions/read_posterior.r b/R/read_posterior.r similarity index 100% rename from R/misc_functions/read_posterior.r rename to R/read_posterior.r diff --git a/R/misc_functions/remove_bad_years.r b/R/remove_bad_years.r similarity index 100% rename from R/misc_functions/remove_bad_years.r rename to R/remove_bad_years.r diff --git a/R/misc_functions/subset_years.r b/R/subset_years.r similarity index 100% rename from R/misc_functions/subset_years.r rename to R/subset_years.r From 1ba848e26fcc7fa9bab299ca5b81f6e02e3cd953 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 11:30:05 +0100 Subject: [PATCH 33/45] Removed exports of functions that will be ran internally --- NAMESPACE | 6 ------ R/BRCindicators-package.R | 7 ------- R/plot_growthrates.R | 9 ++------- R/plot_species.R | 1 - R/simulate_indicator.R | 1 - man/BRCindicators-package.Rd | 34 ---------------------------------- 6 files changed, 2 insertions(+), 56 deletions(-) delete mode 100644 R/BRCindicators-package.R delete mode 100644 man/BRCindicators-package.Rd diff --git a/NAMESPACE b/NAMESPACE index ac4a7c8..cdc5e0d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -9,17 +9,12 @@ export(get_bmaBUGScode) export(lambda_indicator) export(msi) export(msi_tool) -export(plot_growthrates) export(plot_indicator) -export(plot_species) -export(plot_spgrowthrates) export(plot_trend_stack) export(rescale_posterior) export(rescale_species) -export(simulate_indicator) export(summarise_occDet) export(trend_assessment) -export(wrap_plot) import(RColorBrewer) import(car) import(ggplot2) @@ -29,5 +24,4 @@ importFrom(boot,inv.logit) importFrom(car,logit) importFrom(coda,as.mcmc) importFrom(coda,mcmc.list) -importFrom(mockery,stub) importFrom(reshape2,dcast) diff --git a/R/BRCindicators-package.R b/R/BRCindicators-package.R deleted file mode 100644 index 5a5adce..0000000 --- a/R/BRCindicators-package.R +++ /dev/null @@ -1,7 +0,0 @@ -#' @keywords internal -"_PACKAGE" - -## usethis namespace: start -#' @importFrom mockery stub -## usethis namespace: end -NULL diff --git a/R/plot_growthrates.R b/R/plot_growthrates.R index a42ab2b..ad96f71 100644 --- a/R/plot_growthrates.R +++ b/R/plot_growthrates.R @@ -1,5 +1,4 @@ #' @import ggplot2 -#' @export plot_growthrates <- function(logLambda, CIs, year = 1:length(logLambda), main=''){ combo <- as.data.frame(cbind(year, logLambda, CIs)) @@ -18,9 +17,7 @@ plot_growthrates <- function(logLambda, CIs, year = 1:length(logLambda), main='' return(NULL) } - -#' @export - +#' @import ggplot2 plot_spgrowthrates <- function(logLambda, upper, lower, year = 1:ncol(logLambda), main=''){ logLambda <- melt(logLambda, value.name='logLambda') @@ -45,9 +42,7 @@ plot_spgrowthrates <- function(logLambda, upper, lower, year = 1:ncol(logLambda) return(NULL) } - -#' @export - +#' @import ggplot2 wrap_plot <- function(model, name="", data_in=NULL){ plot_growthrates(logLambda = model$mean$logLambda, diff --git a/R/plot_species.R b/R/plot_species.R index 4af28f5..78ec013 100644 --- a/R/plot_species.R +++ b/R/plot_species.R @@ -1,5 +1,4 @@ #' @import ggplot2 -#' @export plot_species <- function(spindex, upper, lower, data_in, year = 1:ncol(spindex), main=''){ diff --git a/R/simulate_indicator.R b/R/simulate_indicator.R index 65bf13d..2003ea5 100644 --- a/R/simulate_indicator.R +++ b/R/simulate_indicator.R @@ -5,7 +5,6 @@ ############################################################## #' @import reshape2 -#' @export # tau.spi is the precision in growth rates around the ANNUAL mean # that modelled by sigma in this simulation diff --git a/man/BRCindicators-package.Rd b/man/BRCindicators-package.Rd deleted file mode 100644 index fba9171..0000000 --- a/man/BRCindicators-package.Rd +++ /dev/null @@ -1,34 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/BRCindicators-package.R -\docType{package} -\name{BRCindicators-package} -\alias{BRCindicators} -\alias{BRCindicators-package} -\title{BRCindicators: Creating multispecies biodiversity indicators} -\description{ -This package contains a number of functions used to create biodiversity indicators. -} -\seealso{ -Useful links: -\itemize{ - \item \url{https://github.com/BiologicalRecordsCentre/BRCindicators} - \item Report bugs at \url{https://github.com/BiologicalRecordsCentre/BRCindicators/issues} -} - -} -\author{ -\strong{Maintainer}: Dylan Carbone \email{dylcar@ceh.ac.uk} (\href{https://orcid.org/0009-0003-5290-786X}{ORCID}) - -Authors: -\itemize{ - \item Tom August \email{tomaug@ceh.ac.uk} (\href{https://orcid.org/0000-0003-1116-3385}{ORCID}) - \item Gary Powney (\href{https://orcid.org/0000-0003-3313-7786}{ORCID}) - \item Charlie Outhwaite (\href{https://orcid.org/0000-0001-9997-6780}{ORCID}) - \item Jack Hatfield (\href{https://orcid.org/0000-0002-6361-0629}{ORCID}) - \item Mark Logie (\href{https://orcid.org/0000-0003-0840-9575}{ORCID}) - \item Stephen Freeman - \item Nick Isaac (\href{https://orcid.org/0000-0002-4869-8052}{ORCID}) -} - -} -\keyword{internal} From daa53a2eccf2d1e94593c48fe63d362d4c997925 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 11:33:33 +0100 Subject: [PATCH 34/45] Returned codecov to readme md for pull_requests --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f2384ae..be2d135 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # BRCindicators [![R-CMD-check](https://github.com/DylanCarbone/BRCindicators/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/DylanCarbone/BRCindicators/actions/workflows/R-CMD-check.yaml) +[![Codecov test coverage](https://codecov.io/gh/biologicalrecordscentre/BRCindicators/branch/master/graph/badge.svg)](https://codecov.io/gh/biologicalrecordscentre/BRCindicators?branch=master) From 29cdfce7c8f5eacd63c8f7b7655cb4f5c260092e Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 12:59:03 +0100 Subject: [PATCH 35/45] ruling out causes of requireNamespace failure to detect absence of JAGS installation --- tests/testthat/test-bayesian_meta_analysis.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-bayesian_meta_analysis.R b/tests/testthat/test-bayesian_meta_analysis.R index f0c0d3d..f632a5f 100644 --- a/tests/testthat/test-bayesian_meta_analysis.R +++ b/tests/testthat/test-bayesian_meta_analysis.R @@ -40,7 +40,7 @@ test_that("Does the function output, assuming 'jagsUI' is available, return a da test_that("Does the function return a Markov Chain Monte Carlo (MCMC) output object?", { # skip the test if jagsUI is not installed - if ((!requireNamespace("jagsUI", quietly = TRUE))) { + if (!requireNamespace("jagsUI", quietly = TRUE)) { skip("jagsUI software not available") } From 23f48d11e27cade401e85fe2e350a86d3c7c7f92 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 15:14:24 +0100 Subject: [PATCH 36/45] test of run_jags package to see whether it can detect JAGS installation within GHA --- DESCRIPTION | 1 + NAMESPACE | 1 + R/bayesian_meta_analysis.R | 8 +++++++- R/detect_jags.r | 5 +++++ tests/testthat/helper-bayesian_meta_analysis.R | 8 +++++++- tests/testthat/test-bayesian_meta_analysis.R | 16 ++++++++++++---- 6 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 R/detect_jags.r diff --git a/DESCRIPTION b/DESCRIPTION index a41d2c8..5f1e4a1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -41,6 +41,7 @@ Imports: Suggests: covr, jagsUI, + runjags, knitr, rmarkdown, snowfall, diff --git a/NAMESPACE b/NAMESPACE index cdc5e0d..c208aa9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -20,6 +20,7 @@ import(car) import(ggplot2) import(mgcv) import(reshape2) +import(runjags) importFrom(boot,inv.logit) importFrom(car,logit) importFrom(coda,as.mcmc) diff --git a/R/bayesian_meta_analysis.R b/R/bayesian_meta_analysis.R index c9c5ecd..0b71485 100644 --- a/R/bayesian_meta_analysis.R +++ b/R/bayesian_meta_analysis.R @@ -79,9 +79,15 @@ bma <- function (data, set.seed(seed = seed) +# Check if JAGS is installed +if (!detect_jags()) { +stop("No installation of JAGS has been detected. You can install JAGS from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", + call. = FALSE) +} + # Check if jagsUI is installed if (!requireNamespace("jagsUI", quietly = TRUE)) { -stop("Package 'jagsUI' is needed for the 'bma' function to work. Please install this from CRAN. You will also be required to install JAGS, which you can download from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", +stop("Package 'jagsUI' is needed for the 'bma' function to work. Please install this from CRAN.", call. = FALSE) } diff --git a/R/detect_jags.r b/R/detect_jags.r new file mode 100644 index 0000000..0825473 --- /dev/null +++ b/R/detect_jags.r @@ -0,0 +1,5 @@ +#' @import runjags + +detect_jags <- function(){ + return(runjags::testjags(silent = TRUE)$JAGS.available) +} \ No newline at end of file diff --git a/tests/testthat/helper-bayesian_meta_analysis.R b/tests/testthat/helper-bayesian_meta_analysis.R index 5575d71..eae6bc8 100644 --- a/tests/testthat/helper-bayesian_meta_analysis.R +++ b/tests/testthat/helper-bayesian_meta_analysis.R @@ -31,9 +31,15 @@ output = list() set.seed(seed = seed) +# Check if JAGS is installed +if (!detect_jags()) { +stop("No installation of JAGS has been detected. You can install JAGS from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", + call. = FALSE) +} + # Check if jagsUI is installed if (!requireNamespace("jagsUI", quietly = TRUE)) { -stop("Package 'jagsUI' is needed for the 'bma' function to work. Please insatll this from CRAN. You will also be required to install JAGS, which you can download from https://sourceforge.net/projects/mcmc-jags/files/JAGS/", +stop("Package 'jagsUI' is needed for the 'bma' function to work. Please install this from CRAN.", call. = FALSE) } diff --git a/tests/testthat/test-bayesian_meta_analysis.R b/tests/testthat/test-bayesian_meta_analysis.R index f632a5f..c90bfe1 100644 --- a/tests/testthat/test-bayesian_meta_analysis.R +++ b/tests/testthat/test-bayesian_meta_analysis.R @@ -1,3 +1,11 @@ +test_that("Does the function stop when there is no jags installation?", { + + # Mock `requireNamespace` to return FALSE + mockery::stub(bma, "detect_jags", FALSE) + + expect_error(bma(bayesian_meta_analysis_fake_data), regexp = "No installation of JAGS has been detected") +}) + # Test that function responds correctly when 'jagsUI' is not available test_that("Does the function stop when 'jagsUI' is not available?", { @@ -29,8 +37,8 @@ expect_error(bma(bayesian_meta_analysis_fake_data, model = "smooth_det"), test_that("Does the function output, assuming 'jagsUI' is available, return a dataframe?", { # skip the test if jagsUI is not installed - if ((!requireNamespace("jagsUI", quietly = TRUE))) { - skip("jagsUI software not available") + if (!runjags::testjags(silent = TRUE)$JAGS.available) { + skip("JAGS software not detectable") } expect_equal(class(suppressWarnings(bma(bayesian_meta_analysis_fake_data, plot = FALSE))), "data.frame") @@ -40,8 +48,8 @@ test_that("Does the function output, assuming 'jagsUI' is available, return a da test_that("Does the function return a Markov Chain Monte Carlo (MCMC) output object?", { # skip the test if jagsUI is not installed - if (!requireNamespace("jagsUI", quietly = TRUE)) { - skip("jagsUI software not available") + if (!runjags::testjags(silent = TRUE)$JAGS.available) { + skip("JAGS software not detectable") } expect_equal(class(suppressWarnings(bma_objects(bayesian_meta_analysis_fake_data, plot = TRUE)$comb.samples)), "mcmc.list") From 6258b2ae332abdcc3fce4b4fe07d143a393870fd Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 15:38:15 +0100 Subject: [PATCH 37/45] jagsUI and runjags moved to Imports --- DESCRIPTION | 6 +++--- NAMESPACE | 2 +- R/detect_jags.r | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5f1e4a1..a3c11cd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -37,11 +37,11 @@ Imports: mgcv, mockery, RColorBrewer, - reshape2 + reshape2, + jagsUI, + runjags Suggests: covr, - jagsUI, - runjags, knitr, rmarkdown, snowfall, diff --git a/NAMESPACE b/NAMESPACE index c208aa9..827e29a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -20,9 +20,9 @@ import(car) import(ggplot2) import(mgcv) import(reshape2) -import(runjags) importFrom(boot,inv.logit) importFrom(car,logit) importFrom(coda,as.mcmc) importFrom(coda,mcmc.list) importFrom(reshape2,dcast) +importFrom(runjags,testjags) diff --git a/R/detect_jags.r b/R/detect_jags.r index 0825473..b16d956 100644 --- a/R/detect_jags.r +++ b/R/detect_jags.r @@ -1,4 +1,4 @@ -#' @import runjags +#' @importFrom runjags testjags detect_jags <- function(){ return(runjags::testjags(silent = TRUE)$JAGS.available) From 24b9a21f4438db5c09fac98683e0cb3c7c0ed18c Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 16:05:44 +0100 Subject: [PATCH 38/45] remove warnings from JAGS detection --- R/detect_jags.r | 2 +- tests/testthat/test-bayesian_meta_analysis.R | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/R/detect_jags.r b/R/detect_jags.r index b16d956..1c0e553 100644 --- a/R/detect_jags.r +++ b/R/detect_jags.r @@ -1,5 +1,5 @@ #' @importFrom runjags testjags detect_jags <- function(){ - return(runjags::testjags(silent = TRUE)$JAGS.available) + return(suppressWarnings(runjags::testjags(silent = TRUE)$JAGS.available)) } \ No newline at end of file diff --git a/tests/testthat/test-bayesian_meta_analysis.R b/tests/testthat/test-bayesian_meta_analysis.R index c90bfe1..8aeca3f 100644 --- a/tests/testthat/test-bayesian_meta_analysis.R +++ b/tests/testthat/test-bayesian_meta_analysis.R @@ -37,7 +37,7 @@ expect_error(bma(bayesian_meta_analysis_fake_data, model = "smooth_det"), test_that("Does the function output, assuming 'jagsUI' is available, return a dataframe?", { # skip the test if jagsUI is not installed - if (!runjags::testjags(silent = TRUE)$JAGS.available) { + if (!detect_jags()) { skip("JAGS software not detectable") } @@ -48,7 +48,7 @@ test_that("Does the function output, assuming 'jagsUI' is available, return a da test_that("Does the function return a Markov Chain Monte Carlo (MCMC) output object?", { # skip the test if jagsUI is not installed - if (!runjags::testjags(silent = TRUE)$JAGS.available) { + if (!detect_jags()) { skip("JAGS software not detectable") } From d7b9d0360869bcde43e1386f216485411cf8d4cd Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 16:20:00 +0100 Subject: [PATCH 39/45] fix to detect jags --- R/detect_jags.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/detect_jags.r b/R/detect_jags.r index 1c0e553..afaa8a5 100644 --- a/R/detect_jags.r +++ b/R/detect_jags.r @@ -1,5 +1,5 @@ #' @importFrom runjags testjags detect_jags <- function(){ - return(suppressWarnings(runjags::testjags(silent = TRUE)$JAGS.available)) + return(suppressWarnings(runjags::testjags(silent = TRUE)$JAGS.found)) } \ No newline at end of file From 33b80464bd77ad89fa3a3de57d38144144f7b68b Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 16:52:14 +0100 Subject: [PATCH 40/45] added skips for when there is no JAGS installation --- tests/testthat/test-bayesian_meta_analysis.R | 47 ++++++++++++-------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/tests/testthat/test-bayesian_meta_analysis.R b/tests/testthat/test-bayesian_meta_analysis.R index 8aeca3f..e45ae5e 100644 --- a/tests/testthat/test-bayesian_meta_analysis.R +++ b/tests/testthat/test-bayesian_meta_analysis.R @@ -1,6 +1,5 @@ test_that("Does the function stop when there is no jags installation?", { - - # Mock `requireNamespace` to return FALSE + # Mock `detect_jags` to return FALSE mockery::stub(bma, "detect_jags", FALSE) expect_error(bma(bayesian_meta_analysis_fake_data), regexp = "No installation of JAGS has been detected") @@ -8,50 +7,62 @@ test_that("Does the function stop when there is no jags installation?", { # Test that function responds correctly when 'jagsUI' is not available test_that("Does the function stop when 'jagsUI' is not available?", { + # Mock `detect_jags` to return TRUE + mockery::stub(bma, "detect_jags", TRUE) # Mock `requireNamespace` to return FALSE mockery::stub(bma, "requireNamespace", FALSE) expect_error(bma(bayesian_meta_analysis_fake_data), regexp = "Package 'jagsUI' is needed") - }) test_that("Does it correctly detect missing columns in the input data?", { + # skip the test if JAGS is not installed + if (!detect_jags()) { + skip("JAGS software not detectable") + } -expect_error(bma(subset(bayesian_meta_analysis_fake_data, select = -c(index))), - regexp = "data column names should include") + expect_error(bma(subset(bayesian_meta_analysis_fake_data, select = -c(index))), + regexp = "data column names should include" + ) }) test_that("Does it correctly detect a missing standard error column?", { + # skip the test if JAGS is not installed + if (!detect_jags()) { + skip("JAGS software not detectable") + } -expect_error(bma(subset(bayesian_meta_analysis_fake_data, select = -c(se)), seFromData = TRUE), - regexp = "Standard errors have not been provided") + expect_error(bma(subset(bayesian_meta_analysis_fake_data, select = -c(se)), seFromData = TRUE), + regexp = "Standard errors have not been provided" + ) }) test_that("Does it correctly return an error if a deprecated model, in this case smooth_det, is chosen?", { + # skip the test if JAGS is not installed + if (!detect_jags()) { + skip("JAGS software not detectable") + } -expect_error(bma(bayesian_meta_analysis_fake_data, model = "smooth_det"), - regexp = "model has been deprecated") + expect_error(bma(bayesian_meta_analysis_fake_data, model = "smooth_det"), + regexp = "model has been deprecated" + ) }) -test_that("Does the function output, assuming 'jagsUI' is available, return a dataframe?", { - - # skip the test if jagsUI is not installed - if (!detect_jags()) { +test_that("Does the function output, assuming jags is available, return a dataframe?", { + # skip the test if JAGS is not installed + if (!detect_jags()) { skip("JAGS software not detectable") } expect_equal(class(suppressWarnings(bma(bayesian_meta_analysis_fake_data, plot = FALSE))), "data.frame") - }) test_that("Does the function return a Markov Chain Monte Carlo (MCMC) output object?", { - - # skip the test if jagsUI is not installed - if (!detect_jags()) { + # skip the test if jagsUI is not installed + if (!detect_jags()) { skip("JAGS software not detectable") } expect_equal(class(suppressWarnings(bma_objects(bayesian_meta_analysis_fake_data, plot = TRUE)$comb.samples)), "mcmc.list") - }) From 5a3db0c1f9905745672b8ebb06f64b18ef077654 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 17:09:58 +0100 Subject: [PATCH 41/45] Fix to bma example --- R/bayesian_meta_analysis.R | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/R/bayesian_meta_analysis.R b/R/bayesian_meta_analysis.R index 0b71485..1e725bf 100644 --- a/R/bayesian_meta_analysis.R +++ b/R/bayesian_meta_analysis.R @@ -43,6 +43,10 @@ #' \emph{Journal of Agricultural Biological and Environmental Statistics}, in revision. #' @export #' @examples +#' +#' # Only run if there is a JAGS installation +#' if(detect_jags()){ +#' #' # Create some example data in the format required #' data <- data.frame(species = rep(letters, each = 50), #' year = rep(1:50, length(letters)), @@ -55,6 +59,8 @@ #' # Plot the resulting indicator #' plot_indicator(indicator = bma_indicator[,'Index.Mprime'], #' CIs = bma_indicator[,c(3,4)]) +#' +#' } bma <- function (data, plot = TRUE, From f5e5e7f1a71e65d89a026bcc058391ab0765520a Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 17:23:38 +0100 Subject: [PATCH 42/45] forgot to document --- man/bma.Rd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/man/bma.Rd b/man/bma.Rd index 263e3cc..7ba06b7 100644 --- a/man/bma.Rd +++ b/man/bma.Rd @@ -90,6 +90,10 @@ There are a number of model to choose from: } } \examples{ + +# Only run if there is a JAGS installation +if(detect_jags()){ + # Create some example data in the format required data <- data.frame(species = rep(letters, each = 50), year = rep(1:50, length(letters)), @@ -102,6 +106,8 @@ bma_indicator <- bma(data, model="smooth", m.scale="logit", n.iter=100) # Plot the resulting indicator plot_indicator(indicator = bma_indicator[,'Index.Mprime'], CIs = bma_indicator[,c(3,4)]) + + } } \references{ Freeman, S.N., Isaac, N.J.B., Besbeas, P.T., Dennis, E.B. & Morgan, B.J.T. (2020) From edb794054df98763242643fb135b0bfb133935e6 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 17:49:47 +0100 Subject: [PATCH 43/45] Added documentation for detect JAGS function --- NAMESPACE | 1 + R/detect_jags.r | 10 ++++++++++ man/detect_jags.Rd | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 man/detect_jags.Rd diff --git a/NAMESPACE b/NAMESPACE index 827e29a..4ae48e7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,6 +5,7 @@ export(CompositeTrend) export(GAM_smoothing) export(bma) export(bootstrap_indicator) +export(detect_jags) export(get_bmaBUGScode) export(lambda_indicator) export(msi) diff --git a/R/detect_jags.r b/R/detect_jags.r index afaa8a5..68ba0b8 100644 --- a/R/detect_jags.r +++ b/R/detect_jags.r @@ -1,3 +1,13 @@ +#' Detect JAGS installation +#' +#' @description small function that detects whether JAGS is installed. +#' @return TRUE or FALSE, indicating whether the JAGS installation has been detected +#' @importFrom runjags testjags +#' @export +#' @examples +#' +#' detect_jags() + #' @importFrom runjags testjags detect_jags <- function(){ diff --git a/man/detect_jags.Rd b/man/detect_jags.Rd new file mode 100644 index 0000000..15649cc --- /dev/null +++ b/man/detect_jags.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/detect_jags.r +\name{detect_jags} +\alias{detect_jags} +\title{Detect JAGS installation} +\usage{ +detect_jags() +} +\value{ +TRUE or FALSE, indicating whether the JAGS installation has been detected +} +\description{ +small function that detects whether JAGS is installed. +} +\examples{ + +detect_jags() +} From 556d71b5c02ba10956fe2ef1dd5cacd95f284902 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 17:50:08 +0100 Subject: [PATCH 44/45] Fix of import error --- R/detect_jags.r | 2 -- 1 file changed, 2 deletions(-) diff --git a/R/detect_jags.r b/R/detect_jags.r index 68ba0b8..0dc109f 100644 --- a/R/detect_jags.r +++ b/R/detect_jags.r @@ -8,8 +8,6 @@ #' #' detect_jags() -#' @importFrom runjags testjags - detect_jags <- function(){ return(suppressWarnings(runjags::testjags(silent = TRUE)$JAGS.found)) } \ No newline at end of file From fb3a2679d0e2ea55fc4029eaa47cf3a88cde06b5 Mon Sep 17 00:00:00 2001 From: DylanCarbone Date: Wed, 17 Apr 2024 18:37:51 +0100 Subject: [PATCH 45/45] Fix of R CMD check badge --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index be2d135..b375a2b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # BRCindicators -[![R-CMD-check](https://github.com/DylanCarbone/BRCindicators/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/DylanCarbone/BRCindicators/actions/workflows/R-CMD-check.yaml) +[![R-CMD-check](https://github.com/BiologicalRecordsCentre/BRCindicators/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/BiologicalRecordsCentre +/BRCindicators/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/biologicalrecordscentre/BRCindicators/branch/master/graph/badge.svg)](https://codecov.io/gh/biologicalrecordscentre/BRCindicators?branch=master)