#' Finds the RSE for the mean of a vector
#'
#' @param vec A vector of the values being predicted
#' @return value
#' @export
mRSE <- function(dat){
dat<-dat[!is.na(dat)]
m <- mean(dat)
residuals <- vector()
for (val in 1:length(dat)) {
residuals[val] <- abs(dat[val] - m)
}
rse <- sd(residuals)
return(rse)
}
#' Builds models for cover dynamics of surveyed Species
#'
#' Input table requires the following fields:
#' Point - numbered point in a transect
#' Species - name of the surveyed Species
#' Age - age of the site since the triggering disturbance
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat The dataframe containing the input data,
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#' @param pnts The number of points measured in a transect
#' @param p The maximum allowable p value for a model
#' @param bTest Multiples of mean + mRSE for which Burr & quadratic models can predict
#' beyond the observed mean + standard deviation
#' @param maxiter The maximum number of iterations for model fitting
#' @return dataframe
#' @export
coverDyn <- function(dat, thres = 5, pnts = 10, p = 0.05, bTest = 10, maxiter = 1000) {
spCov <- specCover(dat = dat, thres = thres, pnts = pnts)
priorList <- unique(spCov$Species, incomparables = FALSE)
#DATA ANALYSIS
fitCov <- data.frame('Species' = character(0), 'lin_a' = numeric(0), 'lin_b' = numeric(0),'lin_Sigma' = numeric(0), 'lin_Rsq' = numeric(0), 'lin_p' = numeric(0),
'k' = numeric(0), 'r' = numeric(0), 'NE_sigma' = numeric(0), 'NE_Rsq' = numeric(0), 'NE_p' = numeric(0),
'Ba' = numeric(0), 'Bb' = numeric(0), 'B_sigma' = numeric(0), 'B_Rsq' = numeric(0), 'B_p' = numeric(0),
'scale' = numeric(0), 'sd' = numeric(0), 'Binm' = numeric(0), 'Bin_sigma' = numeric(0), 'Bin_Rsq' = numeric(0), 'Bin_p' = numeric(0),
'Qa' = numeric(0), 'Qb' = numeric(0), 'Qc' = numeric(0), 'Q_sigma' = numeric(0), 'q_Rsq' = numeric(0), 'Q_p' = numeric(0),
'Mean' = character(0), 'Mean_sigma' = character(0), 'Model' = character(0), stringsAsFactors=F)
for (sp in 1:length(priorList)) {
SpeciesNumber <- sp
control=nls.control(maxiter=maxiter, tol=1e-7, minFactor = 1/999999999)
studySpecies <- spCov %>% filter(Species == priorList[SpeciesNumber])
x <- as.numeric(studySpecies$Age)
y <- as.numeric(studySpecies$Cover)
#Linear
if (!berryFunctions::is.error(lm(y ~ x))) {
LM<-lm(y ~ x)
LMSum <- base::summary(LM)
LMa <- LMSum$coefficients[2]
LMb <- LMSum$coefficients[1]
LMRSE <- LMSum$sigma
LMRsq <- LMSum$r.squared
LMp <- if (LMRSE == 0) {
0
} else {
round(max(LMSum$coefficients[7],LMSum$coefficients[8]),5)
}
rm(LM)
} else {
LMa <- NA
LMb <- NA
LMRSE <- 100
LMRsq <- 0
LMp <- 1
}
#Negative exponential
init1<-c(k=50,r=0.5)
if (!berryFunctions::is.error(nls(y~k * (1-exp(-r*x)),data=studySpecies,start=init1,trace=T))) {
NE<-nls(y~k * (1-exp(-r*x)),data=studySpecies,start=init1,trace=T)
NESum <- base::summary(NE)
k <- NESum$coefficients[1]
r <- NESum$coefficients[2]
NERSE <- NESum$sigma
NERsq <- cor(predict(NE, newdata=x), y)**2
NEp <- round(max(NESum$coefficients[7],NESum$coefficients[8]),5)
rm(NE)
} else {
k <- NA
r <- NA
NERSE <- 100
NERsq <- 0
NEp <- 1
}
#Burr
init2<-c(a=3,b=2)
if (!berryFunctions::is.error(nls(y~a*b*((0.1*x^(a-1))/((1+(0.1*x)^a)^b+1)),data=studySpecies,start=init2,trace=T, control = control))) {
Burr<-nls(y~a*b*((0.1*x^(a-1))/((1+(0.1*x)^a)^b+1)),data=studySpecies,start=init2,trace=T, control = control)
BSum <- base::summary(Burr)
Ba <- BSum$coefficients[1]
Bb <- BSum$coefficients[2]
#Added control for Burr
f <- function(x){Ba*Bb*((0.1*x^(Ba-1))/((1+(0.1*x)^Ba)^Bb+1))}
if (bTest * (sd(y, na.rm = TRUE) + mean(y, na.rm = TRUE)) < (optimize(f = f, interval=c(0, 150), maximum=TRUE))$objective) {
BRSE <- 100
BRsq <- 0
Bp <- 1
} else if (bTest * (mean(y, na.rm = TRUE) - sd(y, na.rm = TRUE)) > (optimize(f = f, interval=c(0, 150), maximum=FALSE))$objective) {
BRSE <- 100
BRsq <- 0
Bp <- 1
} else {
BRSE <- BSum$sigma
BRsq <- cor(predict(Burr, newdata=x), y)**2
Bp <- round(max(BSum$coefficients[7],BSum$coefficients[8]),5)
}
rm(Burr)
} else {
Ba <- NA
Bb <- NA
BRSE <- 100
BRsq <- 0
Bp <- 1
}
#Binomial
init3<-c(s=1,sd=3, m=20)
if (!berryFunctions::is.error(nls(y~s*(1/(sd*sqrt(2*pi)))*exp(-((x-m)^2)/(2*sd^2)),data=studySpecies,start=init3,trace=T, control = control))) {
Bin<-nls(y~s*(1/(sd*sqrt(2*pi)))*exp(-((x-m)^2)/(2*sd^2)),data=studySpecies,start=init3,trace=T, control = control)
BinSum <- base::summary(Bin)
Bs <- BinSum$coefficients[1]
Bsd <- BinSum$coefficients[2]
Bm <- BinSum$coefficients[3]
BinRSE <- BinSum$sigma
BinRsq <- cor(predict(Bin, newdata=x), y)**2
Binp <- round(max(BinSum$coefficients[10],BinSum$coefficients[11],BinSum$coefficients[12]),5)
rm(Bin)
} else {
Bs <- NA
Bsd <- NA
Bm <- NA
BinRSE <- 100
BinRsq <- 0
Binp <- 1
}
#Quadratic
init4 <- c(a = -1, b = 2, c = 0)
if (!berryFunctions::is.error(nls(y ~ a*x^2 + b*x + c, data = studySpecies,
start = init4, trace = T, control = control))) {
q <- nls(y ~ a*x^2 + b*x + c, data = studySpecies, start = init4,
trace = T, control = control)
qSum <- base::summary(q)
qa <- qSum$coefficients[1]
qb <- qSum$coefficients[2]
qc <- qSum$coefficients[3]
# Added control for Quadratic
# Also prevents quadratic increases
f <- function(x){qa*x^2 + qb*x + qc}
if ((bTest * (sd(y, na.rm = TRUE) + mean(y, na.rm = TRUE)) < (optimize(f = f, interval=c(0, 150), maximum=TRUE))$objective)||qa > 0) {
qRSE <- 100
qRsq <- 0
qp <- 1
} else {
qRSE <- qSum$sigma
qRsq <- cor(predict(q, newdata=x), y)**2
qp <- round(max(qSum$coefficients[10], qSum$coefficients[11],
qSum$coefficients[12]), 5)
}
rm(q)
} else {
qa <- NA
qb <- NA
qc <- NA
qRSE <- 100
qRsq <- 0
qp <- 1
}
#Summary stats
meanCov <- round(mean(y, na.rm = TRUE),1)
m_sig <- round(mRSE(dat = y),3)
#Choose the best model
listRsq <- c(LMRsq, NERsq, BRsq, BinRsq, qRsq)
listRSE <- c(LMRSE, NERSE, BRSE, BinRSE, qRSE)
listMod <- c("Linear", "NegExp", "Burr", "Binomial", "Quadratic")
if (listRSE[which(listRsq == max(listRsq))]<=m_sig) {
model <- listMod[which(listRsq == max(listRsq))]
} else {
model <= "Mean"
}
#Record values
fitCov[nrow(fitCov)+1,] <- c(as.character(priorList[SpeciesNumber]), LMa, LMb, LMRSE, LMRsq, LMp, k, r, NERSE, NERsq, NEp,
Ba, Bb, BRSE, BRsq, Bp, Bs, Bsd, Bm, BinRSE, BinRsq, Binp, qa, qb, qc, qRSE, qRsq, qp,
meanCov, m_sig, model)
}
return(fitCov)
}
#' Builds models for top height dynamics of surveyed Species
#'
#' Input table requires the following fields:
#' Point - numbered point in a transect
#' Species - name of the surveyed Species
#' Age - age of the site since the triggering disturbance
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat The dataframe containing the input data,
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#' @param pnts The number of points measured in a transect
#' @param p The maximum allowable p value for a model
#' @param bTest Multiples of mean + mRSE for which Burr & quadratic models can predict
#' beyond the observed mean + standard deviation
#' @param maxiter The maximum number of iterations for model fitting
#' @return dataframe
#' @export
topDyn <- function(dat, base = "base", top = "top", he = "he", ht = "ht",
thres = 5, pnts = 10, p = 0.05, bTest = 10, maxiter = 1000) {
spCov <- specCover(dat = dat, thres = 0, pnts = pnts)%>%
group_by(Species)%>%
summarise_if(is.numeric, mean)
dat <- left_join(dat, spCov)%>%
mutate(Species = replace(Species, which(Cover < thres), "Minor Species"))
priorList <- unique(dat$Species, incomparables = FALSE)
#DATA ANALYSIS
fitTop <- data.frame('Species' = character(0), 'lin_a' = numeric(0), 'lin_b' = numeric(0),'lin_Sigma' = numeric(0), 'lin_Rsq' = numeric(0), 'lin_p' = numeric(0),
'k' = numeric(0), 'r' = numeric(0), 'NE_sigma' = numeric(0), 'NE_Rsq' = numeric(0), 'NE_p' = numeric(0),
'Ba' = numeric(0), 'Bb' = numeric(0), 'B_sigma' = numeric(0), 'B_Rsq' = numeric(0), 'B_p' = numeric(0),
'scale' = numeric(0), 'sd' = numeric(0), 'Binm' = numeric(0), 'Bin_sigma' = numeric(0), 'Bin_Rsq' = numeric(0), 'Bin_p' = numeric(0),
'Qa' = numeric(0), 'Qb' = numeric(0), 'Qc' = numeric(0), 'Q_sigma' = numeric(0), 'q_Rsq' = numeric(0), 'Q_p' = numeric(0),
'Mean' = character(0), 'Mean_sigma' = character(0), 'Model' = character(0), stringsAsFactors=F)
for (sp in 1:length(priorList)) {
SpeciesNumber <- sp
control=nls.control(maxiter=maxiter, tol=1e-7, minFactor = 1/999999999)
studySpecies <- dat %>% filter(Species == priorList[SpeciesNumber])
x <- as.numeric(studySpecies$Age)
y <- as.numeric(studySpecies$top)
if (length(unique(x, incomparables = FALSE))>2 & length(unique(y, incomparables = FALSE))>1) {
#Linear
if (!berryFunctions::is.error(lm(y ~ x))) {
LM<-lm(y ~ x)
LMSum <- base::summary(LM)
LMa <- LMSum$coefficients[2]
LMb <- LMSum$coefficients[1]
LMRSE <- LMSum$sigma
LMRsq <- LMSum$r.squared
LMp <- if (LMRSE == 0) {
0
} else {
round(max(LMSum$coefficients[7],LMSum$coefficients[8]),5)
}
rm(LM)
} else {
LMa <- NA
LMb <- NA
LMRSE <- 100
LMRsq <- 0
LMp <- 1
}
#Negative exponential
init1<-c(k=50,r=0.5)
if (!berryFunctions::is.error(nls(y~k * (1-exp(-r*x)),data=studySpecies,start=init1,trace=T))) {
NE<-nls(y~k * (1-exp(-r*x)),data=studySpecies,start=init1,trace=T)
NESum <- base::summary(NE)
k <- NESum$coefficients[1]
r <- NESum$coefficients[2]
NERSE <- NESum$sigma
NERsq <- cor(predict(NE, newdata=x), y)**2
NEp <- round(max(NESum$coefficients[7],NESum$coefficients[8]),5)
rm(NE)
} else {
k <- NA
r <- NA
NERSE <- 100
NERsq <- 0
NEp <- 1
}
#Burr
init2<-c(a=3,b=2)
if (!berryFunctions::is.error(nls(y~a*b*((0.1*x^(a-1))/((1+(0.1*x)^a)^b+1)),data=studySpecies,start=init2,trace=T, control = control))) {
Burr<-nls(y~a*b*((0.1*x^(a-1))/((1+(0.1*x)^a)^b+1)),data=studySpecies,start=init2,trace=T, control = control)
BSum <- base::summary(Burr)
Ba <- BSum$coefficients[1]
Bb <- BSum$coefficients[2]
#Added control for Burr
f <- function(x){Ba*Bb*((0.1*x^(Ba-1))/((1+(0.1*x)^Ba)^Bb+1))}
if (bTest * (sd(y, na.rm = TRUE) + mean(y, na.rm = TRUE)) < (optimize(f = f, interval=c(0, 150), maximum=TRUE))$objective) {
BRSE <- 100
BRsq <- 0
Bp <- 1
} else if (bTest * (mean(y, na.rm = TRUE) - sd(y, na.rm = TRUE)) > (optimize(f = f, interval=c(0, 150), maximum=FALSE))$objective) {
BRSE <- 100
BRsq <- 0
Bp <- 1
} else {
BRSE <- BSum$sigma
BRsq <- cor(predict(Burr, newdata=x), y)**2
Bp <- round(max(BSum$coefficients[7],BSum$coefficients[8]),5)
}
rm(Burr)
} else {
Ba <- NA
Bb <- NA
BRSE <- 100
BRsq <- 0
Bp <- 1
}
#Binomial
init3<-c(s=1,sd=3, m=20)
if (!berryFunctions::is.error(nls(y~s*(1/(sd*sqrt(2*pi)))*exp(-((x-m)^2)/(2*sd^2)),data=studySpecies,start=init3,trace=T, control = control))) {
Bin<-nls(y~s*(1/(sd*sqrt(2*pi)))*exp(-((x-m)^2)/(2*sd^2)),data=studySpecies,start=init3,trace=T, control = control)
BinSum <- base::summary(Bin)
Bs <- BinSum$coefficients[1]
Bsd <- BinSum$coefficients[2]
Bm <- BinSum$coefficients[3]
BinRSE <- BinSum$sigma
BinRsq <- cor(predict(Bin, newdata=x), y)**2
Binp <- round(max(BinSum$coefficients[10],BinSum$coefficients[11],BinSum$coefficients[12]),5)
rm(Bin)
} else {
Bs <- NA
Bsd <- NA
Bm <- NA
BinRSE <- 100
BinRsq <- 0
Binp <- 1
}
#Quadratic
init4 <- c(a = 1, b = 2, c = 0)
if (!berryFunctions::is.error(nls(y ~ a*x^2 + b*x + c, data = studySpecies,
start = init4, trace = T, control = control))) {
q <- nls(y ~ a*x^2 + b*x + c, data = studySpecies, start = init4,
trace = T, control = control)
qSum <- base::summary(q)
qa <- qSum$coefficients[1]
qb <- qSum$coefficients[2]
qc <- qSum$coefficients[3]
#Added control for Quadratic
f <- function(x){qa*x^2 + qb*x + qc}
if (bTest * (sd(y, na.rm = TRUE) + mean(y, na.rm = TRUE)) < (optimize(f = f, interval=c(0, 150), maximum=TRUE))$objective) {
qRSE <- 100
qRsq <- 0
qp <- 1
} else {
qRSE <- qSum$sigma
qRsq <- cor(predict(q, newdata=x), y)**2
qp <- round(max(qSum$coefficients[10], qSum$coefficients[11],
qSum$coefficients[12]), 5)
}
rm(q)
} else {
qa <- NA
qb <- NA
qc <- NA
qRSE <- 100
qRsq <- 0
qp <- 1
}
#Summary stats
meanTop <- round(mean(y, na.rm = TRUE),1)
m_sig <- round(mRSE(dat = y),3)
#Choose the best model
listRsq <- c(LMRsq, NERsq, BRsq, BinRsq, qRsq)
listRSE <- c(LMRSE, NERSE, BRSE, BinRSE, qRSE)
listMod <- c("Linear", "NegExp", "Burr", "Binomial", "Quadratic")
if (listRSE[which(listRsq == max(listRsq))]<=m_sig) {
model <- listMod[which(listRsq == max(listRsq))]
} else {
model <- "Mean"
}
} else {
LMa <- NA
LMb <- NA
LMRSE <- 100
LMRsq <- 0
LMp <- 1
k <- NA
r <- NA
NERSE <- 100
NERsq <- 0
NEp <- 1
Ba <- NA
Bb <- NA
BRSE <- 100
BRsq <- 0
Bp <- 1
Bs <- NA
Bsd <- NA
Bm <- NA
BinRSE <- 100
BinRsq <- 0
Binp <- 1
qa <- NA
qb <- NA
qc <- NA
qRSE <- 100
qRsq <- 0
qp <- 1
model <- "Mean"
#Summary stats
meanTop <- round(mean(y, na.rm = TRUE),1)
m_sig <- round(mRSE(dat = y),3)
}
#Record values
fitTop[nrow(fitTop)+1,] <- c(as.character(priorList[SpeciesNumber]), LMa, LMb, LMRSE, LMRsq, LMp, k, r, NERSE, NERsq, NEp,
Ba, Bb, BRSE, BRsq, Bp, Bs, Bsd, Bm, BinRSE, BinRsq, Binp, qa, qb, qc, qRSE, qRsq, qp,
meanTop, m_sig, model)
}
return(fitTop)
}
#' Builds models for top-base height allometry dynamics of surveyed Species
#'
#' Input table requires the following fields:
#' Point - numbered point in a transect
#' Species - name of the surveyed Species
#' Age - age of the site since the triggering disturbance
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat The dataframe containing the input data,
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#' @param pnts The number of points measured in a transect
#' @param p The maximum allowable p value for a model
#' @param bTest Multiples of mean + mRSE for which Burr & quadratic models can predict
#' beyond the observed mean + standard deviation
#' @param maxiter The maximum number of iterations for model fitting
#' @return dataframe
#' @export
baseDyn <- function(dat, base = "base", top = "top", he = "he", ht = "ht",
thres = 5, pnts = 10, p = 0.05, bTest = 10, maxiter = 1000) {
spCov <- specCover(dat = dat, thres = 0, pnts = pnts)%>%
group_by(Species)%>%
summarise_if(is.numeric, mean)
dat <- left_join(dat, spCov)%>%
mutate(Species = replace(Species, which(Cover < thres), "Minor Species"),
bRat = base/top)
priorList <- unique(dat$Species, incomparables = FALSE)
#DATA ANALYSIS
fitBase <- data.frame('Species' = character(0), 'lin_a' = numeric(0), 'lin_b' = numeric(0),'lin_Sigma' = numeric(0), 'lin_Rsq' = numeric(0), 'lin_p' = numeric(0),
'k' = numeric(0), 'r' = numeric(0), 'NE_sigma' = numeric(0), 'NE_Rsq' = numeric(0), 'NE_p' = numeric(0),
'Ba' = numeric(0), 'Bb' = numeric(0), 'B_sigma' = numeric(0), 'B_Rsq' = numeric(0), 'B_p' = numeric(0),
'scale' = numeric(0), 'sd' = numeric(0), 'Binm' = numeric(0), 'Bin_sigma' = numeric(0), 'Bin_Rsq' = numeric(0), 'Bin_p' = numeric(0),
'Qa' = numeric(0), 'Qb' = numeric(0), 'Qc' = numeric(0), 'Q_sigma' = numeric(0), 'q_Rsq' = numeric(0), 'Q_p' = numeric(0),
'Mean' = character(0), 'Mean_sigma' = character(0), 'Model' = character(0), stringsAsFactors=F)
for (sp in 1:length(priorList)) {
SpeciesNumber <- sp
control=nls.control(maxiter=maxiter, tol=1e-7, minFactor = 1/999999999)
studySpecies <- dat %>% filter(Species == priorList[SpeciesNumber])
x <- as.numeric(studySpecies$Age)
y <- as.numeric(studySpecies$bRat)
if (length(unique(x, incomparables = FALSE))>2 & length(unique(y, incomparables = FALSE))>1) {
#Linear
if (!berryFunctions::is.error(lm(y ~ x))) {
LM<-lm(y ~ x)
LMSum <- base::summary(LM)
LMa <- LMSum$coefficients[2]
LMb <- LMSum$coefficients[1]
LMRSE <- LMSum$sigma
LMRsq <- LMSum$r.squared
LMp <- if (LMRSE == 0) {
0
} else {
round(max(LMSum$coefficients[7],LMSum$coefficients[8]),5)
}
rm(LM)
} else {
LMa <- NA
LMb <- NA
LMRSE <- 100
LMRsq <- 0
LMp <- 1
}
#Negative exponential
init1<-c(k=50,r=0.5)
if (!berryFunctions::is.error(nls(y~k * (1-exp(-r*x)),data=studySpecies,start=init1,trace=T))) {
NE<-nls(y~k * (1-exp(-r*x)),data=studySpecies,start=init1,trace=T)
NESum <- base::summary(NE)
k <- NESum$coefficients[1]
r <- NESum$coefficients[2]
NERSE <- NESum$sigma
NERsq <- cor(predict(NE, newdata=x), y)**2
NEp <- round(max(NESum$coefficients[7],NESum$coefficients[8]),5)
rm(NE)
} else {
k <- NA
r <- NA
NERSE <- 100
NERsq <- 0
NEp <- 1
}
#Burr
init2<-c(a=3,b=2)
if (!berryFunctions::is.error(nls(y~a*b*((0.1*x^(a-1))/((1+(0.1*x)^a)^b+1)),data=studySpecies,start=init2,trace=T, control = control))) {
Burr<-nls(y~a*b*((0.1*x^(a-1))/((1+(0.1*x)^a)^b+1)),data=studySpecies,start=init2,trace=T, control = control)
BSum <- base::summary(Burr)
Ba <- BSum$coefficients[1]
Bb <- BSum$coefficients[2]
#Added control for Burr
f <- function(x){Ba*Bb*((0.1*x^(Ba-1))/((1+(0.1*x)^Ba)^Bb+1))}
if (bTest * (sd(y, na.rm = TRUE) + mean(y, na.rm = TRUE)) < (optimize(f = f, interval=c(0, 150), maximum=TRUE))$objective) {
BRSE <- 100
BRsq <- 0
Bp <- 1
} else if (bTest * (mean(y, na.rm = TRUE) - sd(y, na.rm = TRUE)) > (optimize(f = f, interval=c(0, 150), maximum=FALSE))$objective) {
BRSE <- 100
BRsq <- 0
Bp <- 1
} else {
BRSE <- BSum$sigma
BRsq <- cor(predict(Burr, newdata=x), y)**2
Bp <- round(max(BSum$coefficients[7],BSum$coefficients[8]),5)
}
rm(Burr)
} else {
Ba <- NA
Bb <- NA
BRSE <- 100
BRsq <- 0
Bp <- 1
}
#Binomial
init3<-c(s=1,sd=3, m=20)
if (!berryFunctions::is.error(nls(y~s*(1/(sd*sqrt(2*pi)))*exp(-((x-m)^2)/(2*sd^2)),data=studySpecies,start=init3,trace=T, control = control))) {
Bin<-nls(y~s*(1/(sd*sqrt(2*pi)))*exp(-((x-m)^2)/(2*sd^2)),data=studySpecies,start=init3,trace=T, control = control)
BinSum <- base::summary(Bin)
Bs <- BinSum$coefficients[1]
Bsd <- BinSum$coefficients[2]
Bm <- BinSum$coefficients[3]
BinRSE <- BinSum$sigma
BinRsq <- cor(predict(Bin, newdata=x), y)**2
Binp <- round(max(BinSum$coefficients[10],BinSum$coefficients[11],BinSum$coefficients[12]),5)
rm(Bin)
} else {
Bs <- NA
Bsd <- NA
Bm <- NA
BinRSE <- 100
BinRsq <- 0
Binp <- 1
}
#Quadratic
init4 <- c(a = 1, b = 2, c = 0)
if (!berryFunctions::is.error(nls(y ~ a*x^2 + b*x + c, data = studySpecies,
start = init4, trace = T, control = control))) {
q <- nls(y ~ a*x^2 + b*x + c, data = studySpecies, start = init4,
trace = T, control = control)
qSum <- base::summary(q)
qa <- qSum$coefficients[1]
qb <- qSum$coefficients[2]
qc <- qSum$coefficients[3]
#Added control for Quadratic
f <- function(x){qa*x^2 + qb*x + qc}
if (bTest * (sd(y, na.rm = TRUE) + mean(y, na.rm = TRUE)) < (optimize(f = f, interval=c(0, 150), maximum=TRUE))$objective) {
qRSE <- 100
qRsq <- 0
qp <- 1
} else {
qRSE <- qSum$sigma
qRsq <- cor(predict(q, newdata=x), y)**2
qp <- round(max(qSum$coefficients[10], qSum$coefficients[11],
qSum$coefficients[12]), 5)
}
rm(q)
} else {
qa <- NA
qb <- NA
qc <- NA
qRSE <- 100
qRsq <- 0
qp <- 1
}
#Summary stats
meanBase <- round(mean(y, na.rm = TRUE),1)
m_sig <- round(mRSE(dat = y),3)
#Choose the best model
listRsq <- c(LMRsq, NERsq, BRsq, BinRsq, qRsq)
listRSE <- c(LMRSE, NERSE, BRSE, BinRSE, qRSE)
listMod <- c("Linear", "NegExp", "Burr", "Binomial", "Quadratic")
if (listRSE[which(listRsq == max(listRsq))]<=m_sig) {
model <- listMod[which(listRsq == max(listRsq))]
} else {
model <- "Mean"
}
} else {
LMa <- NA
LMb <- NA
LMRSE <- 100
LMRsq <- 0
LMp <- 1
k <- NA
r <- NA
NERSE <- 100
NERsq <- 0
NEp <- 1
Ba <- NA
Bb <- NA
BRSE <- 100
BRsq <- 0
Bp <- 1
Bs <- NA
Bsd <- NA
Bm <- NA
BinRSE <- 100
BinRsq <- 0
Binp <- 1
qa <- NA
qb <- NA
qc <- NA
qRSE <- 100
qRsq <- 0
qp <- 1
model <- "Mean"
#Summary stats
meanBase <- round(mean(y, na.rm = TRUE),1)
m_sig <- round(mRSE(dat = y),3)
}
#Record values
fitBase[nrow(fitBase)+1,] <- c(as.character(priorList[SpeciesNumber]), LMa, LMb, LMRSE, LMRsq, LMp, k, r, NERSE, NERsq, NEp,
Ba, Bb, BRSE, BRsq, Bp, Bs, Bsd, Bm, BinRSE, BinRsq, Binp, qa, qb, qc, qRSE, qRsq, qp,
meanBase, m_sig, model)
}
return(fitBase)
}
#' Builds models for top-he height allometry dynamics of surveyed Species
#'
#' Input table requires the following fields:
#' Point - numbered point in a transect
#' Species - name of the surveyed Species
#' Age - age of the site since the triggering disturbance
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat The dataframe containing the input data,
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#' @param pnts The number of points measured in a transect
#' @param p The maximum allowable p value for a model
#' @return dataframe
#' @export
heDyn <- function(dat, thres = 5, pnts = 10, p = 0.05,
base = "base", top = "top", he = "he", ht = "ht") {
spCov <- specCover(dat = dat, thres = 0, pnts = pnts)%>%
group_by(Species)%>%
summarise_if(is.numeric, mean)
dat <- left_join(dat, spCov)%>%
mutate(Species = replace(Species, which(Cover < thres), "Minor Species"),
bRat = he/top)
priorList <- unique(dat$Species, incomparables = FALSE)
#DATA ANALYSIS
fithe <- data.frame('Species' = character(0), 'lin_a' = numeric(0), 'lin_b' = numeric(0),'lin_Sigma' = numeric(0), 'lin_p' = numeric(0),
'linear' = character(0), 'Mean' = character(0), 'Mean_sigma' = character(0), 'Model' = character(0), stringsAsFactors=F)
for (sp in 1:length(priorList)) {
SpeciesNumber <- sp
studySpecies <- dat %>% filter(Species == priorList[SpeciesNumber])
x <- as.numeric(studySpecies$Age)
y <- as.numeric(studySpecies$bRat)
if (length(unique(x, incomparables = FALSE))>2 & length(unique(y, incomparables = FALSE))>1) {
#Linear
if (!berryFunctions::is.error(lm(y ~ x)) && length(unique(x, incomparables = FALSE))>1) {
LM<-lm(y ~ x)
LMSum <- base::summary(LM)
LMa <- LMSum$coefficients[2]
LMb <- LMSum$coefficients[1]
LMRSE <- LMSum$sigma
LMp <- if (LMRSE == 0) {
0
} else {
round(max(LMSum$coefficients[7],LMSum$coefficients[8]),5)
}
LM_sig <- if (berryFunctions::is.error(LMp)) {
""
}
else if (LMp < 0.001) {
"***"
} else if (LMp < 0.01) {
"**"
} else if (LMp < 0.05) {
"*"
} else {
""
}
rm(LM)
} else {
LMa <- NA
LMb <- NA
LRSE <- NA
LMp <- 1
LM_sig <- ""
}
}
#Summary stats
meanhe <- round(mean(y, na.rm = TRUE),1)
m_sig <- round(mRSE(dat = y),3)
model <- if (LMp < p) {
"Linear"
} else {"Mean"}
model <- if (LMRSE < m_sig){
model
} else {"Mean"}
#Record values
fithe[nrow(fithe)+1,] <- c(as.character(priorList[SpeciesNumber]), LMa, LMb, LMRSE, LMp,
LM_sig, meanhe, m_sig, model)
}
return(fithe)
}
#' Builds models for top-ht height allometry dynamics of surveyed Species
#'
#' Input table requires the following fields:
#' Point - numbered point in a transect
#' Species - name of the surveyed Species
#' Age - age of the site since the triggering disturbance
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat The dataframe containing the input data,
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#' @param pnts The number of points measured in a transect
#' @param p The maximum allowable p value for a model
#' @return dataframe
#' @export
htDyn <- function(dat, thres = 5, pnts = 10, p = 0.05) {
spCov <- specCover(dat = dat, thres = 0, pnts = pnts)%>%
group_by(Species)%>%
summarise_if(is.numeric, mean)
dat <- left_join(dat, spCov)%>%
mutate(Species = replace(Species, which(Cover < thres), "Minor Species"),
bRat = ht/top)
priorList <- unique(dat$Species, incomparables = FALSE)
#DATA ANALYSIS
fitht <- data.frame('Species' = character(0), 'lin_a' = numeric(0), 'lin_b' = numeric(0),'lin_Sigma' = numeric(0), 'lin_p' = numeric(0),
'linear' = character(0), 'Mean' = character(0), 'Mean_sigma' = character(0), 'Model' = character(0), stringsAsFactors=F)
for (sp in 1:length(priorList)) {
SpeciesNumber <- sp
studySpecies <- dat %>% filter(Species == priorList[SpeciesNumber])
x <- as.numeric(studySpecies$Age)
y <- as.numeric(studySpecies$bRat)
if (length(unique(x, incomparables = FALSE))>2 & length(unique(y, incomparables = FALSE))>1) {
#Linear
if (!berryFunctions::is.error(lm(y ~ x)) && length(unique(x, incomparables = FALSE))>1) {
LM<-lm(y ~ x)
LMSum <- base::summary(LM)
LMa <- LMSum$coefficients[2]
LMb <- LMSum$coefficients[1]
LMRSE <- LMSum$sigma
LMp <- if (LMRSE == 0) {
0
} else {
round(max(LMSum$coefficients[7],LMSum$coefficients[8]),5)
}
LM_sig <- if (berryFunctions::is.error(LMp)) {
""
}
else if (LMp < 0.001) {
"***"
} else if (LMp < 0.01) {
"**"
} else if (LMp < 0.05) {
"*"
} else {
""
}
rm(LM)
} else {
LMa <- NA
LMb <- NA
LRSE <- NA
LMp <- 1
LM_sig <- ""
}
}
#Summary stats
meanht <- round(mean(y, na.rm = TRUE),1)
m_sig <- round(mRSE(dat = y),3)
model <- if (LMp < p) {
"Linear"
} else {"Mean"}
model <- if (LMRSE < m_sig){
model
} else {"Mean"}
#Record values
fitht[nrow(fitht)+1,] <- c(as.character(priorList[SpeciesNumber]), LMa, LMb, LMRSE, LMp,
LM_sig, meanht, m_sig, model)
}
return(fitht)
}
#' Builds models for top-w height allometry dynamics of surveyed Species
#'
#' Input table requires the following fields:
#' Point - numbered point in a transect
#' Species - name of the surveyed Species
#' Age - age of the site since the triggering disturbance
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat The dataframe containing the input data,
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#' @param pnts The number of points measured in a transect
#' @param p The maximum allowable p value for a model
#' @param bTest Multiples of mean + mRSE for which Burr & quadratic models can predict
#' beyond the observed mean + standard deviation
#' @param maxiter The maximum number of iterations for model fitting
#' @return dataframe
#' @export
wDyn <- function(dat, width = "width", top = "top",
thres = 5, pnts = 10, p = 0.05, bTest = 10, maxiter = 1000) {
spCov <- specCover(dat = dat, thres = 0, pnts = pnts)%>%
group_by(Species)%>%
summarise_if(is.numeric, mean)
dat <- left_join(dat, spCov)%>%
mutate(Species = replace(Species, which(Cover < thres), "Minor Species"),
Rat = as.numeric(width)/top)
# Find missing data
entries <- which(is.na(dat[width]))
if (length(entries)>0) {
cat(" Removed these rows as they were missing crown widths", "\n", entries, "\n", "\n")
dat <- dat[-entries,]
}
priorList <- unique(dat$Species, incomparables = FALSE)
#DATA ANALYSIS
fitw <- data.frame('Species' = character(0), 'lin_a' = numeric(0), 'lin_b' = numeric(0),'lin_Sigma' = numeric(0), 'lin_Rsq' = numeric(0), 'lin_p' = numeric(0),
'k' = numeric(0), 'r' = numeric(0), 'NE_sigma' = numeric(0), 'NE_Rsq' = numeric(0), 'NE_p' = numeric(0),
'Ba' = numeric(0), 'Bb' = numeric(0), 'B_sigma' = numeric(0), 'B_Rsq' = numeric(0), 'B_p' = numeric(0),
'scale' = numeric(0), 'sd' = numeric(0), 'Binm' = numeric(0), 'Bin_sigma' = numeric(0), 'Bin_Rsq' = numeric(0), 'Bin_p' = numeric(0),
'Qa' = numeric(0), 'Qb' = numeric(0), 'Qc' = numeric(0), 'Q_sigma' = numeric(0), 'q_Rsq' = numeric(0), 'Q_p' = numeric(0),
'Mean' = character(0), 'Mean_sigma' = character(0), 'Model' = character(0), stringsAsFactors=F)
for (sp in 1:length(priorList)) {
SpeciesNumber <- sp
control=nls.control(maxiter=maxiter, tol=1e-7, minFactor = 1/999999999)
studySpecies <- dat %>% filter(Species == priorList[SpeciesNumber])
x <- as.numeric(studySpecies$Age)
y <- as.numeric(studySpecies$Rat)
if (length(unique(x, incomparables = FALSE))>2 & length(unique(y, incomparables = FALSE))>1) {
#Linear
if (!berryFunctions::is.error(lm(y ~ x))) {
LM<-lm(y ~ x)
LMSum <- base::summary(LM)
LMa <- LMSum$coefficients[2]
LMb <- LMSum$coefficients[1]
LMRSE <- LMSum$sigma
LMRsq <- LMSum$r.squared
LMp <- if (LMRSE == 0) {
0
} else {
round(max(LMSum$coefficients[7],LMSum$coefficients[8]),5)
}
rm(LM)
} else {
LMa <- NA
LMb <- NA
LMRSE <- 100
LMRsq <- 0
LMp <- 1
}
#Negative exponential
init1<-c(k=50,r=0.5)
if (!berryFunctions::is.error(nls(y~k * (1-exp(-r*x)),data=studySpecies,start=init1,trace=T))) {
NE<-nls(y~k * (1-exp(-r*x)),data=studySpecies,start=init1,trace=T)
NESum <- base::summary(NE)
k <- NESum$coefficients[1]
r <- NESum$coefficients[2]
NERSE <- NESum$sigma
NERsq <- cor(predict(NE, newdata=x), y)**2
NEp <- round(max(NESum$coefficients[7],NESum$coefficients[8]),5)
rm(NE)
} else {
k <- NA
r <- NA
NERSE <- 100
NERsq <- 0
NEp <- 1
}
#Burr
init2<-c(a=3,b=2)
if (!berryFunctions::is.error(nls(y~a*b*((0.1*x^(a-1))/((1+(0.1*x)^a)^b+1)),data=studySpecies,start=init2,trace=T, control = control))) {
Burr<-nls(y~a*b*((0.1*x^(a-1))/((1+(0.1*x)^a)^b+1)),data=studySpecies,start=init2,trace=T, control = control)
BSum <- base::summary(Burr)
Ba <- BSum$coefficients[1]
Bb <- BSum$coefficients[2]
#Added control for Burr
f <- function(x){Ba*Bb*((0.1*x^(Ba-1))/((1+(0.1*x)^Ba)^Bb+1))}
if (bTest * (sd(y, na.rm = TRUE) + mean(y, na.rm = TRUE)) < (optimize(f = f, interval=c(0, 150), maximum=TRUE))$objective) {
BRSE <- 100
BRsq <- 0
Bp <- 1
} else if (bTest * (mean(y, na.rm = TRUE) - sd(y, na.rm = TRUE)) > (optimize(f = f, interval=c(0, 150), maximum=FALSE))$objective) {
BRSE <- 100
BRsq <- 0
Bp <- 1
} else {
BRSE <- BSum$sigma
BRsq <- cor(predict(Burr, newdata=x), y)**2
Bp <- round(max(BSum$coefficients[7],BSum$coefficients[8]),5)
}
rm(Burr)
} else {
Ba <- NA
Bb <- NA
BRSE <- 100
BRsq <- 0
Bp <- 1
}
#Binomial
init3<-c(s=1,sd=3, m=20)
if (!berryFunctions::is.error(nls(y~s*(1/(sd*sqrt(2*pi)))*exp(-((x-m)^2)/(2*sd^2)),data=studySpecies,start=init3,trace=T, control = control))) {
Bin<-nls(y~s*(1/(sd*sqrt(2*pi)))*exp(-((x-m)^2)/(2*sd^2)),data=studySpecies,start=init3,trace=T, control = control)
BinSum <- base::summary(Bin)
Bs <- BinSum$coefficients[1]
Bsd <- BinSum$coefficients[2]
Bm <- BinSum$coefficients[3]
BinRSE <- BinSum$sigma
BinRsq <- cor(predict(Bin, newdata=x), y)**2
Binp <- round(max(BinSum$coefficients[10],BinSum$coefficients[11],BinSum$coefficients[12]),5)
rm(Bin)
} else {
Bs <- NA
Bsd <- NA
Bm <- NA
BinRSE <- 100
BinRsq <- 0
Binp <- 1
}
#Quadratic
init4 <- c(a = 1, b = 2, c = 0)
if (!berryFunctions::is.error(nls(y ~ a*x^2 + b*x + c, data = studySpecies,
start = init4, trace = T, control = control))) {
q <- nls(y ~ a*x^2 + b*x + c, data = studySpecies, start = init4,
trace = T, control = control)
qSum <- base::summary(q)
qa <- qSum$coefficients[1]
qb <- qSum$coefficients[2]
qc <- qSum$coefficients[3]
#Added control for Quadratic
f <- function(x){qa*x^2 + qb*x + qc}
if (bTest * (sd(y, na.rm = TRUE) + mean(y, na.rm = TRUE)) < (optimize(f = f, interval=c(0, 150), maximum=TRUE))$objective) {
qRSE <- 100
qRsq <- 0
qp <- 1
} else {
qRSE <- qSum$sigma
qRsq <- cor(predict(q, newdata=x), y)**2
qp <- round(max(qSum$coefficients[10], qSum$coefficients[11],
qSum$coefficients[12]), 5)
}
rm(q)
} else {
qa <- NA
qb <- NA
qc <- NA
qRSE <- 100
qRsq <- 0
qp <- 1
}
#Summary stats
meanw <- round(mean(y, na.rm = TRUE),1)
m_sig <- round(mRSE(dat = y),3)
#Choose the best model
listRsq <- c(LMRsq, NERsq, BRsq, BinRsq, qRsq)
listRSE <- c(LMRSE, NERSE, BRSE, BinRSE, qRSE)
listMod <- c("Linear", "NegExp", "Burr", "Binomial", "Quadratic")
if (listRSE[which(listRsq == max(listRsq))]<=m_sig) {
model <- listMod[which(listRsq == max(listRsq))]
} else {
model <- "Mean"
}
} else {
LMa <- NA
LMb <- NA
LMRSE <- 100
LMRsq <- 0
LMp <- 1
k <- NA
r <- NA
NERSE <- 100
NERsq <- 0
NEp <- 1
Ba <- NA
Bb <- NA
BRSE <- 100
BRsq <- 0
Bp <- 1
Bs <- NA
Bsd <- NA
Bm <- NA
BinRSE <- 100
BinRsq <- 0
Binp <- 1
qa <- NA
qb <- NA
qc <- NA
qRSE <- 100
qRsq <- 0
qp <- 1
model <- "Mean"
#Summary stats
meanw <- round(mean(y, na.rm = TRUE),1)
m_sig <- round(mRSE(dat = y),3)
}
#Record values
fitw[nrow(fitw)+1,] <- c(as.character(priorList[SpeciesNumber]), LMa, LMb, LMRSE, LMRsq, LMp, k, r, NERSE, NERsq, NEp,
Ba, Bb, BRSE, BRsq, Bp, Bs, Bsd, Bm, BinRSE, BinRsq, Binp, qa, qb, qc, qRSE, qRsq, qp,
meanw, m_sig, model)
}
return(fitw)
}
#' Builds the plant growth table used in dynamic modelling
#'
#' Models plant growth from field data in a series of models,
#' selecting the best model and providing error statistics.
#'
#' Input table requires the following fields:
#' Point - numbered point in a transect
#' Species - name of the surveyed Species
#' Age - age of the site since the triggering disturbance
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat The dataframe containing the input data,
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#' @param pnts The number of points measured in a transect
#' @param p The maximum allowable p value for a model
#' @param bTest
#' @param cTest
#' @param Sr Rate of increase for surface litter in a negative exponential curve
#' @param Sk Asymptote for surface litter in a negative exponential curve
#' @param Sa
#' @param Sb
#' @param Sc
#' @param NSr Rate of increase for NS fuels in a negative exponential curve
#' @param NSk Asymptote for NS fuels in a negative exponential curve
#' @param NSa
#' @param NSb
#' @param NSc
#' @param maxiter The maximum number of iterations for model fitting
#'
#' @return dataframe
#' @export
floraDynamics <- function(dat, thres = 5, pnts = 10, p = 0.01, bTest = 2, cTest = 10, maxiter = 1000,
Sr = 0, Sk = 0, Sa = 0, Sb = 0, Sc = 0,
NSr = 0, NSk = 0, NSa = 0, NSb = 0, NSc = 0){
# Check for faults, then create tables
dat <- datClean(veg = dat, base, top, he, ht)
coverChange <- coverDyn(dat, thres = thres, pnts = pnts, p = p, bTest = cTest, maxiter = maxiter)
topChange <- topDyn(dat, thres = thres, pnts = pnts, p = p, bTest = bTest, maxiter = maxiter)
baseChange <- baseDyn(dat, thres = thres, pnts = pnts, p = p, bTest = bTest, maxiter = maxiter)
he_Change <- heDyn(dat, thres = thres, pnts = pnts, p = p)
ht_Change <- htDyn(dat, thres = thres, pnts = pnts, p = p)
w_Change <- wDyn(dat, thres = thres, pnts = pnts, p = p, bTest = bTest, maxiter = maxiter)
# Collect models
# Cover
a <- rep(NA, length(coverChange$Species))
for (sp in 1:length(coverChange$Species)) {
if (coverChange$Model[sp] == 'Linear') {
a[sp] <- coverChange$lin_a[sp]
} else
if (coverChange$Model[sp] == 'NegExp') {
a[sp] <- coverChange$k[sp]
} else
if (coverChange$Model[sp] == 'Burr') {
a[sp] <- coverChange$Ba[sp]
} else
if (coverChange$Model[sp] == 'Binomial') {
a[sp] <- coverChange$scale[sp]
} else
if (coverChange$Model[sp] == 'Quadratic') {
a[sp] <- coverChange$Qa[sp]
} else {a[sp] <- 0}
}
b <- rep(NA, length(coverChange$Species))
for (sp in 1:length(coverChange$Species)) {
if (coverChange$Model[sp] == 'Linear') {
b[sp] <- coverChange$lin_b[sp]
} else
if (coverChange$Model[sp] == 'NegExp') {
b[sp] <- coverChange$r[sp]
} else
if (coverChange$Model[sp] == 'Burr') {
b[sp] <- coverChange$Bb[sp]
} else
if (coverChange$Model[sp] == 'Binomial') {
b[sp] <- coverChange$sd[sp]
} else
if (coverChange$Model[sp] == 'Quadratic') {
b[sp] <- coverChange$Qb[sp]
} else {b[sp] <- coverChange$Mean[sp]}
}
c <- rep(NA, length(coverChange$Species))
for (sp in 1:length(coverChange$Species)) {
if (coverChange$Model[sp] == 'Linear') {
c[sp] <- NA
} else
if (coverChange$Model[sp] == 'NegExp') {
c[sp] <- NA
} else
if (coverChange$Model[sp] == 'Burr') {
c[sp] <- NA
} else
if (coverChange$Model[sp] == 'Binomial') {
c[sp] <- coverChange$Binm[sp]
} else
if (coverChange$Model[sp] == 'Quadratic') {
c[sp] <- coverChange$Qc[sp]
} else {c[sp] <- NA}
}
RSE <- rep(NA, length(coverChange$Species))
for (sp in 1:length(coverChange$Species)) {
if (coverChange$Model[sp] == 'Linear') {
RSE[sp] <- coverChange$lin_Sigma[sp]
} else
if (coverChange$Model[sp] == 'NegExp') {
RSE[sp] <- coverChange$NE_sigma[sp]
} else
if (coverChange$Model[sp] == 'Burr') {
RSE[sp] <- coverChange$B_sigma[sp]
} else
if (coverChange$Model[sp] == 'Binomial') {
RSE[sp] <- coverChange$Bin_sigma[sp]
} else
if (coverChange$Model[sp] == 'Quadratic') {
RSE[sp] <- coverChange$Q_sigma[sp]
} else {RSE[sp] <- coverChange$Mean_sigma[sp]}
}
Rsq <- rep(NA, length(coverChange$Species))
for (sp in 1:length(coverChange$Species)) {
if (coverChange$Model[sp] == 'Linear') {
Rsq[sp] <- coverChange$lin_Rsq[sp]
} else
if (coverChange$Model[sp] == 'NegExp') {
Rsq[sp] <- coverChange$NE_Rsq[sp]
} else
if (coverChange$Model[sp] == 'Burr') {
Rsq[sp] <- coverChange$B_Rsq[sp]
} else
if (coverChange$Model[sp] == 'Binomial') {
Rsq[sp] <- coverChange$Bin_Rsq[sp]
} else
if (coverChange$Model[sp] == 'Quadratic') {
Rsq[sp] <- coverChange$q_Rsq[sp]
} else {Rsq[sp] <- NA}
}
CovMean <- rep(NA, length(coverChange$Species))
for (sp in 1:length(coverChange$Species)) {
CovMean[sp] <- coverChange$Mean[sp]
}
# Height
aa <- rep(NA, length(topChange$Species))
for (sp in 1:length(topChange$Species)) {
if (topChange$Model[sp] == 'Linear') {
aa[sp] <- topChange$lin_a[sp]
} else
if (topChange$Model[sp] == 'NegExp') {
aa[sp] <- topChange$k[sp]
} else
if (topChange$Model[sp] == 'Burr') {
aa[sp] <- topChange$Ba[sp]
} else
if (topChange$Model[sp] == 'Binomial') {
aa[sp] <- topChange$scale[sp]
} else
if (topChange$Model[sp] == 'Quadratic') {
aa[sp] <- topChange$Qa[sp]
} else {aa[sp] <- 0}
}
bb <- rep(NA, length(topChange$Species))
for (sp in 1:length(topChange$Species)) {
if (topChange$Model[sp] == 'Linear') {
bb[sp] <- topChange$lin_b[sp]
} else
if (topChange$Model[sp] == 'NegExp') {
bb[sp] <- topChange$r[sp]
} else
if (topChange$Model[sp] == 'Burr') {
bb[sp] <- topChange$Bb[sp]
} else
if (topChange$Model[sp] == 'Binomial') {
bb[sp] <- topChange$sd[sp]
} else
if (topChange$Model[sp] == 'Quadratic') {
bb[sp] <- topChange$Qb[sp]
} else {bb[sp] <- topChange$Mean[sp]}
}
cc <- rep(NA, length(topChange$Species))
for (sp in 1:length(topChange$Species)) {
if (topChange$Model[sp] == 'Linear') {
cc[sp] <- NA
} else
if (topChange$Model[sp] == 'NegExp') {
cc[sp] <- NA
} else
if (topChange$Model[sp] == 'Burr') {
cc[sp] <- NA
} else
if (topChange$Model[sp] == 'Binomial') {
cc[sp] <- topChange$Binm[sp]
} else
if (topChange$Model[sp] == 'Quadratic') {
cc[sp] <- topChange$Qc[sp]
} else {cc[sp] <- NA}
}
RSE1 <- rep(NA, length(topChange$Species))
for (sp in 1:length(topChange$Species)) {
if (topChange$Model[sp] == 'Linear') {
RSE1[sp] <- topChange$lin_Sigma[sp]
} else
if (topChange$Model[sp] == 'NegExp') {
RSE1[sp] <- topChange$NE_sigma[sp]
} else
if (topChange$Model[sp] == 'Burr') {
RSE1[sp] <- topChange$B_sigma[sp]
} else
if (topChange$Model[sp] == 'Binomial') {
RSE1[sp] <- topChange$Bin_sigma[sp]
} else
if (topChange$Model[sp] == 'Quadratic') {
RSE1[sp] <- topChange$Q_sigma[sp]
} else {RSE1[sp] <- topChange$Mean_sigma[sp]}
}
Rsq1 <- rep(NA, length(topChange$Species))
for (sp in 1:length(topChange$Species)) {
if (topChange$Model[sp] == 'Linear') {
Rsq1[sp] <- topChange$lin_Rsq[sp]
} else
if (topChange$Model[sp] == 'NegExp') {
Rsq1[sp] <- topChange$NE_Rsq[sp]
} else
if (topChange$Model[sp] == 'Burr') {
Rsq1[sp] <- topChange$B_Rsq[sp]
} else
if (topChange$Model[sp] == 'Binomial') {
Rsq1[sp] <- topChange$Bin_Rsq[sp]
} else
if (topChange$Model[sp] == 'Quadratic') {
Rsq1[sp] <- topChange$q_Rsq[sp]
} else {Rsq1[sp] <- NA}
}
hMean <- rep(NA, length(topChange$Species))
for (sp in 1:length(topChange$Species)) {
hMean[sp] <- topChange$Mean[sp]
}
# Base
aaa <- rep(NA, length(baseChange$Species))
for (sp in 1:length(baseChange$Species)) {
if (baseChange$Model[sp] == 'Linear') {
aaa[sp] <- baseChange$lin_a[sp]
} else
if (baseChange$Model[sp] == 'NegExp') {
aaa[sp] <- baseChange$k[sp]
} else
if (baseChange$Model[sp] == 'Burr') {
aaa[sp] <- baseChange$Ba[sp]
} else
if (baseChange$Model[sp] == 'Binomial') {
aaa[sp] <- baseChange$scale[sp]
} else
if (baseChange$Model[sp] == 'Quadratic') {
aaa[sp] <- baseChange$Qa[sp]
} else {aaa[sp] <- 0}
}
bbb <- rep(NA, length(baseChange$Species))
for (sp in 1:length(baseChange$Species)) {
if (baseChange$Model[sp] == 'Linear') {
bbb[sp] <- baseChange$lin_b[sp]
} else
if (baseChange$Model[sp] == 'NegExp') {
bbb[sp] <- baseChange$r[sp]
} else
if (baseChange$Model[sp] == 'Burr') {
bbb[sp] <- baseChange$Bb[sp]
} else
if (baseChange$Model[sp] == 'Binomial') {
bbb[sp] <- baseChange$sd[sp]
} else
if (baseChange$Model[sp] == 'Quadratic') {
bbb[sp] <- baseChange$Qb[sp]
} else {bbb[sp] <- baseChange$Mean[sp]}
}
ccc <- rep(NA, length(baseChange$Species))
for (sp in 1:length(baseChange$Species)) {
if (baseChange$Model[sp] == 'Linear') {
ccc[sp] <- NA
} else
if (baseChange$Model[sp] == 'NegExp') {
ccc[sp] <- NA
} else
if (baseChange$Model[sp] == 'Burr') {
ccc[sp] <- NA
} else
if (baseChange$Model[sp] == 'Binomial') {
ccc[sp] <- baseChange$Binm[sp]
} else
if (baseChange$Model[sp] == 'Quadratic') {
ccc[sp] <- baseChange$Qc[sp]
} else {ccc[sp] <- NA}
}
RSE2 <- rep(NA, length(baseChange$Species))
for (sp in 1:length(baseChange$Species)) {
if (baseChange$Model[sp] == 'Linear') {
RSE2[sp] <- baseChange$lin_Sigma[sp]
} else
if (baseChange$Model[sp] == 'NegExp') {
RSE2[sp] <- baseChange$NE_sigma[sp]
} else
if (baseChange$Model[sp] == 'Burr') {
RSE2[sp] <- baseChange$B_sigma[sp]
} else
if (baseChange$Model[sp] == 'Binomial') {
RSE2[sp] <- baseChange$Bin_sigma[sp]
} else
if (baseChange$Model[sp] == 'Quadratic') {
RSE2[sp] <- baseChange$Q_sigma[sp]
} else {RSE2[sp] <- baseChange$Mean_sigma[sp]}
}
Rsq2 <- rep(NA, length(baseChange$Species))
for (sp in 1:length(baseChange$Species)) {
if (baseChange$Model[sp] == 'Linear') {
Rsq2[sp] <- baseChange$lin_Rsq[sp]
} else
if (baseChange$Model[sp] == 'NegExp') {
Rsq2[sp] <- baseChange$NE_Rsq[sp]
} else
if (baseChange$Model[sp] == 'Burr') {
Rsq2[sp] <- baseChange$B_Rsq[sp]
} else
if (baseChange$Model[sp] == 'Binomial') {
Rsq2[sp] <- baseChange$Bin_Rsq[sp]
} else
if (baseChange$Model[sp] == 'Quadratic') {
Rsq2[sp] <- baseChange$q_Rsq[sp]
} else {Rsq2[sp] <- NA}
}
bMean <- rep(NA, length(baseChange$Species))
for (sp in 1:length(baseChange$Species)) {
bMean[sp] <- baseChange$Mean[sp]
}
# He
aA <- rep(NA, length(he_Change$Species))
for (sp in 1:length(he_Change$Species)) {
if (he_Change$Model[sp] == 'Linear') {
aA[sp] <- he_Change$lin_a[sp]
} else
if (he_Change$Model[sp] == 'NegExp') {
aA[sp] <- he_Change$k[sp]
} else
if (he_Change$Model[sp] == 'Burr') {
aA[sp] <- he_Change$Ba[sp]
} else
if (he_Change$Model[sp] == 'Binomial') {
aA[sp] <- he_Change$scale[sp]
} else
if (he_Change$Model[sp] == 'Quadratic') {
aA[sp] <- he_Change$Qa[sp]
} else {aA[sp] <- 0}
}
bB <- rep(NA, length(he_Change$Species))
for (sp in 1:length(he_Change$Species)) {
if (he_Change$Model[sp] == 'Linear') {
bB[sp] <- he_Change$lin_b[sp]
} else
if (he_Change$Model[sp] == 'NegExp') {
bB[sp] <- he_Change$r[sp]
} else
if (he_Change$Model[sp] == 'Burr') {
bB[sp] <- he_Change$Bb[sp]
} else
if (he_Change$Model[sp] == 'Binomial') {
bB[sp] <- he_Change$sd[sp]
} else
if (he_Change$Model[sp] == 'Quadratic') {
bB[sp] <- he_Change$Qb[sp]
} else {bB[sp] <- he_Change$Mean[sp]}
}
cC <- rep(NA, length(he_Change$Species))
for (sp in 1:length(he_Change$Species)) {
if (he_Change$Model[sp] == 'Linear') {
cC[sp] <- NA
} else
if (he_Change$Model[sp] == 'NegExp') {
cC[sp] <- NA
} else
if (he_Change$Model[sp] == 'Burr') {
cC[sp] <- NA
} else
if (he_Change$Model[sp] == 'Binomial') {
cC[sp] <- he_Change$Binm[sp]
} else
if (he_Change$Model[sp] == 'Quadratic') {
cC[sp] <- he_Change$Qc[sp]
} else {cC[sp] <- NA}
}
RSE3 <- rep(NA, length(he_Change$Species))
for (sp in 1:length(he_Change$Species)) {
if (he_Change$Model[sp] == 'Linear') {
RSE3[sp] <- he_Change$lin_Sigma[sp]
} else
if (he_Change$Model[sp] == 'NegExp') {
RSE3[sp] <- he_Change$NE_sigma[sp]
} else
if (he_Change$Model[sp] == 'Burr') {
RSE3[sp] <- he_Change$B_sigma[sp]
} else
if (he_Change$Model[sp] == 'Binomial') {
RSE3[sp] <- he_Change$Bin_sigma[sp]
} else
if (he_Change$Model[sp] == 'Quadratic') {
RSE3[sp] <- he_Change$Q_sigma[sp]
} else {RSE3[sp] <- he_Change$Mean_sigma[sp]}
}
heMean <- rep(NA, length(he_Change$Species))
for (sp in 1:length(he_Change$Species)) {
heMean[sp] <- he_Change$Mean[sp]
}
# Ht
aT <- rep(NA, length(ht_Change$Species))
for (sp in 1:length(ht_Change$Species)) {
if (ht_Change$Model[sp] == 'Linear') {
aT[sp] <- ht_Change$lin_a[sp]
} else
if (ht_Change$Model[sp] == 'NegExp') {
aT[sp] <- ht_Change$k[sp]
} else
if (ht_Change$Model[sp] == 'Burr') {
aT[sp] <- ht_Change$Ba[sp]
} else
if (ht_Change$Model[sp] == 'Binomial') {
aT[sp] <- ht_Change$scale[sp]
} else
if (ht_Change$Model[sp] == 'Quadratic') {
aT[sp] <- ht_Change$Qa[sp]
} else {aT[sp] <- 0}
}
bT <- rep(NA, length(ht_Change$Species))
for (sp in 1:length(ht_Change$Species)) {
if (ht_Change$Model[sp] == 'Linear') {
bT[sp] <- ht_Change$lin_b[sp]
} else
if (ht_Change$Model[sp] == 'NegExp') {
bT[sp] <- ht_Change$r[sp]
} else
if (ht_Change$Model[sp] == 'Burr') {
bT[sp] <- ht_Change$Bb[sp]
} else
if (ht_Change$Model[sp] == 'Binomial') {
bT[sp] <- ht_Change$sd[sp]
} else
if (ht_Change$Model[sp] == 'Quadratic') {
bT[sp] <- ht_Change$Qb[sp]
} else {bT[sp] <- ht_Change$Mean[sp]}
}
cT <- rep(NA, length(ht_Change$Species))
for (sp in 1:length(ht_Change$Species)) {
if (ht_Change$Model[sp] == 'Linear') {
cT[sp] <- NA
} else
if (ht_Change$Model[sp] == 'NegExp') {
cT[sp] <- NA
} else
if (ht_Change$Model[sp] == 'Burr') {
cT[sp] <- NA
} else
if (ht_Change$Model[sp] == 'Binomial') {
cT[sp] <- ht_Change$Binm[sp]
} else
if (ht_Change$Model[sp] == 'Quadratic') {
cT[sp] <- ht_Change$Qc[sp]
} else {cT[sp] <- NA}
}
RSEt <- rep(NA, length(ht_Change$Species))
for (sp in 1:length(ht_Change$Species)) {
if (ht_Change$Model[sp] == 'Linear') {
RSEt[sp] <- ht_Change$lin_Sigma[sp]
} else
if (ht_Change$Model[sp] == 'NegExp') {
RSEt[sp] <- ht_Change$NE_sigma[sp]
} else
if (ht_Change$Model[sp] == 'Burr') {
RSEt[sp] <- ht_Change$B_sigma[sp]
} else
if (ht_Change$Model[sp] == 'Binomial') {
RSEt[sp] <- ht_Change$Bin_sigma[sp]
} else
if (ht_Change$Model[sp] == 'Quadratic') {
RSEt[sp] <- ht_Change$Q_sigma[sp]
} else {RSEt[sp] <- ht_Change$Mean_sigma[sp]}
}
htMean <- rep(NA, length(ht_Change$Species))
for (sp in 1:length(ht_Change$Species)) {
htMean[sp] <- ht_Change$Mean[sp]
}
# Width
aw <- rep(NA, length(w_Change$Species))
for (sp in 1:length(w_Change$Species)) {
if (w_Change$Model[sp] == 'Linear') {
aw[sp] <- w_Change$lin_a[sp]
} else
if (w_Change$Model[sp] == 'NegExp') {
aw[sp] <- w_Change$k[sp]
} else
if (w_Change$Model[sp] == 'Burr') {
aw[sp] <- w_Change$Ba[sp]
} else
if (w_Change$Model[sp] == 'Binomial') {
aw[sp] <- w_Change$scale[sp]
} else
if (w_Change$Model[sp] == 'Quadratic') {
aw[sp] <- w_Change$Qa[sp]
} else {aw[sp] <- 0}
}
bw <- rep(NA, length(w_Change$Species))
for (sp in 1:length(w_Change$Species)) {
if (w_Change$Model[sp] == 'Linear') {
bw[sp] <- w_Change$lin_b[sp]
} else
if (w_Change$Model[sp] == 'NegExp') {
bw[sp] <- w_Change$r[sp]
} else
if (w_Change$Model[sp] == 'Burr') {
bw[sp] <- w_Change$Bb[sp]
} else
if (w_Change$Model[sp] == 'Binomial') {
bw[sp] <- w_Change$sd[sp]
} else
if (w_Change$Model[sp] == 'Quadratic') {
bw[sp] <- w_Change$Qb[sp]
} else {bw[sp] <- w_Change$Mean[sp]}
}
cw <- rep(NA, length(w_Change$Species))
for (sp in 1:length(w_Change$Species)) {
if (w_Change$Model[sp] == 'Linear') {
cw[sp] <- NA
} else
if (w_Change$Model[sp] == 'NegExp') {
cw[sp] <- NA
} else
if (w_Change$Model[sp] == 'Burr') {
cw[sp] <- NA
} else
if (w_Change$Model[sp] == 'Binomial') {
cw[sp] <- w_Change$Binm[sp]
} else
if (w_Change$Model[sp] == 'Quadratic') {
cw[sp] <- w_Change$Qc[sp]
} else {cw[sp] <- NA}
}
RSEw <- rep(NA, length(w_Change$Species))
for (sp in 1:length(w_Change$Species)) {
if (w_Change$Model[sp] == 'Linear') {
RSEw[sp] <- w_Change$lin_Sigma[sp]
} else
if (w_Change$Model[sp] == 'NegExp') {
RSEw[sp] <- w_Change$NE_sigma[sp]
} else
if (w_Change$Model[sp] == 'Burr') {
RSEw[sp] <- w_Change$B_sigma[sp]
} else
if (w_Change$Model[sp] == 'Binomial') {
RSEw[sp] <- w_Change$Bin_sigma[sp]
} else
if (w_Change$Model[sp] == 'Quadratic') {
RSEw[sp] <- w_Change$Q_sigma[sp]
} else {RSEw[sp] <- w_Change$Mean_sigma[sp]}
}
Rsqw <- rep(NA, length(w_Change$Species))
for (sp in 1:length(w_Change$Species)) {
if (w_Change$Model[sp] == 'Linear') {
Rsqw[sp] <- w_Change$lin_Rsq[sp]
} else
if (w_Change$Model[sp] == 'NegExp') {
Rsqw[sp] <- w_Change$NE_Rsq[sp]
} else
if (w_Change$Model[sp] == 'Burr') {
Rsqw[sp] <- w_Change$B_Rsq[sp]
} else
if (w_Change$Model[sp] == 'Binomial') {
Rsqw[sp] <- w_Change$Bin_Rsq[sp]
} else
if (w_Change$Model[sp] == 'Quadratic') {
Rsqw[sp] <- w_Change$q_Rsq[sp]
} else {Rsqw[sp] <- NA}
}
wMean <- rep(NA, length(w_Change$Species))
for (sp in 1:length(w_Change$Species)) {
wMean[sp] <- w_Change$Mean[sp]
}
models <- data.frame('Species'=coverChange$Species,
'Cover' = coverChange$Model, 'C_a' = a, 'C_b' = b, 'C_c' = c, 'C_RSE' = RSE, 'C_Rsq' = Rsq, 'meanCover' = CovMean,
'Top' = topChange$Model, 'T_a' = aa, 'T_b' = bb, 'T_c' = cc, 'T_RSE' = RSE1, 'T_Rsq' = Rsq1, 'meanTop' = hMean,
'Base' = baseChange$Model, 'B_a' = aaa, 'B_b' = bbb, 'B_c' = ccc, 'B_RSE' = RSE2, 'B_Rsq' = Rsq2, 'meanBase' = bMean,
'He' = he_Change$Model, 'He_a' = aA, 'He_b' = bB, 'He_c' = cC, 'He_RSE' = RSE3, 'meanHe' = heMean,
'Ht' = ht_Change$Model, 'Ht_a' = aT, 'Ht_b' = bT, 'Ht_c' = cT, 'Ht_RSE' = RSEt, 'meanHt' = htMean,
'Width' = w_Change$Model, 'w_a' = aw, 'w_b' = bw, 'w_c' = cw, 'w_RSE' = RSEw, 'w_Rsq' = Rsqw, 'meanW' = wMean)
# Dead biomass
litter <- case_when(
Sr != 0 ~ "NegExp",
Sa != 0 ~ "Quadratic",
TRUE ~ as.character("Set")
)
suspNS <- case_when(
NSr != 0 ~ "NegExp",
NSa != 0 ~ "Quadratic",
TRUE ~ as.character("Set")
)
models[length(models$Species)+1,1] <- "Litter"
models$Dead[length(models$Species)] <- litter
models$d_Max[length(models$Species)] <- Sk
models$d_Rate[length(models$Species)] <- Sr
models$d_a[length(models$Species)] <- Sa
models$d_b[length(models$Species)] <- Sb
models$d_c[length(models$Species)] <- Sc
models[length(models$Species)+1,1] <- "suspNS"
models$Dead[length(models$Species)] <- suspNS
models$d_Max[length(models$Species)] <- NSk
models$d_Rate[length(models$Species)] <- NSr
models$d_a[length(models$Species)] <- NSa
models$d_b[length(models$Species)] <- NSb
models$d_c[length(models$Species)] <- NSc
return(models)
}
#' Predicts proportion cover at a given age
#'
#' Selects model from tabled models per species
#' @param mods A table of models fit to species, format as per modCollector
#' @param sp The name of a species in the table mods
#' @param Age The age of the plant (years since fire)
#' @return value
#' @export
#'
pCover <- function(mods, sp, Age = 10){
n <- as.numeric(which(mods$Species == sp))
if (length(n)>1) {
cat("There is more than one entry for a species", "\n")
}
if (mods$Cover[n] == "NegExp") {
c <- as.numeric(mods$C_a[n]) * (1 - exp(-as.numeric(mods$C_b[n]) * Age))
} else if (mods$Cover[n] == "Burr") {
c <- as.numeric(mods$C_a[n]) * as.numeric(mods$C_b[n]) * ((0.1 * Age ^ (as.numeric(mods$C_a[n]) - 1)) / ((1 + (0.1 * Age) ^ as.numeric(mods$C_a[n])) ^ as.numeric(mods$C_b[n]) + 1))
} else if (mods$Cover[n] == "Linear") {
c <- as.numeric(mods$C_a[n]) * Age + as.numeric(mods$C_b[n])
} else if (mods$Cover[n] == "Binomial") {
c <- as.numeric(mods$C_a[n])*(1/(as.numeric(mods$C_b[n])*sqrt(2*pi)))*exp(-((Age-as.numeric(mods$C_c[n]))^2)/(2*as.numeric(mods$C_b[n])^2))
} else if (mods$Cover[n] == "Quadratic") {
c <- as.numeric(mods$C_a[n])*Age^2 + as.numeric(mods$C_b[n])*Age + as.numeric(mods$C_c[n])
} else if (mods$Cover[n] == "Mean") {
c <- as.numeric(mods$meanCover[n])
}
c <- min(max(0,c),100)
return(c)
}
#' Predicts plant top height at a given age
#'
#' Selects model from tabled models per species
#' @param mods A table of models fit to species, format as per modCollector
#' @param sp The name of a species in the table mods
#' @param Age The age of the plant (years since fire)
#' @return value
#' @export
#'
pTop <- function(mods, sp, Age = 10){
n <- as.numeric(which(mods$Species == sp))
if (length(n)>1) {
cat("There is more than one entry for a species", "\n")
}
if (mods$Top[n] == "NegExp") {
c <- as.numeric(mods$T_a[n]) * (1 - exp(-as.numeric(mods$T_b[n]) * Age))
} else if (mods$Top[n] == "Burr") {
c <- as.numeric(mods$T_a[n]) * as.numeric(mods$T_b[n]) * ((0.1 * Age ^ (as.numeric(mods$T_a[n]) - 1)) / ((1 + (0.1 * Age) ^ as.numeric(mods$T_a[n])) ^ as.numeric(mods$T_b[n]) + 1))
} else if (mods$Top[n] == "Linear") {
c <- as.numeric(mods$T_a[n]) * Age + as.numeric(mods$T_b[n])
} else if (mods$Top[n] == "Binomial") {
c <- as.numeric(mods$T_a[n])*(1/(as.numeric(mods$T_b[n])*sqrt(2*pi)))*exp(-((Age-as.numeric(mods$T_c[n]))^2)/(2*as.numeric(mods$T_b[n])^2))
} else if (mods$Top[n] == "Quadratic") {
c <- as.numeric(mods$T_a[n])*Age^2 + as.numeric(mods$T_b[n])*Age + as.numeric(mods$T_c[n])
} else if (mods$Top[n] == "Mean") {
c <- as.numeric(mods$meanTop[n])
}
c <- max(0,c)
return(c)
}
#' Predicts plant base height at a given age
#'
#' Selects model from tabled models per species
#' @param mods A table of models fit to species, format as per modCollector
#' @param sp The name of a species in the table mods
#' @param Age The age of the plant (years since fire)
#' @return value
#' @export
#'
pBase <- function(mods, sp, Age = 10){
n <- as.numeric(which(mods$Species == sp))
if (length(n)>1) {
cat("There is more than one entry for a species", "\n")
}
if (mods$Base[n] == "NegExp") {
c <- as.numeric(mods$B_a[n]) * (1 - exp(-as.numeric(mods$B_b[n]) * Age))
} else if (mods$Base[n] == "Burr") {
c <- as.numeric(mods$B_a[n]) * as.numeric(mods$B_b[n]) * ((0.1 * Age ^ (as.numeric(mods$B_a[n]) - 1)) / ((1 + (0.1 * Age) ^ as.numeric(mods$B_a[n])) ^ as.numeric(mods$B_b[n]) + 1))
} else if (mods$Base[n] == "Linear") {
c <- as.numeric(mods$B_a[n]) * Age + as.numeric(mods$B_b[n])
} else if (mods$Base[n] == "Binomial") {
c <- as.numeric(mods$B_a[n])*(1/(as.numeric(mods$B_b[n])*sqrt(2*pi)))*exp(-((Age-as.numeric(mods$B_c[n]))^2)/(2*as.numeric(mods$B_b[n])^2))
} else if (mods$Base[n] == "Quadratic") {
c <- as.numeric(mods$B_a[n])*Age^2 + as.numeric(mods$B_b[n])*Age + as.numeric(mods$B_c[n])
} else if (mods$Base[n] == "Mean") {
c <- as.numeric(mods$meanBase[n])
}
c <- min(max(0,c),1)
return(c)
}
#' Predicts plant He at a given age
#'
#' Selects model from tabled models per species
#' @param mods A table of models fit to species, format as per modCollector
#' @param sp The name of a species in the table mods
#' @param Age The age of the plant (years since fire)
#' @return value
#' @export
#'
pHe <- function(mods, sp, Age = 10){
n <- as.numeric(which(mods$Species == sp))
if (length(n)>1) {
cat("There is more than one entry for a species", "\n")
}
if (mods$He[n] == "NegExp") {
c <- as.numeric(mods$He_a[n]) * (1 - exp(-as.numeric(mods$He_b[n]) * Age))
} else if (mods$He[n] == "Burr") {
c <- as.numeric(mods$He_a[n]) * as.numeric(mods$He_b[n]) * ((0.1 * Age ^ (as.numeric(mods$He_a[n]) - 1)) / ((1 + (0.1 * Age) ^ as.numeric(mods$He_a[n])) ^ as.numeric(mods$He_b[n]) + 1))
} else if (mods$He[n] == "Linear") {
c <- as.numeric(mods$He_a[n]) * Age + as.numeric(mods$He_b[n])
} else if (mods$He[n] == "Binomial") {
c <- as.numeric(mods$He_a[n])*(1/(as.numeric(mods$He_b[n])*sqrt(2*pi)))*exp(-((Age-as.numeric(mods$He_c[n]))^2)/(2*as.numeric(mods$He_b[n])^2))
} else if (mods$He[n] == "Quadratic") {
c <- as.numeric(mods$He_a[n])*Age^2 + as.numeric(mods$He_b[n])*Age + as.numeric(mods$He_c[n])
} else if (mods$He[n] == "Mean") {
c <- as.numeric(mods$meanHe[n])
}
c <- max(0,c)
return(c)
}
#' Predicts plant Ht at a given age
#'
#' Selects model from tabled models per species
#' @param mods A table of models fit to species, format as per modCollector
#' @param sp The name of a species in the table mods
#' @param Age The age of the plant (years since fire)
#' @return value
#' @export
#'
pHt <- function(mods, sp, Age = 10){
n <- as.numeric(which(mods$Species == sp))
if (length(n)>1) {
cat("There is more than one entry for a species", "\n")
}
if (mods$Ht[n] == "NegExp") {
c <- as.numeric(mods$Ht_a[n]) * (1 - exp(-as.numeric(mods$Ht_b[n]) * Age))
} else if (mods$Ht[n] == "Burr") {
c <- as.numeric(mods$Ht_a[n]) * as.numeric(mods$Ht_b[n]) * ((0.1 * Age ^ (as.numeric(mods$Ht_a[n]) - 1)) / ((1 + (0.1 * Age) ^ as.numeric(mods$Ht_a[n])) ^ as.numeric(mods$Ht_b[n]) + 1))
} else if (mods$Ht[n] == "Linear") {
c <- as.numeric(mods$Ht_a[n]) * Age + as.numeric(mods$Ht_b[n])
} else if (mods$Ht[n] == "Binomial") {
c <- as.numeric(mods$Ht_a[n])*(1/(as.numeric(mods$Ht_b[n])*sqrt(2*pi)))*exp(-((Age-as.numeric(mods$Ht_c[n]))^2)/(2*as.numeric(mods$Ht_b[n])^2))
} else if (mods$Ht[n] == "Quadratic") {
c <- as.numeric(mods$Ht_a[n])*Age^2 + as.numeric(mods$Ht_b[n])*Age + as.numeric(mods$Ht_c[n])
} else if (mods$Ht[n] == "Mean") {
c <- as.numeric(mods$meanHt[n])
}
c <- max(0,c)
return(c)
}
#' Predicts plant crown width at a given age
#'
#' Selects model from tabled models per species
#' @param mods A table of models fit to species, format as per modCollector
#' @param sp The name of a species in the table mods
#' @param Age The age of the plant (years since fire)
#' @return value
#' @export
#'
pWidth <- function(mods, sp, Age = 10){
n <- as.numeric(which(mods$Species == sp))
if (length(n)>1) {
cat("There is more than one entry for a species", "\n")
}
if (mods$Width[n] == "NegExp") {
c <- as.numeric(mods$w_a[n]) * (1 - exp(-as.numeric(mods$w_b[n]) * Age))
} else if (mods$Width[n] == "Burr") {
c <- as.numeric(mods$w_a[n]) * as.numeric(mods$w_b[n]) * ((0.1 * Age ^ (as.numeric(mods$w_a[n]) - 1)) / ((1 + (0.1 * Age) ^ as.numeric(mods$w_a[n])) ^ as.numeric(mods$w_b[n]) + 1))
} else if (mods$Width[n] == "Linear") {
c <- as.numeric(mods$w_a[n]) * Age + as.numeric(mods$w_b[n])
} else if (mods$Width[n] == "Binomial") {
c <- as.numeric(mods$w_a[n])*(1/(as.numeric(mods$w_b[n])*sqrt(2*pi)))*exp(-((Age-as.numeric(mods$w_c[n]))^2)/(2*as.numeric(mods$w_b[n])^2))
} else if (mods$Width[n] == "Quadratic") {
c <- as.numeric(mods$w_a[n])*Age^2 + as.numeric(mods$w_b[n])*Age + as.numeric(mods$w_c[n])
} else if (mods$Width[n] == "Mean") {
c <- as.numeric(mods$meanW[n])
}
c <- max(0,c)
return(c)
}
#' Stratum test
#' FAULTY, DON'T USE
#'
#' @param clust
stratTestX <- function(clust) {
clust <- clust %>%
mutate(mid = (base+top+he+ht)/4)
sTab <- clust %>%
group_by(cluster)%>%
summarise_if(is.numeric, mean)
o<- sTab[wrapr::orderv(sTab[,11]),]
o$test <- 0
for (n in 2:nrow(o)) {
o$test[n] <- as.numeric(o$mid[n]<sum(o$top[1]:o$top[n-1])) # Using a sequence of this form adds numbers in increments of 1
}
out <- sum(o$test)
return(out)
}
#' Stratum test
#'
#' @param clust
stratTest <- function(clust) {
clust <- clust %>%
mutate(low = (base+he)/2,
high = (top + ht + base + he)/4)
sTab <- clust %>%
group_by(cluster)%>%
summarise_if(is.numeric, mean)
o<- sTab[wrapr::orderv(sTab[,11]),]
o$test <- 0
for (n in 2:nrow(o)) {
o$test[n] <- as.numeric(o$low[n]<= o$high[n-1])
}
out <- sum(o$test)
return(out)
}
#' Arranges survey data into strata using k-means clustering
#'
#' Data are stratified into 2-4 strata, then the largest number of strata
#' are chosen where p<0.01. If none qualify, then the most significant
#' division is chosen.
#'
#' Strata are sorted by top height and appended to the original data
#'
#'
#' @param veg A dataframe listing plant species with columns describing crown dimensions using standardised names:
#' veg, pN, spName, base, top, he, ht, wid, Site, sN
#' @param mStrat Maximum number of strata
#' @param sepSig p value to define significant stratum separation
#'
#' @return Dataframe
#' @export
frameStratify <- function(veg, mStrat = 4, sepSig = 0.001)
{
# veg_subset <- veg %>% dplyr::select(all_of(c(pN, spName, base, top, he, ht)))
veg_subset <- veg %>% dplyr::select(pN, spName, base, top, he, ht)
veg_subset <- veg_subset[complete.cases(veg_subset), ] # Omit NAs in relevant columns
veg_subset <- veg_subset %>% #log-scale dimensions for stratification
mutate(base = pmax(veg_subset$base,0.001),
lBase = log(veg_subset$base),
lBase = case_when(is.infinite(lBase) ~ -6.9, TRUE ~ lBase),
lTop = log(veg_subset$top),
he = case_when(veg_subset$top == 0 ~ 0.001, TRUE ~ veg_subset$top),
lhe = log(veg_subset$he),
lhe = case_when(is.infinite(lhe) ~ -6.9, TRUE ~ lhe),
lht = log(veg_subset$ht))
df <- scale(veg_subset[, c(7,8,9,10)])
# Find the best division of strata
sig <- vector()
sig[1] <- sepSig
set.seed(123)
if (!berryFunctions::is.error(kmeans(df, centers = 2, nstart = 25))) {
for (nstrat in 2:mStrat) {
set.seed(123)
if (!berryFunctions::is.error(kmeans(df, centers = nstrat, nstart = 25))){
km.res <- kmeans(df, centers = nstrat, nstart = 25)
clust <- cbind(veg_subset, cluster = km.res$cluster)
testa <- frame:::stratTest(clust)
# testa <- 0
test <- aov(cluster ~ base * top * he * ht, data = clust)
sig[nstrat] <- base::summary(test)[[1]][["Pr(>F)"]][[4]]+testa
}
}
if (length(which(sig < sepSig)) > 0) {
nstrat <- as.numeric(max(which(sig < sepSig)))
} else {
if (length(sig[!is.na(sig)])>0) {
nstrat <- as.numeric(min(which(sig == min(sig, na.rm = TRUE))))
} else {
nstrat <- 1
}
}
rm(list=".Random.seed", envir=globalenv())
set.seed(123)
km.res <- kmeans(df, centers = nstrat, nstart = 25)
clust <- cbind(veg_subset, cluster = km.res$cluster)
# Summarise strata and order by mean height
h <- clust %>%
mutate(mid = (base+top+he+ht)/4)%>%
group_by(cluster) %>%
summarise_if(is.numeric, mean)
h <- h[wrapr::orderv(h[,11]),] %>%
mutate(Stratum = 1:nstrat) %>%
select(cluster, Stratum)
strat <- left_join(clust, h, by = "cluster") %>%
dplyr::select(all_of(c(pN, spName, top, "Stratum")))
veg <- left_join(veg, strat, by = c(pN, spName, top))
} else {
veg$Stratum <- 1
}
rm(list=".Random.seed", envir=globalenv())
return(veg)
}
#' Finds the distribution of species richness at a point
#'
#' Input table requires the following fields:
#' Point - numbered point in a transect
#' Species - name of the surveyed Species
#' Age - age of the site since the triggering disturbance
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat The dataframe containing the input data,
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#' @param pnts The number of points measured in a transect
#' @return dataframe
#' @export
rich <- function(dat, thres = 5, pnts = 10) {
# Group minor species
spCov <- frame::specCover(dat = dat, thres = 0, pnts = pnts)%>%
group_by(Species)%>%
summarise_if(is.numeric, mean)
dat <- suppressMessages(left_join(dat, spCov))%>%
mutate(Species = replace(Species, which(Cover < thres), "Minor Species"))
y <- suppressMessages(dat %>%
group_by(Site, Point) %>%
summarise(n_distinct(Species)))
#DATA ANALYSIS
fitr <- data.frame('Mean' = character(0), 'SD' = character(0), 'Min' = character(0), 'Max' = character(0), stringsAsFactors=F)
#Summary stats
meanw <- round(mean(y$`n_distinct(Species)`, na.rm = TRUE),1)
sdw <- round(sd(y$`n_distinct(Species)`, na.rm = TRUE), 2)
minw <- as.numeric(min(y$`n_distinct(Species)`, na.rm = TRUE))
maxw <- as.numeric(max(y$`n_distinct(Species)`, na.rm = TRUE))
#Record values
fitr[nrow(fitr)+1,] <- c(meanw, sdw, minw, maxw)
return(fitr)
}
#' Finds the distribution of species richness per stratum
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat A dataframe listing plant species with columns describing crown dimensions using standardised names:
#' veg, pN, spName, base, top, he, ht, wid, Site, sN
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#' @param sepSig Threshold for determining significance
#' @param pnts The number of points measured in a transect
#'
#' @return dataframe
richS <- function(dat, thres = 0, pnts = 10, sepSig = 0.001) {
spCov <- frame::specCover(dat = dat, thres = 0, pnts = pnts)%>%
group_by(Species)%>%
summarise_if(is.numeric, mean)
dat <- suppressMessages(left_join(dat, spCov))%>%
mutate(Species = replace(Species, which(Cover < thres), "Minor Species"))
datS <- frame::frameStratify(veg = dat, sepSig = sepSig)
out <- suppressMessages(datS %>%
group_by(Stratum) %>%
summarise(n_distinct(Species)))
out$Richness <- as.numeric(out$`n_distinct(Species)`)
out <- out %>% select(Stratum, Richness)
return(out)
}
#' Divides site data into consecutively numbered strata with
#' base and top heights
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat A dataframe listing plant species with columns describing crown dimensions using standardised names:
#' veg, pN, spName, base, top, he, ht, wid, Site, sN
#' @param sepSig
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#'
#' @return dataframe
#' @export
#'
stratSite <- function(dat, thres = 0, sepSig = 0.001) {
pnts <- nrow(dat)
strataDet <- data.frame(Stratum = numeric(0), Cover = numeric(0),
Base = numeric(0), Top = numeric(0), stringsAsFactors = F)
strat <- frame::frameStratify(veg = dat, sepSig = sepSig)
for (st in 1:as.numeric(max(strat$Stratum))) {
stratSub <- strat %>% filter(Stratum == st)
spnts <- unique(stratSub$Point, incomparables = FALSE)
co <- length(spnts)/pnts
b <- mean(stratSub$base)
t <- mean(stratSub$top)
strataDet[nrow(strataDet) + 1, ] <- c(as.numeric(st), as.numeric(co),
as.numeric(b), as.numeric(t))
}
return(strataDet)
}
#' Calculates the range, mean and sd of
#' species richness in each plant stratum
#'
#' Input table requires the following fields:
#' Point - numbered point in a transect
#' A field with the base height of the plants
#' A field with the top height of the plants
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat The dataframe containing the input data
#' @param thres The minimum percent cover (0-100) of a Species that will be analysed
#' @param pnts The number of points measured in a transect
#' @param pN The number of the point in the transect
#' @param spName Name of the field with the species name
#' @param base Name of the field with the base height
#' @param top Name of the field with the top height
#' @param he Name of the field with dimension he
#' @param ht Name of the field with dimension ht
#' @return dataframe
#' @export
#'
stratRich <- function(dat, thres = 5, pnts = 10) {
richList <- data.frame('S1' = numeric(0), 'S2' = numeric(0),
'S3' = numeric(0), 'S4' = numeric(0))
slist <- unique(dat$Site, incomparables = FALSE)
for (s in slist) {
datSite <- filter(dat, Site == s)
sRich <- richS(dat = datSite, thres = thres, pnts = pnts)
nstrat <- as.numeric(max(sRich$Stratum))
# Record values
richList[which(slist == s), 1] <- sRich$Richness[1]
if (nstrat > 1) {
richList[which(slist == s),2] <- sRich$Richness[2]
}
if (nstrat > 2) {
richList[which(slist == s),3] <- sRich$Richness[3]
}
if (nstrat > 3) {
richList[which(slist == s),4] <- sRich$Richness[4]
}
}
#DATA ANALYSIS
fitr <- data.frame('Stratum' = numeric(0), 'Mean' = numeric(0), 'SD' = numeric(0),
'Min' = numeric(0), 'Max' = numeric(0), stringsAsFactors=F)
fitr[1,1] <- 1
fitr[1,2] <- mean(richList$S1, na.rm = TRUE)
fitr[1,3] <- sd(richList$S1, na.rm = TRUE)
fitr[1,4] <- min(richList$S1, na.rm = TRUE)
fitr[1,5] <- max(richList$S1, na.rm = TRUE)
fitr[2,1] <- 2
fitr[2,2] <- mean(richList$S2, na.rm = TRUE)
fitr[2,3] <- sd(richList$S2, na.rm = TRUE)
fitr[2,4] <- min(richList$S2, na.rm = TRUE)
fitr[2,5] <- max(richList$S2, na.rm = TRUE)
fitr[3,1] <- 3
fitr[3,2] <- mean(richList$S3, na.rm = TRUE)
fitr[3,3] <- sd(richList$S3, na.rm = TRUE)
fitr[3,4] <- min(richList$S3, na.rm = TRUE)
fitr[3,5] <- max(richList$S3, na.rm = TRUE)
fitr[4,1] <- 4
fitr[4,2] <- mean(richList$S4, na.rm = TRUE)
fitr[4,3] <- sd(richList$S4, na.rm = TRUE)
fitr[4,4] <- min(richList$S4, na.rm = TRUE)
fitr[4,5] <- max(richList$S4, na.rm = TRUE)
return(fitr)
}
#' Constructs the table F_flora from formatted survey data
#'
#' @param veg A dataframe listing plant species with columns describing crown dimensions using standardised names:
#' veg, pN, spName, base, top, he, ht, wid, Site, sN
#' @param sepSig
#' @param surf Weight of surface litter in t/ha
#'
#' @return dataframe
#' @export
#'
#'
buildFlora <- function(veg, surf = 20, sepSig = 0.001) {
vegA <- frame::frameStratify(veg = veg, sepSig = sepSig)
# Summarise species
spCount <- vegA %>%
dplyr::count(Stratum, Species, name = "comp")
suppressMessages(spM <- vegA %>%
group_by(Stratum, Species) %>%
summarise(across(where(is.numeric), ~ mean(.x, na.rm = TRUE))))
# Remove faulty data
entries <- which(is.na(spM[wid]))
if (length(entries)>0) {
cat(length(entries), "Species were removed due to faulty crown width data", "\n")
spM <- spM[-entries,]
}
suppressMessages(spSD <- vegA %>%
group_by(Stratum, Species) %>%
summarise(across(where(is.numeric), ~ sd(.x, na.rm = TRUE))))
if (length(entries)>0) {
spSD <- spSD[-entries,]
}
# Correct empty entries
singles <- which(is.na(spSD[top]))
if (length(singles)>0) {
spSD[top][is.na(spSD[top])]<-0.01
}
vegShort <- vegA %>%
select(Stratum, Species, top)
suppressMessages(spMax <- vegShort %>%
group_by(Stratum, Species) %>%
summarise(across(where(is.numeric), ~ max(.x, na.rm = TRUE))))
if (length(entries)>0) {
spMax <- spMax[-entries,]
}
suppressMessages(spMin <- vegShort %>%
group_by(Stratum, Species) %>%
summarise(across(where(is.numeric), ~ min(.x, na.rm = TRUE))))
if (length(entries)>0) {
spMin <- spMin[-entries,]
}
tab <- (left_join(spMin, spCount, by = c("Stratum", "Species")))
# Collate into table
ns <- vegA %>% dplyr::select(all_of(c(Site, sN)))
record <- matrix(nrow = length(spMin$Species))
florTab <- data.frame(record)
florTab$record <- ns$Site[1]
florTab$site <- ns$SiteName[1]
florTab$species <- tab$Species
florTab$stratum <- tab$Stratum
florTab$comp <- tab$comp
florTab$base <- round(spM$base,2)
florTab$he <- round(spM$he,2)
florTab$ht <- round(spM$ht,2)
florTab$top <- round(spM$top,2)
florTab$w <- round(spM$width,2)
florTab$Hs <- round(spSD$top,2)
florTab$Hr <- max(0.001,round(spMax$top - spMin$top,2))
florTab$weight <- NA
florTab$diameter <- NA
s <- c(florTab$record[1], florTab$site[1], "Litter", NA, NA, NA, NA, NA, NA, NA, NA, NA, surf, 0.005)
florTab <- rbind(florTab, s)
return(florTab)
}
#' Constructs the table F_structure from formatted survey data
#'
#' @param veg A dataframe listing plant species with columns describing crown dimensions using standardised names:
#' veg, pN, spName, base, top, he, ht, wid, Site, sN
#' @param overlap Either 'automatic', or threshold occurrence at which overlap is set to TRUE.
#' @param sepSig Significance at which to recognise separate strata
#'
#' @return dataframe
#' @export
#'
buildStructure <- function(veg, overlap = 0.5, sepSig = 0.001) {
# 1. Horizontal relationships
vegA <- frame::frameStratify(veg = veg, sepSig = sepSig)
suppressMessages(StratC <- vegA %>%
select(Point, Stratum)%>%
group_by(Stratum, Point) %>%
summarise(across(where(is.numeric), ~ mean(.x, na.rm = TRUE))))
suppressMessages(StratW <- vegA %>%
select(Stratum, width)%>%
group_by(Stratum) %>%
summarise(across(where(is.numeric), ~ mean(.x, na.rm = TRUE))))
sepTab <- as.data.frame(table(StratC$Stratum))
pnts <- n_distinct(StratC$Point)
sep <- vector()
for (s in 1:max(StratC$Stratum)) {
sep[s] <- max(sqrt((as.numeric(StratW[s,2])^2)/(sepTab[s,2]/pnts)), as.numeric(StratW[s,2]))
}
# 2. Vertical relationships
StratO <- as.data.frame(table(StratC))
ns_e <- vector()
ns_m <- vector()
e_m <- vector()
e_c <- vector()
m_c <- vector()
for (pt in unique(StratO$Point, incomparables = FALSE)) {
point <- filter(StratO, Point == pt)
if (max(StratC$Stratum) > 2) {
ns_e[pt] <- point$Freq[1]*point$Freq[2]
if (max(StratC$Stratum) == 4) {
ns_m[pt] <- point$Freq[1]*point$Freq[3]
e_m[pt] <- point$Freq[2]*point$Freq[3]
e_c[pt] <- point$Freq[2]*point$Freq[4]
m_c[pt] <- point$Freq[3]*point$Freq[4]
} else {
e_c[pt] <- point$Freq[2]*point$Freq[3]
}
}
}
# 3. Species richness
suppressMessages(StratR <- vegA %>%
select(Stratum, Species)%>%
group_by(Stratum) %>%
summarise(across(everything(), n_distinct)))
# Collate into table
ns <- vegA %>% dplyr::select(all_of(c(Site, sN)))
record <- matrix(nrow = 1)
strucTab <- data.frame(record)
strucTab$record <- ns$Site[1]
strucTab$site <- ns$SiteName[1]
## Separation
strucTab$NS <- NA
strucTab$El <- NA
strucTab$Mid <- NA
strucTab$Can <- NA
strucTab$Can <- round(sep[length(sep)],2)
if (max(StratC$Stratum) > 1) {
strucTab$NS <- round(sep[1],2)
if (max(StratC$Stratum) > 2) {
strucTab$El <- round(sep[2],2)
if (max(StratC$Stratum) > 3) {
strucTab$Mid <- round(sep[3],2)
}
}
}
## Overlap
strucTab$ns_e <- NA
strucTab$ns_m <- NA
strucTab$e_m <- NA
strucTab$e_c <- NA
strucTab$m_c <- NA
if(overlap != "automatic"){
if (max(StratC$Stratum) > 2) {
strucTab$ns_e <- sum(ns_e)>=as.numeric(pnts * overlap)
if (max(StratC$Stratum) == 4) {
strucTab$ns_m <- sum(ns_m)>=as.numeric(pnts * overlap)
strucTab$e_m <- sum(e_m)>=as.numeric(pnts * overlap)
strucTab$e_c <- sum(e_c)>=as.numeric(pnts * overlap)
strucTab$m_c <- sum(m_c)>=as.numeric(pnts * overlap)
} else {
strucTab$e_c <- sum(e_c)>=as.numeric(pnts * overlap)
}
}
}
## Richness
strucTab$nsR <- NA
strucTab$eR <- NA
strucTab$mR <- NA
strucTab$cR <- StratR$Species[length(sep)]
if (max(StratC$Stratum) > 1) {
strucTab$nsR <- StratR$Species[1]
if (max(StratC$Stratum) > 2) {
strucTab$eR <- StratR$Species[2]
if (max(StratC$Stratum) > 3) {
strucTab$mR <- StratR$Species[3]
}
}
}
return(strucTab)
}
#' Finds the height of near-surface litter
#'
#' @param default.species.params Plant traits database
#' @param density Wood density (kg.m-3)
#' @param cover Percent cover of suspended layer
#' @param wNS Width of NS patches (m)
#' @param age Years since last fire
#' @param aQ Parameter for a quadratic trend; leave as NA if trend is negative exponential
#' @param bQ Parameter for a quadratic trend; leave as NA if trend is negative exponential
#' @param cQ Parameter for a quadratic trend; leave as NA if trend is negative exponential
#' @param dec Logical - TRUE allows for quadratic model to decline as vegetation thins,
#' @param maxNS Asymptote for negative exponential increase in NS
#' @param rateNS Rate for negative exponential increase in NS
#'
#' @return List
#' @export
susp <- function(default.species.params, density = 300, cover = 0.8,
age = 10, aQ = NA, bQ = NA, cQ = NA, maxNS = NA, rateNS = NA, dec = TRUE)
{
suspDat <- filter(default.species.params, name == "suspNS")
# Model packing
if (dec == TRUE) {
suspNS <- if (!is.na(maxNS)) {
maxNS*(1-exp(-rateNS*age))
} else if (!is.na(aQ)) {
pmax(0,(aQ * age^2 + bQ * age + cQ))
} else {
0.1
}
} else {
preLit <- vector()
for (x in 1:age) {
preLit[x] <- if (!is.na(maxNS)) {
maxNS*(1-exp(-rateNS*x))
} else if (!is.na(aQ)) {
pmax(0,(aQ * x^2 + bQ * x + cQ))
} else {
0.1
}
}
suspNS <- max(preLit)
}
nSticks <- (0.1*suspNS/cover)/(pi*(suspDat$leafThickness/2)^2*density) #Number of sticks
top <- max(0.1,floor(nSticks*suspDat$leafSeparation) - 1) * suspDat$leafSeparation # Sets the lowest layer on the ground, finds height as separation * number of layers
if (length(top) == 0) {
top <- 0
}
return(list(top, suspNS))
}
#' Models the weight of surface litter from time since fire
#' using either an Olson negative exponential function or a Burr curve
#'
#' @param negEx Value determining the model used.
#' 1 = olson, 2 = Burr
#' @param max Maximum weight (t/ha)
#' @param rate Growth rate for a negative exponential function
#' @param a Parameter in the Burr equation
#' @param b Parameter in the Burr equation
#' @param age The number of years since last fire
#' @return value
#' @export
litter <- function(negEx = 1, max = 54.22, rate = 0.026, a = 3.35, b = 0.832, age = 10)
{
if (negEx == 1) {
surf <- max(4, max*(1-exp(-rate*age)))
} else {
surf <- max(4, a*b*((0.1*age^(a-1))/((1+(0.1*age)^a)^b+1)))
}
return(surf)
}
#' Combines multiple transects into one
#'
#' @param alldata Raw survey data with one or more transects
#'
#' @return dataframe
#' @export
#'
transectLong <- function(alldata){
Sites <- unique(alldata$Site)
maxPoint <- max(dplyr::filter(alldata, Site == Sites[1])$Point)
out <- dplyr::filter(alldata, Site == Sites[1])
# Rename consecutive sites
for (s in Sites) {
if (s > Sites[1]) {
outA <- dplyr::filter(alldata, Site == Sites[s]) %>%
mutate(Site = Sites[1],
Point = Point + maxPoint)
maxPoint <- max(outA$Point)
out <- rbind(out, outA)
}
}
return(out)
}
#' Processes field survey data into tables formatted for fire modelling
#'
#' @param dat The dataframe containing the formatted field survey data
#' @param default.species.params Plant traits database
#' @param pN The number of the point in the transect
#' @param spName Name of the field with the species name
#' @param base Name of the field with the base height
#' @param top Name of the field with the top height
#' @param he Name of the field with dimension he
#' @param ht Name of the field with dimension ht
#' @param wid Name of the field with dimension width
#' @param Site Name of the field with the record number
#' @param sN Optional field with a site name
#' @param max Maximum weight of surface litter (t/ha)
#' @param rate Growth rate for surface litter in a negative exponential function
#' @param age Optional field with site age
#' @param surf Weight of surface litter in t/ha
#' @param density Wood density (kg.m-3)
#' @param cover Percent cover of suspended layer
#' @param aQ Parameter for a quadratic trend; leave as NA if trend is negative exponential
#' @param bQ Parameter for a quadratic trend; leave as NA if trend is negative exponential
#' @param cQ Parameter for a quadratic trend; leave as NA if trend is negative exponential
#' @param maxNS Parameter for a negative exponential trend; leave as NA if trend is quadratic
#' @param rateNS Parameter for a negative exponential trend; leave as NA if trend is quadratic
#' @param thin Logical - TRUE uses plant self-thinning models in Dynamics,
#' otherwise plant cover remains at the highest point it has reached by that age
#' @param sLit Logical - TRUE allows surface litter to decline if the model does so, otherwise
#' the maximum value to that age is maintained
#' @param dec Logical - TRUE allows near surface surface litter to decline if the model does so, otherwise
#' @param negEx Value determining the model used. 1 = olson, 2 = Burr
#' @param a
#' @param b
#' @param wNS Width of near surface patches (m)
#' @param sepSig
#' @param messages T or F to display messages from component functions
#' @param overlap Either 'automatic', or threshold occurrence at which overlap is set to TRUE.
#' the maximum value to that age is maintained
#' @return list
#' @export
#'
frameSurvey <- function(dat, default.species.params, pN ="Point", spName ="Species", base = "base", top = "top", he = "he", ht = "ht",
wid = "width", Site = "Site", sN = "SiteName", negEx = 1, max = 54.22, rate = 0.026, a = 3.35, b = 0.832, age = NA,
surf = 10, density = 300, cover = 0.8, aQ = NA, bQ = NA, cQ = NA, maxNS = NA, rateNS = NA, wNS = 1,
thin = TRUE, sLit = TRUE, dec = TRUE, sepSig = 0.001, messages = F, overlap = 0.5) {
# dat <- standardiseNames(dat, pN, spName, base, top, he, ht,
# wid, Site, sN)
# Find missing data
entries <- which(is.na(dat[top]))
if (length(entries)>0) {
if (messages == T) {
cat(" These rows were removed as they were missing top heights", "\n", entries, "\n", "\n")
}
dat <- dat[-entries,]
}
# Fill empty dimensions
entries <- which(is.na(dat[ht])|is.na(dat[he]))
if (length(entries)>0) {
if (messages == T) {
cat(" Empty values of ht & he were filled with top and base heights for these rows", "\n", entries, "\n", "\n")
}
for (row in 1:nrow(dat)) {
if (is.na(dat[row,ht])) {
dat[row,ht] <- dat[row,top]
}
if (is.na(dat[row,he])) {
dat[row,he] <- dat[row,base]
}
}
}
# Remove faulty data
entries <- which(dat[ht]<dat[he]|dat[top]<dat[base])
if (length(entries)>0) {
if (messages == T) {
cat(" These rows were removed as upper and lower heights conflicted", "\n", entries)
}
dat <- dat[-entries,]
}
# Loop through records
silist <- unique(dat$Site, incomparables = FALSE)
Structure <- data.frame()
Flora <- data.frame()
for (rec in silist) {
veg <- filter(dat, Site == rec)
# Find surface litter
if (!is.na(age)) {
AGE <- veg[1,age]
# Control self-thinning
(if (thin == TRUE && sLit == TRUE) {
surf <- round(litter(negEx, max, rate, a, b, AGE),0)
} else {
preLit <- vector()
for (x in 1:AGE) {
preLit[x] <- litter(negEx, max, rate, a, b, x)
}
surf <- max(preLit)
})
}
# Add suspended litter
if (cover != 0) {
if (thin == TRUE && dec == TRUE) {
decline <- TRUE
} else {
decline <- FALSE
}
suspNS <- susp(default.species.params, density = density, cover = cover,
age = AGE, aQ = aQ, bQ = bQ, cQ = cQ, maxNS = maxNS, rate = rateNS, dec = decline)
topNS <- suspNS[[1]]
#Update tables
if (topNS > 0) {
row <- nrow(veg)
rows <- round(cover*length(unique(veg$Point)),0)
for (r in 1:rows) {
veg[row+r,1] <- AGE
veg[row+r,2] <- "suspNS"
veg[row+r,3] <- 0
veg[row+r,4] <- topNS
veg[row+r,5] <- 0
veg[row+r,6] <- topNS
veg[row+r,7] <- wNS
veg[row+r,8] <- AGE
veg[row+r,9] <- rec
}
}
}
# Check for faults, then create tables
veg <- datClean(veg = veg, base, top, he, ht)
Struct <- buildStructure(veg, overlap = overlap, sepSig = sepSig)
Flor <- buildFlora(veg, surf = surf, sepSig = sepSig)
Structure <- rbind(Structure, Struct)
Flora <- rbind(Flora, Flor)
}
return(list(Flora, Structure))
}
#' Grows a table of species to a given age, listing cover and variability
#'
#' Provides options to control for growth, self-thinning and self-pruning
#'
#' @param Dynamics Table of growth models as output by floraDynamics
#' @param Age Age of the site in years
#' @param growth Logical - TRUE uses plant growth models in Dynamics,
#' otherwise mean values are used and plants are not grown
#' @param thin Logical - TRUE uses plant self-thinning models in Dynamics,
#' otherwise plant cover remains at the highest point it has reached by that age
#' @param prune Logical - TRUE uses plant self-pruning models in Dynamics,
#' otherwise plants are not self-pruned and lower canopy heights remain at their lowest points
#' @return dataframe
#' @export
growPlants <- function(Dynamics, Age = 10, growth = TRUE, thin = TRUE, prune = TRUE) {
Contenders <- data.frame('Species' = character(0), 'Cover' = numeric(0), 'cRSE' = numeric(0),
'base' = numeric(0), 'he' = numeric(0), 'ht' = numeric(0),
'top' = numeric(0), 'tRSE' = numeric(0), 'w' = numeric(0))
for (sp in 1:(nrow(Dynamics)-2)) {
Contenders[sp,1] <- max(Dynamics$Species[sp],0)
Contenders[sp,3] <- as.numeric(Dynamics$C_RSE[sp])/100
# Control growth
if (growth == TRUE) {
Contenders[sp,7] <- max(pTop(mods = Dynamics, sp=Dynamics$Species[sp], Age = Age),0)
Contenders[sp,8] <- as.numeric(Dynamics$T_RSE[sp])/100
Contenders[sp,9] <- max(pWidth(mods = Dynamics, sp=Dynamics$Species[sp], Age = Age),0)
} else {
Contenders[sp,7] <- max(as.numeric(Dynamics$meanTop[sp]),0)
Contenders[sp,8] <- 0.01
Contenders[sp,9] <- max(as.numeric(Dynamics$meanW[sp]),0)
}
# Plant Ht follows top height in all circumstances
Contenders[sp,6] <- max(pHt(mods = Dynamics, sp=Dynamics$Species[sp], Age = Age),0)
# Control self-pruning by keeping base heights at minimum values if prune == FALSE
if (prune == TRUE) {
Contenders[sp,5] <- max(min(pHe(mods = Dynamics, sp=Dynamics$Species[sp], Age = Age), Contenders[sp,6]),0)
Contenders[sp,4] <- max(min(pBase(mods = Dynamics, sp=Dynamics$Species[sp], Age = Age), Contenders[sp,7]),0)
} else {
preHe <- vector()
preBase <- vector()
for (x in 1:Age) {
preHe[x] <- max(min(pHe(mods = Dynamics, sp=Dynamics$Species[sp], Age = x), Contenders[sp,6]),0)
preBase[x] <- max(min(pBase(mods = Dynamics, sp=Dynamics$Species[sp], Age = x), Contenders[sp,7]),0)
}
Contenders[sp,5] <- min(preHe)
Contenders[sp,4] <- min(preBase)
}
# Assign no cover to plants with 0 height or foliage
covTest <- if ((Contenders[sp,7] < 0.05)
|| (Contenders[sp,4] == Contenders[sp,7] & Contenders[sp,6] == Contenders[sp,5])
|| (Contenders[sp,9] <= 0.05)) {
0
} else {
1
}
# Control self-thinning
(if (thin == TRUE) {
Contenders[sp,2] <- covTest * pCover(mods = Dynamics, sp=Dynamics$Species[sp], Age = Age)
} else {
preCov <- vector()
for (x in 1:Age) {
preCov[x] <- pCover(mods = Dynamics, sp=Dynamics$Species[sp], Age = x)
}
Contenders[sp,2] <- covTest * max(preCov)
})
}
# Remove species with no cover
entries <- which(Contenders$Cover == 0)
if (length(entries)>0) {
Contenders <- Contenders[-entries,]
}
return(Contenders)
}
#' Creates a dataset of plants with expected size and cover
#' randomly varied within natural ranges
#'
#' @param Dynamics Table of growth models as output by floraDynamics
#' @param pointRich table of species richness likelihood for a point
#' as output by function rich
#' @param default.species.params Plant traits database
#' @param pnts number of points in the transect
#' @param Age Age of the site in years
#' @param growth Logical - TRUE uses plant growth models in Dynamics,
#' otherwise mean values are used and plants are not grown
#' @param thin Logical - TRUE uses plant self-thinning models in Dynamics,
#' otherwise mean values are used and plant cover remains constant
#' @param prune Logical - TRUE uses plant self-pruning models in Dynamics,
#' otherwise plants are not self-pruned and lower canopy heights remain at their lowest points
#' @param perspective Use FALSE for vertical to calculate LAI, otherwise TRUE
#' @return dataframe
#' @export
pseudoTransect <- function(Dynamics, pointRich, default.species.params, perspective = FALSE,
pnts = 10, Age = 10, growth = TRUE, thin = TRUE, prune = TRUE)
{
# Build species choice function
chooseSp <- function(L, spList, drops) {
temp <- L
xRow <- data.frame()
Lentry <- as.numeric(nrow(xRow))
tot <- sum(temp)
while (Lentry == 0 & tot >= nSpecies) {
entry <-
spList[which(temp == Rfast::nth(temp, 1, descending = TRUE)),]
if (Rfast::nth(temp, 1, descending = TRUE) > 100 * runif(1)*(drops>0)) {
xRow <- rbind(xRow, entry)
} else {
temp[which(temp == Rfast::nth(temp, 1, descending = TRUE))] <-
0
}
Lentry <- as.numeric(nrow(xRow))
tot <- sum(temp > 0)
}
return(xRow)
}
# Potential species
spList <- growPlants(Dynamics, Age = Age, growth = growth, thin = thin, prune = prune) %>%
mutate(Cover = if(perspective){100-(pmin(1,5/top)*(100-Cover))} else {Cover}) # Weight cover by height above 5m to reflect angle to canopy
out <- data.frame('Point' = numeric(0), 'Species' = character(0), 'base' = numeric(0),
'top' = numeric(0), 'he' = numeric(0), 'ht' = numeric(0), 'width' = numeric(0),
'Age' = numeric(0), 'Site' = numeric(0), 'SiteName' = character(0), stringsAsFactors = FALSE)
# Build pseudo-transect
for (r in 1:pnts) {
# Find the number of species at this point
spN <- round(rtnorm(n = 1, mean = as.numeric(pointRich$Mean[1]),
sd = as.numeric(pointRich$SD[1]),
a = as.numeric(pointRich$Min[1]),
b = as.numeric(pointRich$Max[1])),0)
# Calculate likelihood for each species
L <- vector()
for (sp in 1:length(spList$Species)) {
L[sp] <- rtnorm(n = 1, mean = as.numeric(spList$Cover[sp]),
sd = as.numeric(spList$cRSE[sp]), a = 0, b = 100)
}
# Choose species and record dimensions
drops <- length(spList$Species) - spN
for (nSpecies in 1:spN) {
count <- nrow(out)+1
entry <- chooseSp(L, spList, drops)
if (nrow(entry) > 0) {
# Remove chosen species from next option
L[which(spList$Species == entry$Species)] <- 0
out[count, 1] <- r
out[count, 2] <- entry$Species[1]
out[count, 4] <- round(rtnorm(n = 1, mean = as.numeric(entry$top[1]),
sd = as.numeric(entry$tRSE[1]), a = 0, b = 150),2)
out[count, 3] <- round(out[count, 4] * as.numeric(entry$base[1]),2)
out[count, 5] <- round(out[count, 4] * as.numeric(entry$he[1]),2)
out[count, 6] <- round(out[count, 4] * as.numeric(entry$ht[1]),2)
out[count, 7] <- round(out[count, 4] * as.numeric(entry$w[1]),2)
out[count, 8] <- Age
out[count, 9] <- Age
out[count, 10] <- Age
} else {
drops <- drops - 1
}
}
}
return(out)
}
#' Performs basic data checks on survey data,
#' fixes or removes data and lists changes
#'
#' @param dat The dataframe to be cleaned
#' @param base Name of the field with the base height
#' @param top Name of the field with the top height
#' @param he Name of the field with dimension he
#' @param ht Name of the field with dimension ht
#' @return dataframe
#' @export
datClean <- function(veg, base = "base", top = "top", he = "he", ht = "ht", messages = F) {
# Find missing data
entries <- which(is.na(veg[top]))
if (length(entries)>0) {
if (messages == T) {
cat(" These rows were removed as they were missing top heights", "\n", entries, "\n", "\n")
}
veg <- veg[-entries,]
}
# Fill empty dimensions
entries <- which(is.na(veg[ht])|is.na(veg[he]))
if (length(entries)>0) {
if (messages == T) {
cat(" Filled empty values of ht & he with top and base heights for these rows", "\n", entries, "\n", "\n")
}
veg[ht][is.na(veg[ht])]<-veg[top][is.na(veg[ht])]
veg[he][is.na(veg[he])]<-veg[base][is.na(veg[he])]
}
# Remove plants <1cm
entries <- which(veg[top]<0.01)
if (length(entries)>0) {
if (messages == T) {
cat(" Removed these rows as species were too small to model", "\n", entries, "\n", "\n")
}
veg <- veg[-entries,]
}
# Set low bases to ground level
entries <- which(veg[he]<0.01 | veg[base]<0.01)
if (length(entries)>0) {
if (messages == T) {
cat(" Set one or both base values for these rows to zero", "\n", entries, "\n", "\n")
}
veg <- veg %>%
mutate(he = case_when(he < 0.01 ~ 0, TRUE ~ he)) %>%
mutate(base = case_when(base < 0.01 ~ 0, TRUE ~ base))
}
# Remove faulty data
entries <- which(veg[ht]<veg[he]|veg[top]<veg[base])
if (length(entries)>0) {
if (messages == T) {
cat(" Removed these rows as upper and lower heights conflicted", "\n", entries)
}
veg <- veg[-entries,]
}
return(veg)
}
#' Removes leaf trait diversity
#'
#' Initially pulls out species names "suspNS" and "Log"
#'
#' @param default.species.params Plant traits database
#' @return dataframe
#' @export
ctrlDiversity <- function(default.species.params){
no.succession.params <- filter(default.species.params, name != "suspNS" & name != "Log")
no.succession.params$leafForm <- "Flat"
means <- no.succession.params %>%
summarise_if(is.numeric, mean)
for (name in colnames(means)) {
no.succession.params[,name] <- means[1,name]
}
sus <- filter(default.species.params, name == "suspNS" | name == "Log")
no.succession.params <- rbind(no.succession.params, sus)
return(no.succession.params)
}
#' Finds percent cover of surveyed Species and groups minor Species
#'
#' Input table requires the following fields:
#' Point - numbered point in a transect
#' Species - name of the surveyed Species
#' Age - age of the site since the triggering disturbance
#'
#' Species that are less common than the set threshold are combined as "Minor Species"
#'
#' @param dat The dataframe containing the input data,
#' @param thres The minimum percent cover (0-100) of a Species that will be kept single
#' @param pnts The number of points measured in a transect
#' @return dataframe
#' @export
specCover <- function(dat, thres = 5, pnts = 10) {
#List Species and ages
spList <- unique(dat$Species, incomparables = FALSE)
ages <- unique(dat$Age, incomparables = FALSE)
#Create empty summary dataframe
spCover <- data.frame('Species' = character(0), 'Age' = numeric(0), 'Cover' = numeric(0), stringsAsFactors=F)
#DATA COLLECTION
for (sp in 1:length(spList)) {
for (age in ages) {
spName <- dat %>% filter(Species == spList[sp])
spAge <- spName %>% filter(Age == age)
#Percent cover
sppnts <- unique(spAge$Point, incomparables = FALSE)
covSp <- as.numeric(length(sppnts))*(100/pnts)
#Record values
spCover[nrow(spCover)+1,] <- c(as.character(spList[sp]), as.numeric(age), as.numeric(covSp))
}
}
#Group minor Species
spCover$Cover <- as.numeric(as.character(spCover$Cover))
spShort <- spCover %>%
group_by(Species) %>%
summarise_if(is.numeric, mean)
#List minor Species, then rename in dataset
minor <- spShort %>% filter(Cover < thres)
minList <- unique(minor$Species, incomparables = FALSE)
if (length(minList)>0) {
for (snew in 1:length(minList)) {
spCover[spCover == minor$Species[snew]] <- "Minor Species"
}
}
return(spCover)
}
#' Adds standardised names to a table of field data
#'
#' @param dat The input table
#' @param pN A number specifying the point location of a vertical transect
#' @param spName Name of the species
#' @param base Base height of the plant crown (m)
#' @param top Top height of the plant crown (m)
#' @param he Lower edge height of the plant crown (m)
#' @param ht Upper height of the plant crown (m)
#' @param wid Width of the plant crown (m)
#' @param Site A number identifying the transect
#' @param sN A name identifying the transect
#'
#' @return table
#'
standardiseNames <- function(dat, pN, spName, base, top, he, ht,
wid, Site, sN) {
dat$pN <- as.vector(dat[,pN])[[1]]
dat$spName <- as.vector(dat[,spName])[[1]]
dat$base <- as.vector(dat[,base])[[1]]
dat$top <- as.vector(dat[,top])[[1]]
dat$he <- as.vector(dat[,he])[[1]]
dat$ht <- as.vector(dat[,ht])[[1]]
dat$wid <- as.vector(dat[,wid])[[1]]
dat$Site <- as.vector(dat[,Site])[[1]]
dat$sN <- as.vector(dat[,sN])[[1]]
return(dat)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.