Created
July 8, 2015 11:34
-
-
Save Pakillo/cbbb1c79e8c737627e6d to your computer and use it in GitHub Desktop.
Calculate plant and bare ground cover in vegetation transects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#' Calculate total cover per species in vegetation transects | |
#' | |
#' Calculate total cover per species in each transect, layer within transect, etc. | |
#' Optionally, the function can also calculate length of bare ground (as ground not covered | |
#' by any plant species). | |
#' | |
#' @export | |
#' @author F Rodriguez-Sanchez | |
#' @importFrom plyr ddply | |
#' @importFrom gtools odd even | |
#' @importFrom tidyr gather | |
#' @importFrom dplyr arrange_ | |
#' @param df A dataframe with one column per recorded species, plus additional columns | |
#' to identify transects (and possibly sections within transects, shrub or tree layers, etc). | |
#' For each species, introduce initial and ending distances every time the species appears along | |
#' the transect (see example). | |
#' @param split.cols A character vector with the names of the variables in \code{df} | |
#' that identify transects or any other structure of the data (e.g. sections within transects, | |
#' shrub or tree layers, etc.) | |
#' @param first.spcol Integer. Number of the first column containing species cover data. | |
#' @param last.spcol Integer. Number of the last column containing species cover data | |
#' (last column in \code{df} by default). | |
#' @param check.incremental Logical (default is \code{TRUE}). Check that cover data are | |
#' sorted incrementally? Must be \code{TRUE} if calculating bare cover with this function. | |
#' @param bare Logical. Calculate bare cover? Default is \code{TRUE}. | |
#' @param tr.length Numeric. Total transect length (e.g. in meters). | |
#' @param precision Numeric. Precision of species cover measurements. Only used to calculate | |
#' bare cover by exclusion. | |
#' @param prop Logical. If \code{TRUE}, results are expressed as proportion | |
#' of \code{tr.length} covered by each species. If \code{FALSE} (the default), results represent | |
#' linear absolute length covered by each species. | |
#' @param long.format Logical. Produce the output dataframe in long format? Default is wide format. | |
#' See \code{\link[tidyr]{gather}}. | |
#' @param show.progress Logical. Show progress bar? | |
#' @return A dataframe with total cover values per species and transect (plus optionally bare ground). | |
#' @examples | |
#' df <- data.frame(transect = sort(rep(c(1,2,3), 20)), | |
#' section = rep(1, 60), | |
#' species1 = c(replicate(3, sort(round(runif(20, 0, 100), digits = 1)))), | |
#' species2 = c(sort(round(runif(8, 0, 100), digits = 1)), rep(NA, times = 60 - 8))) | |
#' View(df) | |
#' calculate_cover(df, split.cols = "transect") | |
#' calculate_cover(df, split.cols = "transect", long.format = TRUE) | |
calculate_cover <- function(df, split.cols, | |
first.spcol, last.spcol = ncol(df), | |
tr.length, | |
bare = FALSE, precision, | |
check.incremental = TRUE, | |
prop = FALSE, long.format = FALSE, | |
show.progress = TRUE){ | |
out <- plyr::ddply(df, split.cols, sotobosque:::calculate_cover_transect, | |
# calculate_cover_transect args below: | |
id.col = split.cols[1], | |
first.col = first.spcol, last.col = last.spcol, | |
check.increm = check.incremental, | |
calc.bare = bare, tr.len = tr.length, precis = precision, | |
# back to ddply args: | |
.progress = ifelse(show.progress, "text", "none")) | |
if (prop){ | |
out[, (length(split.cols)+1):ncol(out)] <- out[, (length(split.cols)+1):ncol(out)] / tr.length | |
} | |
if (long.format){ | |
out.long <- tidyr::gather(out, "species", "cover", (length(split.cols)+1):ncol(out)) | |
out <- dplyr::arrange_(out.long, .dots = split.cols) | |
} | |
out | |
} | |
calculate_cover_transect <- function(df.split, id.col, | |
first.col, last.col, | |
check.increm, | |
calc.bare, tr.len, precis) { | |
fdata <- df.split[, first.col:last.col] # retain cover data only; exclude other columns | |
### check that number of values is even for each column | |
nvals <- apply(fdata, 2, function(x) sum(!is.na(x))) | |
if (any(gtools::odd(nvals))){ | |
stop("\nIn transect ", as.character(df.split[1, id.col]), | |
", the following taxa do not have an even number of values:\n", | |
paste(names(fdata[which(gtools::odd(nvals))]), collapse = " "), | |
"\nPlease check your data.") | |
} | |
### check that values are always incrementing positively | |
if (calc.bare) check.increm <- TRUE # bare cover calculations require cover data sorted incrementally | |
if (check.increm){ | |
increm <- apply(fdata, 2, is.unsorted, na.rm = TRUE, strictly = TRUE) | |
if (any(increm)){ | |
stop("\nIn transect ", as.character(df.split[1, id.col]), | |
", cover values for the following taxa seem not to be sorted incrementally:\n", | |
paste(names(fdata[which(increm)]), collapse = " "), | |
"\nPlease check your data.") | |
} | |
} | |
### Now add up values to get total cover per species | |
cover.allsp <- apply(fdata, 2, addup) | |
## Calculate bare cover | |
if (calc.bare){ | |
cover.sp <- apply(fdata, 2, covered_species, precis, tr.len) | |
bare.c <- apply(cover.sp, 1, function(x) ifelse(isTRUE(any(x)), 0, precis)) | |
bare.cover <- round(sum(bare.c)/precis) * precis | |
cover.allsp <- c(cover.allsp, bare = bare.cover) | |
} else cover.allsp | |
## Check that total cover values are < total transect length | |
if (any(cover.allsp > tr.len)){ | |
stop("\nIn transect ", as.character(df.split[1, id.col]), | |
", total cover for ", | |
paste(names(cover.allsp[which(cover.allsp > tr.len)]), collapse = " "), | |
" exceeds the transect length (", tr.len, ")") | |
} | |
return(cover.allsp) | |
} | |
addup <- function(species){ | |
species <- as.matrix(species) | |
sum(species[even(row(species))] - species[odd(row(species))], na.rm = TRUE) | |
} | |
covered_species <- function(species, precision, tr.length){ | |
species <- as.matrix(species) | |
mat <- matrix(c(species[odd(row(species))], species[even(row(species))]), | |
nrow(species)/2, ncol = 2) | |
spcover <- as.character(unlist(apply(mat, 1, function(x) { | |
if (!is.na(x[1])) {seq(from = x[1], to = x[2], by = precision)} | |
}))) | |
# to avoid overestimating species cover, eliminate all starting points | |
spcover2 <- spcover[!spcover %in% as.character(mat[, 1])] | |
allvals <- seq(precision, tr.length, by = precision) | |
as.character(allvals) %in% spcover2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment