require(data.table) 
library(geiger)
require(nlme)
require(phytools)
require(phangorn)
require(caper)
require(quantreg)

############################################################
############################################################
## Load in and prepare data
############################################################
############################################################
## Load brain size data from Fristoe et al. 2017.
## Also includes body size data from Myhrvold et al. 2015.
mydata <- as.data.frame(fread('https://static-content.springer.com/esm/art%3A10.1038%2Fs41559-017-0316-2/MediaObjects/41559_2017_316_MOESM3_ESM.csv'))
## Trim to relevant columns
mydata <- mydata[, 1:7]
names(mydata)[6:7] <- c('Body.Mass', 'Brain.Mass')
row.names(mydata) <- mydata$Species

##########
## Calculate brain size residuals from OLS regression
mydata$BrainResiNonPhy <- residuals(lm(log10(mydata$Brain.Mass) ~ log10(mydata$Body.Mass)))

##########
## Calculate brain size residuals from pgls regression
## Load bird phylogeny (1000 trees from Jetz et al. 2012)
temp <- tempfile()
download.file("https://data.vertlife.org/birdtree/Stage2/HackettStage2_3001_4000.zip",temp)
trees <- read.tree(unz(temp, "mnt/data/projects/birdphylo/Tree_sets/Stage2_full_data/CombinedTrees/BirdzillaHackett4.tre"))

## Initiate objects to hold stats from regressions across each tree
PhyBrainResi <- matrix(NA, nrow = nrow(mydata), ncol = 1000)
Lambdas <- NA
Slopes <- NA
Ints <- NA

## Loop through each of 1000 trees, run PGLS, store stats
## Warning... takes some time to run
for (i in 1:1000) {
  mytree.1 <- treedata(trees[[i]], mydata, warnings = F)$phy
  # generate variance covariance data
  myVCV <- corPagel(1,phy=mytree.1)
  
  myPGLS <- gls(log10(Brain.Mass) ~ log10(Body.Mass),
                correlation = myVCV, data = mydata)
  
  PhyBrainResi[, i] <- resid(myPGLS)
  Slopes[i] <- summary(myPGLS)$coefficients[2]
  Ints[i] <- summary(myPGLS)$coefficients[1]
  Lambdas[i] <- attr(myPGLS$apVar, "Pars")["corStruct"]
  print(paste0(i, ' treez completed!'))
}

## Report means across trees for PGLS statistics
print(paste0('mean slope = ', round(mean(Slopes), 3), ' ± ', round(sd(Slopes), 3)))
print(paste0('mean intercept = ', round(mean(Ints), 3), ' ± ', round(sd(Ints), 3)))
print(paste0('mean λ = ', round(mean(Lambdas), 3), ' ± ', round(sd(Lambdas), 3)))

## For each species, the median value of residuals ('PhyResMode') is attached
## to mydata and used as relative brain size in subsequent analyses
mydata$PhyResMode <- NA
for(i in 1:nrow(PhyBrainResi)) {
  mydata$PhyResMode[i] <- quantile(PhyBrainResi[i, ], .5, na.rm = T)
}

##########
## Check correlation between PGLS and OLS brain size residuals
## Supplementary figure 3
print(paste0('r = ', round(cor(mydata$BrainResiNonPhy, mydata$PhyResMode), 2)))
plot(mydata$BrainResiNonPhy,
     mydata$PhyResMode,
     pch = 20,
     ylab = 'PGLS residuals',
     xlab = 'OLS residuals')

## Clean up
rm(PhyBrainResi, i, Ints, Lambdas, myPGLS, mytree.1, myVCV, Slopes, temp, trees)

##########
## Load additional variables and ammend to mydata
##
## PC1min; PC1max; PC2min; PC2mac:
## These are the minimum and maximum raster values of environmental PCs 1 and 2 
## (see text) extracted from shapefiles of the breeding range for each species
## (shapefiles available from birdlife.org upon request). Environmental variables
## were computed using climate monthly resolution temperature and precipitation
## raster for the years 1901-2005 (obtained upon request from ecoclimate.org).
##
## Migration:
## assigned 1 if breeding and winter distributions differ (migrant),
## otherwise 0 (resident).
##
## Eggs:
## Annual reproductive output computed as number of clutches per year
## multiplied by number of eggs per clutch (from Myhrvold et al. 2015).
##
## PercVeg:
## percent of vegetative material (plant matter other than fruit, seeds, or nectar)
## in species diet (from Wilman et al. 2014)
##
## Pelagic:
## See text for the taxonomic groups that were classified as pelagic (value of 1)
AddVars <- fread('http://fristoeecology.com/wp-content/uploads/2019/01/BrainDistData-1.csv')
## ^ this data is also available in the Source data file published alongside this manuscript.

AddVars <- AddVars[, c(2, 11:18)]

mydata <- merge(mydata, AddVars, by = 'Species')
rm(AddVars)

############################################################
############################################################
## Start Analyses ##########################################
####################################
## !!!!! CHOICE !!!!!
## Choose whether brain size residuals are calculated 
## from PGLS (ResidMode <- 'PGLS')
## or 'OLS' regression (ResidMode <- 'OLS')
ResidMode <- 'OLS'

## Create ceiling and floor functions for decimals
floor_dec <- function(x, level=1) round(x - 5*10^(-level-1), level)
ceiling_dec <- function(x, level=1) round(x + 5*10^(-level-1), level)
## Define relative brain size based on decision for ResiMode
if (ResidMode == 'PGLS') {
  mydata$RelBrain <- mydata$PhyResMode
  ## Define range of relative brain size for plots
  range(mydata$RelBrain)
  BrRange <- c(floor_dec(min(mydata$RelBrain), 1), 
               ceiling_dec(max(mydata$RelBrain), 1))
} else {
  mydata$RelBrain <- mydata$BrainResiNonPhy
  range(mydata$RelBrain)
  BrRange <- c(floor_dec(min(mydata$RelBrain), 2), 
               ceiling_dec(max(mydata$RelBrain), 2))
}

############################################################
############################################################
## Global Distribution of brain size:
## Climate PC1: figure 1 (PGLS residuals); Supplementary table 2 (OLS residuals)
############################################################
############################################################

## Isolate resident species
ResSp <- mydata[which(mydata$Migration == 0), ]
## Remove pelagic species
ResSp <- ResSp[which(ResSp$Pelagic == 0), ]

## Generate 10000 randomizations for creating null distributions
Rand <- ResSp[, c('PC1min', 'PC1max', 'RelBrain')]
for (i in 1:10000) {
  Rand <- cbind(Rand, sample(ResSp$RelBrain, length(ResSp$RelBrain), replace = F))
}

## Define the number of environmental regions
Ebreaks <- 6
## Define the range of environmental PC1
range(c(mydata$PC1max, mydata$PC1min), na.rm = T)
Emin <- -1.83
Emax <- 1.89

## initiate objects to store stats:
## First define brain size bins to name rows
BrBins <- seq(BrRange[1], BrRange[2], (BrRange[2] - BrRange[1])/15)
BrNames <- NA
for (i in 1:15) {
  BrNames[i] <- paste0('RelBrain: ',
                       round(BrBins[i], 2), ' - ',
                       round(BrBins[i + 1], 2))
}
## Define bins for environment PC1 to name columns
EnvBins <- seq(Emin, Emax, (Emax - Emin)/Ebreaks)
EnvNames <- NA
for (i in 1:Ebreaks) {
  EnvNames[i] <- paste0('EnvPC1: ',
                        round(EnvBins[i], 2), ' - ',
                        round(EnvBins[i + 1], 2))
}
## For each of the following stat storing objects:
## rows - comparisons at 15 relative brain sizes
## columns - each represent environmental regions
## - Ps stores p-values
Ps <- matrix(NA, nrow = 15, ncol = Ebreaks)
row.names(Ps) <- BrNames
colnames(Ps) <- EnvNames
## - BPs stores p-values after correction for multiple comparisons
BPs <- Ps
## - Zs store the z-scores from comparison 
## between observed frequencies and null expectations
Zs <- Ps


## Loop through environmental regions
## - Plot histograms of relative brain size
## and for species occurring within the region
## - Compare observed frequency to null expectations
for(i in 1:Ebreaks) {
  
  ## Extract species that occur within the current environmental region
  current <- Rand[which(Rand$PC1max >=  Emin + (((Emax + -1*Emin)/Ebreaks)*(i - 1))
                        & Rand$PC1min <= Emin + (((Emax + -1*Emin)/Ebreaks)*(i))), ]
  ## This is their relative brain sizes
  currentB <- current$RelBrain
  ## This is how many species there are
  count <- length(currentB)
  
  ## Store historam of relative brain size
  h = hist(currentB, plot = F, 
           breaks = seq(BrRange[1], BrRange[2], (BrRange[2] - BrRange[1])/22))
  ## Convert counts into frequencies
  h$density = h$counts/sum(h$counts)
  
  ## Plot histogram
  par(mar = c(6.1, 4.5, 1, 1))
  plot(h,freq=FALSE, xlim = c(BrRange[1], BrRange[2]), 
       ylim = c(0,.25), 
       col = rgb(.89, .75, .79),
       border = rgb(.89, .75, .79),
       main = EnvNames[i],
       ylab = 'Proportion species',
       xlab = 'Relative brain size',
       cex.lab = 1.8)
  text(.6,.23, paste0('n = ', count), pos = 2, cex = 1.5)
  
  ## Calculate density estimates of relative brain size for observed species
  RealD <- density(current[, 3], 
                   from = range(ResSp$RelBrain)[1],
                   to = range(ResSp$RelBrain)[2])
  
  ## Initiate objects to store density estimates for randomizations
  Xs <- matrix(NA, nrow = 512, ncol = 10000)
  Ys <- matrix(NA, nrow = 512, ncol = 10000)
  
  ## Calculate and store  density estimates of relative brain size
  ## for first randomization
  RanDen <- density(current[, 4], 
                    from = range(ResSp$RelBrain)[1],
                    to = range(ResSp$RelBrain)[2])
  Xs[, 1] <- RanDen$x
  Ys[, 1] <- RanDen$y
  
  ## Loop through the remaining 9999 randomizations
  ## - estimate and store density estimates of relative brain size
  for (j in 2:10000) {
    RanDen <- density(current[, j + 3], 
                      from = range(ResSp$RelBrain)[1],
                      to = range(ResSp$RelBrain)[2])
    Xs[, j] <- RanDen$x
    Ys[, j] <- RanDen$y
    
  }
  
  ## Loop through 15 evenly spaced relative brain size values
  ## test whether observed density differs statistically from
  ## the null expectations derived from randomizations
  for (j in 1:15) {
    
    ## Create bins corresponding to density estimates
    Points <- seq(32, (512- 32), 32)
    ## Calculate z-score (observed compared to distribution of null estimates)
    Zs[j, i] <- (RealD$y[Points[j]] - mean(Ys[Points[j], ]))/sd(Ys[Points[j], ])
    ## calculate p-value
    Ps[j, i] <- 2*pnorm(-1*abs(Zs[j, i]))
  }
  
  ## Correct p-values from current environmental region for multiple comparisons
  BPs[, i] <- p.adjust(Ps[, i], method = "holm")
  
}

## Check stats
## Zs shows whether a given brain size (rows) is under-represented (negative value)
## or over-represented (positive value) in a given environments (columns)
round(Zs, 2)
## BPs are corrected P-values and show whether the frequency of species
## of a given brain size (rows) is significantly different from null expectations
## in a given environmental bin (columns)
round(BPs, 2)

## Clean up
rm(current, Rand, Xs, Ys,
   count, currentB, Ebreaks, Emax,
   Emin, h, i, j, Points, RanDen, RealD,
   EnvBins, EnvNames, BrBins, BrNames)

############################################################
############################################################
## Global Distribution of brain size:
## Climate PC2: Supplementary figure 1 (PGLS residuals)
############################################################
############################################################

## Isolate resident species
ResSp <- mydata[which(mydata$Migration == 0), ]
## Remove pelagic species
ResSp <- ResSp[which(ResSp$Pelagic == 0), ]

## Generate 10000 randomizations for creating null distributions
Rand <- ResSp[, c('PC2min', 'PC2max', 'RelBrain')]
for (i in 1:10000) {
  Rand <- cbind(Rand, sample(ResSp$RelBrain, length(ResSp$RelBrain), replace = F))
}

## Define the number of environmental regions
Ebreaks <- 6
## Define the range of environmental PC2
range(c(mydata$PC2max, mydata$PC2min), na.rm = T)
Emin <- -2.73
Emax <- 2.72

## initiate objects to store stats:
## First define brain size bins to name rows
BrBins <- seq(BrRange[1], BrRange[2], (BrRange[2] - BrRange[1])/15)
BrNames <- NA
for (i in 1:15) {
  BrNames[i] <- paste0('RelBrain: ',
                       round(BrBins[i], 2), ' - ',
                       round(BrBins[i + 1], 2))
}
## Define bins for environment PC2 to name columns
EnvBins <- seq(Emin, Emax, (Emax - Emin)/Ebreaks)
EnvNames <- NA
for (i in 1:Ebreaks) {
  EnvNames[i] <- paste0('EnvPC2: ',
                        round(EnvBins[i], 2), ' - ',
                        round(EnvBins[i + 1], 2))
}
## For each of the following stat storing objects:
## rows - comparisons at 15 relative brain sizes
## columns - each represent environmental regions
## - Ps stores p-values
Ps <- matrix(NA, nrow = 15, ncol = Ebreaks)
row.names(Ps) <- BrNames
colnames(Ps) <- EnvNames
## - BPs stores p-values after correction for multiple comparisons
BPs <- Ps
## - Zs store the z-scores from comparison 
## between observed frequencies and null expectations
Zs <- BPs

## Loop through environmental regions
## - Plot histograms of relative brain size
## and for species occurring within the region
## - Compare observed frequency to null expectations
for(i in 1:Ebreaks) {
  
  ## Extract species that occur within the current environmental region
  current <- Rand[which(Rand$PC2max >=  Emin + (((Emax + -1*Emin)/Ebreaks)*(i - 1))
                        & Rand$PC2min <= Emin + (((Emax + -1*Emin)/Ebreaks)*(i))), ]
  ## This is their relative brain sizes
  currentB <- current$RelBrain
  ## This is how many species there are
  count <- length(currentB)
  
  ## Store historam of relative brain size
  h = hist(currentB, plot = F, breaks = seq(BrRange[1], BrRange[2], (BrRange[2] - BrRange[1])/22))
  ## Convert counts into frequencies
  h$density = h$counts/sum(h$counts)
  
  ## Plot histogram
  par(mar = c(6.1, 4.5, 1, 1))
  plot(h,freq=FALSE, xlim = c(BrRange[1], BrRange[2]), 
       ylim = c(0,.25), 
       col = rgb(.89, .75, .79),
       border = rgb(.89, .75, .79),
       main = EnvNames[i],
       ylab = 'Proportion species',
       xlab = 'Relative brain size',
       cex.lab = 1.8)
  text(.6,.23, paste0('n = ', count), pos = 2, cex = 1.5)
  
  ## Calculate density estimates of relative brain size for observed species
  RealD <- density(current[, 3], 
                   from = range(ResSp$RelBrain)[1],
                   to = range(ResSp$RelBrain)[2])
  
  ## Initiate objects to store density estimates for randomizations
  Xs <- matrix(NA, nrow = 512, ncol = 10000)
  Ys <- matrix(NA, nrow = 512, ncol = 10000)
  
  ## Calculate and store  density estimates of relative brain size
  ## for first randomization
  RanDen <- density(current[, 4], 
                    from = range(ResSp$RelBrain)[1],
                    to = range(ResSp$RelBrain)[2])
  Xs[, 1] <- RanDen$x
  Ys[, 1] <- RanDen$y
  
  
  ## Loop through the remaining 9999 randomizations
  ## - estimate and store density estimates of relative brain size
  for (j in 2:10000) {
    RanDen <- density(current[, j + 3], 
                      from = range(ResSp$RelBrain)[1],
                      to = range(ResSp$RelBrain)[2])
    Xs[, j] <- RanDen$x
    Ys[, j] <- RanDen$y
    
  }
  
  ## Loop through 15 evenly spaced relative brain size values
  ## test whether observed density differs statistically from
  ## the null expectations derived from randomizations
  for (j in 1:15) {
    
    ## Create bins corresponding to density estimates
    Points <- seq(32, (512- 32), 32)
    ## Calculate z-score (observed compared to distribution of null estimates)
    Zs[j, i] <- (RealD$y[Points[j]] - mean(Ys[Points[j], ]))/sd(Ys[Points[j], ])
    ## calculate p-value
    Ps[j, i] <- 2*pnorm(-1*abs(Zs[j, i]))
  }
  
  ## Correct p-values from current environmental region for multiple comparisons
  BPs[, i] <- p.adjust(Ps[, i], method = "holm")
  
}

## Check stats
## Zs shows whether a given brain size (rows) is under-represented (negative value)
## or over-represented (positive value) in a given environments (columns)
round(Zs, 2)
## BPs are corrected P-values and show whether the frequency of species
## of a given brain size (rows) is significantly different from null expectations
## in a given environmental bin (columns)
round(BPs, 2)

## Clean up
rm(current, Rand, Xs, Ys, EnvBins, EnvNames,
   count, currentB, Ebreaks, Emax, BrBins, BrNames,
   Emin, h, i, j, Points, RanDen, RealD)
############################################################
############################################################
## Global Distribution of brain size:
## Migrants Climate PC1: figure 2 (PGLS residuals); Supplementary table 3 (OLS residuals)
############################################################
############################################################

## Isolate resident species
ResSp <- mydata[which(mydata$Migration == 0), ]
## Remove pelagic species
ResSp <- ResSp[which(ResSp$Pelagic == 0), ]
## Isolate Migratory species
MigSp <- mydata[which(mydata$Migration != 0), ]
## Remove pelagic species
MigSp <- MigSp[which(MigSp$Pelagic == 0), ]

## Generate 1000 randomizations for creating null distributions
Rand <- MigSp[, c('PC1min', 'PC1max', 'RelBrain')]
for (i in 1:10000) {
  Rand <- cbind(Rand, sample(MigSp$RelBrain, length(MigSp$RelBrain), replace = F))
}

## Define the number of environmental regions (Only extremes will be plotted)
Ebreaks <- 3
## Define the range of environmental PC1
range(c(mydata$PC1max, mydata$PC1min), na.rm = T)
Emin <- -1.83
Emax <- 1.89

## initiate objects to store stats:
## First define brain size bins to name rows
BrBins <- seq(BrRange[1], BrRange[2], (BrRange[2] - BrRange[1])/15)
BrNames <- NA
for (i in 1:15) {
  BrNames[i] <- paste0('RelBrain: ',
                       round(BrBins[i], 2), ' - ',
                       round(BrBins[i + 1], 2))
}
## Define bins for environment PC1 to name columns
EnvBins <- seq(Emin, Emax, (Emax - Emin)/Ebreaks)
EnvNames <- NA
for (i in 1:Ebreaks) {
  EnvNames[i] <- paste0('EnvPC1: ',
                        round(EnvBins[i], 2), ' - ',
                        round(EnvBins[i + 1], 2))
}
## For each of the following stat storing objects:
## rows - comparisons at 15 relative brain sizes
## columns - each represent environmental regions
## - Ps stores p-values
Ps <- matrix(NA, nrow = 15, ncol = Ebreaks)
row.names(Ps) <- BrNames
colnames(Ps) <- EnvNames
## - BPs stores p-values after correction for multiple comparisons
BPs <- Ps
## - Zs stores the z-scores from comparison 
## between observed frequencies and null expectations
Zs <- BPs

## Loop through environmental regions
## - Plot histograms of relative brain size
## and for species occurring within the region
## - Compare observed frequency to null expectations
for(i in c(1,Ebreaks)) {
  
  ## Extract migratory species that occur within the current environmental region
  current <- Rand[which(Rand$PC1max >=  Emin + (((Emax + -1*Emin)/Ebreaks)*(i - 1))
                        & Rand$PC1min <= Emin + (((Emax + -1*Emin)/Ebreaks)*(i))), ]
  ## Extract resident species that occur within the current environmental region
  Res <- ResSp[which(ResSp$PC1max >=  Emin + (((Emax + -1*Emin)/Ebreaks)*(i - 1))
                     & ResSp$PC1min <= Emin + (((Emax + -1*Emin)/Ebreaks)*(i))), ]
  ## Number of migrants
  countM <- nrow(current)
  ## Number of all species
  count <- nrow(current) + nrow(Res)
  ## This is the relative brain sizes for migrants
  currentB <- current$RelBrain
  
  ## Store historam of relative brain size for migrants
  h = hist(currentB, plot = F, breaks = seq(BrRange[1], BrRange[2], (BrRange[2] - BrRange[1])/22))
  ## Convert counts into frequencies
  h$density = h$counts/count
  ## Store relative brain size for residents
  hR = hist(Res$RelBrain, plot = F, breaks = seq(BrRange[1], BrRange[2], (BrRange[2] - BrRange[1])/22))
  ## Convert counts into frequencies
  hR$density = hR$counts/count
  
  ## Plot frequency distribution of relative brain size
  ## for migratory and resident species.
  ## The order of drawing migrants and residents is switched 
  ## after first environmental region for visibility
  if (i == 1) {
    par(mar = c(5.1, 4.5, 1, 1))
    plot(h,freq=FALSE, xlim = c(BrRange[1], BrRange[2]), 
         ylim = c(0,.2), 
         col = rgb(1,.7,.4),
         border = rgb(1,.7,.4),
         main = EnvNames[i],
         ylab = 'Proportion species',
         xlab = 'Relative brain size',
         cex.lab = 1.8)
    plot(hR, freq=FALSE, 
         add = T, 
         col = rgb(.7,.5,.7, .5),
         border = rgb(.7,.5,.7))
    text(.6,.17, paste0('n = ', countM), pos = 2, cex = 1.5)
  } else {
    par(mar = c(5.1, 4.5, 1, 1))
    plot(hR,freq=FALSE, xlim = c(BrRange[1], BrRange[2]), 
         ylim = c(0,.2), 
         col = rgb(.7,.5,.7, .5),
         border = rgb(.7,.5,.7),
         main = EnvNames[i],
         ylab = 'Proportion species',
         xlab = 'Relative brain size',
         cex.lab = 1.8)
    plot(h, freq=FALSE, 
         add = T, 
         col = rgb(1,.7,.4),
         border = rgb(1,.7,.4)
         # col = rgb(1,.7,.4),
         # border = rgb(.9,.55,.3),
    )
    text(.6,.17, paste0('n = ', countM), pos = 2, cex = 1.5)
  }
  
  ## Calculate density estimates of relative brain size for observed species
  RealD <- density(current[, 3], 
                   from = range(mydata$RelBrain)[1],
                   to = range(mydata$RelBrain)[2])
  
  ## Initiate objects to store density estimates for randomizations
  Xs <- matrix(NA, nrow = 512, ncol = 10000)
  Ys <- matrix(NA, nrow = 512, ncol = 10000)
  
  ## Calculate and store  density estimates of relative brain size
  ## for first randomization
  RanDen <- density(current[, 4], 
                    from = range(mydata$RelBrain)[1],
                    to = range(mydata$RelBrain)[2])
  Xs[, 1] <- RanDen$x
  Ys[, 1] <- RanDen$y
  
  ## Loop through the remaining 9999 randomizations
  ## - estimate and store density estimates of relative brain size
  for (j in 2:10000) {
    RanDen <- density(current[, j + 3], 
                      from = range(mydata$RelBrain)[1],
                      to = range(mydata$RelBrain)[2])
    Xs[, j] <- RanDen$x
    Ys[, j] <- RanDen$y
    
  }
  
  ## Loop through 15 evenly spaced relative brain size values
  ## test whether observed density differs statistically from
  ## the null expectations derived from randomizations
  for (j in 1:15) {
    
    ## Create bins corresponding to density estimates
    Points <- seq(32, (512- 32), 32)
    ## Calculate z-score (observed compared to distribution of null estimates)
    Zs[j, i] <- (RealD$y[Points[j]] - mean(Ys[Points[j], ]))/sd(Ys[Points[j], ])
    ## calculate p-value
    Ps[j, i] <- 2*pnorm(-1*abs(Zs[j, i]))
  }
  
  ## Correct p-values from current environmental region for multiple comparisons
  BPs[, i] <- p.adjust(Ps[, i], method = "holm")
  
}

## Check stats
## Zs shows whether a given brain size (rows) is under-represented (negative value)
## or over-represented (positive value) in a given environments (columns)
round(Zs, 2)
## BPs are corrected P-values and show whether the frequency of species
## of a given brain size (rows) is significantly different from null expectations
## in a given environmental bin (columns)
round(BPs, 2)

## Clean up
rm(current, Rand, Xs, Ys, EnvBins, EnvNames,
   count, currentB, Ebreaks, Emax, BrBins, BrNames,
   Emin, h, i, j, Points, RanDen, RealD,
   countM, hR, Res, MigSp)
############################################################
############################################################
## Trait constraints imposed by relative brain size:
## Percent vegetative diet: figure 3a (PGLS residuals); Supplementary figure 4a (OLS residuals)
############################################################
############################################################

## Isolate resident species
ResSp <- mydata[which(mydata$Migration == 0), ]
## Remove pelagic species
ResSp <- ResSp[which(ResSp$Pelagic == 0), ]

## set the maximum for percent vegetative diet
MaxPlant <- 110
## define the boundaries of 'low' and 'high' vegetative diets
## based on the mean trait value
bPercVeg <- c(-10, mean(ResSp$PercVeg, na.rm = T), MaxPlant)
## define the boundaries of 'small' and 'large' relative brain size
## based on the mean trait value
bBrain <- c(BrRange[1], mean(ResSp$RelBrain), BrRange[2])

## Run quantile regression with tau = 0.1 (lower constraint)
## and tau = 0.9 (upp constraint)
QuantR <- rq(ResSp$PercVeg ~ ResSp$RelBrain, c(.1,.9))

## Check results
sumQuantR <- summary(QuantR, se = 'boot', R = 1000)
sumQuantR

## Initiate plot spacae
par(mar = c(5.1, 4.5, 3, 0))
plot(ResSp$RelBrain, ResSp$PercVeg, pch = 20, 
     ylim = c(-10, MaxPlant), 
     xlim = c(BrRange[1], BrRange[2]),
     col = rgb(.33, .33, .33, 0),
     cex = 1.5,
     bty = 'l',
     ylab = '% vegetative diet',
     xlab = 'Relative brain size',
     cex.lab = 1.2,
     cex.sub = 1)

## Divide trait space based on mean values
## (for comparison with trait combinations across environments analyses)
rect(bBrain[1], bPercVeg[1], bBrain[3], bPercVeg[3],
     border = rgb(.4, .4, .4), lty = 2)
lines(c(bBrain[1], bBrain[3]), c(bPercVeg[2], bPercVeg[2]),
      col = rgb(.4,.4,.4), lty = 2)
lines(c(bBrain[2], bBrain[2]), c(bPercVeg[1], bPercVeg[3]),
      col = rgb(.4,.4,.4), lty = 2)

## Add points
points(ResSp$RelBrain, ResSp$PercVeg, pch = 20, 
       col = rgb(.15, .15, .15), cex = .5)

## Add lower quantile regression line
## - solid if significant; dashed if not
if (sumQuantR[[1]]$coefficients[2, 4] >= 0.05 | is.na(sumQuantR[[1]]$coefficients[2, 4])) {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 1]*(BrRange[1]) + QuantR$coefficients[1, 1],
          QuantR$coefficients[2, 1]*(BrRange[2]) + QuantR$coefficients[1, 1]),
        col = rgb(1,0,0),
        lwd = 1.5,
        lty = 2)
} else {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 1]*(BrRange[1]) + QuantR$coefficients[1, 1],
          QuantR$coefficients[2, 1]*(BrRange[2]) + QuantR$coefficients[1, 1]),
        col = rgb(1,0,0),
        lwd = 1.5)
}

## Add upper quantile regression line
## - solid if significant; dashed if not
if (sumQuantR[[2]]$coefficients[2, 4] >= 0.05 | is.na(sumQuantR[[2]]$coefficients[2, 4])) {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 2]*(BrRange[1]) + QuantR$coefficients[1, 2],
          QuantR$coefficients[2, 2]*(BrRange[2]) + QuantR$coefficients[1, 2]),
        col = rgb(1,0,0),
        lwd = 1.5,
        lty = 2)
} else {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 2]*(BrRange[1]) + QuantR$coefficients[1, 2],
          QuantR$coefficients[2, 2]*(BrRange[2]) + QuantR$coefficients[1, 2]),
        col = rgb(1,0,0),
        lwd = 1.5)
}


## Clean up
rm(bBrain, bPercVeg, MaxPlant, QuantR)
############################################################
############################################################
## Trait constraints imposed by relative brain size:
## Annual reproductive output: figure 3b (PGLS residuals); Supplementary figure 4b (OLS residuals)
############################################################
############################################################

## Isolate resident species
ResSp <- mydata[which(mydata$Migration == 0), ]
## Remove pelagic species
ResSp <- ResSp[which(ResSp$Pelagic == 0), ]

## set the maximum for annual reproductive output
MaxEggs <- 20
## define the boundaries of 'low' and 'high' reproductive output
## based on the mean trait value
bEggs <- c(0, mean(ResSp$Eggs, na.rm = T), MaxEggs)
## define the boundaries of 'small' and 'large' relative brain size
## based on the mean trait value
bBrain <- c(BrRange[1], mean(ResSp$RelBrain), BrRange[2])

## Run quantile regression with tau = 0.1 (lower constraint)
## and tau = 0.9 (upp constraint)
QuantR <- rq(ResSp$Eggs ~ ResSp$RelBrain, c(.1,.9))

## Check results
sumQuantR <- summary(QuantR, se = 'boot', R = 1000)
sumQuantR


## Initiate plot spacae
par(mar = c(5.1, 4.5, 3, 0))
plot(ResSp$RelBrain, ResSp$Eggs, pch = 20, 
     ylim = c(0, MaxEggs), 
     xlim = c(BrRange[1], BrRange[2]),
     col = rgb(.33, .33, .33, 0),
     cex = 1.5,
     bty = 'l',
     ylab = 'Eggs/year',
     xlab = 'Relative brain size',
     cex.lab = 1.2,
     cex.sub = 1)

## Divide trait space based on mean values
## (for comparison with trait combinations across environments analyses)
rect(bBrain[1], bEggs[1], bBrain[3], bEggs[3],
     border = rgb(.4, .4, .4), lty = 2)
lines(c(bBrain[1], bBrain[3]), c(bEggs[2], bEggs[2]),
      col = rgb(.4,.4,.4), lty = 2)
lines(c(bBrain[2], bBrain[2]), c(bEggs[1], bEggs[3]),
      col = rgb(.4,.4,.4), lty = 2)

## Add points
points(ResSp$RelBrain, ResSp$Eggs, pch = 20, 
       col = rgb(.15, .15, .15), cex = .5)

## Add lower quantile regression line
## - solid if significant; dashed if not
if (sumQuantR[[1]]$coefficients[2, 4] >= 0.05 | is.na(sumQuantR[[1]]$coefficients[2, 4])) {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 1]*(BrRange[1]) + QuantR$coefficients[1, 1],
          QuantR$coefficients[2, 1]*(BrRange[2]) + QuantR$coefficients[1, 1]),
        col = rgb(1,0,0),
        lwd = 1.5,
        lty = 2)
} else {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 1]*(BrRange[1]) + QuantR$coefficients[1, 1],
          QuantR$coefficients[2, 1]*(BrRange[2]) + QuantR$coefficients[1, 1]),
        col = rgb(1,0,0),
        lwd = 1.5)
}

## Add upper quantile regression line
## - solid if significant; dashed if not
if (summary(QuantR, se = 'boot')[[2]]$coefficients[2, 4] >= 0.05 | is.na(summary(QuantR, se = 'boot')[[2]]$coefficients[2, 4])) {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 2]*(BrRange[1]) + QuantR$coefficients[1, 2],
          QuantR$coefficients[2, 2]*(BrRange[2]) + QuantR$coefficients[1, 2]),
        col = rgb(1,0,0),
        lwd = 1.5,
        lty = 2)
} else {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 2]*(BrRange[1]) + QuantR$coefficients[1, 2],
          QuantR$coefficients[2, 2]*(BrRange[2]) + QuantR$coefficients[1, 2]),
        col = rgb(1,0,0),
        lwd = 1.5)
}


## Clean up
rm(bBrain, bEggs, MaxEggs, QuantR)
############################################################
############################################################
## Trait constraints imposed by relative brain size:
## Body Size: figure 3c (PGLS residuals); Supplementary figure 4c (OLS residuals)
############################################################
############################################################

## Isolate resident species
ResSp <- mydata[which(mydata$Migration == 0), ]
## Remove pelagic species
ResSp <- ResSp[which(ResSp$Pelagic == 0), ]

## Log body mass
ResSp$Body.Mass <- log10(ResSp$Body.Mass)

## set the maximum for annual reproductive output
MaxBody.Mass <- max(ResSp$Body.Mass)
## define the boundaries of 'low' and 'high' reproductive output
## based on the mean trait value
bBody.Mass <- c(0, mean(ResSp$Body.Mass, na.rm = T), MaxBody.Mass)
## define the boundaries of 'small' and 'large' relative brain size
## based on the mean trait value
bBrain <- c(BrRange[1], mean(ResSp$RelBrain), BrRange[2])

## Run quantile regression with tau = 0.1 (lower constraint)
## and tau = 0.9 (upp constraint)
QuantR <- rq(ResSp$Body.Mass ~ ResSp$RelBrain, c(.1,.9))

## Check results
sumQuantR <- summary(QuantR, se = 'boot', R = 1000)
sumQuantR

## Initiate plot spacae
par(mar = c(5.1, 4.5, 3, 0))
plot(ResSp$RelBrain, ResSp$Body.Mass, pch = 20, 
     ylim = c(0, MaxBody.Mass), 
     xlim = c(BrRange[1], BrRange[2]),
     col = rgb(.33, .33, .33, 0),
     cex = 1.5,
     bty = 'l',
     ylab = 'Body.Mass/year',
     xlab = 'Relative brain size',
     cex.lab = 1.2,
     cex.sub = 1)

## Divide trait space based on mean values
## (for comparison with trait combinations across environments analyses)
rect(bBrain[1], bBody.Mass[1], bBrain[3], bBody.Mass[3],
     border = rgb(.4, .4, .4), lty = 2)
lines(c(bBrain[1], bBrain[3]), c(bBody.Mass[2], bBody.Mass[2]),
      col = rgb(.4,.4,.4), lty = 2)
lines(c(bBrain[2], bBrain[2]), c(bBody.Mass[1], bBody.Mass[3]),
      col = rgb(.4,.4,.4), lty = 2)

## Add points
points(ResSp$RelBrain, ResSp$Body.Mass, pch = 20, 
       col = rgb(.15, .15, .15), cex = .5)

## Add lower quantile regression line
## - solid if significant; dashed if not
if (sumQuantR[[1]]$coefficients[2, 4] >= 0.05 | is.na(sumQuantR[[1]]$coefficients[2, 4])) {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 1]*(BrRange[1]) + QuantR$coefficients[1, 1],
          QuantR$coefficients[2, 1]*(BrRange[2]) + QuantR$coefficients[1, 1]),
        col = rgb(1,0,0),
        lwd = 1.5,
        lty = 2)
} else {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 1]*(BrRange[1]) + QuantR$coefficients[1, 1],
          QuantR$coefficients[2, 1]*(BrRange[2]) + QuantR$coefficients[1, 1]),
        col = rgb(1,0,0),
        lwd = 1.5)
}

## Add upper quantile regression line
## - solid if significant; dashed if not
if (summary(QuantR, se = 'boot')[[2]]$coefficients[2, 4] >= 0.05 | is.na(summary(QuantR, se = 'boot')[[2]]$coefficients[2, 4])) {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 2]*(BrRange[1]) + QuantR$coefficients[1, 2],
          QuantR$coefficients[2, 2]*(BrRange[2]) + QuantR$coefficients[1, 2]),
        col = rgb(1,0,0),
        lwd = 1.5,
        lty = 2)
} else {
  lines(c(BrRange[1], BrRange[2]),
        c(QuantR$coefficients[2, 2]*(BrRange[1]) + QuantR$coefficients[1, 2],
          QuantR$coefficients[2, 2]*(BrRange[2]) + QuantR$coefficients[1, 2]),
        col = rgb(1,0,0),
        lwd = 1.5)
}

## Clean up
rm(bBrain, bBody.Mass, MaxBody.Mass, QuantR)
############################################################
############################################################
## Trait combinations across environments:
## Diet: figure 4a-c (PGLS residuals); Supplementary table 4 (OLS residuals)
############################################################
############################################################

## Isolate resident species
ResSp <- mydata[which(mydata$Migration == 0), ]
## Remove pelagic species
ResSp <- ResSp[which(ResSp$Pelagic == 0), ]

#### Extract variable of interest and remove NAs
ResSp <- ResSp[, c('PC1min', 'PC1max', 'RelBrain', 'PercVeg')]
ResSp <- na.omit(ResSp)

## set the maximum for percent vegetative diet
MaxPlant <- 110
## define the boundaries of 'low' and 'high' vegetative diets
## based on the mean trait value
bPercVeg <- c(-10, mean(ResSp$PercVeg, na.rm = T), MaxPlant)
## define the boundaries of 'small' and 'large' relative brain size
## based on the mean trait value
bBrain <- c(BrRange[1], mean(ResSp$RelBrain), BrRange[2])

## Set the number of (partially overlapping) environmental bins
Ebreaks <- 20
## ChunkS is used to define the width of environmental bins
## (see BinEnd calculation below)
ChunkS <- 6
## Define ends of environmental PC1
Emin <- -1.83
Emax <- 1.89
## First bin starts at Emin...
BinStart <- Emin
## ... and ends depending on value chosen for ChunkS
BinEnd <- Emin + ((Emax + -1*Emin)/ChunkS)
## This environmental will slide along the gradient by an amount
## that depends on the value chosen for Ebreaks
BinInt <- (Emax + abs(BinEnd))/(Ebreaks - 1)

## Initiate objects to store stats for each trait combination at each environmental bin
## Create names for columns that identify the environmental bin
EnvNames <- NA
for (i in 1:Ebreaks) {
  EnvNames[i] <- paste0('EnvPC1: ',
                        round(BinStart + BinInt*(i - 1), 2), ' - ',
                        round(BinEnd + BinInt*(i - 1), 2))
}
## - SpacePs stores p-values
SpacePs <- as.data.frame(matrix(NA, nrow = (length(bPercVeg) - 1)*(length(bBrain) - 1), ncol = Ebreaks))
colnames(SpacePs) <- EnvNames
row.names(SpacePs) <- c('Small brain, low veg',
                        'Small brain, high veg',
                        'Large brain, low veg',
                        'Large brain, high veg')
## - SpacePsB stores p-values after correcting for multiple comparisons
## - zs stores the z-scores from comparison between 
## observed number of species with each trait combination and null expectations
zs <- SpacePsB <- SpacePs

## Initiate object to hold the observed species count
## with each trait combination at within each environmental bin
SpaceCounts <- as.data.frame(matrix(NA, nrow = (length(bPercVeg) - 1)*(length(bBrain) - 1), ncol = Ebreaks))

## Loop through environmental bins
## and compare observed species counts for each trait combination to null expectations
for (i in 1:Ebreaks) {
  ## initiate object to hold counts from randomizations
  RandCounts <- as.data.frame(matrix(NA, nrow = 10000, 
                                     ncol = (length(bPercVeg) - 1)*(length(bBrain) - 1)))
  ## Isolate the species that occur within the current environmental region
  currentBlock <- ResSp[which(ResSp$PC1max >=  BinStart + BinInt*(i - 1)
                              & ResSp$PC1min <= BinEnd + BinInt*(i - 1)), ]
  ## Generate 10000 randomizations
  for (j in 1:10000) {
    ## Randomly select many species as are observed in the current environmental region
    current <- ResSp[sample(1:nrow(ResSp), nrow(currentBlock)), c(1:4)]
    ## Loop through 'small' and 'large' brain sizes,
    for (k in 1:(length(bBrain) - 1)) {
      ## loop through 'low' and 'high' vegetative diet,
      for (l in 1:(length(bPercVeg) - 1)) {
        ## and count the number of species in the current random sample
        ## that posses the current trait combination
        RandCounts[j, (k - 1)*(length(bBrain) - 1) + (l)] <- nrow(current[which(current$RelBrain >= bBrain[k]
                                                                                & current$RelBrain < bBrain[k + 1] 
                                                                                & current$PercVeg >= bPercVeg[l]
                                                                                & current$PercVeg < bPercVeg[l + 1]), ])
      }
    }
  }
  
  ## Once again, loop through 'small' and 'large' brain sizes,
  for (k in 1:(length(bBrain) - 1)) {
    ## loop through 'low' and 'high' vegetative diet
    for (l in 1:(length(bPercVeg) - 1)) {
      ## and this time count the number of species in the observed species sample
      ## that posses the current trait combination
      SpaceCounts[(k - 1)*(length(bBrain) - 1) + (l), i] <- nrow(currentBlock[which(currentBlock$RelBrain >= bBrain[k]
                                                                                    & currentBlock$RelBrain < bBrain[k + 1] 
                                                                                    & currentBlock$PercVeg >= bPercVeg[l]
                                                                                    & currentBlock$PercVeg < bPercVeg[l + 1]), ])
      ## compute z-score for the comparison of observed species count 
      ## to the distribution of null species counts
      zs[(k - 1)*(length(bBrain) - 1) + (l), i] <- (SpaceCounts[(k - 1)*(length(bBrain) - 1) + (l), i] - mean(RandCounts[, (k - 1)*(length(bBrain) - 1) + (l)]))/sd(RandCounts[, (k - 1)*(length(bBrain) - 1) + (l)])
      ## calculate p-value
      SpacePs[(k - 1)*(length(bBrain) - 1) + (l), i] <- 2*pnorm(-1*abs(zs[(k - 1)*(length(bBrain) - 1) + (l), i]))
    }
  }
  ## Correct p-values for current environmental window for multiple comparisons
  SpacePsB[, i] <- p.adjust(SpacePs[, i], method = 'holm')
  
}

## Check stats
## Zs shows whether a given trait combination (rows) is under-represented (negative value)
## or over-represented (positive value) in a given environment (columns)
round(zs, 2)
## BPs are corrected P-values and show whether the frequency of species
## with a given trait combination (rows) is significantly different from null expectations
## in a given environmental bin (columns)
round(SpacePsB, 2)

## Clean up
rm(current, currentBlock, RandCounts, ResSp, SpaceCounts,
   bBrain, BinEnd, BinInt, BinStart,
   bPercVeg, ChunkS, Ebreaks, Emax, Emin,
   i ,j, k, l, MaxPlant, EnvNames, EnvBins)
############################################################
############################################################
## Trait combinations across environments:
## Annual reproductive output: figure 4d-f (PGLS residuals); Supplementary table 5 (OLS residuals)
############################################################
############################################################

## Isolate resident species
ResSp <- mydata[which(mydata$Migration == 0), ]
## Remove pelagic species
ResSp <- ResSp[which(ResSp$Pelagic == 0), ]

#### Extract variable of interest and remove NAs
ResSp <- ResSp[, c('PC1min', 'PC1max', 'RelBrain', 'Eggs')]
ResSp <- na.omit(ResSp)

## set the maximum for annual reproductive output
MaxEggs <- 20
## define the boundaries of 'low' and 'high' reproductive outputs
## based on the mean trait value
bEggs <- c(0, mean(ResSp$Eggs, na.rm = T), MaxEggs)
## define the boundaries of 'small' and 'large' relative brain size
## based on the mean trait value
bBrain <- c(BrRange[1], mean(ResSp$RelBrain), BrRange[2])

## Set the number of (partially overlapping) environmental bins
Ebreaks <- 20
## ChunkS is used to define the width of environmental bins
## (see BinEnd calculation below)
ChunkS <- 6
## Define ends of environmental PC1
Emin <- -1.83
Emax <- 1.89
## First bin starts at Emin...
BinStart <- Emin
## ... and ends depending on value chosen for ChunkS
BinEnd <- Emin + ((Emax + -1*Emin)/ChunkS)
## This environmental will slide along the gradient by an amount
## that depends on the value chosen for Ebreaks
BinInt <- (Emax + abs(BinEnd))/(Ebreaks - 1)

## Initiate objects to store stats for each trait combination at each environmental bin
## Create names for columns that identify the environmental bin
EnvNames <- NA
for (i in 1:Ebreaks) {
  EnvNames[i] <- paste0('EnvPC1: ',
                        round(BinStart + BinInt*(i - 1), 2), ' - ',
                        round(BinEnd + BinInt*(i - 1), 2))
}
## - SpacePs stores p-values
SpacePs <- as.data.frame(matrix(NA, nrow = (length(bEggs) - 1)*(length(bBrain) - 1), ncol = Ebreaks))
colnames(SpacePs) <- EnvNames
row.names(SpacePs) <- c('Small brain, low eggs',
                        'Small brain, high eggs',
                        'Large brain, low eggs',
                        'Large brain, high eggs')
## - SpacePsB stores p-values after correcting for multiple comparisons
## - zs stores the z-scores from comparison between 
## observed number of species with each trait combination and null expectations
zs <- SpacePsB <- SpacePs

## Initiate object to hold the observed species count
## with each trait combination at within each environmental bin
SpaceCounts <- as.data.frame(matrix(NA, nrow = (length(bEggs) - 1)*(length(bBrain) - 1), ncol = Ebreaks))

## Loop through environmental bins
## and compare observed species counts for each trait combination to null expectations
for (i in 1:Ebreaks) {
  ## initiate object to hold counts from randomizations
  RandCounts <- as.data.frame(matrix(NA, nrow = 10000, 
                                     ncol = (length(bEggs) - 1)*(length(bBrain) - 1)))
  ## Isolate the species that occur within the current environmental region
  currentBlock <- ResSp[which(ResSp$PC1max >=  BinStart + BinInt*(i - 1)
                              & ResSp$PC1min <= BinEnd + BinInt*(i - 1)), ]
  ## Generate 10000 randomizations
  for (j in 1:10000) {
    ## Randomly select as many species as are observed in the current environmental region
    current <- ResSp[sample(1:nrow(ResSp), nrow(currentBlock)), c(1:4)]
    ## Loop through 'small' and 'large' brain sizes,
    for (k in 1:(length(bBrain) - 1)) {
      ## loop through 'low' and 'high' reproductive outputs,
      for (l in 1:(length(bEggs) - 1)) {
        ## and count the number of species in the current random sample
        ## that posses the current trait combination
        RandCounts[j, (k - 1)*(length(bBrain) - 1) + (l)] <- nrow(current[which(current$RelBrain >= bBrain[k]
                                                                                & current$RelBrain < bBrain[k + 1] 
                                                                                & current$Eggs >= bEggs[l]
                                                                                & current$Eggs < bEggs[l + 1]), ])
      }
    }
  }
  
  ## Once again, loop through 'small' and 'large' brain sizes,
  for (k in 1:(length(bBrain) - 1)) {
    ## loop through 'low' and 'high' reproductive outputs
    for (l in 1:(length(bEggs) - 1)) {
      ## and this time count the number of species in the observed species sample
      ## that posses the current trait combination
      SpaceCounts[(k - 1)*(length(bBrain) - 1) + (l), i] <- nrow(currentBlock[which(currentBlock$RelBrain >= bBrain[k]
                                                                                    & currentBlock$RelBrain < bBrain[k + 1] 
                                                                                    & currentBlock$Eggs >= bEggs[l]
                                                                                    & currentBlock$Eggs < bEggs[l + 1]), ])
      ## compute z-score for the comparison of observed species count 
      ## to the distribution of null species counts
      zs[(k - 1)*(length(bBrain) - 1) + (l), i] <- (SpaceCounts[(k - 1)*(length(bBrain) - 1) + (l), i] - mean(RandCounts[, (k - 1)*(length(bBrain) - 1) + (l)]))/sd(RandCounts[, (k - 1)*(length(bBrain) - 1) + (l)])
      ## calculate p-value
      SpacePs[(k - 1)*(length(bBrain) - 1) + (l), i] <- 2*pnorm(-1*abs(zs[(k - 1)*(length(bBrain) - 1) + (l), i]))
    }
  }
  ## Correct p-values for current environmental window for multiple comparisons
  SpacePsB[, i] <- p.adjust(SpacePs[, i], method = 'holm')
  
}

