#' @title Plot Demographic Model
#'
#' This function creates a graphical representation of a demographic model defined by a template (.tpl) and estimation (.est) file.
#' Concern: Setting initial growthrate values to a variable name will probably lead to failure.
#' fastsimcoal features not implemented: 'nomig' historical event directive.
#'
#' @param tpl.path Path to tpl file of model
#' @param est.path Path to est file of model
#' @param model.title Character string with the title text to use above the plot, e.g., the model name/ID. Default is "".
#' @param popnames Optional character vector with label names to use for populations. Default is NULL (don't print names on plot); supplying a single integer 1 will use names of the form "pop0", "pop1",... If a character vector is supplied, the number of names supplied should equal the number of populations indicated on line two of template file. Ordering of names correspond to population 0, population 1, ..., as used in the template and estimation files.
#' @param show.priors Whether or not to also plot the prior probability distributions of parameters. Default is FALSE. This is still in progress.
#' @param pops.spacer Number from 1 to 2 indicating amount of white space to allow between populations. Default is 1.05.
#' @param plotmargin Numerical vector indicating how much extra plotting area to add below, left, above, and rigt of the graph.
#' @param label.cex Size to use for text labels (not including axis labels)
#' @param warn.about.comments Whether or not to warn about in-line comments found in unusual places of '.tpl' file. Default is FALSE.
#' @param priors.panel.width Fraction of the plot to use for plotting the priors panel. Default is 0.3. Ignored if show.priors is FALSE. Setting this argument to zero is the same as setting show.priors FALSE.
#' @param use.mean Whether or not to use the mean or a random sample from each paramater's prior distribution when drawing graphs. Default is TRUE.
#' @return An object of class 'recordedplot' that stores a copy of the plot. See examples.
#' @export plot_model
plot_model <- function(tpl.path, est.path, model.title="", popnames=NULL, show.priors=TRUE,pops.spacer=1.1, plotmargin=c(1.1,1.01,1.05,1), label.cex=0.75, warn.about.comments=FALSE,priors.panel.width=0.3,use.mean =TRUE){
##### Defaults for debugging
if(!exists("model.title")){
model.title <- ""
}
if(!exists("popnames")){
popnames <- NULL
}
if(!exists("show.priors")){
show.priors <- TRUE
}
if(!exists("pops.spacer")){
pops.spacer <- 1.1
}
if(!exists("plotmargin")){
plotmargin=c(1.1,1.01,1.05,1)
}
if(!exists("label.cex")){
label.cex=0.75
}
if(!exists("warn.about.comments")){
warn.about.comments=FALSE
}
if(!exists("priors.panel.width")){
priors.panel.width=0.3
}
if(!exists("use.mean")){
use.mean=TRUE
}
########
tpl.lines <- readLines(tpl.path, warn=F)
est.lines.temp <- readLines(est.path, warn=F)
## drops comment lines and empty lines
est.lines <- est.lines.temp[-c(grep("^//",est.lines.temp),which(est.lines.temp==""))]
rules.header.line <- grep("^.RULES",est.lines)
### Lines in est file that start with an upper or lowercase letter define rules
est.rules.lines <- as.list(est.lines[grep("^[letters,LETTERS]",est.lines)])
### Lines that start with 0 or 1 and occur earlier than the RULES header line define simple parameters
est.parameters.lines <- as.list(est.lines[grep("^[0-1]",est.lines[1:rules.header.line])])
### Lines that start with 0 or 1 and occur later than the RULES header line define complex parameters
est.complex.lines <- as.list(est.lines[grep("^[0-1]",est.lines[rules.header.line:length(est.lines)]) + (rules.header.line-1)])
if(length(est.parameters.lines)>0){
variable.names <- unlist(lapply(X=est.parameters.lines,FUN=function(x){unlist(strsplit(x,split=" +"))[2]}))
if(length(est.complex.lines)>0){
variable.names <- c(variable.names,unlist(lapply(X=est.complex.lines,FUN=function(x){unlist(strsplit(x,split=" +"))[2]})))
}
}
if(length(est.rules.lines)>0){
rules.exist <- TRUE
est.rules.lines <- gsub(" ","",est.rules.lines)
for(i in 1:length(variable.names)){
est.rules.lines <- gsub(variable.names[i],paste0(" ",variable.names[i]," "),est.rules.lines,fixed=T)
}
est.rules.lines <- gsub("^ +","",est.rules.lines)
est.rules.lines <- as.list(gsub(" +$","",est.rules.lines))
rules.mat <- do.call(rbind,lapply(X=est.rules.lines,FUN=function(x){unlist(strsplit(x,split=" +"))}))
colnames(rules.mat) <- c("Parameter1_Name","relationship","Parameter2_Name")
} else {
rules.exist <- FALSE
rules.mat <- NULL
}
if(length(est.parameters.lines)>0){
parameters.exist <- TRUE
for(i in 1:length(variable.names)){
est.parameters.lines <- gsub(variable.names[i],paste0(" ",variable.names[i]," "),est.parameters.lines,fixed=T)
}
est.parameters.lines <- gsub("^ +","",est.parameters.lines)
est.parameters.lines <- as.list(gsub(" +$","",est.parameters.lines))
parameters.mat <- do.call(rbind,lapply(X=est.parameters.lines,FUN=function(x){unlist(strsplit(x,split=" +"))}))
colnames(parameters.mat) <- c("IntegerThen1","ParameterName","Distribution","Min","Max","Output")
### For each parameter, check that the "Max" value is greater than the "Min" value
if(!all(as.numeric(parameters.mat[,"Max"]) > as.numeric(parameters.mat[,"Min"]))){
offending.parameters <- paste(parameters.mat[!(as.numeric(parameters.mat[,"Max"]) > as.numeric(parameters.mat[,"Min"])),"ParameterName"],collapse=", ")
stop.message <- paste0("In '.est' file: ",offending.parameters,": max value must be greater than min value")
stop(stop.message)
}
### Check that names of distributions used for simple parameters are valid
if(!all(parameters.mat[,"Distribution"] %in% c("unif","logunif"))){
stop("simple parameters must have a prior distribution of type 'unif' or 'logunif'")
}
simple.pars <- parameters.mat[,"ParameterName"]
} else {
parameters.exist <- FALSE
parameters.mat <- NULL
}
if(length(est.complex.lines)>0){
if(!parameters.exist){
stop("Simple parameters must be defined when complex parameters are defined")
}
# puts a space in front of and behind each parameter name
for(i in 1:length(variable.names)){
est.complex.lines <- gsub(variable.names[i],paste0(" ",variable.names[i]," "),est.complex.lines,fixed=T)
}
# puts a space in front of and behind each operator type. This will cause a problem if operators are used as part of parameter names.
operator.types <- c("=","+","-","*","/","%min%","%max%","^","%abs%")
for(i in 1:length(operator.types)){
est.complex.lines <- gsub(operator.types[i],paste0(" ",operator.types[i]," "),est.complex.lines,fixed=T)
}
# removes spaces at beginning of strings
est.complex.lines <- gsub("^ +","",est.complex.lines)
# removes spaces at end of strings
est.complex.lines <- as.list(gsub(" +$","",est.complex.lines))
complex.exist <- TRUE
# split by strings of spaces
complex.list <- lapply(X=est.complex.lines,FUN=function(x){unlist(strsplit(x,split=" +"))})
# add empty entries to vectors that are shorter than the longest vector in complex.list, until all vectors are as long as the longest vector
# The empty entries must be added before the last entry, which must be non-empty
complex.list.lengths <- lengths(complex.list)
if(any(complex.list.lengths < max(complex.list.lengths))){
complex.list.lengths.diffs <- max(complex.list.lengths) - complex.list.lengths
for(i in 1:length(complex.list)){
complex.list[[i]] <- c(complex.list[[i]], rep("",complex.list.lengths.diffs[i]))
}
}
complex.mat <- do.call(rbind,complex.list)
if(ncol(complex.mat)>5){
complex.mat.values <- complex.mat[,4:(ncol(complex.mat)-1),drop=F]
complex.value <- do.call(rbind,lapply(X=1:nrow(complex.mat.values),FUN=function(x){paste(complex.mat.values[x,],collapse=" ")}))
#complex.value <- as.matrix(paste(complex.mat[,4:(ncol(complex.mat)-1)],collapse=" "),byrow=T)
complex.mat <- cbind(complex.mat[,1:3,drop=F],complex.value,complex.mat[,ncol(complex.mat),drop=F])
}
colnames(complex.mat) <- c("IntegerThen1","ParameterName","function","Value","Output")
complex.mat <- gsub(" ","",complex.mat)
complex.pars <- complex.mat[,"ParameterName"]
all.pars <- c(simple.pars,complex.pars)
#### Making sure that parameters defined as complex are actually complex and not constants
obj.mat <- matrix(rep(complex.mat[,"Value"],length(all.pars)),ncol=length(all.pars))
pat.mat <- matrix(rep(all.pars,nrow(obj.mat)),ncol=length(all.pars),byrow=T)
# Logical matrix indicating if the ith complex parameter uses the jth simple parameter.
complex.of.all.mat <- matrix(vapply(X=1:length(obj.mat),FUN=function(x){any(grep(pat.mat[x],obj.mat[x],fixed=T))},FUN.VALUE=TRUE),nrow=nrow(obj.mat))
rownames(complex.of.all.mat) <- complex.pars
colnames(complex.of.all.mat) <- all.pars
complex.of.all <- apply(X=complex.of.all.mat,MARGIN=1,FUN=any)
if(!all(complex.of.all)){
stop("The value of each complex parameter must be a function of at least one previously defined simple or complex parameter")
}
} else {
complex.exist <- FALSE
complex.mat <- NULL
complex.pars <- NULL
}
initialization.attempt <- 1
initialization.max <- 100
initialization.passed <- FALSE
while(initialization.attempt <= initialization.max & !initialization.passed){
### Obtaining values to use for parameters.
if(parameters.exist){
parameters.meanval <- list(); length(parameters.meanval) <- nrow(parameters.mat)
parameters.sampled <- list(); length(parameters.sampled) <- nrow(parameters.mat)
for(i in 1:nrow(parameters.mat)){
if(parameters.mat[i,"Distribution"]=="unif"){
#### Keep the next line even though its commented out
if(initialization.attempt==1 & use.mean){
parameters.sampled[[i]] <- runif(100000,as.numeric(parameters.mat[i,"Min"]),as.numeric(parameters.mat[i,"Max"]))
parameters.meanval[[i]] <- mean(c(as.numeric(parameters.mat[i,"Min"]),as.numeric(parameters.mat[i,"Max"])))
} else {
parameters.sampled[[i]] <- runif(1,as.numeric(parameters.mat[i,"Min"]),as.numeric(parameters.mat[i,"Max"]))
parameters.meanval[[i]] <- parameters.sampled[[i]]
}
}
if(parameters.mat[i,"Distribution"]=="logunif"){
if(initialization.attempt==1 & use.mean){
parameters.sampled[[i]] <- rlogunif(100000,as.numeric(parameters.mat[i,"Min"]),as.numeric(parameters.mat[i,"Max"]))
parameters.meanval[[i]] <- mean(parameters.sampled[[i]])
} else {
parameters.sampled[[i]] <- rlogunif(1,as.numeric(parameters.mat[i,"Min"]),as.numeric(parameters.mat[i,"Max"]))
parameters.meanval[[i]] <- parameters.sampled[[i]]
}
}
if(parameters.mat[i,"IntegerThen1"]==1){
parameters.sampled[[i]] <- round(parameters.sampled[[i]])
parameters.meanval[[i]] <- round(parameters.meanval[[i]])
}
}
parameters.meanval <- unlist(parameters.meanval)
names(parameters.meanval) <- parameters.mat[,"ParameterName"]
parameters.sampled.mat <- do.call(cbind,parameters.sampled)
colnames(parameters.sampled.mat) <- parameters.mat[,"ParameterName"]
### Holding each distributions as an object, using the 'distr' package
}
if(complex.exist){
for(i in 1:length(all.pars)){
if(i == 1){
complex.operators <- gsub(all.pars[i],"",complex.mat[,"Value"],fixed=T)
} else {
complex.operators <- gsub(all.pars[i],"",complex.operators,fixed=T)
}
}
for(i in 0:9){
complex.operators <- gsub(i,"",complex.operators,fixed=T)
}
names(complex.operators) <- complex.mat[,"ParameterName"]
#print(complex.operators)
#### This may bring a problem for abs or exp parameters
complex.operands <- list(); length(complex.operands) <- length(complex.operators)
for(i in 1:length(complex.operators)){
#if(i == 1){
if(complex.operators[i] %in% c("log()","abs()","exp()")){
complex.operands[[i]] <- c("","")
next
}
complex.operands[[i]] <- gsub(" ","",unlist(strsplit(complex.mat[i,"Value"],split=complex.operators[i], fixed=T)))
#complex.operands[[i]] <- unlist(sapply(1:length(complex.mat[,"Value"]),FUN=function(x){unlist(strsplit(complex.mat[x,"Value"],split=complex.operators[i],fixed=T))}))
#complex.rightoperand <- sapply(1:length(complex.mat[,"Value"]),FUN=function(x){unlist(strsplit(complex.mat[x,"Value"],split=complex.operators[i],fixed=T))})
#complex.leftoperand <- sapply(1:length(complex.mat[,"Value"]),FUN=function(x){unlist(strsplit(complex.mat[x,"Value"],split=complex.operators[i],fixed=T))[1]})
#complex.rightoperand <- sapply(1:length(complex.mat[,"Value"]),FUN=function(x){unlist(strsplit(complex.mat[x,"Value"],split=complex.operators[i],fixed=T))[2]})
#} else {
# complex.operands[[i]] <- unlist(sapply(1:length(complex.operands),FUN=function(x){unlist(strsplit(complex.operands[x],split=complex.operators[i],fixed=T))}))
#complex.rightoperand <- sapply(1:length(complex.rightoperand),FUN=function(x){unlist(strsplit(complex.rightoperand[x],split=complex.operators[i],fixed=T))})
#}
}
complex.operands.mat <- do.call(rbind,complex.operands)
#complex.operands <- gsub(" ","",complex.operands)
#complex.operands.mat <- matrix(complex.operands,ncol=2,byrow=TRUE)
if(!is.null(complex.operands.mat)){
colnames(complex.operands.mat) <- c("left","right")
rownames(complex.operands.mat) <- names(complex.operators)[lengths(complex.operands)>0]
#print(complex.operands.mat)
#test.left <- complex.operands.mat[,"left"]
#test.right <- complex.operands.mat[,"right"]
complex.leftoperand <- complex.operands.mat[,"left"]
complex.rightoperand <- complex.operands.mat[,"right"]
complex.value.strings <- paste(complex.leftoperand,complex.operators[lengths(complex.operands)>0],complex.rightoperand,sep=" ")
} else {
complex.leftoperand <- NULL
complex.rightoperand <- NULL
}
#print(complex.value.strings)
#print(complex.mat)
#print(test.right)
#print(complex.operands.mat[,"left"])
#print(complex.operands.mat[,"right"])
complex.val.string <- list(); length(complex.val.string) <- nrow(complex.mat)
complex.text.string <- list(); length(complex.text.string) <- nrow(complex.mat)
complex.val <- list(); length(complex.val) <- nrow(complex.mat)
names(complex.val) <- complex.mat[,"ParameterName"]
names(complex.val.string) <- complex.mat[,"ParameterName"]
names(complex.text.string) <- complex.mat[,"ParameterName"]
for(i in 1:nrow(complex.mat)){
if(complex.operators[i] %in% c("log()","abs()","exp()")){
value.string <- complex.mat[i,"Value"]
} else {
value.string <- complex.value.strings[i]
}
value.vector <- unlist(strsplit(value.string,split=" "))
search.parameters <- unlist(lapply(X=1:nrow(parameters.mat),FUN=function(z){any(grep(names(parameters.meanval)[z],value.vector,fixed=T))}))
if(any(search.parameters)){
parameters.temp <- names(parameters.meanval)[which(search.parameters)]
parameters.meanval.temp <- parameters.meanval[which(search.parameters)]
} else {
parameters.temp <- NULL
parameters.meanval.temp <- NULL
}
if(i>1){
### look in previously evaluated complex parameters
search.complex <- unlist(lapply(X=1:(i-1),FUN=function(z){any(grep(names(complex.val)[z],value.vector,fixed=T))}))
if(any(search.complex)){
complex.temp <- names(complex.val)[which(search.complex)]
complex.val.temp <- complex.val[which(search.complex)]
} else {
complex.temp <- NULL
complex.val.temp <- NULL
}
} else {
complex.temp <- NULL
complex.val.temp <- NULL
}
value.vector2 <- value.vector
value.string2 <- value.string
text.string <- value.string
parameters.temp <- c(parameters.temp,complex.temp)
parameters.val.temp <- c(parameters.meanval.temp,complex.val.temp)
if(!is.null(parameters.temp)){
for(j in 1:length(parameters.temp)){
value.string2 <- gsub(parameters.temp[j],parameters.val.temp[j],value.string2,fixed=T)
}
}
if(any(grep("%min%",value.string2,fixed=T))){
complex.val[[i]] <- min(unlist(parameters.val.temp))
complex.val.string[[i]] <- paste0("min(",parameters.val.temp[1],",",parameters.val.temp[2],")")
complex.text.string[[i]] <- paste0("min(",names(parameters.val.temp)[1],",",names(parameters.val.temp)[2],")")
} else {
if(any(grep("%max%",value.string2,fixed=T))){
complex.val[[i]] <- max(unlist(parameters.val.temp))
complex.val.string[[i]] <- paste0("max(",parameters.val.temp[1],",",parameters.val.temp[2],")")
complex.text.string[[i]] <- paste0("max(",names(parameters.val.temp)[1],",",names(parameters.val.temp)[2],")")
} else {
complex.val.string[[i]] <- value.string2
complex.val[[i]] <- eval(parse(text=value.string2))
complex.text.string[[i]] <- text.string
}
}
}
complex.val <- unlist(complex.val)
names(complex.val) <- complex.mat[,"ParameterName"]
complex.val.string <- unlist(complex.val.string)
names(complex.val.string) <- complex.mat[,"ParameterName"]
complex.text.string <- unlist(complex.text.string)
names(complex.text.string) <- complex.mat[,"ParameterName"]
}
### If simple and complex parameters exist
if(parameters.exist & complex.exist){
variables <- c(parameters.meanval,complex.val)
}
### If all parameters are simple
if(parameters.exist & !complex.exist){
variables <- c(parameters.meanval)
}
#### Check that the rules are obeyed. If not obeyed, resample from prior until rules are satisfied, or until maximum initialization attempts reached.
if(rules.exist){
variables.with.rules <- variables[names(variables) %in% rules.mat]
# initialize rules.values.mat by setting it equal to rules.mat
rules.values.mat <- rules.mat
for(i in 1:length(variables.with.rules)){
rules.values.mat[rules.values.mat == names(variables.with.rules)[i]] <- variables.with.rules[i]
}
rules.vals.strings <- apply(X=rules.values.mat,MARGIN=1,FUN=paste,collapse=" ")
#print(rules.vals.strings)
rules.evaluated <- sapply(X=1:length(rules.vals.strings), FUN=function(x){eval(parse(text=rules.vals.strings[x]))})
if(!all(rules.evaluated)){
if(initialization.attempt < initialization.max){
initialization.attempt <- (initialization.attempt + 1)
#initialization.passed <- FALSE
initialization.check1 <- FALSE
next
} else {
stop(paste("Rules",which(!rules.evaluated),"failed to initialize as true. Model may be valid but unable to find solution for plotting."))
}
} else {
#initialization.attempt <- initialization.max+1
initialization.check1 <- TRUE
}
} else {
initialization.check1 <- TRUE
}
### vector with lines containing historical events
events.lines.temp <- tpl.lines[(grep(pattern="^//historical event:", tpl.lines)+2):(grep(pattern="^//Number of independent loci", tpl.lines)-1)]
### Checks if end-of-line comments exist, and warns if they do because fsc26 seems to fail when c++ comments are in unusual places
if(any(grep("\\\\",events.lines.temp)) | any(grep("\\/\\/",events.lines.temp)) | any(grep("#",events.lines.temp))){
events.lines.temp <- gsub("\\\\.+","",events.lines.temp)
events.lines.temp <- gsub("\\/\\/.+","",events.lines.temp)
events.lines.temp <- gsub("#.+","",events.lines.temp)
if(warn.about.comments){
warning("Comments were found on one or more lines defining historical events in '.tpl' file; these comments may not be supported by fsc.")
}
}
### Remove whitespace before linebreaks
events.lines.temp <- gsub(" +$","",events.lines.temp)
### Hold events as a list
events.lines <- as.list(events.lines.temp)
num.events <- as.numeric(unlist(strsplit(tpl.lines[(grep(pattern="^//historical event:", tpl.lines)+1)],split=" "))[1])
events.mat <- do.call(rbind,lapply(X=events.lines,FUN=function(x){unlist(strsplit(x,split=" "))}))
colnames(events.mat) <- c("time","source","sink","migrants","newsize","growthrate","migration matrix")
if(any(names(variables) %in% events.mat)){
variables.in.events <- variables[names(variables) %in% events.mat]
### version of events.mat that contains values instead of variable names
events.mat.values <- events.mat
for(i in 1:length(variables.in.events)){
variable.temp <- variables.in.events[i]
events.mat.values <- gsub(names(variable.temp),variable.temp,events.mat.values,fixed=T)
}
} else {
events.mat.values <- events.mat
}
# Sort rows of events.mat.values and events.mat by increasing value in the first column and then by presence or absence of "keep" for any value in a row
rows.reorder0 <- rep(0,nrow(events.mat.values))
rows.reorder0[sapply(X=1:nrow(events.mat.values),FUN=function(x){"keep" %in% events.mat.values[x,]})] <- 1
rows.reorder <- order( as.numeric(events.mat.values[,"time"]), rows.reorder0)
events.mat.values <- events.mat.values[rows.reorder,,drop=F]
events.mat <- events.mat[rows.reorder,,drop=F]
### Number of populations at present (i.e., zero generations ago) and their population sizes
npops.present <- as.numeric(tpl.lines[2])
### names of variables that specify intial population sizes
popsizes.initial <- tpl.lines[(grep("//Population effective sizes",tpl.lines)+1):(grep("//Population effective sizes",tpl.lines)+npops.present)]
if(any(grep("^[0-9]+$",popsizes.initial))){
popsizes.pars <- popsizes.initial[!grep("^[0-9]+$",popsizes.initial)]
} else {
popsizes.pars <- popsizes.initial
}
if(length(popsizes.pars)>0){
popsizes.present <- parameters.meanval[names(parameters.meanval) %in% popsizes.pars]
} else {
popsizes.present <- as.numeric(popsizes.initial)
}
### names of variables that specify changes in growth rate
if(any(is.na(suppressWarnings(as.numeric(events.mat[,"growthrate"]))))){
growthrate.pars <- events.mat[is.na(suppressWarnings(as.numeric(events.mat[,"growthrate"]))),"growthrate"]
} else {
growthrate.pars <- NULL
}
### names of variables that specify instantaneous changes in size
if(any(is.na(suppressWarnings(as.numeric(events.mat[,"newsize"]))))){
newsize.pars <- events.mat[is.na(suppressWarnings(as.numeric(events.mat[,"newsize"]))),"newsize"]
} else {
newsize.pars <- NULL
}
### names of variables that specify instantaneous migration
if(any(is.na(suppressWarnings(as.numeric(events.mat[,"migrants"]))))){
migrants.pars <- events.mat[is.na(suppressWarnings(as.numeric(events.mat[,"migrants"]))),"migrants"]
} else {
migrants.pars <- NULL
}
### Number of migration matrices
num.mig.matrices <- as.numeric(tpl.lines[grep("//Number of migration matrices",tpl.lines)+1])
# Replace "keep" values with their migration matrix index
if(any(events.mat.values[,"migration matrix"]=="keep")){
for(i in 1:nrow(events.mat.values)){
if(i==1 & events.mat.values[i,"migration matrix"]=="keep"){
events.mat.values[i,"migration matrix"] <- "0"
}
if(i!=1 & events.mat.values[i,"migration matrix"]=="keep"){
events.mat.values[i,"migration matrix"] <- events.mat.values[(i-1),"migration matrix"]
}
}
}
## Initial values for growth rate
initial.growthrate <- as.numeric(tpl.lines[(grep("//Growth rates",tpl.lines)+1):(grep("//Growth rates",tpl.lines)+npops.present)])
### If 'keep' is used as a directive for growth rate, remove the growthrates column from the historical events matrix so that this information can be stored in a list of numerical vectors
growthrates.list <- list(); length(growthrates.list) <- (nrow(events.mat.values)+1)
growthrates.list[[1]] <- as.numeric(initial.growthrate)
for(i in 1:nrow(events.mat.values)){
if(events.mat.values[i,"growthrate"]=="keep"){
growthrates.list[[i+1]] <- growthrates.list[[i]]
} else {
### Not sure how to incorporate popsize change information yet
growthrates.list[[i+1]] <- as.numeric(rep(events.mat.values[i,"growthrate"],npops.present))
}
}
# Remove growthrates column from events.mat.values now that this info has been moved to a list.
events.mat.values <- events.mat.values[, !(colnames(events.mat.values) %in% "growthrate"),drop=F]
### change the mode of events.mat.values to numeric
mode(events.mat.values) <- "numeric"
time.list <- list(); length(time.list) <- nrow(events.mat.values)+1
for(i in 1:length(time.list)){
if(i == 1){
start.time <- 0
} else {
start.time <- events.mat.values[i-1,"time"]
}
if(i < length(time.list)){
end.time <- events.mat.values[i,"time"]-1
} else {
end.time <- start.time*1.5
}
time.list[[i]] <- rbind(start.time,end.time)
}
### Number of time periods
num.periods <- nrow(events.mat.values)+1
#}
### Internal names to use for populations
popnames.short <- paste0("pop",c(0:(length(popsizes.present)-1)))
### Don't delete period.sizes.v2
period.sizes.v2 <- list(); length(period.sizes.v2) <- num.periods
for(i in 1:length(period.sizes.v2)){
if(i == 1){
start.sizes <- popsizes.present
names(start.sizes) <- popnames.short
GROWTH <- start.sizes*growthrates.list[[i]]*(time.list[[i]]["end.time",]-time.list[[i]]["start.time",])
end.sizes <- start.sizes + GROWTH
period.sizes.v2[[i]] <- rbind(start.sizes,end.sizes)
} else {
sourcepop <- events.mat.values[i-1,"source"]
sinkpop <- events.mat.values[i-1,"sink"]
MIGRANTS <- period.sizes.v2[[i-1]]["end.sizes",sourcepop+1] * events.mat.values[i-1,"migrants"]
# temporarily set start sizes equal to previous end.sizes
start.sizes <- period.sizes.v2[[i-1]]["end.sizes",]
start.sizes[sourcepop+1] <- start.sizes[sourcepop+1] - MIGRANTS
start.sizes[sinkpop+1] <- start.sizes[sinkpop+1] + MIGRANTS
# Now modify by the value of newsize
start.sizes <- start.sizes * events.mat.values[i-1,"newsize"]
GROWTH <- start.sizes * growthrates.list[[i]]*(time.list[[i]]["end.time",]-time.list[[i]]["start.time",])
end.sizes <- start.sizes + GROWTH
period.sizes.v2[[i]] <- rbind(start.sizes,end.sizes)
}
}
period.sizes.v3 <- list(); length(period.sizes.v3) <- length(period.sizes.v2)
for(i in 1:length(period.sizes.v3)){
period.sizes.v3[[i]] <- cbind(time.list[[i]],period.sizes.v2[[i]])
}
## list with migration matrices
if(num.mig.matrices>0){
matrix.header.lines <- grep("//migrationmatrix", tpl.lines)
migration.matrices <- lapply(X=1:length(matrix.header.lines),FUN=function(x){matrix(unlist(strsplit(tpl.lines[(matrix.header.lines[x]+1):(matrix.header.lines[x]+npops.present)],split=" ")),ncol=npops.present,nrow=npops.present,byrow=T)})
for(i in 1:length(migration.matrices)){
colnames(migration.matrices[[i]]) <- popnames.short
rownames(migration.matrices[[i]]) <- popnames.short
}
} else {
migration.matrices <- NULL
}
slices.popsizes.all <- do.call(rbind,period.sizes.v3)[,-1]
### Unique combinations of presence/absence among populations at any point in time.
pops.present <- slices.popsizes.all
pops.present[pops.present > 0] <- 1
unique.pops.present <- unique(pops.present)
if(any(unique.pops.present < 0)){
if(initialization.attempt < initialization.max){
initialization.attempt <- initialization.attempt + 1
next
} else {
stop("Model may be valid, but plotting failed when using means of priors because negative population sizes produced. A future version of this function may resample prior to find valid solution.")
}
} else {
initialization.check2 <- TRUE
}
if(initialization.check1 & initialization.check2){
initialization.passed <- TRUE
if(initialization.attempt>1){
print(paste("initialized after",initialization.attempt,"attempts"))
}
}
}
max.popsize.eachpop.list <- list(); length(max.popsize.eachpop.list) <- nrow(unique.pops.present)
names(max.popsize.eachpop.list) <- apply(X= unique.pops.present,MARGIN=1,FUN=paste,collapse=" ")
for(i in 1:nrow(unique.pops.present)){
combo.temp <- unique.pops.present[i,]
## subset of slices.popsizes.all containing columns with the ith combination of pops and rows without zeros.
slices.popsizes.i <- slices.popsizes.all[,which(unique.pops.present[i,]==1)]
rows.keep <- apply(X=pops.present,MARGIN=1,FUN=function(x){all(x==combo.temp)})
slices.popsizes.temp <- slices.popsizes.all[rows.keep,]
max.popsize.eachpop.temp <- apply(X=slices.popsizes.temp,MARGIN=2,FUN=max)
max.popsize.eachpop.list[[i]] <- max.popsize.eachpop.temp[max.popsize.eachpop.temp>0]
}
####
y.extents <- list(); length(y.extents) <- length(max.popsize.eachpop.list)
names(y.extents) <- names(max.popsize.eachpop.list)
for(z in 1:length(max.popsize.eachpop.list)){
ytops <- list(); length(ytops) <- length(max.popsize.eachpop.list[[z]])
for(i in 1:length(ytops)){
if(i==1){
top.current <- 0
}
ytops[[i]] <- max.popsize.eachpop.list[[z]][i] + (top.current*pops.spacer)
top.current <- ytops[[i]]
}
ytops <- unlist(ytops)
ybottoms <- ytops-max.popsize.eachpop.list[[z]]
ymids <- colMeans(rbind(ybottoms,ytops))
y.extents[[z]] <- rbind(ybottoms,ymids,ytops)
}
shapes.list <- list(); length(shapes.list) <- length(period.sizes.v3)
for(i in 1:length(period.sizes.v3)){
# matrix with popsizes and start and end of ith time period
period.mat <- period.sizes.v3[[i]]
# number of populations in the ith period
period.numpops <- length(which(apply(X=period.mat[,-1],MARGIN=2,FUN=function(x){all(x!=0)})))
# number of populations in each of the first through ith periods
if(i==1){
period.numpops.vector <- period.numpops
} else {
period.numpops.vector <- c(period.numpops.vector,period.numpops)
}
### y-axis midlines for populations
pops.present.temp <- paste(as.numeric(unique(period.mat[,-1] > 0)),collapse=" ")
y.extents.temp <- y.extents[[which(names(y.extents) %in% pops.present.temp)]]
period.mat <- period.mat[,c(1,which(colnames(period.mat) %in% colnames(y.extents.temp))),drop=F]
shapes.list.i <- list(); length(shapes.list.i) <- ncol(y.extents.temp)
for(j in 1:ncol(y.extents.temp)){
## start and end population size for population j-1 during period i
period.mat.j <- period.mat[,c(1,(j+1))]
x_left <- period.mat.j[1,1]
x_right <- period.mat.j[2,1]
y_mid <- y.extents.temp["ymids",j]
y_top_left <- y_mid+(period.mat.j[1,2]/2)
y_top_right <- y_mid+(period.mat.j[2,2]/2)
y_bottom_left <- y_mid-(period.mat.j[1,2]/2)
y_bottom_right <- y_mid-(period.mat.j[2,2]/2)
shape.points.temp <- c(period=i,x_left,x_right,y_top_left,y_top_right,y_bottom_left,y_bottom_right)
shapes.list.i[[j]] <- shape.points.temp
}
shapes.list[[i]] <- do.call(rbind,shapes.list.i)
colnames(shapes.list[[i]]) <- c("period","x_left","x_right","y_top_left","y_top_right","y_bottom_left","y_bottom_right")
rownames(shapes.list[[i]]) <- colnames(period.mat)[-1]
}
if(npops.present>1){
# Check if at least one divergence/coalescent event occurs, and if so create matrices holding info on coalescence and divergence events, and possibly also fusions
if(any(events.mat.values[,"migrants",drop=F]==1)){
### Rows of historical events matrix that define divergences (coalescences)
coalescence.event.rows <- which(events.mat.values[,"migrants",drop=F]==1)
### Matrix with coalescence events
coalescence.events.mat <- events.mat.values[coalescence.event.rows,,drop=F]
### reverse-order of divergence matrix
divergence.events.mat <- coalescence.events.mat[rev(1:nrow(coalescence.events.mat)),,drop=F]
### matrix with only the source and sink columns of the divergence.events.mat
divergence.mat <- divergence.events.mat[,c("source","sink"),drop=F]
### populations that were sources during at least one coalescence event
coal.sources.unique <- unique(divergence.mat[,"source"])
### Historical events in which a nonzero fraction of a population is transferred from between two different populations.
migration.events.rows <- which(events.mat.values[,"migrants"]!=0 & events.mat.values[,"source"] != events.mat.values[,"sink"])
migration.events.mat <- events.mat.values[migration.events.rows,,drop=F]
## the length of fusion.events mat is initially set to the number of divergence events
fusion.events <- list(); length(fusion.events) <- nrow(divergence.mat)
for(i in 1:length(coal.sources.unique)){
# divergence events in which the ith population of coal.sources.unique is the source population
divergence.mat.i <- divergence.events.mat[which(divergence.events.mat[,"source"] == coal.sources.unique),,drop=F]
# a list to hold only the fusions reversing divergences with the ith population as a source
fusion.events.i <- list(); length(fusion.events.i) <- nrow(divergence.mat.i)
for(j in 1:nrow(divergence.mat.i)){
divergence.ij.time <- divergence.mat.i[j,"time"]
## check if any more recent migration events involve the ith source pop as a sink pop
if(any(migration.events.mat[,"sink"]==coal.sources.unique[i] & migration.events.mat[,"time"] < divergence.ij.time)){
nextevent.row <- which(migration.events.mat[,"sink"]==coal.sources.unique[i] & migration.events.mat[,"time"] < divergence.ij.time)
## the next event should be a fusion event
nextevent <- migration.events.mat[nextevent.row,,drop=F]
fusion.events.i[[j]] <- nextevent
} else {
fusion.events.i[[j]] <- rep(NA,6)
}
}
fusion.events.i <- do.call(rbind,fusion.events.i)
fusion.events[[i]] <- fusion.events.i
}
fusion.events.mat <- do.call(rbind,fusion.events)
if(all(is.na(fusion.events.mat[,1]))){
fusions.exist=F
} else {
fusions.exist=T
### Remove NA rows that were created for divergences that not followed by fusions
if(any(is.na(fusion.events.mat[,1]))){
fusion.events.mat <- fusion.events.mat[-which(is.na(fusion.events.mat[,1])),,drop=F]
}
### Ensure that fusion events are ordered from most ancient to most recent.
fusion.events.mat[order(fusion.events.mat[,"time"],decreasing=T),,drop=F]
### Simpler version of fusion.events.mat containing only the source and sink columns. This will be used during plotting.
fusions.mat <- fusion.events.mat[,c("source","sink"),drop=F]
}
}
### Construct the newick string representing relationships among populations, and then convert to phylo. Used to test if priors are consistent with a bifurcating phylogeny.
for(i in 1:nrow(divergence.events.mat)){
divergence.i <- divergence.events.mat[i,c("source","sink"),drop=F]
divergence.string.temp <- paste0("(",paste0(divergence.i,collapse=","),")")
if(i==1){
divergence.string <- divergence.string.temp
} else {
sink.i <- divergence.i[,"sink"]
### check if the current sink is in the divergence string and if so replace the sink value in the divergence string with the value of divergence.string.temp
### This works as long as you dont have more than 10 populations (pop0-pop9)
if(any(grep(sink.i,divergence.string))){
divergence.string <- gsub(sink.i,divergence.string.temp,divergence.string)
}
}
if(i==nrow(divergence.events.mat)){
divergence.string <- paste0("(",divergence.string,");")
}
}
# tree.phylo <- ape::read.tree(text=divergence.string)
# plot(tree.phylo)
# result.test <- recordPlot()
# return(result.test)
if(length(shapes.list)>1){
# initialize object to adjust during loop
shapes.list.v2 <- rev(shapes.list)
for(i in 2:length(shapes.list.v2)){
# initial settings for counters. Will add +1 to div.temp after each divergence event, and +to fus.temp after each fusion event
if(i==2){
div.temp=0
fus.temp=0
}
shapes.list.v2.temp <- shapes.list.v2[[i]]
### Checks if all populations in the current period were also in the previous period. If so, it means no divergence events in that period.
if(length(setdiff(rownames(shapes.list.v2.temp),rownames(shapes.list.v2[[i-1]])))==0){
### Checks if all populations in the previous period are also in the current period. If so, it means no fusion events in that period.
if(length(setdiff(rownames(shapes.list.v2[[i-1]]), rownames(shapes.list.v2.temp)))==0){
# Align all popshapes with their previous period popshapes
pops.other <- rownames(shapes.list.v2.temp)
oldmidlines.i <- apply(shapes.list.v2.temp[,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F],MARGIN=1,FUN=mean)
newmidlines.i <- apply(shapes.list.v2[[i-1]][,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F],MARGIN=1,FUN=mean)
dif.midlines <- newmidlines.i-oldmidlines.i
### updates shapes.list.v2
pops.other.vals <- shapes.list.v2[[i]][,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F] + dif.midlines
shapes.list.v2[[i]][,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right")] <- pops.other.vals
} else {
fus.temp = fus.temp + 1
pop.lost <- setdiff(rownames(shapes.list.v2[[i-1]]), rownames(shapes.list.v2.temp))
# Population that was sister to pop.lost in the previous period.
pop.sis <- paste0("pop",fusions.mat[fus.temp,!(fusions.mat[fus.temp,] %in% gsub("pop","",pop.lost))])
### current (not adjusted) midline of pop.sis in the current period
oldmidlines.pop.sis <- apply(shapes.list.v2.temp[pop.sis,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F],MARGIN=1,FUN=mean)
### Get the mean y value of y_bottom_left of the upper shape and y_top_left of the lower shape for pop.lost and pop.sis in the previous generation
if(shapes.list.v2[[i-1]][pop.sis,"y_bottom_left",drop=F] > shapes.list.v2[[i-1]][pop.lost,"y_top_left",drop=F]){
newmidlines.pop.sis <- mean(c(shapes.list.v2[[i-1]][pop.sis,"y_bottom_left",drop=F],shapes.list.v2[[i-1]][pop.lost,"y_top_left",drop=F]))
} else {
newmidlines.pop.sis <- mean(c(shapes.list.v2[[i-1]][pop.lost,"y_bottom_left",drop=F],shapes.list.v2[[i-1]][pop.sis,"y_top_left",drop=F]))
}
### Amount and direction to shift y values of pop.sis
diff.sis <- newmidlines.pop.sis - oldmidlines.pop.sis
### New y values for pop.sis
pop.sis.vals <- shapes.list.v2[[i]][pop.sis,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F] + c(diff.sis)
if(length(rownames(shapes.list.v2.temp))>1){
pops.other <- setdiff(rownames(shapes.list.v2.temp),c(pop.sis))
oldmidlines.i <- apply(shapes.list.v2.temp[pops.other,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F],MARGIN=1,FUN=mean)
newmidlines.i <- apply(shapes.list.v2[[i-1]][pops.other,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F],MARGIN=1,FUN=mean)
diff.midlines <- newmidlines.i-oldmidlines.i
### updates shapes.list.v2 midlines for shapes not involved in a divergence event
pops.other.vals <- shapes.list.v2[[i]][pops.other,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F] + diff.midlines
shapes.list.v2[[i]][pops.other,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right")] <- pops.other.vals
}
}
} else {
div.temp = div.temp + 1
# Determine which population is 'new', and call this pop.new
pop.new <- setdiff(rownames(shapes.list.v2.temp),rownames(shapes.list.v2[[i-1]]))
# Consult the divergence matrix to determine the sister population to pop.new
pop.sis <- paste0("pop",divergence.mat[div.temp,!(divergence.mat[div.temp,] %in% gsub("pop","",pop.new))])
### Want to set the mean of the daughter shape midlines equal to the parent shape midline.
oldmidlines.pop.new <- apply(shapes.list.v2.temp[pop.new,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F],MARGIN=1,FUN=mean)
oldmidlines.pop.sis <- apply(shapes.list.v2.temp[pop.sis,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F],MARGIN=1,FUN=mean)
### Setting the midlines for the upper and lower shapes of period i equal to the y values of the top left and bottom left corners of the parent shape, respectively
if(oldmidlines.pop.new > oldmidlines.pop.sis){
newmidlines.pop.new <- shapes.list.v2[[i-1]][pop.sis,"y_top_left",drop=F]
newmidlines.pop.sis <- shapes.list.v2[[i-1]][pop.sis,"y_bottom_left",drop=F]
} else {
newmidlines.pop.sis <- shapes.list.v2[[i-1]][pop.sis,"y_top_left",drop=F]
newmidlines.pop.new <- shapes.list.v2[[i-1]][pop.sis,"y_bottom_left",drop=F]
}
diff.new <- newmidlines.pop.new - oldmidlines.pop.new
diff.sis <- newmidlines.pop.sis - oldmidlines.pop.sis
# ### updates shapes.list.v2 midlines for the two shapes that are involved in a divergence event
pop.new.vals <- shapes.list.v2[[i]][pop.new,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F] + c(diff.new)
pop.sis.vals <- shapes.list.v2[[i]][pop.sis,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F] + c(diff.sis)
shapes.list.v2[[i]][pop.new,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right")] <- pop.new.vals
shapes.list.v2[[i]][pop.sis,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right")] <- pop.sis.vals
if(length(rownames(shapes.list.v2.temp))>2){
pops.other <- setdiff(rownames(shapes.list.v2.temp),c(pop.new,pop.sis))
oldmidlines.i <- apply(shapes.list.v2.temp[pops.other,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F],MARGIN=1,FUN=mean)
newmidlines.i <- apply(shapes.list.v2[[i-1]][pops.other,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F],MARGIN=1,FUN=mean)
diff.midlines <- newmidlines.i-oldmidlines.i
### updates shapes.list.v2 midlines for shapes not involved in a divergence event
pops.other.vals <- shapes.list.v2[[i]][pops.other,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right"),drop=F] + diff.midlines
shapes.list.v2[[i]][pops.other,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right")] <- pops.other.vals
}
}
}
}
shapes.list <- shapes.list.v2
}
### adjust all y-coordinates to ensure that all parts of the plot are visible.
shapes.mat <- do.call(rbind,shapes.list)
if(min(shapes.mat[,c("y_bottom_left","y_bottom_right")]) < 0){
y.adjust <- abs(min(shapes.mat[,c("y_bottom_left","y_bottom_right")]))
shapes.mat[,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right")] <- shapes.mat[,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right")] + y.adjust
}
### Subsetting parameters names by type. Maybe move this to a slightly earlier spot.
if(length(intersect(events.mat[,"time"],names(variables)))!=0){
time.pars <- intersect(events.mat[,"time"],names(variables))
} else {
time.pars <- NULL
}
#if(length(intersect(events.mat[,"time"],names(variables)))!=0){
# time.pars <- intersect(events.mat[,"time"],names(variables))
#}
if(length(popsizes.pars)==0){
popsizes.pars <- NULL
}
if(length(migration.matrices)!=0){
migrationrate.pars <- intersect(unique(unlist(migration.matrices)),names(variables))
if(length(migrationrate.pars)==0){
migrationrate.pars <- NULL
}
} else {
migrationrate.pars <- NULL
}
other.pars <- setdiff(names(variables),c(time.pars,migrationrate.pars,popsizes.pars,migrants.pars,newsize.pars,growthrate.pars))
if(length(other.pars)==0){
other.pars <- NULL
other.simple.pars <- NULL
} else {
other.simple.pars <- intersect(other.pars,simple.pars)
}
if(length(other.simple.pars)==0){
other.simple.pars <- NULL
}
### Separating variables.sampled.mat by parameters that are uniformly distributed and those that are loguniformly distributed
# Not sure what to do if a complex parameter is evaluated from both a uniform and loguniformly distributed parameter
# unif.simple.pars <- intersect(simple.pars,parameters.mat[parameters.mat[,"Distribution"] %in% "unif","ParameterName"])
# logunif.simple.pars <- intersect(simple.pars,parameters.mat[parameters.mat[,"Distribution"] %in% "logunif","ParameterName"])
# The purpose of this is to classify the complex parameters as having either a uniform or loguniform probability density.
if(length(complex.pars)>0){
if(!is.null(complex.leftoperand) & !is.null(complex.rightoperand)){
complex.dist.list <- list(); length(complex.dist.list) <- nrow(complex.mat)
names(complex.dist.list) <- complex.mat[,"ParameterName"]
for(i in 1:nrow(complex.mat)){
if(complex.leftoperand[i] %in% simple.pars & any(grep("^[0-9]+$",complex.rightoperand[i]))){
if(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Distribution"] == "logunif"){
complex.dist.list <- NULL
} else {
if(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Distribution"] == "unif"){
### uniform simple parameter divided by a constant
if(complex.operators[i] == "/"){
complex.min.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Min"])/as.numeric(complex.rightoperand[i])
complex.max.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Max"])/as.numeric(complex.rightoperand[i])
result.i <- matrix(c(complex.mat[i,"ParameterName"],complex.min.temp,complex.max.temp,"Unif/Constant"),nrow=1,ncol=4)
colnames(result.i) <- c("Parameter","Min","Max","DistClass")
complex.dist.list[[i]] <- as.data.frame(result.i)
}
### uniform simple parameter multiplied by a constant
if(complex.operators[i] == "*"){
complex.min.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Min"])*as.numeric(complex.rightoperand[i])
complex.max.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Max"])*as.numeric(complex.rightoperand[i])
result.i <- matrix(c(complex.mat[i,"ParameterName"],complex.min.temp,complex.max.temp,"Unif*Constant"),nrow=1,ncol=4)
colnames(result.i) <- c("Parameter","Min","Max","DistClass")
complex.dist.list[[i]] <- as.data.frame(result.i)
}
### uniform simple parameter plus a constant
if(complex.operators[i] == "+"){
complex.min.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Min"])+as.numeric(complex.rightoperand[i])
complex.max.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Max"])+as.numeric(complex.rightoperand[i])
result.i <- matrix(c(complex.mat[i,"ParameterName"],complex.min.temp,complex.max.temp,"Unif+Constant"),nrow=1,ncol=4)
colnames(result.i) <- c("Parameter","Min","Max","DistClass")
complex.dist.list[[i]] <- as.data.frame(result.i)
}
### uniform simple parameter minus a constant
if(complex.operators[i] == "-"){
complex.min.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Min"])-as.numeric(complex.rightoperand[i])
complex.max.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Max"])-as.numeric(complex.rightoperand[i])
result.i <- matrix(c(complex.mat[i,"ParameterName"],complex.min.temp,complex.max.temp,"Unif-Constant"),nrow=1,ncol=4)
colnames(result.i) <- c("Parameter","Min","Max","DistClass")
complex.dist.list[[i]] <- as.data.frame(result.i)
}
}
}
} else {
if(complex.leftoperand[i] %in% simple.pars & complex.rightoperand[i] %in% simple.pars){
if(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Distribution"] == "logunif" | parameters.mat[grep(complex.rightoperand[i],simple.pars,fixed=T),"Distribution"] == "logunif"){
complex.dist.list <- NULL
} else {
if(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Distribution"] == "lunif" | parameters.mat[grep(complex.rightoperand[i],simple.pars,fixed=T),"Distribution"] == "unif"){
### uniform simple parameter divided by a uniform simple parameter
if(complex.operators[i] == "/"){
complex.min1.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Min"])
complex.max1.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Max"])
complex.min2.temp <- as.numeric(parameters.mat[grep(complex.rightoperand[i],simple.pars,fixed=T),"Min"])
complex.max2.temp <- as.numeric(parameters.mat[grep(complex.rightoperand[i],simple.pars,fixed=T),"Max"])
result.i <- rbind(c(complex.mat[i,"ParameterName"],complex.min1.temp,complex.max1.temp,"Unif/Unif"),c(complex.mat[i,"ParameterName"],complex.min2.temp,complex.max2.temp,"Unif/Unif"))
colnames(result.i) <- c("Parameter","Min","Max","DistClass")
complex.dist.list[[i]] <- as.data.frame(result.i)
}
### uniform simple parameter multiplied by a uniform simple parameter
if(complex.operators[i] == "*"){
complex.min1.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Min"])
complex.max1.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Max"])
complex.min2.temp <- as.numeric(parameters.mat[grep(complex.rightoperand[i],simple.pars,fixed=T),"Min"])
complex.max2.temp <- as.numeric(parameters.mat[grep(complex.rightoperand[i],simple.pars,fixed=T),"Max"])
result.i <- rbind(c(complex.mat[i,"ParameterName"],complex.min1.temp,complex.max1.temp,"Unif*Unif"),c(complex.mat[i,"ParameterName"],complex.min2.temp,complex.max2.temp,"Unif*Unif"))
colnames(result.i) <- c("Parameter","Min","Max","DistClass")
complex.dist.list[[i]] <- as.data.frame(result.i)
}
### uniform simple parameter plus a uniform simple parameter
if(complex.operators[i] == "+"){
complex.min1.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Min"])
complex.max1.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Max"])
complex.min2.temp <- as.numeric(parameters.mat[grep(complex.rightoperand[i],simple.pars,fixed=T),"Min"])
complex.max2.temp <- as.numeric(parameters.mat[grep(complex.rightoperand[i],simple.pars,fixed=T),"Max"])
result.i <- rbind(c(complex.mat[i,"ParameterName"],complex.min1.temp,complex.max1.temp,"Unif+Unif"),c(complex.mat[i,"ParameterName"],complex.min2.temp,complex.max2.temp,"Unif+Unif"))
colnames(result.i) <- c("Parameter","Min","Max","DistClass")
complex.dist.list[[i]] <- as.data.frame(result.i)
}
### uniform simple parameter minus a uniform simple parameter
if(complex.operators[i] == "-"){
complex.min1.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Min"])
complex.max1.temp <- as.numeric(parameters.mat[grep(complex.leftoperand[i],simple.pars,fixed=T),"Max"])
complex.min2.temp <- as.numeric(parameters.mat[grep(complex.rightoperand[i],simple.pars,fixed=T),"Min"])
complex.max2.temp <- as.numeric(parameters.mat[grep(complex.rightoperand[i],simple.pars,fixed=T),"Max"])
result.i <- rbind(c(complex.mat[i,"ParameterName"],complex.min1.temp,complex.max1.temp,"Unif-Unif"),c(complex.mat[i,"ParameterName"],complex.min2.temp,complex.max2.temp,"Unif-Unif"))
colnames(result.i) <- c("Parameter","Min","Max","DistClass")
complex.dist.list[[i]] <- as.data.frame(result.i)
}
} else {
### If none of the previous conditions were true set the ith entry to NULL
complex.dist.list[[i]] <- NULL
}
}
}
}
}
if(any(lengths(complex.dist.list)>0)){
complex.dist.df <- do.call(rbind,complex.dist.list)
mode(complex.dist.df[,"Min"]) <- "numeric"
mode(complex.dist.df[,"Max"]) <- "numeric"
rownames(complex.dist.df) <- NULL
} else {
complex.dist.df <- NULL
}
} else {
complex.dist.list <- NULL
complex.dist.df <- NULL
}
} else {
complex.dist.list <- NULL
complex.dist.df <- NULL
}
#print(complex.dist.df)
##### Initial Trial Plot used for calculating space necessary for popnames and popsize labels.
ymax.temp <- max(shapes.mat[,c("y_top_left","y_top_right")])*plotmargin[3]
xmax.temp <- max(shapes.mat[,c("x_right")])*plotmargin[4]
ymin.temp <- 0 + ymax.temp*(1-plotmargin[1])
xmin.temp <- 0 + xmax.temp*(1-plotmargin[2])
# Defining the blank plotting area
par(mar=c(3,0.5,3,1.5),oma=c(2,2,2,2))
plot(x=c(xmin.temp, xmax.temp), y=c(ymin.temp,ymax.temp), col="white",xlab="", ylab="",axes=F)
### Adjusts plotmargin[2] to be sure that popnames and popsizes fit in plotting area.
if(is.null(popnames)){
### I changed this to require popnames, such that popnames = 1 and popnames = NULL do the same thing and will use "pop0", "pop1", etc.
popnames.labels <- paste0("pop",c(0:(length(popsizes.present)-1)))
xleft.adjust <- max(strwidth(names(popsizes.present)))
} else {
if(popnames[1]==1 & length(popnames)==1){
popnames.labels <- paste0("pop",c(0:(length(popsizes.present)-1)))
xleft.adjust <- max(strwidth(names(popsizes.present)) + strwidth(popnames.labels))
} else {
if(length(popnames)!=length(popsizes.present)){
warning("number of popnames should equal the number of population samples on line 2 of template file")
xleft.adjust <- max(strwidth(names(popsizes.present)))
} else {
popnames.labels <- popnames
xleft.adjust <- max(strwidth(names(popsizes.present)) + strwidth(popnames.labels))
}
}
}
### limits for the plotting area
ymax <- max(shapes.mat[,c("y_top_left","y_top_right")])*plotmargin[3]
xmax <- max(shapes.mat[,c("x_right")])*plotmargin[4]
ymin <- 0 + ymax*(1-plotmargin[1])
xmin <- (0 - xleft.adjust) + xmax*(1-plotmargin[2])
#### Do not move the next section before the test plotting section.
if(show.priors & priors.panel.width > 0){
### Setting the layout of the plotting area
# Determines fraction of horizontal plotting area to use for plotting density functions of priors
# number of graphical columns for the priors panel
ncol.var <- round(100*priors.panel.width)
# number of graphical columns for the model
ncol.model <- (100-ncol.var)
simple.par.per.type.list <- lapply(X=list(popsizes.pars,time.pars,migrationrate.pars,migrants.pars,newsize.pars,growthrate.pars),FUN=function(x){intersect(x,simple.pars)})
if(!is.null(complex.dist.df)){
complex.par.per.type.list <- lapply(X=list(popsizes.pars,time.pars,migrationrate.pars,migrants.pars,newsize.pars,growthrate.pars),FUN=function(x){intersect(x,complex.pars)})
names(complex.par.per.type.list) <- c("popsizes","time","migrationrate","migrants.pars","newsize.pars","growthrate.pars")
complex.pars.to.graph.temp <- unique(complex.dist.df[,"Parameter"])
complex.pars.to.graph <- lapply(X=complex.par.per.type.list,FUN=function(x){intersect(x,complex.pars.to.graph.temp)})
all.pars.to.graph <- lapply(X=1:length(simple.par.per.type.list),FUN=function(x){union(complex.pars.to.graph[[x]],simple.par.per.type.list[[x]])})
} else {
all.pars.to.graph <- simple.par.per.type.list
complex.pars.to.graph <- NULL
}
other.simple.and.complex.list <- list(c(other.simple.pars,complex.pars))
num.pars.per.type <- sapply(X=c(all.pars.to.graph,other.simple.and.complex.list),FUN=length)
num.partypes <- length(num.pars.per.type[num.pars.per.type!=0])
numplots <- num.partypes
# construct matrix defining layout of density plots
layout.variables <- matrix(rep(2:(numplots+1),ncol.var),ncol=ncol.var)
# construct matrix for the model part of the layout
layout.model <- matrix(data=1,ncol=ncol.model,nrow=nrow(layout.variables))
# Define layout matrix
layout.mat <- cbind(layout.model,layout.variables)
# Set plotting layout
layout(layout.mat)
}
### For the case of all populations collapsed as a single population at all times, plot a single rectangle.
if(all(shapes.mat[,"x_right"] %in% c(0,-1))){
shapes.mat.temp <- unique(shapes.mat[,-1])
plot(x=c(xmin, 10), y=c(ymin, ymax), col="white",xlab="", ylab="",axes=F)
polygon(x=c(0,10,10,0), y=c(min(as.numeric(shapes.mat.temp[,"y_bottom_left"])),min(as.numeric(shapes.mat.temp[,"y_bottom_right"])),max(as.numeric(shapes.mat.temp[,"y_top_right"])),max(as.numeric(shapes.mat.temp[,"y_top_left"]))),col="gray",border="darkgray")
} else {
###
# Defining the blank plotting area for model
plot(x=c(xmin, xmax), y=c(ymin, ymax), col="white",xlab="", ylab="",axes=F)
for(i in 1:nrow(shapes.mat)){
shape.i <- shapes.mat[i,]
if(shape.i["x_right"] %in% c(-1:0)){
next
}
polygon(x=c(shape.i["x_left"],shape.i["x_right"],shape.i["x_right"],shape.i["x_left"]), y=c(shape.i["y_bottom_left"],shape.i["y_bottom_right"],shape.i["y_top_right"],shape.i["y_top_left"]),col="gray",border="darkgray")
}
}
# ggplot2::ggplot() + ggplot2::scale_x_continuous(name="x") + ggplot2::scale_y_continuous(name="y") + ggplot2::geom_rect(data=test.df,mapping=ggplot2::aes(xmin= x_left,xmax= x_right,ymin= y_bottom_left,ymax= y_top_left))
### Add arrows to show migration
if(num.mig.matrices > 0){
for(i in 1:length(period.sizes.v3)){
period.mat <- period.sizes.v3[[i]]
shapes.mat.i <- shapes.mat[c(shapes.mat[,"period"] %in% i),,drop=F]
# skip to next i if only one population during the period
if(nrow(shapes.mat.i)==1){
next
}
if(period.mat["end.time","time"] %in% c(-1)){
next
}
# x (time) coordinate where arrows will be drawn if there is migration during the period
xmids.i <- mean(range(shapes.mat.i[,c("x_left","x_right")]))
xlefts.i <- shapes.mat.i[,c("x_left")]
xwidths.i <- diff(range(shapes.mat.i[,c("x_left","x_right")]))
# ymids of each population (shape)
ymids.i <- sapply(X=1:nrow(shapes.mat.i),FUN=function(x){mean(range(shapes.mat.i[x,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right")]))})
names(ymids.i) <- rownames(shapes.mat.i)
### Get the migration matrix that applies to the region
if(period.mat[1,"time"] %in% events.mat.values[,"time"]){
# migration matrix index
mig.mat <- events.mat.values[which(events.mat.values[,"time"] == period.mat[1,"time"]),"migration matrix"]
## There should never be more than one migration matrix in a time period.
if(length(unique(mig.mat))>1){
conincident.time.variables <- events.mat[which(events.mat.values[,"time"] == period.mat[1,"time"]),"time"]
conflicting.migration.matrices <- unique(mig.mat)
stop(paste("Invalid model. Time variables:", paste(conincident.time.variables, collapse=" "),"have same value but different migration matrices:",paste(unique(mig.mat),collapse=" ")))
}
mig.mat <- unique(mig.mat)
# Corresponding migration matrix
migration.matrix <- migration.matrices[[mig.mat+1]]
} else {
# The first migration matrix is the one used for time 0, if it exists, unless the events matrix specifies otherwise.
migration.matrix <- migration.matrices[[1]]
}
# if migration matrix is not the zero matrix, get row and column indices of the nonzero values. Probably should use names instead of numbers.
if(!all(migration.matrix==0)){
ymids.source.mat.i <- matrix(data=rep(ymids.i,length(ymids.i)), nrow=length(ymids.i),ncol=length(ymids.i),byrow=F)
ymids.sink.mat.i <- matrix(data=rep(ymids.i,length(ymids.i)), nrow=length(ymids.i),ncol=length(ymids.i),byrow=T)
source.sink.mat <- cbind(pop.source=rep( rownames(shapes.mat.i), each=length(ymids.i)), pop.sink=rep(rownames(shapes.mat.i),length(ymids.i)),ymids.source = c(ymids.source.mat.i), ymids.sink = c(ymids.sink.mat.i), migration.parameter = c(migration.matrix[rownames(shapes.mat.i),rownames(shapes.mat.i)]))
source.sink.mat.ordered <- source.sink.mat[order(source.sink.mat[,"pop.source"], source.sink.mat[,"pop.sink"]),]
pop.pair.mig <- do.call(rbind,lapply(X=1:nrow(source.sink.mat.ordered),FUN=function(x){paste(paste(unname(sort(source.sink.mat[x,c("pop.source","pop.sink")])),collapse=" "),source.sink.mat[x,c("migration.parameter")],collapse=" ")}))
source.sink.df <- as.data.frame(source.sink.mat.ordered)
mode(source.sink.df[,"ymids.source"]) <- "numeric"
mode(source.sink.df[,"ymids.sink"]) <- "numeric"
source.sink.df[,"poppair.mig"] <- pop.pair.mig
if(all(source.sink.df$migration.parameter==0)){
next
}
source.sink.df <- source.sink.df[which(source.sink.df$migration.parameter!=0),]
x.adj.tol <- 0.15
x.adj <- seq(from=(-x.adj.tol),to=x.adj.tol ,length.out=length(unique(source.sink.df[,"poppair.mig"])))
source.sink.df[,"xadj"] <- x.adj[match(source.sink.df[,"poppair.mig"],unique(source.sink.df[,"poppair.mig"]))]
mode(source.sink.df[,"xadj"]) <- "numeric"
source.sink.df[,"x"] <- (xmids.i+(max(shapes.mat.i[,"x_right"]-shapes.mat.i[,"x_left"])*source.sink.df[,"xadj"]))
apply(X=source.sink.df[,c("ymids.source","ymids.sink")],MARGIN=1,FUN=mean)
source.sink.df[,"ytext"] <- apply(X=source.sink.df[,c("ymids.source","ymids.sink")],MARGIN=1,FUN=mean)
arrows(x0=source.sink.df[,"x"],y0=source.sink.df[,"ymids.source"],y1=source.sink.df[,"ymids.sink"],length=0.05,col="black")
text.df <- unique(source.sink.df[,c("migration.parameter","x","ytext")])
text(x=text.df[,"x"],y=text.df[,"ytext"],labels=text.df[,"migration.parameter"],adj=c(0.5,-0.5),cex=label.cex,srt=90)
}
} ### end arrows loop
} ### end if statement
### Add dashed vertical lines to separate time periods at historical events
segments(x0=events.mat.values[,"time"],y0=ymin,y1=ymax,lty=2)
### Add dashed vertical line at t=0
segments(x0=0,y0=ymin,y1=ymax,lty=2)
### Add text labels for event times except for events that occur at time zero...
if(num.events>0){
if(any(events.mat.values[,"time"] != 0)){
events.mark <- which(events.mat.values[,"time"] != 0)
xylab.event.mat <- cbind(x=events.mat.values[events.mark,"time"],y=rep(mean(c(0,ymin)),length(events.mark)),lab=events.mat[events.mark,"time"])
### identify and merge labels for events that occur at the same time
while(any(table(xylab.event.mat[,"x"])>1)){
num.concurrent.events <- length(which(table(xylab.event.mat[,"x"])>1))
time.events <- names(which(table(xylab.event.mat[,"x"])>1)[1])
merged.lab <- paste0(xylab.event.mat[which(xylab.event.mat[,"x"]==time.events),"lab"],collapse=", ")
merged.row <- cbind(unique(xylab.event.mat[which(xylab.event.mat[,"x"]==time.events),c("x","y"),drop=F]),merged.lab)
xylab.event.mat <- rbind(xylab.event.mat[-which(xylab.event.mat[,"x"]==time.events),],merged.row)
}
if(nrow(xylab.event.mat)>1){
xylab.event.mat[which((1:nrow(xylab.event.mat) / 2 ) == round((1:nrow(xylab.event.mat) / 2 ))),"y"] <- mean(c(0,0,0,ymin))
}
xylab.event.mat[which((1:nrow(xylab.event.mat) / 2 ) != round((1:nrow(xylab.event.mat) / 2 ))),"y"] <- mean(c(0,ymin,ymin,ymin))
### plot text labels for event times. Even and odd rows are plotted separately
text(x=as.numeric(xylab.event.mat[,"x"]), y=as.numeric(xylab.event.mat[,"y"]), labels=paste0(" ",xylab.event.mat[,"lab"]), adj= c(0,0.5), cex=label.cex)
} else {
xylab.event.mat <- NULL
}
} else {
xylab.event.mat <- NULL
}
### Add time 0 label. Labels of time parameters that equal zero should be merged with the "0" label
if(any(events.mat.values[,"time"] == 0)){
events.mark.0 <- which(events.mat.values[,"time"] == 0)
xylab.event.0.mat <- cbind(x=events.mat.values[events.mark.0,"time"],y=rep(mean(c(0,ymin)),length(events.mark.0)),lab=events.mat[events.mark.0,"time"])
xylab.event.0.mat <- unique(rbind(c(x=0,y=mean(c(0,ymin)),lab="0"),xylab.event.0.mat))
while(any(table(xylab.event.0.mat[,"x"])>1)){
merged.lab <- paste0(xylab.event.0.mat[,"lab"],collapse=", ")
xylab.event.0.mat <- cbind(x=0,y=mean(c(0,ymin)),lab=merged.lab)
}
### If the merged time zero label is still just "0", replace it with " 0"
if(xylab.event.0.mat[,"lab"]=="0"){
xylab.event.0.mat[,"lab"] <- " 0"
}
### plot text label for time 0 and for events that occur at time 0
text(x=as.numeric(xylab.event.0.mat[,"x"]), y=as.numeric(xylab.event.0.mat[,"y"]), labels=xylab.event.0.mat[,"lab"], adj= c(-0.1,0.5), cex=label.cex)
} else {
text(x=0,y=mean(c(0,ymin)),labels=" 0",adj= c(-0.1,0.5),cex=label.cex)
xylab.event.0.mat <- NULL
}
### Add labels for initial population sizes
shapes.mat1 <- shapes.mat[shapes.mat[,"period"] == 1,]
y.poplabels <- sapply(X=1:nrow(shapes.mat1),FUN=function(x){mean(range(shapes.mat1[x,c("y_top_left","y_top_right","y_bottom_left","y_bottom_right")]))})
text(x=0,y=y.poplabels,labels=names(popsizes.present),adj= c(1.2,0.5),cex=label.cex)
### Add labels for popnames
if(!is.null(popnames)){
text(x=xmin,y=y.poplabels,labels=popnames.labels,adj= c(0,0.5),cex=label.cex)
}
### Add labels for growthrate change parameters, migration events, and newsize events
if(length(growthrate.pars)>0){
times.at.growthrate <- events.mat.values[grep(growthrate.pars,events.mat[,"growthrate"],fixed=T),"time"]
} else {
times.at.growthrate <- NULL
}
if(length(migrants.pars)>0){
times.at.migrants <- events.mat.values[grep(migrants.pars,events.mat[,"migrants"],fixed=T),"time"]
} else {
times.at.migrants <- NULL
}
if(length(newsize.pars)>0){
times.at.newsize <- events.mat.values[grep(newsize.pars,events.mat[,"newsize"],fixed=T),"time"]
} else {
times.at.newsize <- NULL
}
time.at.event.mat <- cbind(time=c(times.at.growthrate,times.at.migrants,times.at.newsize),parameter=c(growthrate.pars,migrants.pars,newsize.pars))
xylab.event.mat.all <- rbind(xylab.event.0.mat, xylab.event.mat)
if(!is.null(time.at.event.mat) & !is.null(xylab.event.mat.all)){
rownames(xylab.event.mat.all) <- NULL
for(i in 1:nrow(time.at.event.mat)){
event.i <- grep(time.at.event.mat[i,"time"],xylab.event.mat.all[,"x"])
timelabel.i.height <- strheight(xylab.event.mat.all[event.i,"lab"])
event.i.y <- as.numeric(xylab.event.mat.all[event.i,"y"])-timelabel.i.height
event.i.x <- as.numeric(xylab.event.mat.all[event.i,"x"])
label.i <- time.at.event.mat[i,"parameter"]
text(x=event.i.x, y=event.i.y, labels=paste0(" ",label.i), adj= c(0,1), cex=label.cex)
xylab.event.mat.all <- rbind(xylab.event.mat.all,c(event.i.x,event.i.y,label.i))
}
}
### Add x axis label
mtext(text="generations ago (increasing -->) ",side=1)
### Add title
box(which="figure")
mtext(text=model.title,outer=T,line=0.3,adj=c(mean(c(0,1-(priors.panel.width)))))
###### Experimenting here #####
if(show.priors & priors.panel.width > 0){
## Determine max and min values with nonzero probability for complex parameters that should be plotted
if(!is.null(complex.dist.df)){
if(any(complex.dist.df[,"DistClass"] %in% c("Unif/Constant","Unif*Constant","Unif+Constant","Unif-Constant"))){
#complex.ranges.df.temp <- complex.dist.df[complex.dist.df[,"DistClass"] %in% c("Unif/Constant","Unif*Constant","Unif+Constant","Unif-Constant"),]
complex.ranges.df <- complex.dist.df[complex.dist.df[,"DistClass"] %in% c("Unif/Constant","Unif*Constant","Unif+Constant","Unif-Constant"),]
colnames(complex.ranges.df) <- c("Parameter","Min","Max","DistClass")
mode(complex.ranges.df[,"Min"]) <- "numeric"
mode(complex.ranges.df[,"Max"]) <- "numeric"
} else {
complex.ranges.df <- NULL
}
if(any(complex.dist.df[,"DistClass"] %in% "Unif*Unif")){
complex.ranges.df.temp <- complex.dist.df[complex.dist.df[,"DistClass"] %in% "Unif*Unif",]
colnames(complex.ranges.df.temp) <- c("Parameter","Min","Max","DistClass")
mode(complex.ranges.df.temp[,"Min"]) <- "numeric"
mode(complex.ranges.df.temp[,"Max"]) <- "numeric"
complex.pars.temp <- unique(complex.ranges.df.temp[,"Parameter"])
for(i in 1:length(complex.pars.temp)){
param.temp <- complex.pars.temp[i]
rows.temp <- which(complex.ranges.df.temp[,"Parameter"]==param.temp)
min.temp <- prod(complex.ranges.df.temp[rows.temp,"Min"])
max.temp <- prod(complex.ranges.df.temp[rows.temp,"Max"])
#complex.ranges.df <- rbind(complex.ranges.df, c(param.temp,min.temp,max.temp,"Unif*Unif"))
complex.ranges.df <- rbind(complex.ranges.df, data.frame('Parameter'=param.temp,'Min'=min.temp,'Max'=max.temp,'DistClass'="Unif*Unif"))
}
#colnames(complex.ranges.df) <- c("Parameter","Min","Max","DistClass")
#mode(complex.ranges.df[,"Min"]) <- "numeric"
#mode(complex.ranges.df[,"Max"]) <- "numeric"
}
if(any(complex.dist.df[,"DistClass"] %in% "Unif+Unif")){
complex.ranges.df.temp <- complex.dist.df[complex.dist.df[,"DistClass"] %in% "Unif+Unif",]
colnames(complex.ranges.df.temp) <- c("Parameter","Min","Max","DistClass")
mode(complex.ranges.df.temp[,"Min"]) <- "numeric"
mode(complex.ranges.df.temp[,"Max"]) <- "numeric"
complex.pars.temp <- unique(complex.ranges.df.temp[,"Parameter"])
for(i in 1:length(complex.pars.temp)){
param.temp <- complex.pars.temp[i]
rows.temp <- which(complex.ranges.df.temp[,"Parameter"]==param.temp)
min.temp <- sum(complex.ranges.df.temp[rows.temp,"Min"])
max.temp <- sum(complex.ranges.df.temp[rows.temp,"Max"])
#complex.ranges.df <- rbind(complex.ranges.df,c(param.temp,min.temp,max.temp,"Unif+Unif"))
complex.ranges.df <- rbind(complex.ranges.df, data.frame('Parameter'=param.temp,'Min'=min.temp,'Max'=max.temp,'DistClass'="Unif+Unif"))
}
#colnames(complex.ranges.df) <- c("Parameter","Min","Max","DistClass")
#mode(complex.ranges.df[,"Min"]) <- "numeric"
#mode(complex.ranges.df[,"Max"]) <- "numeric"
}
} else {
complex.ranges.df <- NULL
}
### Data frame with range, distribution type, and parameter type for both simple and complex parameters to be graphed
if(!is.null(parameters.mat)){
simple.ranges.df <- as.data.frame(parameters.mat[,c("ParameterName","Min","Max","Distribution")])
colnames(simple.ranges.df) <- c("Parameter","Min","Max","DistClass")
mode(simple.ranges.df[,"Min"]) <- "numeric"
mode(simple.ranges.df[,"Max"]) <- "numeric"
if(!is.null(complex.ranges.df)){
ranges.df <- rbind(simple.ranges.df,complex.ranges.df)
} else {
ranges.df <- simple.ranges.df
}
ranges.df[,"ParameterType"] <- ""
if(any(ranges.df[,"Parameter"] %in% time.pars)){
ranges.df[ranges.df[,"Parameter"] %in% time.pars,"ParameterType"] <- "time"
min.time <- min(ranges.df$Min[ranges.df$ParameterType %in% "time"])
max.time <- min(ranges.df$Max[ranges.df$ParameterType %in% "time"])
}
if(any(ranges.df[,"Parameter"] %in% popsizes.pars)){
ranges.df[ranges.df[,"Parameter"] %in% popsizes.pars,"ParameterType"] <- "popsizes"
min.popsizes <- min(ranges.df$Min[ranges.df$ParameterType %in% "popsizes"])
max.popsizes <- min(ranges.df$Max[ranges.df$ParameterType %in% "popsizes"])
}
if(any(ranges.df[,"Parameter"] %in% migrationrate.pars)){
ranges.df[ranges.df[,"Parameter"] %in% migrationrate.pars,"ParameterType"] <- "migrationrate"
min.migrationrate <- min(ranges.df$Min[ranges.df$ParameterType %in% "migrationrate"])
max.migrationrate <- min(ranges.df$Max[ranges.df$ParameterType %in% "migrationrate"])
}
if(any(ranges.df[,"Parameter"] %in% migrants.pars)){
ranges.df[ranges.df[,"Parameter"] %in% migrants.pars,"ParameterType"] <- "migrants"
min.migrants <- min(ranges.df$Min[ranges.df$ParameterType %in% "migrants"])
max.migrants <- min(ranges.df$Max[ranges.df$ParameterType %in% "migrants"])
}
if(any(ranges.df[,"Parameter"] %in% newsize.pars)){
ranges.df[ranges.df[,"Parameter"] %in% newsize.pars,"ParameterType"] <- "newsize"
min.newsize <- min(ranges.df$Min[ranges.df$ParameterType %in% "newsize"])
max.newsize <- min(ranges.df$Max[ranges.df$ParameterType %in% "newsize"])
}
if(any(ranges.df[,"Parameter"] %in% growthrate.pars)){
ranges.df[ranges.df[,"Parameter"] %in% growthrate.pars,"ParameterType"] <- "growthrate"
min.growthrate <- min(ranges.df$Min[ranges.df$ParameterType %in% "growthrate"])
max.growthrate <- min(ranges.df$Max[ranges.df$ParameterType %in% "growthrate"])
}
if(any(ranges.df[,"Parameter"] %in% other.pars)){
ranges.df[ranges.df[,"Parameter"] %in% other.pars,"ParameterType"] <- "other"
min.other <- min(ranges.df$Min[ranges.df$ParameterType %in% "other"])
max.other <- min(ranges.df$Max[ranges.df$ParameterType %in% "other"])
}
} else {
ranges.df <- NULL
}
#######################################
####### Probability Distributions #####
#######################################
###### Better way to get pdf vals
if(!is.null(ranges.df)){
dist.df <- as.data.frame(matrix(data=0,ncol=nrow(ranges.df),nrow=500))
colnames(dist.df) <- ranges.df[,"Parameter"]
rownames(dist.df) <- NULL
#mode(dist.df) <- "numeric"
for(i in 1:nrow(ranges.df)){
type.temp <- ranges.df[i,"ParameterType"]
## Min and Max among parameters of the same type
min.temp <- min(ranges.df[ranges.df[,"ParameterType"] %in% type.temp,"Min"])
max.temp <- max(ranges.df[ranges.df[,"ParameterType"] %in% type.temp,"Max"])
x0 <- min.temp-abs((min.temp*0.1))
x1 <- max.temp+abs((max.temp*0.1))
z.samples <- seq(from=x0,to=x1,length.out=500)
if(ranges.df[i,"DistClass"] %in% c("unif","Unif*Constant","Unif/Constant","Unif+Constant","Unif-Constant")){
#dist.df.col.i <- dunif(z.samples,min=ranges.df[i,"Min"],max=ranges.df[i,"Max"])
#dist.df[,i] <- dist.df.col.i
dist.df[,i] <- dunif(z.samples,min=ranges.df[i,"Min"],max=ranges.df[i,"Max"])
}
if(ranges.df[i,"DistClass"] %in% c("logunif","Logunif*Constant","Logunif/Constant","Logunif+Constant","Logunif-Constant")){
#dist.df.col.i <- dunif(z.samples,min=ranges.df[i,"Min"],max=ranges.df[i,"Max"])
#dist.df[,i] <- dist.df.col.i
dist.df[,i] <- dlogunif(z.samples,min=ranges.df[i,"Min"],max=ranges.df[i,"Max"])
}
if(ranges.df[i,"DistClass"] == "Unif+Unif"){
a.vals <- complex.dist.df[complex.dist.df[,"Parameter"] %in% ranges.df[i,"Parameter"],"Min"]
b.vals <- complex.dist.df[complex.dist.df[,"Parameter"] %in% ranges.df[i,"Parameter"],"Max"]
dist.df[,i] <- sapply(z.samples,FUN=function(x){sumunif(z=x,a=a.vals,b=b.vals)})
}
if(ranges.df[i,"DistClass"] == "Unif*Unif"){
a.vals <- complex.dist.df[complex.dist.df[,"Parameter"] %in% ranges.df[i,"Parameter"],"Min"]
b.vals <- complex.dist.df[complex.dist.df[,"Parameter"] %in% ranges.df[i,"Parameter"],"Max"]
dist.df[,i] <- sapply(z.samples,FUN=function(x){produnif(z=x,a=a.vals,b=b.vals)})
}
if(ranges.df[i,"DistClass"] %in% c("Unif-Unif","Unif/Unif","Logunif*Unif","Logunif/Unif","Logunif+Unif","Logunif-Unif","Logunif*Logunif","Logunif/Logunif","Logunif+Logunif","Logunif-Logunif")){
next
}
}
}
############
### Graphing the Probability Density Curves
############
linecols <- c("red","blue","darkgreen","darkorange","purple","black","gray","yellow","red")
line.types <- c(1,1,3,4,1,1,1,1,3,1,1,1)
line.widths <- c(2,1,1,1,0.75,0.75,0.75,0.75,0.75,0.75)
## Extra space to add above density plots for including variable lables. Value of 1 leaves no space.
varnames.spacer <- 2
panel.title <- "Parameter Probability Densities"
#pars.to.plot.list <- list(popsizes.pars.temp,time.pars.temp,migrationrate.pars.temp,migrants.pars.temp,newsize.pars.temp,growthrate.pars.temp)
pars.to.plot.list <- c("popsizes","time","migrationrate","migrants","newsize","growthrate")
xaxis.label <- c("effective pop. sizes (t = 0)","generations ago","migration rate","migrants (fraction of source population)","population size change (new/old)","population growth rate")
for(z in 1:length(pars.to.plot.list)){
if(z==1){
par(mar=c(3,2,0,1))
} else {
par(mar=c(3,2,0,1))
}
# pars.temp <- pars.to.plot.list[[z]]
if(any(ranges.df[,"ParameterType"] %in% pars.to.plot.list[[z]])){
pars.temp <- ranges.df[(ranges.df[,"ParameterType"] %in% pars.to.plot.list[[z]]),"Parameter"]
pars.min <- min(ranges.df[(ranges.df[,"ParameterType"] %in% pars.to.plot.list[[z]]),"Min"])
pars.max <- max(ranges.df[(ranges.df[,"ParameterType"] %in% pars.to.plot.list[[z]]),"Max"])
x0 <- pars.min-abs((pars.min*0.1))
x1 <- pars.max+abs((pars.max*0.1))
dist.df.temp <- dist.df[,pars.temp,drop=F]
pd.Max <- max(dist.df.temp)
if(pd.Max==0){
next
}
ycurves_top <- pd.Max
ylabelarea_top <- ycurves_top*varnames.spacer
z.samples <- seq(from=x0,to=x1,length.out=500)
plot(x=c(x0,x1),y=c(0,ylabelarea_top),main="",xlab="",ylab="",axes=F,type="l",lty=1, col="white")
box(which="figure")
for(i in 1:ncol(dist.df.temp)){
if(max(dist.df.temp[,i])==0){
next
}
lines(z.samples, dist.df.temp[,i], col=linecols[i], lty=line.types[i],lwd=line.widths[i])
}
if(z==1){
mtext(side=3,text=panel.title,font=1,cex=0.75,line=0.3)
}
xmin.label <- formatC(x0,format="g",digits=2)
xmax.label <- formatC(x1,format="g",digits=2)
axis(side=1,at=c(x0,x1),labels=c(xmin.label,xmax.label),cex.axis=0.8,hadj=(0.5),padj=(-1))
mtext(text=xaxis.label[z],side=1,line=1.2,cex=0.6)
plot_area_width <- par("usr")[2]-par("usr")[1]
plot_area_height <- par("usr")[4]-par("usr")[3]
plot_area_ymax <- par("usr")[4]
plot_area_ymin <- par("usr")[3]
plot_area_xmax <- par("usr")[2]
plot_area_xmin <- par("usr")[1]
cex.temp <- 0.8
label_heights <- max(strheight(pars.temp,cex=cex.temp))
label_widths <- strwidth(pars.temp,cex=cex.temp)
cumulative_label_widths <- cumsum(label_widths)
### Place nth label to the left of the nth cumalative width
ybottom_varlabels <- ycurves_top*1.2
x_varlabels <- (cumulative_label_widths*1.2)+x0
x_varlabels_right.edge <- (max(cumulative_label_widths)*1.25)+x1
# line color and type legend
y_linelabels <- (ybottom_varlabels + label_heights)*1.1
x0_linelabels <- (x_varlabels-label_widths)
x1_linelabels <- x_varlabels
counter=0
segments(x0=x0_linelabels,x1=x1_linelabels,y0=y_linelabels,col=linecols[1:length(pars.temp)],lty=line.types[1:length(pars.temp)],lwd=line.widths[i])
text(x=x_varlabels,y=ybottom_varlabels,labels=pars.temp,col=linecols[1:length(pars.temp)],cex=cex.temp,adj=c(1,0))
}
}
### Rather than plotting the other parameters, just show them in a text table
other.and.complex.pars <- unique(c(other.pars,complex.pars))
if(length(rules.mat) > 0){
numlines <- length(other.and.complex.pars) + nrow(rules.mat) + 2
} else {
numlines <- length(other.and.complex.pars) + 1
}
if(length(other.and.complex.pars) > 0){
par(mar=c(0,0,0,0))
plot(x=0:numlines,y=0:numlines,col="white",axes=F,xlab="",ylab="",main="",cex.main=1)
box(which="figure")
# names of parameters in column 1
if(any(parameters.mat[,"ParameterName"] %in% other.pars)){
others.simple.mat <- parameters.mat[parameters.mat[,"ParameterName"] %in% other.pars,c("ParameterName","Distribution","Min","Max"),drop=F]
others.simple.string <- list(); length(others.simple.string) <- nrow(others.simple.mat)
for(i in 1:nrow(others.simple.mat)){
others.simple.string[[i]] <- paste0(others.simple.mat[i,1]," ~ ",others.simple.mat[i,2],"(",others.simple.mat[i,3],",",others.simple.mat[i,4],")")
}
others.simple.string <- unlist(others.simple.string)
} else {
others.simple.string <- NULL
}
if(length(complex.pars)>0){
complex.text.string.mat <- cbind(complex.mat[,c("ParameterName","function"),drop=F],complex.text.string)
complex.string <- unlist(apply(X=complex.text.string.mat,MARGIN=1,FUN=paste,collapse=" "))
} else {
complex.string <- NULL
}
if(length(rules.mat)>0){
rules.string <- c("Rules",unlist(apply(X=rules.mat,MARGIN=1,FUN=paste,collapse=" ")))
rules_height <- strheight(rules.string)
rules_width <- strwidth(rules.string)
} else {
rules.string <- NULL
}
others.complex.string <- c("Complex and other parameters",others.simple.string,complex.string)
others.complex_height <- strheight(others.complex.string)
others.complex_widths <- strwidth(others.complex.string)
cex.temp <- 1
y_others.complex <- (par("usr")[4]-(cumsum(others.complex_height)*1.4)) + others.complex_height[1]
if(length(others.complex.string)>0){
text(x=rep(0,length(others.complex.string)),y=(y_others.complex-0.5),labels=others.complex.string,cex=cex.temp,pos=4,font=c(2,rep(1,length(others.complex.string)-1)))
}
if(length(rules.string)>0){
y_rules <- (min(y_others.complex)-(cumsum(rules_height)*1.4))-rules_height[1]
text(x=0,y=(y_rules-0.5),labels=rules.string,cex=cex.temp,pos=4,font=c(2,rep(1,length(rules.string)-1)))
}
}
}
### hold the plot as an object
result <- recordPlot()
### return graphical settings back to their original state
layout(1)
### output the plot
result
}
#' @examples
#' source("plot_model.R")
#' dev.new(width=10,height=6)
#' Model5.plot <- plot_model(tpl.path="example_5.tpl", est.path="example_5.est")
#' Model6.plot <- plot_model(tpl.path="example_6.tpl", est.path="example_6.est")
#' Model7.plot <- plot_model(tpl.path="example_7.tpl", est.path="example_7.est")
#' Model8.plot <- plot_model(tpl.path="example_8.tpl", est.path="example_8.est")
#' Model9.plot <- plot_model(tpl.path="example_9.tpl", est.path="example_9.est")
#' Model10.plot <- plot_model(tpl.path="example_10.tpl", est.path="example_10.est")
#' Model11.plot <- plot_model(tpl.path="example_11.tpl", est.path="example_11.est")
#' Model12.plot <- plot_model(tpl.path="example_12.tpl", est.path="example_12.est")
#' Model13.plot <- plot_model(tpl.path="example_13.tpl", est.path="example_13.est")
#' Model14.plot <- plot_model(tpl.path="example_14.tpl", est.path="example_14.est")
#' Model15.plot <- plot_model(tpl.path="example_15.tpl", est.path="example_15.est")
#' Model16.plot <- plot_model(tpl.path="example_16.tpl", est.path="example_16.est")
#' Model17.plot <- plot_model(tpl.path="example_17.tpl", est.path="example_17.est")
#' Model18.plot <- plot_model(tpl.path="example_18.tpl", est.path="example_18.est")
#'
#' list.of.models <- list(Model5.plot,Model6.plot,Model7.plot,Model8.plot,Model9.plot,Model10.plot,Model11.plot,Model12.plot,Model13.plot,Model14.plot,Model15.plot,Model16.plot,Model17.plot,Model18.plot)
#' pdf("cartoon_models.pdf",width=10,height=6)
#' list.of.models
#' dev.off()
#' @title dlunif function
#'
#' loguniform probability density function
#'
#' @param x sample quantile
#' @param min minimum of sampling distribution
#' @param max maximum of sampling distribution
#' @param base base of exponent. Default exp(1)
#' @return probability density at x
#' @export dlogunif
dlogunif <- function (x, min, max, base = exp(1)) {
return((1/(log(max, base) - log(min, base))) * (1/x))
}
#' @title Sample from loguniform distribution
#'
#'
#' @param n Number of samples to draw
#' @param min minimum of sampling distribution
#' @param max maximum of sampling distribution
#' @param base base of exponential parameter. Default exp(1).
rlogunif <- function (n, min, max, base = exp(1)){
return(base^(runif(n, log(min, base),log(max, base))))
}
#' @title Probability density function for the quotient of two uniform distributions that both have minimum value at zero.
#'
#' Probability density for z in Z ~ X1/X2, where X1 ~ U(0,b1) and X2 ~ U(0,b2), b1 > 0, b2 > 0.
#'
#' @param z quantile
#' @param b Numerical vector with b1 and b2 for X1~Unif(0,b1) and X2~Unif(0,b2).
#' @return Probability density of z in Z
#' @export dunif.quotient.a0
dunif.quotient.a0 <- function(z,b){
d.standard <- (0.5*((((sign(z-1)+1)/2)/(z^2))+((sign(1-z)+1)/2)))
result <- d.standard*(b[1]/b[2])
result
}
#' @title Probability density function for the quotient of two identically independent uniform distributions.
#'
#' Probability density for z in Z ~ X1/X2, where X1 & X2 ~ U(a,b), a & b in Reals, and a < b.
#' The function dunif.quotient.a0 does not require X1 and X2 to be identical, but has the requirement that the lower limit of both distributions equal 0.
#'
#' @param z quantile
#' @param a Number indicating the lower limit of X1 and X2
#' @param b Number indicating the lower upper of X1 and X2
#' @return Probability density of z in Z
#' @export dunif.quotient.iid
dunif.quotient.iid <- function(z,a,b){
if(a > b){
stop("'a' must be < 'b'")
}
if(0 < a & a < b){
if(z > (b/a) | z < (a/b)){
result <- 0
} else {
if(z <= 1){
result <- (((z^2)*(b^2))-(a^2)) / (2*(z^2)*((b-a)^2))
}
if(z > 1){
result <- (((b^2)-((a^2)*(z^2)))/(2 * (z^2) * ((b-a)^2)))
}
}
}
if(a < 0 & 0 < b & abs(b) > abs(a)){
if(z <= (b/a)){
result <- ((b^2) + (a^2)) / (2 * (z^2) * ((b-a)^2))
}
if((b/a) < z & z <= (a/b)){
result <- ((a^2)*((z^2) + 1)) / (2 * (z^2) * ((b-a)^2))
}
if((a/b) < z & z <= 1){
result <- ((a^2) + (b^2)) / (2 * ((b-a)^2))
}
if(1 < z){
result <- ((a^2) + (b^2)) / (2 * (z^2) * ((b-a)^2))
}
}
if(a < 0 & 0 < b & abs(b) < abs(a)){
if(z <= (a/b)){
result <- ((a^2) + (b^2)) / (2 * (z^2) * ((b-a)^2))
}
if((a/b) < z & z <= (b/a)){
((b^2) + (1 + (z^2))) / (2 * (z^2) * ((b-a)^2))
}
if((b/a) < z & z <= 1){
result <- ((a^2) + (b^2)) / (2 * ((b-a)^2))
}
if(1 < z){
result <- ((a^2) + (b^2)) / (2 * (z^2) * ((b-a)^2))
}
}
if(a < b & b < 0){
if(z > (a/b) | z < (b/a)){
result <- 0
} else {
if(1 < z){
result <- abs((((z^2)*(b^2))-(a^2)) / (2*(z^2)*((b-a)^2)))
}
if(z <= 1){
result <- abs(((b^2)-((a^2)*(z^2))) / (2 * (z^2) * ((b-a)^2)))
}
}
}
result
}
#' @title PDF or CDF for the sum of n independent uniformly continuous random variables.
#'
#' Evaluates the probability density function or cumulative distribution of Z=z for Z(z) = Xi+...+Xn, for i = 1...n, Xi ~ U(ai,bi), where 0 < ai < bi
#' The PDF and CDF is calculated using Equations 2.4 and 2.7, respectively, from Ishihara, Tatsuo. 2002. The Distribution of the Sum and the Product of Independent Uniform Random Variables Distributed at Different Intervals. Transactions - Japan Society for Industrial and Applied Mathematics, 12(3), 197-208.
#'
#' @param z Number or numerical vector of quantiles
#' @param a Vector holding the mins of each X
#' @param b Vector holding the max of each X
#' @param cumulative Whether or not return the cumulative probability instead of the probability. Default is FALSE.
#' @return Number or numerical vector with either the probability density or the cumulative density for each input value z.
#' @export sumunif
sumunif <- function(z,a,b,cumulative=F){
if(!cumulative & (z <= sum(a) | z > sum(b))){
return(0)
}
if(cumulative & z <= sum(a)){
return(0)
}
if(cumulative & z >= sum(b)){
return(1)
}
n = length(a)
powerSet.list <- pset(1:n)
solution.mat <- matrix(data=NA,nrow=length(powerSet.list),ncol=9)
colnames(solution.mat) <- c("No.","alpha","(-1)Nalpha","Sn,alpha","z","(z-Sn,alpha)","((z-Sn,alpha)+)","((z-Sn,alpha)^(n-1))","((-1)^Nalpha)*((z-Sn,alpha)^(n-1))")
solution.mat[,"No."] <- 1:nrow(solution.mat)
solution.mat[,"alpha"] <- sapply(X = powerSet.list, FUN=paste, collapse = " ")
solution.mat[,"(-1)Nalpha"] <- (-1)^lengths(powerSet.list)
solution.mat[,"z"] <- rep(z,nrow(solution.mat))
C <- b-a
CN <- max(cumprod(C))
if(cumulative){
part1 <- 1/((factorial(n))*CN)
} else {
part1 <- 1/((factorial(n-1))*CN)
}
for(i in 1:nrow(solution.mat)){
alpha.j <- rep(0,n)
if(i>1){
alpha.j[1:n %in% powerSet.list[[i]]] <- 1
}
Sn.i <- sum(a + (alpha.j*C))
solution.mat[i,"Sn,alpha"] <- Sn.i
}
solution.df <- as.data.frame(solution.mat)
mode(solution.df[,"(-1)Nalpha"]) <- "numeric"
mode(solution.df[,"Sn,alpha"]) <- "numeric"
mode(solution.df[,"z"]) <- "numeric"
mode(solution.df[,"(z-Sn,alpha)"]) <- "numeric"
mode(solution.df[,"((z-Sn,alpha)+)"]) <- "numeric"
mode(solution.df[,"((z-Sn,alpha)^(n-1))"]) <- "numeric"
mode(solution.df[,"((-1)^Nalpha)*((z-Sn,alpha)^(n-1))"]) <- "numeric"
solution.df[,"(z-Sn,alpha)"] <- as.numeric(solution.mat[,"z"])-as.numeric(solution.mat[,"Sn,alpha"])
solution.df[,"((z-Sn,alpha)+)"] <- (solution.df[,"(z-Sn,alpha)"] + abs(solution.df[,"(z-Sn,alpha)"]))/2
if(cumulative){
solution.df[,"((z-Sn,alpha)^(n-1))"] <- solution.df[,"((z-Sn,alpha)+)"]^(n)
} else {
solution.df[,"((z-Sn,alpha)^(n-1))"] <- solution.df[,"((z-Sn,alpha)+)"]^(n-1)
}
solution.df[,"((-1)^Nalpha)*((z-Sn,alpha)^(n-1))"] <- as.numeric(solution.mat[,"(-1)Nalpha"])*solution.df[,"((z-Sn,alpha)^(n-1))"]
part2 <- sum(solution.df[,"((-1)^Nalpha)*((z-Sn,alpha)^(n-1))"])
result <- part1*part2
result
}
#' @title PDF or CDF for the product of n independent uniformly continuous random variables.
#'
#' Evaluates the probability density function or cumulative distribution of Z=z for Z(z) = Xi*...*Xn, for i = 1...n, Xi ~ U(ai,bi), where 0 < ai < bi
#' The PDF and CDF is calculated using Equations 3.4 and 3.9, respectively, from Ishihara, Tatsuo. 2002. The Distribution of the Sum and the Product of Independent Uniform Random Variables Distributed at Different Intervals. Transactions - Japan Society for Industrial and Applied Mathematics, 12(3), 197-208.
#'
#' @param z Number or numerical vector of quantiles
#' @param a Vector holding the mins of each X
#' @param b Vector holding the max of each X
#' @param cumulative Whether or not return the cumulative probability instead of the probability. Default is FALSE. This parameter does not yet calculate CDF correctly.
#' @return Number or numerical vector with either the probability density or the cumulative density for each input value z.
#' @export produnif
produnif <- function(z,a,b,cumulative=F){
if(!cumulative & (z <= prod(a) | z > prod(b))){
return(0)
}
if(cumulative & z <= prod(a)){
return(0)
}
if(cumulative & z >= prod(b)){
return(1)
}
C <- b-a
CN <- prod(C)
n <- length(a)
powerSet.list <- pset(1:n)
solution.mat <- matrix(data=NA,nrow=length(powerSet.list),ncol=5)
colnames(solution.mat) <- c("z","No.","alpha","(-1)Nalpha","Pn,alpha")
solution.df <- as.data.frame(solution.mat)
mode(solution.df[,"z"]) <- "numeric"
mode(solution.df[,"No."]) <- "factor"
mode(solution.df[,"(-1)Nalpha"]) <- "numeric"
mode(solution.df[,"Pn,alpha"]) <- "numeric"
solution.df[,"z"] <- rep(z,nrow(solution.df))
solution.df[,"No."] <- 1:nrow(solution.df)
solution.df[,"alpha"] <- sapply(X = powerSet.list, FUN=paste, collapse = " ")
solution.df[,"(-1)Nalpha"] <- (-1)^lengths(powerSet.list)
if(!cumulative){
part1 <- 1/((factorial(n-1))*CN)
for(i in 1:nrow(solution.df)){
alpha.i <- rep(0,n)
if(i>1){
alpha.i[1:n %in% powerSet.list[[i]]] <- 1
}
Pn.i <- prod(a + (alpha.i*C))
solution.df[i,"Pn,alpha"] <- Pn.i
}
solution.df[,"ln(x/Pn,alpha)+"] <- (log(z/solution.df[,"Pn,alpha"]) + abs(log(z/solution.df[,"Pn,alpha"])))/2
solution.df[,"(ln(x/Pn,alpha)^(d))"] <- solution.df[,"ln(x/Pn,alpha)+"]^(n-1)
result <- (1/((factorial(n-1))*CN))*sum(solution.df[,"(-1)Nalpha"]*solution.df[,"(ln(x/Pn,alpha)^(d))"])
} else {
for(i in 1:nrow(solution.df)){
alpha.i <- rep(0,n)
if(i>1){
alpha.i[1:n %in% powerSet.list[[i]]] <- 1
}
Pn.i <- prod(a + (alpha.i*C))
solution.df[i,"Pn,alpha"] <- Pn.i
part2.i <- solution.df[i,"(-1)Nalpha"]
part3.j <- list(); length(part3.j) <- length(1:(n-1))
for(j in 1:(n-1)){
part3.j1 <- ((-1)^(j+1))/(factorial(n-j))
part3.j2 <- ((log((z/Pn.i)) + abs(log((z/Pn.i))))/2)^(n-j)
#part3.j[[j]] <- (part3.j1*part3.j2)+(part3.j3*part3.j4)
#part3.j[[j]] <- (part3.j1*part3.j2)+((-1)^(n-1))*(((z-Pn.i)+abs(z-Pn.i))/2)
part3.j[[j]] <- (part3.j1*part3.j2)
}
part3.i <- z*sum(unlist(part3.j))
solution.df[i,"part3.i"] <- part3.i
part4.i <- ((-1)^(n-1))*(((z-Pn.i)+abs(z-Pn.i))/2)
solution.df[i,"part4.i"] <- part4.i
}
solution.df[,"parts.i"] <- solution.df[,"(-1)Nalpha"]*(solution.df[,"part3.i"]+solution.df[,"part4.i"])
result <- (sum(solution.df[,"parts.i"])/CN)
}
result
}
#' @title plot PDF for sum or product of two uniform distributions
#'
#' Plots a line chart for the probability density distribution (PDF) or cumulative distribution (CDF) for the sum or product of uniform distributions on positive Reals.
#' Can handle an arbitrary number of input distributions, which must be independent, and can have different ranges.
#'
#' @param a A numerical vector holding the minimum values of input uniform distributions.
#' @param b A numerical vector holding the maximum values of input uniform distributions.
#' @param FUN The function to use to determine PDF or CDF; one of 'simple', 'sum', or 'product'. If a and b are length 1 vectors, 'simple' is used.
#' @param cumulative If TRUE, the CDF will be plotted instead of the PDF
#' @param buffer fraction of extra, zero-probability region along x axis to plot
#' @param show.xy.pd When FUN = 'sum' or 'product', if 'show.xy.pd' = TRUE the pdf or cdf of component distributions will be plotted along with their sum or product.
#' @param line.lty type of curve
#' @param line.col color of curve
#' @param add Whether or not to the graph should be added to the existing plot. Default is FALSE.
#' @param xlim min and max limits for the x-axis plotting area
#' @param ylim min and max limits for the y-axis plotting area
#' @return A line graph showing the PDF of the sum or product of the two input uniform distributions
#' @export plot.funUnif
plot.funUnif <- function(a,b,FUN="sum",cumulative=F,buffer=0.1,nZ=500,show.xy.pd=F,line.col=c("red"),line.lty=c(1),add=F,xlim=NULL,ylim=NULL){
if(FUN=="simple" | (length(a) == 1 & length(b) == 1)){
#a=4e+05;b=9e+05;buffer=0.1;
x0=(a-abs((a*buffer)))
x1=(b+abs((b*buffer)))
if(is.null(xlim)){
xlim=c(x0,x1)
}
z.samples <- seq(from=x0,to=x1,length.out=nZ)
z.pd <- sapply(X=z.samples,FUN=function(x){dunif(x=x,min=a,max=b)})
if(is.null(ylim)){
ylim=c(0,(max(z.pd)*1.05))
}
if(!add){
plot(z.samples,z.pd,col="white",xlab="",ylab="",main="",axes=F,xlim=xlim)
axis(side=1,at=xlim,labels=T)
}
lines(z.samples,z.pd,col=line.col[1],lty=line.lty[1])
} else {
if(FUN=="sum"){
zmin = sum(a)
zmax = sum(b)
}
if(FUN=="product"){
zmin = prod(a)
zmax = prod(b)
}
if(!FUN %in% c("sum","product")){
stop("'FUN' must be 'sum' or 'product'")
}
zmin.buffered <- zmin - abs((zmax-zmin)*buffer)
zmax.buffered <- zmax + abs((zmax-zmin)*buffer)
a2 <- a - abs((b-a)*buffer)
b2 <- b + abs((b-a)*buffer)
zrange <- c(zmin.buffered,zmax.buffered)
xrange <- c(a2[1],b2[1])
yrange <- c(a2[2],b2[2])
z.samples <- seq(from=zrange[1],to=zrange[2],length.out=nZ)
if(FUN=="sum"){
z.pd <- sapply(z.samples,FUN=sumunif,a=a,b=b,cumulative=cumulative)
}
if(FUN=="product"){
z.pd <- sapply(z.samples,FUN=produnif,a=a,b=b,cumulative=cumulative)
}
if(is.null(ylim)){
ylim <- c(0,(max(z.pd)*1.05))
}
if(show.xy.pd){
if(length(a)>2){
stop("'Show.xy.pdf' must be FALSE when using more than two input distributions")
}
x.samples <- seq(from=a2[1],to=b2[1],length.out=nZ)
y.samples <- seq(from=a2[2],to=b2[2],length.out=nZ)
x.pd <- dunif(x.samples,min=a[1],max=b[1])
y.pd <- dunif(y.samples,min=a[2],max=b[2])
if(is.null(xlim)){
xlim <- c(min(a2,zrange[1]), max(b2,zrange[2]))
}
if(!add){
plot(c(x.samples,y.samples,z.samples),c(x.pd,y.pd,z.pd),col="white",xlab="",ylab="",main="",axes=F,xlim=xlim,ylim=ylim)
axis(side=1,at= xlim, labels=T)
}
if(length(line.col) < 3){
line.col=c("red","blue","green")
}
if(length(line.lty) < 3){
line.lty=c(1,2,2)
}
lines(z.samples,z.pd,col=line.col[1],lty=line.lty[1])
lines(x.samples,x.pd,col=line.col[2],lty=line.lty[2])
lines(y.samples,y.pd,col=line.col[3],lty=line.lty[3])
} else {
if(!add){
if(is.null(xlim)){
xlim <- zrange
}
plot(z.samples,z.pd,col="white",xlab="",ylab="",main="",axes=F,xlim=xlim,ylim=ylim)
axis(side=1,at=xlim,labels=T)
}
lines(z.samples,z.pd,col=line.col[1],lty=line.lty[1])
}
}
}
#' @title PDF sum of normal distributions
#'
#' Probability distribution for the sum of n normal distributions
#'
#' @param x sample quantile
#' @param m numerical vector with means of the normal distributions
#' @param v numerical vector with variances of the normal distributions
#' @return probability distribution at x for the sum of the normal distributions with means m and variances v
#' @export sumnorm.PDF
sumnorm.PDF <- function(x,m,v){
dist <- distr::Norm(sum(m),sqrt(sum(v)))
return(distr::d(dist)(x))
}
##### Function to return the powerset for a numeric or character vector of set elements.
##'
#pset <- function (x) {
# m <- length(x)
# out <- list(x[c()])
# for (i in seq_along(x)) {
# out <- c(out, lapply(X=out[lengths(out) < m], FUN=function(y){c(y,x[i])}))
# }
# out
#}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.