## Check stats
## Zs shows whether a given trait combination (rows) is under-represented (negative value)
## or over-represented (positive value) in a given environment (columns)
round(zs, 2)
## BPs are corrected P-values and show whether the frequency of species
## with a given trait combination (rows) is significantly different from null expectations
## in a given environmental bin (columns)
round(SpacePsB, 2)

## Clean up
rm(current, currentBlock, RandCounts, ResSp, SpaceCounts,
   bBrain, BinEnd, BinInt, BinStart,
   bEggs, ChunkS, Ebreaks, Emax, Emin,
   i ,j, k, l, MaxEggs, EnvNames)
############################################################
############################################################
## Trait combinations across environments:
## Body size: figure 4d-f (PGLS residuals); Supplementary table 6 (OLS residuals)
############################################################
############################################################

## Isolate resident species
ResSp <- mydata[which(mydata$Migration == 0), ]
## Remove pelagic species
ResSp <- ResSp[which(ResSp$Pelagic == 0), ]

#### Extract variable of interest and remove NAs
ResSp <- ResSp[, c('PC1min', 'PC1max', 'RelBrain', 'Body.Mass')]
ResSp <- na.omit(ResSp)

## Log body mass
ResSp$Body.Mass <- log10(ResSp$Body.Mass)

## set the maximum for body size
MaxBody.Mass <- max(ResSp$Body.Mass)
## define the boundaries of 'small' and 'large' body sizes
## based on the mean trait value
bBody.Mass <- c(0, mean(ResSp$Body.Mass, na.rm = T), MaxBody.Mass)
## define the boundaries of 'small' and 'large' relative brain size
## based on the mean trait value
bBrain <- c(BrRange[1], mean(ResSp$RelBrain), BrRange[2])

## Set the number of (partially overlapping) environmental bins
Ebreaks <- 20
## ChunkS is used to define the width of environmental bins
## (see BinEnd calculation below)
ChunkS <- 6
## Define ends of environmental PC1
Emin <- -1.83
Emax <- 1.89
## First bin starts at Emin...
BinStart <- Emin
## ... and ends depending on value chosen for ChunkS
BinEnd <- Emin + ((Emax + -1*Emin)/ChunkS)
## This environmental will slide along the gradient by an amount
## that depends on the value chosen for Ebreaks
BinInt <- (Emax + abs(BinEnd))/(Ebreaks - 1)

## Initiate objects to store stats for each trait combination at each environmental bin
## Create names for columns that identify the environmental bin
EnvNames <- NA
for (i in 1:Ebreaks) {
  EnvNames[i] <- paste0('EnvPC1: ',
                        round(BinStart + BinInt*(i - 1), 2), ' - ',
                        round(BinEnd + BinInt*(i - 1), 2))
}
## - SpacePs stores p-values
SpacePs <- as.data.frame(matrix(NA, nrow = (length(bBody.Mass) - 1)*(length(bBrain) - 1), ncol = Ebreaks))
colnames(SpacePs) <- EnvNames
row.names(SpacePs) <- c('Small brain, small body',
                        'Small brain, large body',
                        'Large brain, small body',
                        'Large brain, large body')
## - SpacePsB stores p-values after correcting for multiple comparisons
## - zs stores the z-scores from comparison between 
## observed number of species with each trait combination and null expectations
zs <- SpacePsB <- SpacePs

## Initiate object to hold the observed species count
## with each trait combination within each environmental bin
SpaceCounts <- as.data.frame(matrix(NA, nrow = (length(bBody.Mass) - 1)*(length(bBrain) - 1), ncol = Ebreaks))

## Loop through environmental bins
## and compare observed species counts for each trait combination to null expectations
for (i in 1:Ebreaks) {
  ## initiate object to hold counts from randomizations
  RandCounts <- as.data.frame(matrix(NA, nrow = 10000, 
                                     ncol = (length(bBody.Mass) - 1)*(length(bBrain) - 1)))
  ## Isolate the species that occur within the current environmental region
  currentBlock <- ResSp[which(ResSp$PC1max >=  BinStart + BinInt*(i - 1)
                              & ResSp$PC1min <= BinEnd + BinInt*(i - 1)), ]
  ## Generate 10000 randomizations
  for (j in 1:10000) {
    ## Randomly select as many species as are observed in the current environmental region
    current <- ResSp[sample(1:nrow(ResSp), nrow(currentBlock)), c(1:4)]
    ## Loop through 'small' and 'large' brain sizes,
    for (k in 1:(length(bBrain) - 1)) {
      ## loop through 'small' and 'large' body sizes,
      for (l in 1:(length(bBody.Mass) - 1)) {
        ## and count the number of species in the current random sample
        ## that posses the current trait combination
        RandCounts[j, (k - 1)*(length(bBrain) - 1) + (l)] <- nrow(current[which(current$RelBrain >= bBrain[k]
                                                                                & current$RelBrain < bBrain[k + 1] 
                                                                                & current$Body.Mass >= bBody.Mass[l]
                                                                                & current$Body.Mass < bBody.Mass[l + 1]), ])
      }
    }
  }
  
  ## Once again, loop through 'small' and 'large' brain sizes,
  for (k in 1:(length(bBrain) - 1)) {
    ## loop through 'small' and 'large' body sizes
    for (l in 1:(length(bBody.Mass) - 1)) {
      ## and this time count the number of species in the observed species sample
      ## that posses the current trait combination
      SpaceCounts[(k - 1)*(length(bBrain) - 1) + (l), i] <- nrow(currentBlock[which(currentBlock$RelBrain >= bBrain[k]
                                                                                    & currentBlock$RelBrain < bBrain[k + 1] 
                                                                                    & currentBlock$Body.Mass >= bBody.Mass[l]
                                                                                    & currentBlock$Body.Mass < bBody.Mass[l + 1]), ])
      ## compute z-score for the comparison of observed species count 
      ## to the distribution of null species counts
      zs[(k - 1)*(length(bBrain) - 1) + (l), i] <- (SpaceCounts[(k - 1)*(length(bBrain) - 1) + (l), i] - mean(RandCounts[, (k - 1)*(length(bBrain) - 1) + (l)]))/sd(RandCounts[, (k - 1)*(length(bBrain) - 1) + (l)])
      ## calculate p-value
      SpacePs[(k - 1)*(length(bBrain) - 1) + (l), i] <- 2*pnorm(-1*abs(zs[(k - 1)*(length(bBrain) - 1) + (l), i]))
    }
  }
  ## Correct p-values for current environmental window for multiple comparisons
  SpacePsB[, i] <- p.adjust(SpacePs[, i], method = 'holm')
  
}

## Check stats
## Zs shows whether a given trait combination (rows) is under-represented (negative value)
## or over-represented (positive value) in a given environment (columns)
round(zs, 2)
## BPs are corrected P-values and show whether the frequency of species
## with a given trait combination (rows) is significantly different from null expectations
## in a given environmental bin (columns)
round(SpacePsB, 2)

## Clean up
rm(current, currentBlock, RandCounts, ResSp, SpaceCounts,
   bBrain, BinEnd, BinInt, BinStart,
   bBody.Mass, ChunkS, Ebreaks, Emax, Emin,
   i ,j, k, l,  MaxBody.Mass, EnvNames)

