rm(list=ls())

###############################################################################################################################
## PLOTTING & COLOURS ######################################################################################################### 
###############################################################################################################################

library(scales)

mycol <- c("ESP" = "#bb4542", "AUN"= "#b66b3d", "AUV" = "#c98c33", "MTN" = "#b7de1d", "ORE" = "#638e00", "CHE"= "#00a0cb", "NOR"= "#18c073", "CLC" = "#6c7ed7", "CLS" = "#5f3095","HWI" = "#c26abb","IND"= "#ca3574")

# labels
reg_labels_short <- c("AUN" = "New South Wales, AUS", "AUV" = "Victoria, AUS", "CHE" = "Switzerland", "CLC" = "Central Chile",
                      "CLS" = "South Chile", "ESP" = "Tenerife, Spain", "HWI" = "Hawaii", "IND" = "Kashmir, India",
                      "MTN" = "Montana, USA", "NOR" = "Norway", "ORE" = "Oregon, USA")

reg_labels2 <- c("AUN" = "New South Wales,\n Australia", "AUV" = "Victoria, Australia", "CHE" = "Switzerland", "CLC" = "Central Chile",
                "CLS" = "South Chile", "ESP" = "Tenerife, Spain", "HWI" = "Hawaii, USA", "IND" = "Kashmir, India",
                "MTN" = "Montana, USA", "NOR" = "Norway", "ORE" = "Oregon, USA")

reg_labels3 <- c("AUN" = "New South Wales, Australia", "AUV" = "Victoria, Australia", "CHE" = "Switzerland", "CLC" = "Central Chile",
                 "CLS" = "South Chile", "ESP" = "Tenerife, Spain", "HWI" = "Hawaii, USA", "IND" = "Kashmir, India",
                 "MTN" = "Montana, USA", "NOR" = "Norway", "ORE" = "Oregon, USA")

###############################################################################################################################
## PACKAGES ################################################################################################################### 
###############################################################################################################################

library(nlme);library(MASS);library(lme4); library(bbmle); library(lmerTest) # mixed models

library(ggplot2); library(ggpubr) # ggplot

library(tidyr); library(dplyr); library(purrr) # tidyverse

library(naniar) # dealing with NA's

library(MALDIquant) #for match.closest

library(stringr) # regex

library(sp); library(raster)

###############################################################################################################################
## FUNCTIONS ################################################################################################################## 
###############################################################################################################################

#function to produce model-checking Plots for the fixed effects of an lmer model
fix.check <- function(mod){
  par(mfrow = c(1,3))
  plot(fitted(mod),resid(mod),main="Scale-location Plot") #should have no pattern
  print(anova(lm(fitted(mod)~resid(mod)))) #should be non-significant
  qqnorm(resid(mod), ylab="Residuals") #should be approximately straight line
  qqline(resid(mod))
  plot(density(resid(mod))) #should be roughly normally distributed
  rug(resid(mod))}


# function to make wide data frame
wide_fun <- function(.data, key_name, value_name) {
  .data %>% 
    group_by(across(-{{value_name}})) %>%  # group by everything other than the value column. 
    mutate(row_id = 1:n()) %>% ungroup() %>%  # build group index
    tidyr::pivot_wider(
      names_from = {{key_name}},
      values_from = {{value_name}}) %>%    # spread
    dplyr::select(-row_id)
}

# error bars (confidence intervals)
bars2 <- function(x,y,z1,z2,c){for (k in 1:length(y)){ arrows(x[k], y[k], x[k], z1[k], angle=90, length=0, col=c)
                                                       arrows(x[k], y[k], x[k], z2[k], angle=90, length=0, col=c)}}
                                                       
###############################################################################################################################
## READ & PREPARE DATA ########################################################################################################
###############################################################################################################################

### PREPARE  DATA ############################################################################################################# 

# read data (can be found at https://doi.org/10.5281/zenodo.5529072)
original_data <- read.csv("MIREN_plant_records_data2007-2018.lat.long.Elevation.csv", stringsAsFactors = FALSE)

# filter for alien data, regions and years used in paper, create unique identifier, add transect and plot as separate column
sp_dat <- original_data %>%
  filter(Status == "Alien",
         Year != 2022,
         Road %in% c("AK", "AS", "AT", "BE", "BP", "BR", "BW", "BX", "CH", "CP", "DH", "EM", "FL", "GU", "HH", "HW", "LA", 
                     "LH", "LP", "LQ", "MH", "MK", "MS", "MT", "MW", "NO", "PG", "PL", "RO", "SI", "SJ", "SO", "ST", "VN", "VO")) %>%
  mutate(pidn = paste(Site, Year, sep = ".")) %>%
  separate(Site,  c("Region2", "Road2", "Transect", "Plot")) %>%
  mutate(Transect = as.integer(Transect)) %>%
  dplyr::select(-c(Region2, Road2)) 

# save coordinates for later & get rid of lat/ long
env_dat_coord <- sp_dat %>%
  dplyr::select(c(Region, Road, Transect, Lat, Long, pidn, Year)) 

# delete coordinates and cover and make fYear column
sp_dat <- sp_dat %>%
  dplyr::select(-c(Lat, Long, Cover)) %>%
  mutate(fYear = case_when(Year == 2007 ~ 0, Year == 2008 ~ 0,
                           Year == 2012 ~ 5,
                           Year == 2017 ~ 10, Year == 2018 ~ 10))

sp_dat$fYear <- as.numeric(sp_dat$fYear)

# unify elevations on one transect: take mean if elevations differ between plots of same transect
sp_dat <- sp_dat %>%
  group_by(Region, Road, Transect, Year) %>%
  mutate(Elevation = mean(Elevation)) 

# check whether there are & how many missing elevations
table(sp_dat$Road[is.na(sp_dat$Elevation)==TRUE]) 

# environmental data missing for roads BE and BW - adding elevations from 2017 instead
# create lookup for roads BE and BW with elevations from 2017
lookup_AUV <- sp_dat %>%
  ungroup() %>%
  filter(Year == 2017) %>%
  dplyr::select(c(Region, Road, Transect, Elevation)) %>%
  filter(Road %in% c("BW", "BE")) %>%
  mutate(RegRoadTrans = paste(Region, Road, Transect, sep = ".")) %>%
  group_by(RegRoadTrans) %>%
  summarise(Elevation = mean(Elevation))

sp_dat$RegRoadTrans <- paste(sp_dat$Region, sp_dat$Road, sp_dat$Transect, sep = ".")

sp_dat$Elevation[is.na(sp_dat$Elevation)] <- lookup_AUV$Elevation[match(sp_dat$RegRoadTrans[is.na(sp_dat$Elevation)], lookup_AUV$RegRoadTrans)]

# check whether there are & how many missing elevations
table(sp_dat$Road[is.na(sp_dat$Elevation)==TRUE]) 

sp_dat <- sp_dat[, -12] # delete Reg.Road.Trans

### CLEAN UP SPECIES ######################################################################################################### 

# delete unidentified species ("Species Name CHE" --> Markenzeichen: 3 capital letters): cannot be sure it is not the same species
# that was identified with species name before (note "Taraxacum species" retained, as this is mainly T. officinale but taxonomy is unclear)
sp_dat$Accepted.Name.MIREN <- as.character(sp_dat$Accepted.Name.MIREN)
sp_dat <- sp_dat %>%
  filter(!(str_detect(Accepted.Name.MIREN, "[[:upper:]]{3}") == TRUE)) 

# complete alien data set, cleaned up
dat_alien <- sp_dat 

# exclude species with fewer than x appearances per region & get rid of them
dat_alien_small <- dat_alien %>%
  group_by(Region, Accepted.Name.MIREN) %>%
  dplyr::filter(n_distinct(paste(Region,Road,Transect,Year)) > 1) 

dat_alien_small5 <- dat_alien %>%
  group_by(Region,  Accepted.Name.MIREN) %>%  
  dplyr::filter(n_distinct(paste(Region,Road,Transect,Year)) > 5)

dat_alien_small10_strict <- dat_alien %>% # 10 occurrences per region and year!
  group_by(Region,  Accepted.Name.MIREN, fYear) %>%  
  dplyr::filter(n_distinct(paste(Region,Road,Transect,Year)) > 10)

dat_alien_small10 <- dat_alien %>%
  group_by(Region, Accepted.Name.MIREN) %>%
  dplyr::filter(n_distinct(paste(Region,Road,Transect,Year)) > 10)

# make data set based on only road vs. only away plots (cut-off 1 appearance per road)
dat_alien_Road5 <- dat_alien %>%
  group_by(Region, Accepted.Name.MIREN) %>% 
  filter(Plot == "1" | Plot == "1A") %>%
  dplyr::filter(n_distinct(paste(Region,Road,Transect,Year)) > 1) 
  
dat_alien_away5 <- dat_alien %>%
  group_by(Region, Accepted.Name.MIREN) %>% 
  filter(Plot != "1" & Plot != "1A") %>%
  dplyr::filter(n_distinct(paste(Region,Road,Transect,Year)) > 1) 

# define >1 (over all years) data set as the data set to be used for analysis 
dat_alien <- dat_alien_small

# total # of non-native species in data set with standard filter
length(unique(dat_alien$Accepted.Name.MIREN))
# total # of non-native species in data set with non-identified species removed, but not rare species
length(unique(sp_dat$Accepted.Name.MIREN))


###############################################################################################################################
## TEMPORAL CHANGE ANALYSIS - RICHNESS OVER TIME ############################################################################## 
###############################################################################################################################

### RICHNESS OVER TIME: REGION-LEVEL DATASETS ################################################################################# 

# create dataframe with nr. of aliens per region and year
total_Region<-dat_alien %>% 
  group_by(Region, fYear) %>% 
  summarise(No_aliens = n_distinct(Accepted.Name.MIREN)) # sum up aliens for those unique combinations

# calculate percentage change in species number: first year = 100%
total_Region$No_aliens <- as.numeric(total_Region$No_aliens)
total_Region <- total_Region %>%
  group_by(Region) %>% 
  arrange(fYear, .by_group = TRUE) %>%
  mutate(abs_change = case_when(fYear == 0 ~ 0,
                                fYear == 5 ~ No_aliens - lag(No_aliens),
                                fYear == 10 ~ No_aliens - lag(No_aliens, n = 2))) %>%
  mutate(abs_change = ifelse(Region == "AUV" | Region == "IND" | Region == "NOR", 0, abs_change),
         abs_change = ifelse(c(Region == "AUV" | Region == "IND" | Region == "NOR" | Region == "ESP") & fYear == 10, No_aliens - lag(No_aliens), abs_change)) %>%
  mutate(pct_change = case_when(fYear == 0 ~ 1,
                                fYear == 5 ~ 1/lag(No_aliens) * No_aliens,
                                fYear == 10 ~ 1/ lag(No_aliens, n = 2) * No_aliens)) %>%
  mutate(pct_change = ifelse(Region == "AUV" | Region == "IND" | Region == "NOR", 1, pct_change),
         pct_change = ifelse(c(Region == "AUV" | Region == "IND" | Region == "NOR" | Region == "ESP") & fYear == 10, 1/lag(No_aliens) * No_aliens, pct_change))
total_Region$pct_change <- total_Region$pct_change*100 - 100


# add Years so that 0 = first year of survey, whichever calendar year that happened to be
total_Region$Year <- c(0,5,10,   0,5,    0,5,10,   0,5,10,    0,5,10,   0,10,   0,5,   0,5,   0,5,10,   0,5,   0,5)
total_Region$Region <- as.factor(total_Region$Region)


### RICHNESS OVER TIME: ANALYSIS ##############################################################################################

# change in species richness over years
m <- lmer(No_aliens ~ fYear + (1| Region), data = total_Region)
m0 <- lmer(No_aliens ~ 1 + (1| Region), data = total_Region)
anova(m,m0)
summary(m)
fix.check(m)
#NB year isn't significant with Gaussian model, but variance also increases slightly with mean

# percentage change in species richness over years (fitted as zero intercept, since all regions start at 0)
m <- lmer(pct_change ~ 0+ fYear + (1| Region), data = total_Region)
m0 <- lmer(pct_change ~ 0 + (1| Region), data = total_Region)
anova(m,m0)
summary(m)
fix.check(m)


### RICHNESS OVER TIME: PLOTTING FIGURE 2 #########################################################################

pdf(file="Fig2_221021.pdf",width= 3,height= 8, useDingbats=FALSE)

par(mfrow=c(3,1),xaxs="i",yaxs="i",tck=0.02, mar=c(2,3,0.6,0.5), oma=c(1,0,0,0),bty="l", cex=0.9)

xlimit <- c(-0.9,10.9)
ylimit <- c(0,150)

plot(total_Region$Year,total_Region$No_aliens,xlab="", ylab="Number of species",xlim=xlimit,ylim=ylimit,cex=1, pch=21,bg="grey", frame.plot=T, axes=F,type="n")
axis(1, at=c(0,5,10), labels=c("0","5","10"),  line=0, mgp = c(1.1,0,0),cex.axis=0.8)
axis(2, line=0, mgp = c(1.1,0,0),cex.axis=0.8)

with(total_Region, 
     for(i in 1:length(levels(Region))) {
       points(Year[Region==levels(Region)[i]],No_aliens[Region==levels(Region)[i]], col=mycol[match(levels(Region)[i],labels(mycol))], type="l")
     }
)
with(total_Region, 
     for(i in 1:length(levels(Region))) {
       points(Year[Region==levels(Region)[i]],No_aliens[Region==levels(Region)[i]], pch=21,bg=mycol[match(levels(Region)[i],labels(mycol))])
     }
)

m <- lmer(No_aliens ~ Year + (1| Region), data = total_Region)
abline(fixef(m)[1], fixef(m)[2], lty=2, lwd=3, col="grey33")

mtext("Number of species",side=2,line=1,outer=FALSE,cex=0.8)

mtext("A", side=2, line=1, at=ylimit[2], las=1)

xlimit <- c(-0.9,10.9)
ylimit <- c(-20,80)

with(total_Region, 
     plot(Year,pct_change,xlab="", ylab="",xlim=xlimit,ylim=ylimit,cex=1, pch=21,bg="grey", frame.plot=T, axes=F,type="n")
)
axis(1, at=c(0,5,10), labels=c("0","5","10"),  line=0, mgp = c(1.1,0,0),cex.axis=0.8)
axis(2, line=0, mgp = c(1.1,0,0),cex.axis=0.8)

with(total_Region, 
     for(i in 1:length(levels(Region))) {
       points(Year[Region==levels(Region)[i]],pct_change[Region==levels(Region)[i]], col=mycol[match(levels(Region)[i],labels(mycol))],type="l")
     }
)
with(total_Region, 
     for(i in 1:length(levels(Region))) {
       points(Year[Region==levels(Region)[i]],pct_change[Region==levels(Region)[i]], pch=21,bg=mycol[match(levels(Region)[i],labels(mycol))])
     }
)


m <- lmer(pct_change ~ 0+ Year + (1| Region), data = total_Region)
arrows(0,0,11, 11*fixef(m)[1], col="grey33", lwd=3, angle=0)

mtext("Percentage change \nin species number",side=2,line=1,outer=FALSE,cex=0.8)
mtext("B", side=2, line=1, at=ylimit[2], las=1)


mtext("Years since first survey",side=1,line=-15,outer=TRUE,cex=1)

plot(1:12, 1:12, type="n", axes="F")
legend(2,11, reg_labels_short , pch=21, pt.bg=mycol[match(labels(reg_labels_short),labels(mycol))], lty=1, col=mycol[match(labels(reg_labels_short),labels(mycol))] , cex=0.8, box.lty=0)

dev.off()

###############################################################################################################################
## SPREAD ANALYSIS: RANGE-SHIFTS OVER TIME ####################################################################################
###############################################################################################################################

### CALCULATE SPREAD & CREATE SPREAD DATA SHEETS ##############################################################################

# aggregate data to transect level for different species occurrence cut-offs
dat_alien2 <- dat_alien %>%  
  group_by(Region, Road, Transect,Year, fYear, Accepted.Name.MIREN) %>% 
  summarise(across(Elevation, mean)) 

dat_alien_small5_2 <- dat_alien_small5 %>%  
  group_by(Region, Road, Transect,Year, fYear, Accepted.Name.MIREN) %>% 
  summarise(across(Elevation, mean)) 

dat_alien_small10_strict_2 <- dat_alien_small10_strict %>%  
  group_by(Region, Road, Transect,Year, fYear, Accepted.Name.MIREN) %>% 
  summarise(across(Elevation, mean)) 

dat_alien_small10_2 <- dat_alien_small10 %>%  
  group_by(Region, Road, Transect,Year, fYear, Accepted.Name.MIREN) %>% 
  summarise(across(Elevation, mean))

# standardize elevation
dat_alien2 <- dat_alien2 %>%
  group_by(Region) %>%
  mutate(elev_stand = scale(Elevation, scale = TRUE, center = TRUE))

dat_alien_small5_2 <- dat_alien_small5_2 %>%
  group_by(Region) %>%
  mutate(elev_stand = scale(Elevation, scale = TRUE, center = TRUE))

dat_alien_small10_strict_2 <- dat_alien_small10_strict_2 %>%
  group_by(Region) %>%
  mutate(elev_stand = scale(Elevation, scale = TRUE, center = TRUE))

dat_alien_small10_2 <- dat_alien_small10_2 %>%
  group_by(Region) %>%
  mutate(elev_stand = scale(Elevation, scale = TRUE, center = TRUE))

# calculate quantiles (0.9) with dplyr 
quantiles_RG <- dat_alien2 %>%
  group_by(Region, fYear, Accepted.Name.MIREN) %>% 
  summarise(quantile = quantile(Elevation, 0.9),
            quantile_stand = quantile(elev_stand, 0.9)) %>%
  rename("species" = "Accepted.Name.MIREN") 

quantiles_RG_small5 <- dat_alien_small5_2 %>%
  group_by(Region, fYear, Accepted.Name.MIREN) %>% 
  summarise(quantile = quantile(Elevation, 0.9),
            quantile_stand = quantile(elev_stand, 0.9)) %>%
  rename("species" = "Accepted.Name.MIREN")

quantiles_RG_small10_strict <- dat_alien_small10_strict_2 %>%
  group_by(Region, fYear, Accepted.Name.MIREN) %>% 
  summarise(quantile = quantile(Elevation, 0.9),
            quantile_stand = quantile(elev_stand, 0.9)) %>%
  rename("species" = "Accepted.Name.MIREN")

quantiles_RG_small10 <- dat_alien_small10_2 %>%
  group_by(Region, fYear, Accepted.Name.MIREN) %>% 
  summarise(quantile = quantile(Elevation, 0.9),
            quantile_stand = quantile(elev_stand, 0.9)) %>%
  rename("species" = "Accepted.Name.MIREN")

quantiles <- quantiles_RG[, -5] # exclude quantiles based on standardized elevation
quantiles_stand <- quantiles_RG[, -4] # exclude quantiles based on original elevation

quantiles_small5 <- quantiles_RG_small5[, -5] # exclude quantiles based on standardized elevation
quantiles_stand_small5 <- quantiles_RG_small5[, -4] # exclude quantiles based on original elevation

quantiles_small10_strict <- quantiles_RG_small10_strict[, -5] # exclude quantiles based on standardized elevation
quantiles_stand_small10_strict <- quantiles_RG_small10_strict[, -4] # exclude quantiles based on original elevation

quantiles_small10 <- quantiles_RG_small10[, -5] # exclude quantiles based on standardized elevation
quantiles_stand_small10 <- quantiles_RG_small10[, -4] # exclude quantiles based on original elevation

# generate data frame containing every country only once (longest period) + add timespan (do for both original elevation and standardized elevation)
# make function to apply to all data frames 
red_fun_al <- function(dat) { # alien data
  dat %>%
    mutate(timespan = ifelse(Region %in% c("AUN", "CLC", "CLS", "CHE", "MTN", "ESP") & (fYear == 0 | fYear == 10), 10, 1),
           timespan = ifelse(Region %in% c("ORE", "HWI", "AUV", "IND", "NOR"), 5, timespan)) %>%
    filter(timespan != 1) %>%
    mutate(sampling_timepoint = ifelse(Region %in% c("AUN", "CLC", "CLS", "CHE", "MTN", "ESP", "ORE", "HWI") & fYear == 0, "timepoint_1", "timepoint_2"),
           sampling_timepoint = ifelse(Region %in% c("IND", "AUV", "NOR") & fYear == 5, "timepoint_1", sampling_timepoint)) %>%
    ungroup() %>%
    dplyr::select(-fYear)
}

# apply function to spread data sheets
quantiles_red <- red_fun_al(quantiles)
quantiles_red_stand <- red_fun_al(quantiles_stand)

quantiles_red_small5 <- red_fun_al(quantiles_small5)
quantiles_red_stand_small5 <- red_fun_al(quantiles_stand_small5)

quantiles_red_small10_strict <- red_fun_al(quantiles_small10_strict)
quantiles_red_stand_small10_strict <- red_fun_al(quantiles_stand_small10_strict)

quantiles_red_small10 <- red_fun_al(quantiles_small10)
quantiles_red_stand_small10 <- red_fun_al(quantiles_stand_small10)

# make wide dataframe
quant_wide_red <- wide_fun(quantiles_red, "sampling_timepoint", "quantile")
quant_wide_red_stand <- wide_fun(quantiles_red_stand, "sampling_timepoint", "quantile_stand")

quant_wide_red_small5 <- wide_fun(quantiles_red_small5, "sampling_timepoint", "quantile")
quant_wide_red_stand_small5 <- wide_fun(quantiles_red_stand_small5, "sampling_timepoint", "quantile_stand")

quant_wide_red_small10_strict <- wide_fun(quantiles_red_small10_strict, "sampling_timepoint", "quantile")
quant_wide_red_stand_small10_strict <- wide_fun(quantiles_red_stand_small10_strict, "sampling_timepoint", "quantile_stand")

quant_wide_red_small10 <- wide_fun(quantiles_red_small10, "sampling_timepoint", "quantile")
quant_wide_red_stand_small10 <- wide_fun(quantiles_red_stand_small10, "sampling_timepoint", "quantile_stand")

# calculate spread for normal elevation
quant_wide_red <- quant_wide_red %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

quant_wide_red_small5 <- quant_wide_red_small5 %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

quant_wide_red_small10_strict <- quant_wide_red_small10_strict %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

quant_wide_red_small10 <- quant_wide_red_small10 %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

# based on standardized elevation:
quant_wide_red_stand <- quant_wide_red_stand %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

quant_wide_red_stand_small5 <- quant_wide_red_stand_small5 %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

quant_wide_red_stand_small10_strict <- quant_wide_red_stand_small10_strict %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

quant_wide_red_stand_small10 <- quant_wide_red_stand_small10 %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

# calculate frequency of species (# TRANSECTS they appear in) 
# exclude in-between years from complete data frame aggregated at transect level
dat_alien_red <- red_fun_al(dat_alien2)

dat_alien_red_small5 <- red_fun_al(dat_alien_small5_2)

dat_alien_red_small10_strict <- red_fun_al(dat_alien_small10_strict_2)

dat_alien_red_small10 <- red_fun_al(dat_alien_small10_2)

# count number of TRANSECTS species appear in
dat_alien_freq <- dat_alien_red %>%
  group_by(Region, Accepted.Name.MIREN) %>%
  summarise(frequency = n()) %>%
  rename("species" = "Accepted.Name.MIREN")

dat_alien_freq_small5 <- dat_alien_red_small5 %>%
  group_by(Region, Accepted.Name.MIREN) %>%
  summarise(frequency = n()) %>%
  rename("species" = "Accepted.Name.MIREN")

dat_alien_freq_small10_strict <- dat_alien_red_small10_strict %>%
  group_by(Region, Accepted.Name.MIREN) %>%
  summarise(frequency = n()) %>%
  rename("species" = "Accepted.Name.MIREN")

dat_alien_freq_small10 <- dat_alien_red_small10 %>%
  group_by(Region, Accepted.Name.MIREN) %>%
  summarise(frequency = n()) %>%
  rename("species" = "Accepted.Name.MIREN")

# add number of transects to spread data sets
quant_wide_red_stand_freq <- left_join(quant_wide_red_stand, dat_alien_freq, by = c("Region", "species"))
quant_wide_red_freq <- left_join(quant_wide_red, dat_alien_freq, by = c("Region", "species"))

quant_wide_red_stand_freq_small5 <- left_join(quant_wide_red_stand_small5, dat_alien_freq_small5, by = c("Region", "species"))
quant_wide_red_freq_small5 <- left_join(quant_wide_red_small5, dat_alien_freq_small5, by = c("Region", "species"))

quant_wide_red_stand_freq_small10_strict <- left_join(quant_wide_red_stand_small10_strict, dat_alien_freq_small10_strict, by = c("Region", "species"))
quant_wide_red_freq_small10_strict <- left_join(quant_wide_red_small10_strict, dat_alien_freq_small10_strict, by = c("Region", "species"))

quant_wide_red_stand_freq_small10 <- left_join(quant_wide_red_stand_small10, dat_alien_freq_small10, by = c("Region", "species"))
quant_wide_red_freq_small10 <- left_join(quant_wide_red_small10, dat_alien_freq_small10, by = c("Region", "species"))

# calculate spread per year as well as spread over whole time period
quant_wide_red_freq <- quant_wide_red_freq %>%
  mutate(spread_annual = spread / timespan) 
quant_wide_red_stand_freq <- quant_wide_red_stand_freq %>%
  mutate(spread_annual = spread / timespan) 

quant_wide_red_freq_small5 <- quant_wide_red_freq_small5 %>%
  mutate(spread_annual = spread / timespan) 
quant_wide_red_stand_freq_small5 <- quant_wide_red_stand_freq_small5 %>%
  mutate(spread_annual = spread / timespan) 

quant_wide_red_freq_small10_strict <- quant_wide_red_freq_small10_strict %>%
  mutate(spread_annual = spread / timespan) 
quant_wide_red_stand_freq_small10_strict <- quant_wide_red_stand_freq_small10_strict %>%
  mutate(spread_annual = spread / timespan) 

quant_wide_red_freq_small10 <- quant_wide_red_freq_small10 %>%
  mutate(spread_annual = spread / timespan) 
quant_wide_red_stand_freq_small10 <- quant_wide_red_stand_freq_small10 %>%
  mutate(spread_annual = spread / timespan) 

### DISTANCES BETWEEN TRANSECTS ###############################################################################################

# check differences between ROADS in elevational steps 
dat_alien_elev <- dat_alien %>% group_by(Region, Road, Transect) %>% summarize(mean_elev=mean(Elevation))

elev_diff_Road <- dat_alien_elev %>%
  ungroup() %>%
  dplyr::select(Region, Road, mean_elev) %>%
  group_by(Road) %>%
  arrange(mean_elev) %>%
  distinct() %>%
  mutate(distance = mean_elev - lag(mean_elev, default = mean_elev[1])) %>%
  filter(distance != 0)

mean_elev_rd <- elev_diff_Road %>%
  group_by(Region, Road) %>%
  summarize(mean_diff = mean(distance),
            sd_diff = sd(distance))

# mean distance in Hawaii of all roads taken together
mean(mean_elev_rd[mean_elev_rd$Region=="HWI",]$mean_diff)

# calculate average distance between ALL transects of all roads per region (Table S4)
elev_diff_Region <- dat_alien_elev %>%
  ungroup() %>%
  dplyr::select(Region, mean_elev) %>%
  group_by(Region) %>%
  arrange(mean_elev) %>%
  mutate(distance = mean_elev - lag(mean_elev, default = mean_elev[1])) %>%
  filter(distance != 0)

mean_elev_reg <- elev_diff_Region %>%
  group_by(Region) %>%
  summarize(mean_diff = mean(distance),
            sd_diff = sd(distance))

# How many species occur alongside only one road?
sp_HWI <- dat_alien2 %>%
  ungroup() %>%
  dplyr::select(Region, Road, Accepted.Name.MIREN) %>%
  distinct() %>%
  group_by(Region, Accepted.Name.MIREN) %>%
  summarize(nr_Road = n()) %>%
  ungroup() %>%
  group_by(Region, nr_Road) %>%
  summarize(nr_tot = n()) %>%
  group_by(Region) %>%
  mutate(percentage = 100/sum(nr_tot) * nr_tot)

### SPREAD ANALYSIS: RANGE-SHIFTS OVER TIME ###################################################################################

# model overall standardized spread across all Regions 
s0 <- lmer(spread~1 + (1|Region), data = quant_wide_red_stand_freq, weights = frequency)
summary(s0)
fix.check(s0)

# model spread within Regions, considering frequency of species and unstandardized elevation
# get p-values for spread in Regions separately
estimates_spread_freq <- data.frame()
levels <- unique(quant_wide_red_freq$Region)
x <- quant_wide_red_freq

for (i in 1:11) {
  reg <- levels(as.factor(x$Region))[i]
  mod <- lm(spread ~1, data = x[x$Region == reg,], weights = frequency)
  estimates_spread_freq[i, 1] <- paste(levels[i])
  estimates_spread_freq[i, 2] <- summary(mod)$coef[1]
  estimates_spread_freq[i, 3] <- summary(mod)$coef[2]
  estimates_spread_freq[i, 4] <- summary(mod)$coef[3] 
  estimates_spread_freq[i, 5] <- summary(mod)$coef[4]
  estimates_spread_freq[i, 6] <- length(mod$fitted)
  estimates_spread_freq[i, 7] <- predict(mod, newdata = x[x$Region == reg,], interval = 'confidence')[1,2]
  estimates_spread_freq[i, 8] <- predict(mod, newdata = x[x$Region == reg,], interval = 'confidence')[1,3]
  }

colnames(estimates_spread_freq) <- c("Region", "estimate", "std_error", "t_value","p_value", "N", "lwr", "upr")
estimates_spread_freq[,2:5] <- round(estimates_spread_freq[,2:5],3)

write.table(estimates_spread_freq, "TableS1_WeightedMeanShifts.txt",row.names=FALSE)

# model spread within regions, considering frequency of species and unstandardized elevation FOR ANNUAL SHIFTS
# get p-values for spread in regions separately
estimates_spread_freq_annual <- data.frame()
levels <- unique(quant_wide_red_freq$Region)
x <- quant_wide_red_freq

for (i in 1:11) {
  reg <- levels(as.factor(x$Region))[i]
  mod <- lm(spread_annual ~1, data = x[x$Region == reg,], weights = frequency)
  estimates_spread_freq_annual[i, 1] <- paste(levels[i])
  estimates_spread_freq_annual[i, 2] <- summary(mod)$coef[1]
  estimates_spread_freq_annual[i, 3] <- summary(mod)$coef[2]
  estimates_spread_freq_annual[i, 4] <- summary(mod)$coef[3] 
  estimates_spread_freq_annual[i, 5] <- summary(mod)$coef[4]
  estimates_spread_freq_annual[i, 6] <- length(mod$fitted)
  estimates_spread_freq_annual[i, 7] <- predict(mod, newdata = x[x$Region == reg,], interval = 'confidence')[1,2]
  estimates_spread_freq_annual[i, 8] <- predict(mod, newdata = x[x$Region == reg,], interval = 'confidence')[1,3]
}

colnames(estimates_spread_freq_annual) <- c("Region", "estimate", "std_error", "t_value","p_value", "N", "lwr", "upr")
estimates_spread_freq_annual[,2:5] <- round(estimates_spread_freq_annual[,2:5],3)

write.table(estimates_spread_freq, "TableS2_WeightedMeanShiftsAnnual.txt",row.names=FALSE)

# model spread within regions, considering frequency of species and unstandardized elevation (>5 FILTER)
# get p-values for spread in regions separately
estimates_spread_freq_small5 <- data.frame()
levels <- unique(quant_wide_red_freq_small5$Region)
x <- quant_wide_red_freq_small5

for (i in 1:11) {
  reg <- levels(as.factor(x$Region))[i]
  mod <- lm(spread ~1, data = x[x$Region == reg,], weights = frequency)
  estimates_spread_freq_small5[i, 1] <- paste(levels[i])
  estimates_spread_freq_small5[i, 2] <- summary(mod)$coef[1]
  estimates_spread_freq_small5[i, 3] <- summary(mod)$coef[2]
  estimates_spread_freq_small5[i, 4] <- summary(mod)$coef[3] 
  estimates_spread_freq_small5[i, 5] <- summary(mod)$coef[4]
  estimates_spread_freq_small5[i, 6] <- length(mod$fitted)
  estimates_spread_freq_small5[i, 7] <- predict(mod, newdata = x[x$Region == reg,], interval = 'confidence')[1,2]
  estimates_spread_freq_small5[i, 8] <- predict(mod, newdata = x[x$Region == reg,], interval = 'confidence')[1,3]
}

colnames(estimates_spread_freq_small5) <- c("Region", "estimate", "std_error", "t_value","p_value", "N", "lwr", "upr")
estimates_spread_freq_small5[,2:5] <- round(estimates_spread_freq_small5[,2:5],3)

write.table(estimates_spread_freq_small5, "TableS3_WeightedMeanShifts_small5.txt",row.names=FALSE)

# model spread within regions, considering frequency of species and unstandardized elevation (>10 STRICT FILTER)
# get p-values for spread in regions separately
estimates_spread_freq_small10_strict <- data.frame()
levels <- unique(quant_wide_red_freq_small10_strict$Region)
x <- quant_wide_red_freq_small10_strict

for (i in 1:11) {
  reg <- levels(as.factor(x$Region))[i]
  mod <- lm(spread ~1, data = x[x$Region == reg,], weights = frequency)
  estimates_spread_freq_small10_strict[i, 1] <- paste(levels[i])
  estimates_spread_freq_small10_strict[i, 2] <- summary(mod)$coef[1]
  estimates_spread_freq_small10_strict[i, 3] <- summary(mod)$coef[2]
  estimates_spread_freq_small10_strict[i, 4] <- summary(mod)$coef[3] 
  estimates_spread_freq_small10_strict[i, 5] <- summary(mod)$coef[4]
  estimates_spread_freq_small10_strict[i, 6] <- length(mod$fitted)
  estimates_spread_freq_small10_strict[i, 7] <- predict(mod, newdata = x[x$Region == reg,], interval = 'confidence')[1,2]
  estimates_spread_freq_small10_strict[i, 8] <- predict(mod, newdata = x[x$Region == reg,], interval = 'confidence')[1,3]
}

colnames(estimates_spread_freq_small10_strict) <- c("Region", "estimate", "std_error", "t_value","p_value", "N", "lwr", "upr")
estimates_spread_freq_small10_strict[,2:5] <- round(estimates_spread_freq_small10_strict[,2:5],3)

write.table(estimates_spread_freq_small10_strict, "TableS3_WeightedMeanShifts_small10_strict.txt",row.names=FALSE)

# model spread within regions, considering frequency of species and unstandardized elevation (>10 FILTER)
# get p-values for spread in regions separately
estimates_spread_freq_small10 <- data.frame()
levels <- unique(quant_wide_red_freq_small10$Region)
x <- quant_wide_red_freq_small10

for (i in 1:11) {
  reg <- levels(as.factor(x$Region))[i]
  mod <- lm(spread ~1, data = x[x$Region == reg,], weights = frequency)
  estimates_spread_freq_small10[i, 1] <- paste(levels[i])
  estimates_spread_freq_small10[i, 2] <- summary(mod)$coef[1]
  estimates_spread_freq_small10[i, 3] <- summary(mod)$coef[2]
  estimates_spread_freq_small10[i, 4] <- summary(mod)$coef[3] 
  estimates_spread_freq_small10[i, 5] <- summary(mod)$coef[4]
  estimates_spread_freq_small10[i, 6] <- length(mod$fitted)
  estimates_spread_freq_small10[i, 7] <- predict(mod, newdata = x[x$Region == reg,], interval = 'confidence')[1,2]
  estimates_spread_freq_small10[i, 8] <- predict(mod, newdata = x[x$Region == reg,], interval = 'confidence')[1,3]
}

colnames(estimates_spread_freq_small10) <- c("Region", "estimate", "std_error", "t_value","p_value", "N", "lwr", "upr")
estimates_spread_freq_small10[,2:5] <- round(estimates_spread_freq_small10[,2:5],3)

write.table(estimates_spread_freq_small10, "TableS3_WeightedMeanShifts_small10.txt",row.names=FALSE)


# export range shift table for all species as txt file for supplements
write.table(quant_wide_red_freq[, c(1,2,7,8)], file = "MIREN_RangeShifts_all_species.txt", sep = "\t", row.names = FALSE)


### SPREAD ANALYSIS: RANGE-SHIFTS & Elevation #################################################################################

# model spread with elevation: across regions
# region as RANDOM effect, species WEIGHTED by frequency, STANDARIZED elevation
s0_abs <- lmer(spread ~ timepoint_1 + (1|Region), data = quant_wide_red_stand_freq, weights= frequency, REML = TRUE)
summary(s0_abs)
anova(s0_abs)
fix.check(s0_abs)

# model spread with elevation: separate for all regions
# species NOT WEIGHTED by frequency, NON-STANDARIZED elevation (Table S7)
estimates_spread_el <- data.frame()
levels <- unique(quant_wide_red_freq$Region)
x <- quant_wide_red_freq

for (i in 1:11) {
  reg <- levels(as.factor(x$Region))[i]
  mod <- lm(spread ~timepoint_1, data = x[x$Region == reg,])
  estimates_spread_el[i, 1] <- paste(levels[i])
  estimates_spread_el[i, 2] <- summary(mod)$coef[1]
  estimates_spread_el[i, 3] <- summary(mod)$coef[3]
  estimates_spread_el[i, 4] <- summary(mod)$coef[2]
  estimates_spread_el[i, 5] <- summary(mod)$coef[4] 
  estimates_spread_el[i, 6] <- summary(mod)$fstatistic[1]
  estimates_spread_el[i, 7] <- summary(mod)$fstatistic[3]
  estimates_spread_el[i, 8] <- pf(summary(mod)$fstatistic[1],summary(mod)$fstatistic[2],summary(mod)$fstatistic[3],lower.tail=FALSE)
}

estimates_spread_el[,2:8] <- round(estimates_spread_el[,2:8],3)
colnames(estimates_spread_el) <- c("Region", "estimate_inter", "std_error_inter", "estimate_slope", "std_error_slope", 
                                   "F", "df", "p_Elevation")

write.table(estimates_spread_el, "TableS7_NOTWeightedShiftsElevation.txt",row.names=FALSE)

# model spread with elevation: separate for all regions
# species WEIGHTED by frequency, NON-STANDARIZED elevation (Table S6)
estimates_spread_el_we <- data.frame()
levels <- unique(quant_wide_red_freq$Region)
x <- quant_wide_red_freq

for (i in 1:11) {
  reg <- levels(as.factor(x$Region))[i]
  mod <- lm(spread ~timepoint_1, data = x[x$Region == reg,], weights=frequency)
  estimates_spread_el_we[i, 1] <- paste(levels[i])
  estimates_spread_el_we[i, 2] <- summary(mod)$coef[1]
  estimates_spread_el_we[i, 3] <- summary(mod)$coef[3]
  estimates_spread_el_we[i, 4] <- summary(mod)$coef[2]
  estimates_spread_el_we[i, 5] <- summary(mod)$coef[4] 
  estimates_spread_el_we[i, 6] <- summary(mod)$fstatistic[1]
  estimates_spread_el_we[i, 7] <- summary(mod)$fstatistic[3]
  estimates_spread_el_we[i, 8] <- pf(summary(mod)$fstatistic[1],summary(mod)$fstatistic[2],summary(mod)$fstatistic[3],lower.tail=FALSE)
}

estimates_spread_el_we[,2:8] <- round(estimates_spread_el_we[,2:8],3)
colnames(estimates_spread_el_we) <- c("Region", "estimate_inter", "std_error_inter", "estimate_slope", "std_error_slope", 
                                   "F", "df", "p_Elevation")

write.table(estimates_spread_el_we, "TableS6_WeightedShiftsElevation.txt",row.names=FALSE)

### SPREAD ANALYSIS: DO CI OF REGRESSIONS INCLUDE 0 AT MIDPOINT? ##############################################################

# get fitted values plus CI at midpoints of elevational range for each region (>1 FILTER)
mids <- data.frame(matrix(data=NA,nrow=length(unique(quant_wide_red_freq$Region)),ncol=5,byrow=FALSE))
names(mids)<-c("Region","N", "fit","lwr","upr")

for (k in 1:dim(mids)[1]){
  reg <- levels(as.factor(quant_wide_red_freq$Region))[k] 
  r <-subset(quant_wide_red_freq, quant_wide_red_freq$Region==reg)
  mids[k,1] <- reg 
  x<-r$timepoint_1
  mod <- lm(r$spread ~ x, weights=r$frequency)
  mids[k,2] <- length(r[r$Region == reg,]$species)
  new.dat <- data.frame(x=(max(r$timepoint_1)-min(r$timepoint_1))/2+min(r$timepoint_1))
  mids[k,3:5] <- predict(mod, newdata = new.dat, interval = 'confidence')
}

# get fitted values plus CI at midpoints of elevational range for each region (>1 FILTER FOR ANNUAL SHIFTS)
mids_annual <- data.frame(matrix(data=NA,nrow=length(unique(quant_wide_red_freq$Region)),ncol=5,byrow=FALSE))
names(mids_annual)<-c("Region","N", "fit","lwr","upr")

for (k in 1:dim(mids_annual)[1]){
  reg <- levels(as.factor(quant_wide_red_freq$Region))[k] 
  r_annual <-subset(quant_wide_red_freq, quant_wide_red_freq$Region==reg)
  mids_annual[k,1] <- reg 
  x<-r_annual$timepoint_1
  mod_annual <- lm(r_annual$spread_annual ~ x, weights=r_annual$frequency)
  mids_annual[k,2] <- length(r_annual[r_annual$Region == reg,]$species)
  new.dat_annual <- data.frame(x=(max(r_annual$timepoint_1)-min(r_annual$timepoint_1))/2+min(r_annual$timepoint_1))
  mids_annual[k,3:5] <- predict(mod_annual, newdata = new.dat_annual, interval = 'confidence')
}

# get fitted values plus CI at midpoints of elevational range for each region (>5 FILTER)
mids_small5 <- data.frame(matrix(data=NA,nrow=length(unique(quant_wide_red_freq_small5$Region)),ncol=5,byrow=FALSE))
names(mids_small5)<-c("Region", "N","fit","lwr","upr")

for (k in 1:dim(mids_small5)[1]){
  reg <- levels(as.factor(quant_wide_red_freq_small5$Region))[k] 
  r_small5 <-subset(quant_wide_red_freq_small5, quant_wide_red_freq_small5$Region==reg)
  mids_small5[k,1] <- reg
  x<-r_small5$timepoint_1
  mod_small5 <- lm(r_small5$spread ~ x, weights=r_small5$frequency)
  mids_small5[k,2] <- length(r_small5[r_small5$Region == reg,]$species)
  new.dat_small5 <- data.frame(x=(max(r_small5$timepoint_1)-min(r_small5$timepoint_1))/2+min(r_small5$timepoint_1))
  mids_small5[k,3:5] <- predict(mod_small5, newdata = new.dat_small5, interval = 'confidence')
}

# get fitted values plus CI at midpoints of elevational range for each region (>10 FILTER)
mids_small10 <- data.frame(matrix(data=NA,nrow=length(unique(quant_wide_red_freq_small10$Region)),ncol=5,byrow=FALSE))
names(mids_small10)<-c("Region", "N","fit","lwr","upr")

for (k in 1:dim(mids_small10)[1]){
  reg <- levels(as.factor(quant_wide_red_freq_small10$Region))[k] 
  r_small10 <-subset(quant_wide_red_freq_small10, quant_wide_red_freq_small10$Region==reg)
  mids_small10[k,1] <- reg 
  x<-r_small10$timepoint_1
  mod_small10 <- lm(r_small10$spread ~ x, weights=r_small10$frequency)
  mids_small10[k,2] <- length(r_small10[r_small10$Region == reg,]$species)
  new.dat_small10 <- data.frame(x=(max(r_small10$timepoint_1)-min(r_small10$timepoint_1))/2+min(r_small10$timepoint_1))
  mids_small10[k,3:5] <- predict(mod_small10, newdata = new.dat_small10, interval = 'confidence')
}

# get fitted values plus CI at midpoints of elevational range for each region (>10 STRICT FILTER)
mids_small10_strict <- data.frame(matrix(data=NA,nrow=length(unique(quant_wide_red_freq_small10_strict$Region)),ncol=5,byrow=FALSE))
names(mids_small10_strict)<-c("Region", "N","fit","lwr","upr")

for (k in 1:dim(mids_small10_strict)[1]){
  reg <- levels(as.factor(quant_wide_red_freq_small10_strict$Region))[k] 
  r_small10_strict <-subset(quant_wide_red_freq_small10_strict, quant_wide_red_freq_small10_strict$Region==reg)
  mids_small10_strict[k,1] <- reg 
  x<-r_small10_strict$timepoint_1
  mod_small10_strict <- lm(r_small10_strict$spread ~ x, weights=r_small10_strict$frequency)
  mids_small10_strict[k,2] <- length(r_small10_strict[r_small10_strict$Region == reg,]$species)
  new.dat_small10_strict <- data.frame(x=(max(r_small10_strict$timepoint_1)-min(r_small10_strict$timepoint_1))/2+min(r_small10_strict$timepoint_1))
  mids_small10_strict[k,3:5] <- predict(mod_small10_strict, newdata = new.dat_small10_strict, interval = 'confidence')
}


### SPREAD ANALYSIS: PLOTTING FIGURE 3 ########################################################################################

# preparation for Plotting: SHIFTS OVER WHOLE SAMPLING PERIOD
# arrange rows of regression estimates (region-specific spread regressions) by size
estimates_spread_freq2 <- estimates_spread_freq[order(estimates_spread_freq$estimate, decreasing=TRUE),]
estimates_spread_freq2 <- estimates_spread_freq2[, c(1, 2, 6, 7, 8)]
rownames(estimates_spread_freq2) <- 1:nrow(estimates_spread_freq2)

estimates_spread_freq2[6] <- sub("^", "", estimates_spread_freq2$N)
colnames(estimates_spread_freq2)[6] <- "N_text"

n_text <- sub("^", "", estimates_spread_freq2$N)

# arrange rows of predicted regression values at midpoint in same order
mids2 <-  mids[match(estimates_spread_freq2$Region, mids$Region),]                                                   
rownames(mids2) <- 1:nrow(mids2)

# Plotting (> 1)
pdf("Fig3_221013.pdf", width= 4,height= 6, useDingbats=FALSE)                                                      
par(mfrow=c(2,1),xaxs="i",yaxs="i",tck=0.02, mar=c(1,2,0.5,0.5), oma=c(6,2,0,0),bty="l", cex=0.9)

ylimit <- c(-250,250)
xlimit <- c(0.5,11.5)
plot(estimates_spread_freq2$Region, estimates_spread_freq2$estimate, xlab="", ylab="",xlim=xlimit,ylim=ylimit,cex=1, pch=21,bg="grey", frame.plot=T, axes=F,type="n")
axis(1, line=0, mgp = c(1.1,0,0),cex.axis=0.8, at=1:11, labels=rep("",11), las=2, hadj=1.5)
axis(2, line=0, mgp = c(1.1,0,0),cex.axis=0.8)
abline(0,0,col="grey")
bars2(1:11,estimates_spread_freq2$estimate,estimates_spread_freq2$lwr,estimates_spread_freq2$upr,1)
points(1:11, estimates_spread_freq2$estimate,cex=1.2, pch=21,bg= mycol[ match(estimates_spread_freq2$Region,names(mycol))])

sigs <- ifelse(rowSums(sign(estimates_spread_freq2[,4:5]))!=0, 1, 0)
sig <- as.numeric(rownames(estimates_spread_freq2[sigs==1,])); nsig <- as.numeric(rownames(estimates_spread_freq2[sigs==0,]))
sig_position <- estimates_spread_freq2[sigs==1,]$upr + 30
text(sig,sig_position,"*",cex=1.5) 

#text(c(1:11), -220, estimates_spread_freq2$N_text, cex = 0.8, font = 3) 

text(11,250-35, "A")

ylimit <- c(-200,300)
xlimit <- c(0.5,11.5)
plot(mids2$Region, mids2$fit, xlab="", ylab="",xlim=xlimit,ylim=ylimit,cex=1, pch=21,bg="grey", frame.plot=T, axes=F,type="n")

axis(1, line=0, mgp = c(1.1,0,0),cex.axis=0.8, at=c(2,5,6,8,11), labels= reg_labels2[ match(mids2$Region, labels(reg_labels2))][c(2,5,6,8,11)], las=2, hadj=1.1)
axis(1, line=0, mgp = c(1.1,0,0),cex.axis=0.8, at=c(1,3,4,7,9,10), labels= reg_labels2[ match(mids2$Region, labels(reg_labels2))][c(1,3,4,7,9,10)], font = 2, las=2, hadj=1.1)

axis(2, line=0, mgp = c(1.1,0,0),cex.axis=0.8)
abline(0,0,col="grey")
bars2(1:11,mids2$fit,mids2$lwr,mids2$upr,1)
points(1:11, mids2$fit,cex=1.2, pch=21,bg= mycol[ match(mids2$Region,names(mycol))])

sigs <-  ifelse(sign(mids2[,3]) == 1, ifelse(rowSums(sign(mids2[,3:4]))!=0, 1, 0), ifelse(rowSums(sign(mids2[,c(3,5)]))!=0, 1, 0))
sig <- as.numeric(rownames(mids2[sigs==1,])); nsig <- as.numeric(rownames(mids2[sigs==0,]))
sig_position <- mids2[sigs==1,]$upr + 30
text(sig,sig_position,"*",cex=1.5)

text(c(1:11), -170, estimates_spread_freq2$N_text, cex = 0.8, font = 3)

text(11,300-44, "B")

mtext("Change in upper Elevation limit (m)",adj=0.95,side=2,line=0,outer=TRUE,cex=0.75)
mtext("Mean change in upper Elevation limit\n at centre of Elevation gradient (m)",adj=0.05,side=2,line=0,outer=TRUE,cex=0.75)

dev.off()

# preparation for plotting: ANNUAL SHIFTS
# arrange rows of regression estimates (region-specific spread regressions) by size
estimates_spread_freq_annual2 <- estimates_spread_freq_annual[order(estimates_spread_freq_annual$estimate, decreasing=TRUE),]
rownames(estimates_spread_freq_annual2) <- 1:nrow(estimates_spread_freq_annual2)

estimates_spread_freq_annual2[6] <- sub("^", "", estimates_spread_freq_annual2$N)
colnames(estimates_spread_freq_annual2)[6] <- "N_text"

n_text <- sub("^", "", estimates_spread_freq_annual2$N)

# arrange rows of predicted regression values at midpoint in same order
mids_annual2 <-  mids_annual[match(estimates_spread_freq_annual2$Region, mids_annual$Region),]                                                   
rownames(mids_annual2) <- 1:nrow(mids_annual2)

# Plotting (> 1)
pdf("FigS1_221013.pdf", width= 4,height= 6, useDingbats=FALSE)                                                      
par(mfrow=c(2,1),xaxs="i",yaxs="i",tck=0.02, mar=c(1,2,0.5,0.5), oma=c(6,2,0,0),bty="l", cex=0.9)

ylimit <- c(-50, 42)
xlimit <- c(0.5,11.5)
plot(estimates_spread_freq_annual2$Region, estimates_spread_freq_annual2$estimate, xlab="", ylab="",xlim=xlimit,ylim=ylimit,cex=1, pch=21,bg="grey", frame.plot=T, axes=F,type="n")
axis(1, line=0, mgp = c(1.1,0,0),cex.axis=0.8, at=1:11, labels=rep("",11), las=2, hadj=1.5)
axis(2, line=0, mgp = c(1.1,0,0),cex.axis=0.8)
abline(0,0,col="grey")
bars2(1:11,estimates_spread_freq_annual2$estimate,estimates_spread_freq_annual2$lwr,estimates_spread_freq_annual2$upr,1)
points(1:11, estimates_spread_freq_annual2$estimate,cex=1.2, pch=21,bg= mycol[ match(estimates_spread_freq_annual2$Region,names(mycol))])

sigs <- ifelse(rowSums(sign(estimates_spread_freq_annual2[,7:8]))!=0, 1, 0)
sig <- as.numeric(rownames(estimates_spread_freq_annual2[sigs==1,])); nsig <- as.numeric(rownames(estimates_spread_freq_annual2[sigs==0,]))
sig_position <- estimates_spread_freq_annual2[sigs==1,]$upr + 5
text(sig,sig_position,"*",cex=1.5) 

text(11,38, "A", cex = 1.5)

ylimit <- c(-40,50)
xlimit <- c(0.5,11.5)
plot(mids_annual2$Region, mids_annual2$fit, xlab="", ylab="",xlim=xlimit,ylim=ylimit,cex=1, pch=21,bg="grey", frame.plot=T, axes=F,type="n")

axis(1, line=0, mgp = c(1.1,0,0),cex.axis=0.8, at=c(1,3,4,6,11), labels= reg_labels2[ match(mids_annual2$Region, labels(reg_labels2))][c(1,3,4,6,11)], las=2, hadj=1.1)
axis(1, line=0, mgp = c(1.1,0,0),cex.axis=0.8, at=c(2,5,7,8,9,10), labels= reg_labels2[ match(mids_annual2$Region, labels(reg_labels2))][c(2,5,7,8,9,10)], font = 2, las=2, hadj=1.1)

axis(2, line=0, mgp = c(1.1,0,0),cex.axis=0.8)
abline(0,0,col="grey")
bars2(1:11,mids_annual2$fit,mids_annual2$lwr,mids_annual2$upr,1)
points(1:11, mids_annual2$fit,cex=1.2, pch=21,bg= mycol[ match(mids_annual2$Region,names(mycol))])

sigs <-  ifelse(sign(mids_annual2[,3]) == 1, ifelse(rowSums(sign(mids_annual2[,4:5]))!=0, 1, 0), ifelse(rowSums(sign(mids_annual2[,c(3,5)]))!=0, 1, 0))
sig <- as.numeric(rownames(mids_annual2[sigs==1,])); nsig <- as.numeric(rownames(mids_annual2[sigs==0,]))
sig_position <- mids_annual2[sigs==1,]$upr + 5
text(sig,sig_position,"*",cex=1.5)

text(c(1:11), -36, estimates_spread_freq_annual2$N_text, cex = 0.8, font = 3)

text(11,46, "B", cex = 1.5)

mtext("Change in upper Elevation limit (m)",adj=0.95,side=2,line=0,outer=TRUE,cex=0.75)
mtext("Mean change in upper Elevation limit\n at centre of Elevation gradient (m)",adj=0.05,side=2,line=0,outer=TRUE,cex=0.75)

dev.off()


###############################################################################################################################
## EXPLORE Road VS. AWAY ###################################################################################################### 
###############################################################################################################################

### EXPLORE Road VS. AWAY SHIFTS ##############################################################################################

# aggregate data to transect level 
dat_alien_Road5_2 <- dat_alien_Road5 %>%  
  group_by(Region, Road, Transect,Year, fYear, Accepted.Name.MIREN) %>% 
  summarise(across(Elevation, mean)) 

dat_alien_away5_2 <- dat_alien_away5 %>%  
  group_by(Region, Road, Transect,Year, fYear, Accepted.Name.MIREN) %>% 
  summarise(across(Elevation, mean)) 

# standardize elevation
dat_alien_Road5_2 <- dat_alien_Road5_2 %>%
  group_by(Region) %>%
  mutate(elev_stand = scale(Elevation, scale = TRUE, center = TRUE))

dat_alien_away5_2 <- dat_alien_away5_2 %>%
  group_by(Region) %>%
  mutate(elev_stand = scale(Elevation, scale = TRUE, center = TRUE))

# calculate quantiles (0.9) with dplyr 
quantiles_RG_Road5 <- dat_alien_Road5_2 %>%
  group_by(Region, fYear, Accepted.Name.MIREN) %>% 
  summarise(quantile = quantile(Elevation, 0.9),
            quantile_stand = quantile(elev_stand, 0.9)) %>%
  rename("species" = "Accepted.Name.MIREN") 

quantiles_RG_away5 <- dat_alien_away5_2 %>%
  group_by(Region, fYear, Accepted.Name.MIREN) %>% 
  summarise(quantile = quantile(Elevation, 0.9),
            quantile_stand = quantile(elev_stand, 0.9)) %>%
  rename("species" = "Accepted.Name.MIREN")

quantiles_Road5 <- quantiles_RG_Road5[, -5] # exclude quantiles based on standardized Elevation
quantiles_stand_Road5 <- quantiles_RG_Road5[, -4] # exclude quantiles based on original Elevation

quantiles_away5 <- quantiles_RG_away5[, -5] # exclude quantiles based on standardized Elevation
quantiles_stand_away5 <- quantiles_RG_away5[, -4] # exclude quantiles based on original Elevation

# apply function to spread data sheets
quantiles_red_Road5 <- red_fun_al(quantiles_Road5)
quantiles_red_stand_Road5 <- red_fun_al(quantiles_stand_Road5)

quantiles_red_away5 <- red_fun_al(quantiles_away5)
quantiles_red_stand_away5 <- red_fun_al(quantiles_stand_away5)

# make wide dataframe
quant_wide_red_Road5 <- wide_fun(quantiles_red_Road5, "sampling_timepoint", "quantile")
quant_wide_red_stand_Road5 <- wide_fun(quantiles_red_stand_Road5, "sampling_timepoint", "quantile_stand")

quant_wide_red_away5 <- wide_fun(quantiles_red_away5, "sampling_timepoint", "quantile")
quant_wide_red_stand_away5 <- wide_fun(quantiles_red_stand_away5, "sampling_timepoint", "quantile_stand")

# calculate spread for normal elevation
quant_wide_red_Road5 <- quant_wide_red_Road5 %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

quant_wide_red_away5 <- quant_wide_red_away5 %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

# based on standardized elevation:
quant_wide_red_stand_Road5 <- quant_wide_red_stand_Road5 %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

quant_wide_red_stand_away5 <- quant_wide_red_stand_away5 %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # get rid of all species not occurring both at timepoint_1 and timepoint_2

# calculate frequency of species (# transects they appear in) 
# exclude in-between years from complete data frame aggregated at transect level
dat_alien_red_Road5 <- red_fun_al(dat_alien_Road5_2)

dat_alien_red_away5 <- red_fun_al(dat_alien_away5_2)

# count number of transects species appear in
dat_alien_freq_Road5 <- dat_alien_red_Road5 %>%
  group_by(Region, Accepted.Name.MIREN) %>%
  summarise(frequency = n()) %>%
  rename("species" = "Accepted.Name.MIREN")

dat_alien_freq_away5 <- dat_alien_red_away5 %>%
  group_by(Region, Accepted.Name.MIREN) %>%
  summarise(frequency = n()) %>%
  rename("species" = "Accepted.Name.MIREN")

# add number of transects to spread data sets
quant_wide_red_stand_freq_Road5 <- left_join(quant_wide_red_stand_Road5, dat_alien_freq_Road5, by = c("Region", "species"))
quant_wide_red_freq_Road5 <- left_join(quant_wide_red_Road5, dat_alien_freq_Road5, by = c("Region", "species"))

quant_wide_red_stand_freq_away5 <- left_join(quant_wide_red_stand_away5, dat_alien_freq_away5, by = c("Region", "species"))
quant_wide_red_freq_away5 <- left_join(quant_wide_red_away5, dat_alien_freq_away5, by = c("Region", "species"))


# add the two data sets together
a <- quant_wide_red_freq_Road5 %>%
  mutate(Plot = "Road")
b <- quant_wide_red_freq_away5 %>%
  mutate(Plot = "away")

spread_road_away <- bind_rows(a, b)

# before modelling: exclude AUV & HWI (no away) as well as NOR (not enough away after filtering)
spread_road_away_mod <- spread_road_away %>%
  filter(!Region %in% c("AUV", "HWI", "NOR"))

# model spread with road vs. away as fixed variable
mod0 <- lmer(spread~1 + (1|Region), data = spread_road_away_mod, weights = frequency)
summary(mod0)
fix.check(mod0)

mod1 <- lmer(spread~Plot + (1|Region), data = spread_road_away_mod, weights = frequency)
summary(mod1)
fix.check(mod1)

anova(mod0, mod1)

# model spread with road vs. away as fixed variable, but separately for all regions
estimates_spread_plot <- data.frame()
levels <- unique(spread_road_away_mod$Region)
x <- spread_road_away_mod

for (i in 1:8) {
  reg <- levels(as.factor(x$Region))[i]
  mod <- lm(spread ~Plot, data = x[x$Region == reg,], weights = frequency)
  estimates_spread_plot[i, 1] <- paste(levels[i])
  estimates_spread_plot[i, 2] <- summary(mod)$coef[1]
  estimates_spread_plot[i, 3] <- summary(mod)$coef[3]
  estimates_spread_plot[i, 4] <- summary(mod)$coef[2]
  estimates_spread_plot[i, 5] <- summary(mod)$coef[4] 
  estimates_spread_plot[i, 6] <- summary(mod)$fstatistic[1]
  estimates_spread_plot[i, 7] <- summary(mod)$fstatistic[3]
  estimates_spread_plot[i, 8] <- pf(summary(mod)$fstatistic[1],summary(mod)$fstatistic[2],summary(mod)$fstatistic[3],lower.tail=FALSE)
}

estimates_spread_plot[,2:8] <- round(estimates_spread_plot[,2:8],3)
colnames(estimates_spread_plot) <- c("Region", "estimate_inter", "std_error_inter", "estimate_slope", "std_error_slope", 
                                   "F", "df", "p_Plot")

###############################################################################################################################
## NULL-MODEL APPROACH GEOMETRIC CONSTRAINT ###################################################################################
###############################################################################################################################

### NULL-MODEL APPROACH: PREPARATION ##########################################################################################

# so to what extent is any upward spread greater at low elevation than would be expected were range shifts placed at random on the gradient?
# --> compare the observed regression to that expected from random placement of shifts

# load complete environmental data to extract elevations from all transects (needs complete env. data as some transects are not in dat_alien because no alien records)
env <- read.csv("MIREN_transect_elevations_221021.csv", stringsAsFactors = FALSE)

trans.data <- env %>%  
  group_by(Region,Road,Transect) %>%    
  summarise_at(vars(Elevation),list(mean))

quant_wide_red_freq$Region <- as.factor(quant_wide_red_freq$Region)


### NULL-MODEL APPROACH: DEFINE FUNCTION FOR SIMULATIONS ######################################################################

#this function contains 5 options for randomizing spread values across the elevation gradient. The one presented in the main paper is method m3 (spread values randomized among all surveyed Transects, irrespective of presence of aliens), while results of methods m2 and m5 are presented in Supplementary information of the paper 
geocon <- function(data1, data2, runs, method, fweight){	#fweight either "TRUE" or "FALSE"

start <- 1
runs <- runs # number of simulations

# prepare data frame to store all the information
dat_obs.quant <- tibble("Region" = as.character(rep(NA, length(unique(data1$Region)))), "obs.quant" = as.numeric(rep(NA, length(unique(data1$Region)))))
fitted.sim <- as.data.frame(matrix(data=NA,nrow=4,ncol=runs+2,byrow=FALSE)) #empty dataframe to contain fitted values for each alt
slopes <- data.frame(matrix(data=NA,nrow=length(levels(data1$Region)),ncol=runs+1,byrow=FALSE))

# generate fitted values for alien species
for (k in 1:length(levels(data1$Region))){ 
  
  reg <- levels(data1$Region)[k] # save the k'th Region as reg
  r <- subset(data1, data1$Region==reg) # make a subset of the data (Region k)
  tr <-subset(data2, data2$Region==reg) # make a subset of the data (Region k)
  slopes_temp <- numeric(0)
  
  # simulations
	if(method == "m1") {xs <- seq(min(tr$Elevation),max(tr$Elevation), by = 10)}  else          # spread values randomized among arbitrary Elevations (every 10 m)
	if(method == "m2") {xs <- seq(min(tr$Elevation),max(tr$Elevation), length.out = 200)}  else # spread values randomized among arbitrary Elevations (equal effort across Regions)
	if(method == "m3") {xs <- tr$Elevation}  else                                               # spread values randomized among all surveyed Transects, irrespective of presence of aliens
	if(method == "m4") {xs <- seq(min(r$timepoint_1),max(r$timepoint_1), by = 10)}  else        # range of spread constrained to range of observed initial Elevations, with 10 m Elevation intervals)
	if(method == "m5") {xs <- r$timepoint_1}                                                    # range of spread constrained to range of observed initial Elevations

  	alt <- seq(min(tr$Elevation),max(tr$Elevation),1)								# the alt range of the domain


  for (j in 1:runs){
    timepoint_1.new <- numeric(0)
    
    for (i in 1:length(r$timepoint_1)){										#for every species (initial Elevation)....
    if (sign(r$spread[i])==1) 														#if shifts are up...
    {alt.poss <- xs[xs <= max(alt)-r$spread[i]]  					#possible alts are below maximum Elevation minus that shift
    timepoint_1.new[i] <- sample(alt.poss,1)}	else				#sample new Elevation limit
    {alt.poss <- xs[xs >= min(alt)-r$spread[i]]  					#if shifts are down (or no shift)...possible alts are above min Elevation plus (i.e. - -shift) that shift
    timepoint_1.new[i] <- sample(alt.poss,1)}							#sample new Elevation limit
    }
    
    xes <- seq(min(xs), max(xs), length.out=100)
    length_fit <- length(xes)
    if(fweight=="TRUE") {Weights <- r$frequency} else {Weights <- NULL}
    
    mod <- lm(r$spread~timepoint_1.new, weights= Weights)
    fitted.sim[start:(start + length_fit -1), 1] <- reg
    fitted.sim[start:(start + length_fit -1), 2] <- xes
    fitted.sim[start:(start + length_fit -1), j+2] <- as.vector(predict(mod, list(timepoint_1.new=xes),type="response"))
    slopes[k, 1] <- reg
    slopes[k, j+1] <- coef(mod)[2]
    slopes_temp[j] <- coef(mod)[2]
  }
  
  #find where the observed slope lies in the distribution
  x<-r$timepoint_1
  mod <- lm(r$spread ~ x)
  dat_obs.quant[k, "Region"] <- as.factor(reg)
  dat_obs.quant[k, "obs.quant"] <- ecdf(slopes_temp)(coef(mod)[2])
  print(ecdf(slopes_temp)(coef(mod)[2]))
  
  # prepare for next Region
  start <- start + length_fit
  
}

  # save upper and lower confidence intervals for the k'th Region of all simulations taken together for later Plotting
  fitted_coef <- fitted.sim %>%
    mutate(mean = rowMeans(dplyr::select(., V3:dim(.)[2]))) %>%
    rowwise() %>%
    mutate(
      lo025 = quantile(c_across(V3:dim(.)[2]), prob=c(0.025,0.975))[1],
      up975 = quantile(c_across(V3:dim(.)[2]), prob=c(0.025,0.975))[2]) %>%
    rename("Region" = "V1", "Elevation" = "V2") %>%
    dplyr::select(c(Region, Elevation, mean, lo025, up975))


nam1 <- paste("dat_obs.quant",method,runs,sep="_")
assign(nam1, dat_obs.quant,envir = .GlobalEnv)
nam2 <- paste("fitted.sim",method,runs,sep="_")
assign(nam2, fitted.sim,envir = .GlobalEnv)
nam3 <- paste("slopes",method,runs,sep="_")
assign(nam3, slopes,envir = .GlobalEnv)
nam4 <- paste("fitted_coef",method,runs,sep="_")
assign(nam4, fitted_coef,envir = .GlobalEnv)

}

# NOTE: only m2, m3 and m5 are included in the paper, with m3 as the default model and m2 and m5 a more/ less conservative alternative (supplements)

### NULL-MODEL APPROACH: FUNCTION FOR PLOTING ################################################################################

# function to make a plot based on the simulations, displaying region, the quantile of the observed slope within the simulated slopes and the proportion of observed fitted values that lie outside of the confidence intervals
geocon.plot <- function(data1, data2, obsquant, fittedcoef, mycol, nam.plot,fweight){	

pdf(file= nam.plot,width= 8.5,height= 6, useDingbats=FALSE)

par(mfrow=c(3,4),xaxs="i",yaxs="i",tck=0.02, mar=c(2,2,0.5,0.5), oma=c(2,2,0,0),bty="l", cex=0.9)

for (k in 1:length(levels(data1$Region))){

reg <- levels(data1$Region)[k] 

r <- subset(data1, data1$Region==reg)
tr <-subset(data2, data2$Region==reg) 
obs.quant <- obsquant[k,2]
fc <- subset(fittedcoef, fittedcoef$Region==reg)

xlimit <- c(min(tr$Elevation)-(max(tr$Elevation)-min(tr$Elevation))/20,max(tr$Elevation)+(max(tr$Elevation)-min(tr$Elevation))/20)
ylimit <- c(min(r$spread)-(max(r$spread)-min(r$spread))/20,max(r$spread)+(max(r$spread)-min(r$spread))/20)
plot(r$timepoint_1, r$spread,xlab="", ylab="",xlim=xlimit,ylim=ylimit,cex=1, pch=21,bg="grey", frame.plot=T, axes=F,type="n")
axis(1, line=0, mgp = c(1.1,0,0),cex.axis=0.8); axis(2, line=0, mgp = c(1.1,0,0),cex.axis=0.8)

abline(0,0,col="dark grey")

polygon(c(min(tr$Elevation),	min(tr$Elevation),			max(tr$Elevation),			max(tr$Elevation)),
		c(max(tr$Elevation)-min(tr$Elevation),	max(tr$Elevation)+1000,	max(tr$Elevation)*2+1000,	0),
		col=rgb(0,0,0,0.1),border=FALSE)

polygon(c(min(tr$Elevation),	min(tr$Elevation),			max(tr$Elevation),			max(tr$Elevation)),
		c(0,					max(tr$Elevation)*-1-1000,	max(tr$Elevation)*-1-1000,	(max(tr$Elevation)-min(tr$Elevation))*-1),
		col=rgb(0,0,0,0.1),border=FALSE)

# define alphas over all regions together (not separately for regions)
Weights <- data.frame(weights = data1$frequency, region = data1$Region)
newColor<-col2rgb(mycol[match(reg,labels(mycol))])
dat3 <- data.frame(region = data1$Region, alpha = log(data1$frequency)/max(log(data1$frequency), na.rm=T)*255.) ##frequency logged to reduce the range of variation
dat3$alpha[is.na(dat3$alpha)] <- mean(dat3$alpha, na.rm=T) ## replace NAs by mean
colsnew <- data.frame(region = data1$Region, col_new = rgb(red= newColor[1], green= newColor[2],blue= newColor[3],alpha= dat3$alpha, maxColorValue=255))

points(r$timepoint_1, r$spread,cex=1, pch=21,bg= colsnew$col_new[colsnew$region== reg])

#add regression line
x<-r$timepoint_1
mod <- lm(r$spread ~ x, weights=Weights[Weights$region == reg,]$weights)
xs1 <- seq(min(r$timepoint_1),max(r$timepoint_1),10)
ys <- predict(mod,list(x=xs1),type="response")
lines(xs1,ys,lwd=2, col=2)

xs <- fc$Elevation
lower <- fc$lo025
upper <- fc$up975
points(xs,lower,type="l",lty=2,lwd=1.5)
points(xs,upper,type="l",lty=2,lwd=1.5)

fc$fits <- predict(mod,list(x=xs),type="response")
fc$outside <- ifelse(fc$fits >= fc$lo025 & fc$fits <= fc$up975, 0, 1) #fitted values that are outside of the confidence intervales
fc2 <- fc[(fc$Elevation >= min(r$timepoint_1) & fc$Elevation <= max(r$timepoint_1)),] #trim to range of observed data
prop <- sum(fc2$outside)/dim(fc2)[1]	#proportion of fitted values that are outside of the confidence intervals


xpos <- xlimit[2] - (xlimit[2] - xlimit[1])/20
ypos <- ylimit[2] - (ylimit[2] - ylimit[1])/8
trytext <- paste( reg_labels2[match(reg, names(reg_labels2))], "\n",round(prop,3) , sep="")
text(xpos,ypos, trytext,font=2, cex=0.75, adj=1)

}

mtext("Elevation limit in first survey (m a.s.l.)",side=1,line=0,outer=TRUE,cex=1.25)
mtext("Change in upper Elevation limit (m)",side=2,line=0,outer=TRUE,cex=1.25)
dev.off()
}

# make scale for shading
pdf(file= "Fig.4_legend.pdf",width= 8.5,height= 6, useDingbats=FALSE)
par(mfrow=c(1,1),xaxs="i",yaxs="i",tck=0.02, mar=c(2,2,0.5,0.5), oma=c(2,2,0,0),bty="l", cex=0.9)
plot(r$timepoint_1, r$spread,xlab="", ylab="",xlim=xlimit,ylim=ylimit,cex=1, pch=21,bg="grey", frame.plot=T, axes=F,type="n")

scale <- c(2, 5, 10 , 50, 100, 187)
newColor<-col2rgb("black")
alphas_scale <- log(scale)/max(log(scale), na.rm=T)*255. ##frequency logged to reduce the range of variation
colsnew_scale <- rgb(red= newColor[1], green= newColor[2],blue= newColor[3],alpha= alphas_scale, maxColorValue=255)
legend("topright", pt.bg = colsnew_scale, pch= 21, legend = scale, title = "Frequency of occurrence")
dev.off()

### NULL-MODEL APPROACH: RUN SIMULATIONS (max. time interval) #################################################################

# m1  spread values randomized among arbitrary elevations (every 10 m)
# m2  spread values randomized among arbitrary elevations (equal effort across Regions)
# m3  spread values randomized among all surveyed transects, irrespective of presence of aliens
# m4  # range of spread constrained to range of observed initial elevations, with 10 m elevation intervals)
# m5  # range of spread constrained to range of observed initial elevations

#set.seed(123)
#geocon(quant_wide_red_freq, trans.data, 10000, "m1", "TRUE")
#geocon.plot(quant_wide_red_freq, trans.data, dat_obs.quant_m1_10000, fitted_coef_m1_10000, mycol,"m1_10000_weights_211209.pdf", fweight ="TRUE")

set.seed(123)
geocon(quant_wide_red_freq, trans.data, 10000, "m2", "TRUE")
geocon.plot(quant_wide_red_freq, trans.data, dat_obs.quant_m2_10000, fitted_coef_m2_10000, mycol,"m2_10000_weights_220627.pdf", fweight ="TRUE")

set.seed(123)
geocon(quant_wide_red_freq, trans.data, 10000, "m3", "TRUE")
geocon.plot(quant_wide_red_freq, trans.data, dat_obs.quant_m3_10000, fitted_coef_m3_10000, mycol,"m3_10000_weights_221214.pdf", fweight ="TRUE")

# set.seed(123)
#geocon(quant_wide_red_freq, trans.data, 10000, "m4", "TRUE")
#geocon.Plot(quant_wide_red_freq, trans.data, dat_obs.quant_m4_10000, fitted_coef_m4_10000, mycol,"m4_10000_weights_211209.pdf", fweight ="TRUE")

set.seed(123)
geocon(quant_wide_red_freq, trans.data, 10000, "m5", "TRUE")
geocon.plot(quant_wide_red_freq, trans.data, dat_obs.quant_m5_10000, fitted_coef_m5_10000, mycol,"m5_10000_weights_220627.pdf", fweight ="TRUE")

# checking where the fitted values fall outside the confidence intervals 
fittedcoef <- fitted_coef_m2_10000
#fittedcoef <- fitted_coef_m3_10000
#fittedcoef <- fitted_coef_m5_10000

k=1	#AUN
k=2 #AUV 
k=5	#CLS
k=6 #ESP
k=7 #HWI
k=8	#IND
k=9	#MTN

reg <- levels(quant_wide_red_freq$Region)[k] 
r <- subset(quant_wide_red_freq, quant_wide_red_freq$Region==reg) 
fc <- subset(fittedcoef, fittedcoef$Region==reg)
x<-r$timepoint_1
mod <- lm(r$spread ~ x, weights=r$frequency)
xs <- fc$Elevation
fc$fits <- predict(mod,list(x=xs),type="response")
fc$outside <- ifelse(fc$fits >= fc$lo025 & fc$fits <= fc$up975, 0, 1) #fitted values that are outside of the confidence intervals
fc2 <- fc[(fc$Elevation >= min(r$timepoint_1) & fc$Elevation <= max(r$timepoint_1)),] #trim to range of observed data
fc$outside
print(fc,n=nrow(fc))

### NULL-MODEL APPROACH: RUN SIMULATIONS (5-Year time interval) ###############################################################

# run simulations for 5 Year time intervals for 10-Year regions that show a significant pattern in m3 (AUN, CLS and MTN; not ESP as this wasn't sampled after 5 years)

# reduce data set to these regions and their 5-year-period
quantiles_5.1 <- quantiles %>%
  filter(Region %in% c("AUN", "CLS", "MTN", "CHE", "CLC") &
           (fYear == 0 | fYear == 5)) %>%
  mutate(sampling_timepoint = ifelse(fYear == 0, "timepoint_1", "timepoint_2"))
quantiles_5.1 <- quantiles_5.1[,-2]

quantiles_5.2 <- quantiles %>%
  filter(Region %in% c("AUN", "CLS", "MTN", "CHE", "CLC") &
           (fYear == 5 | fYear == 10)) %>%
  mutate(sampling_timepoint = ifelse(fYear == 5, "timepoint_1", "timepoint_2")) # would be sampling timepoint 2 and 3, but code always uses 1 & 2
quantiles_5.2 <- quantiles_5.2[,-2]

# make wide dataframe
quant_wide_5.1 <- wide_fun(quantiles_5.1, "sampling_timepoint", "quantile")
quant_wide_5.2 <- wide_fun(quantiles_5.2, "sampling_timepoint", "quantile")

# calculate spread 
quant_wide_5.1 <- quant_wide_5.1 %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # gets rid of emerging NA's if species appear only in one Year

quant_wide_5.2 <- quant_wide_5.2 %>%
  mutate(spread = timepoint_2 - timepoint_1) %>% 
  drop_na(spread) # gets rid of emerging NA's if species appear only in one Year

# get frequency of species in each time step: build function to extract only relevant data (Year 0 & 5 or 5 and 10) from dat_alien2 
red_fun_al_5.1 <- function(dat) { 
  dat %>%
    mutate(timespan = 5) %>%
    mutate(sampling_timepoint = ifelse(Region %in% c("AUN", "CLC", "CLS", "CHE", "MTN", "ESP", "ORE", "HWI") & fYear == 0, "timepoint_1", "timepoint_2")) %>%
    ungroup() %>%
    dplyr::select(-fYear)}

red_fun_al_5.2 <- function(dat) { 
  dat %>%
    mutate(timespan = 5) %>%
    mutate(sampling_timepoint = ifelse(Region %in% c("AUN", "CLC", "CLS", "CHE", "MTN", "ESP", "ORE", "HWI") & fYear == 5, "timepoint_1", "timepoint_2")) %>%
    ungroup() %>%
    dplyr::select(-fYear)}

# extract data
dat_alien_red_5.1 <- red_fun_al_5.1(dat_alien2[dat_alien2$fYear !=10,])
dat_alien_red_5.2 <- red_fun_al_5.2(dat_alien2[dat_alien2$fYear !=0,])

# count number of TTRANSECTS species appear in
dat_alien_freq_5.1 <- dat_alien_red_5.1 %>%
  group_by(Region, Accepted.Name.MIREN) %>% 
  summarise(frequency = n()) %>%
  rename("species" = "Accepted.Name.MIREN")
dat_alien_freq_5.2 <- dat_alien_red_5.2 %>%
  group_by(Region, Accepted.Name.MIREN) %>% 
  summarise(frequency = n()) %>%
  rename("species" = "Accepted.Name.MIREN")

# add frequency of occurence to spread data frame
quant_wide_red_freq_5.1 <- left_join(quant_wide_5.1, dat_alien_freq_5.1, by = c("Region", "species"))
quant_wide_red_freq_5.1$Region <- as.factor(quant_wide_red_freq_5.1$Region)

quant_wide_red_freq_5.2 <- left_join(quant_wide_5.2, dat_alien_freq_5.2, by = c("Region", "species"))
quant_wide_red_freq_5.2$Region <- as.factor(quant_wide_red_freq_5.2$Region)

set.seed(123)
geocon(quant_wide_red_freq_5.1, trans.data, 10000, "m3", "TRUE")
geocon.plot(quant_wide_red_freq_5.1, trans.data, dat_obs.quant_m3_10000, fitted_coef_m3_10000, mycol,"m3_10000_weights_220627_5.1.pdf", fweight ="TRUE")
fittedcoef_5.1 <- fitted_coef_m3_10000

set.seed(123)
geocon(quant_wide_red_freq_5.2, trans.data, 10000, "m3", "TRUE")
geocon.plot(quant_wide_red_freq_5.2, trans.data, dat_obs.quant_m3_10000, fitted_coef_m3_10000, mycol,"m3_10000_weights_220627_5.2.pdf", fweight ="TRUE")
fittedcoef_5.2 <- fitted_coef_m3_10000


###############################################################################################################################
## ADDITIONAL INFORMATION GIVEN IN TEXT AND TABLE S4 ##########################################################################
###############################################################################################################################

# information in Figure 4
freq<-as.data.frame(table(quant_wide_red_freq$frequency))									#table of frequencies of occurrence (transect level)
quant_wide_red_freq[quant_wide_red_freq$frequency==187,]						#most frequent species	- H. radicata in AUV
unique(quant_wide_red_freq$Region[quant_wide_red_freq$frequency==2])		#Regions where occurrences are 2

# total number of nonnative species
# total # of non-native species in data set with non-identified species removed, but not rare species
length(unique(sp_dat$Accepted.Name.MIREN)) #616

# total number of transects
sum(as.vector(table(trans.data$Region)))


# make Table S4 including summary information by region
# make table with center of coordinates for every region
coords <- env_dat_coord %>%
  drop_na(Long, Lat) %>%
  group_by(Region) %>%
  summarise(long = mean(Long),
            lat = mean(Lat))

coords$long <- round(as.numeric(as.character(coords$long)),3)
coords$lat <- round(as.numeric(as.character(coords$lat)),3)

TS4 <- data.frame(
Region = reg_labels3[match(labels(reg_labels3),unique(trans.data$Region))],
Lat = coords$lat,
Long = coords$long,
Transects = as.vector(table(trans.data$Region)),											#number of transects per region
Roads = rowSums(sign(table(list(trans.data$Region,trans.data$Road)))),		#number of Roads per region
Elev_min = round(tapply(trans.data$Elevation,list(trans.data$Region),min),0),	#min elevation per region
Elev_max = round(tapply(trans.data$Elevation,list(trans.data$Region),max),0),	#max elevation per region
TotalSpeciesRichness =tapply(dat_alien$Accepted.Name.MIREN, list(dat_alien$Region), function(x)length(unique(x))),	#species richness per region (total across the sampling period)
t1 = tapply(env$Year,env$Region,function(x) unique(x)[1]),
t2 = tapply(env$Year,env$Region,function(x) unique(x)[2]),
t3 = tapply(env$Year,env$Region,function(x) unique(x)[3]),
row.names=NULL)

write.table(TS4, "TableS4_RegionalSummaries.txt",row.names=FALSE)

# information on MIREN in introduction
dim(dat_alien2)										#14890 observations at transect level
length(unique(as.factor(substr(env$Site,1,11))))	#Plots: 1573
length(unique(as.factor(substr(env$Site,1,9))))		#Transects: 651

dim(dat_alien)[1]									#total observations in all plots = 20536 (all years)
dim(dat_alien[dat_alien$Plot==1 ,])[1]		#13154 obs in Plot 1
dim(dat_alien[dat_alien$Plot==2 ,])[1]		#3763 obs in Plot 2
dim(dat_alien[dat_alien$Plot==3 ,])[1]		#3402 obs in Plot 3
1-(dim(dat_alien[dat_alien$Plot==1 ,])[1] / dim(dat_alien)[1] )		#proportion of observations away from road = 0.35

# information in methods: number of each plot surveyed
length(unique(substr(env$Site,1,9)[env$Plot=="1" |env$Plot=="1A"]))	#651
length(unique(substr(env$Site,1,9)[env$Plot==2]))	#481
length(unique(substr(env$Site,1,9)[env$Plot==3]))	#440

# How many observations are not in plot 1?
length(dat_alien[dat_alien$Plot == "2" |dat_alien$Plot == "3",]$pidn) # 7222
(100/length(dat_alien$pidn)) *7222

###############################################################################################################################
## CLIMATE CHANGE ANALYSIS ####################################################################################################
###############################################################################################################################

### GET COORDINATES READY #####################################################################################################
# add missing coordinates for AUV BE and BW 2012 from 2017
# create lookup 
lookup_coord_AUV <- env_dat_coord %>%
  ungroup() %>%
  filter(Year == 2017) %>%
  dplyr::select(c(Region, Road, Transect, Lat, Long)) %>%
  filter(Road %in% c("BW", "BE")) %>%
  mutate(RegRoadTrans = paste(Region, Road, Transect, sep = ".")) %>%
  group_by(RegRoadTrans) %>%
  summarise(Lat = mean(Lat), Long = mean(Long))

climate <- env_dat_coord
climate$RegRoadTrans <- paste(climate$Region, climate$Road, climate$Transect, sep = ".")

climate$Lat[is.na(climate$Lat)] <- lookup_coord_AUV$Lat[match(climate$RegRoadTrans[is.na(climate$Lat)], lookup_coord_AUV$RegRoadTrans)]
climate$Long[is.na(climate$Long)] <- lookup_coord_AUV$Long[match(climate$RegRoadTrans[is.na(climate$Long)], lookup_coord_AUV$RegRoadTrans)]


# get coordinates
coordinates <- climate[,c(5,4)]

# turning this into a spatial points dataframe to extract data at each coordinate
Coordinates_spatial<-SpatialPoints(coordinates, proj4string = CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs+ towgs84=0,0,0"))


### READ & PROCESS CRUTS DATA FOR 2000 - 2016 ################################################################################

# read timeseries data for tmax
setwd("~/Desktop/CHELSA Cruts/tmax") # CHELSA Cruts data can be downloaded on https://chelsa-climate.org/chelsacruts/

# read tifs for timeseries data
rasValue_late_tmax<-c()
rasValue_annual_late_tmax<-c()
for (j in 1:17) #for a period of 17 Years, starting in 1999+1 = 2000
{
  print(j)
  for (i in 1:12) {
    s <- raster(paste(paste("CHELSAcruts_tmax",i, 1999+j,"V.1.0",sep="_"),"tif",sep="."))
    rasValue_late_tmax=cbind(rasValue_late_tmax,raster::extract(s, Coordinates_spatial)) # extract monthly means
  } 
  rasValue_annual_late_tmax=cbind(rasValue_annual_late_tmax,rowMeans(rasValue_late_tmax[,(ncol(rasValue_late_tmax)-11):ncol(rasValue_late_tmax)])) #calculating annual means
}

# Plot the temperature data of the last Year & the sampling coordinates
plot(s) # very low scale is because of some ESP data points - removed later (in ocean?)
points(Coordinates_spatial)

#convert to degrees, as data is saved a bit unusual (°C/10)
rasValue_late_tmax<-rasValue_late_tmax/10
rasValue_annual_late_tmax<-rasValue_annual_late_tmax/10


# add coordinates and pidn to temperature data (rasValue)
combinePointValue_late_tmax=cbind(as.character(climate$pidn),coordinates,rasValue_late_tmax) 

# create sensible column names
column_names<-c()
for (j in 1:17) {
  for (i in 1:12) {
    column_names<-c(column_names,paste("CHELSA",1999+j,i,sep="_"))
  }
}
column_names<-c("Plotcode","x","y",column_names)
colnames(combinePointValue_late_tmax)<-column_names

# add coordinates and pidn to temperature data (rasValue_annual)
combinePointValue_annual_late_tmax=cbind(as.character(climate$pidn),as.data.frame(cbind(coordinates,rasValue_annual_late_tmax)))

# create sensible column names
column_names<-c()
for (j in 1:17) {
  column_names<-c(column_names,paste("CHELSA",1999+j,sep="_"))
}

column_names<-c("Plotcode","x","y",column_names)
colnames(combinePointValue_annual_late_tmax)<-column_names

# transform into data frame
MIREN_annual_late_tmax<-as.data.frame(combinePointValue_annual_late_tmax)

# same for tmin
setwd("~/Desktop/CHELSA Cruts/tmin") # CHELSA Cruts data can be downloaded on https://chelsa-climate.org/chelsacruts/
# read tifs for timeseries data
rasValue_late_tmin<-c()
rasValue_annual_late_tmin<-c()
for (j in 1:17) #for a period of 17 Years, starting in 1999+1 = 2000
{
  print(j)
  for (i in 1:12) {
    s <- raster(paste(paste("CHELSAcruts_tmin",i, 1999+j,"V.1.0",sep="_"),"tif",sep="."))
    rasValue_late_tmin=cbind(rasValue_late_tmin,raster::extract(s, Coordinates_spatial))
  } 
  rasValue_annual_late_tmin=cbind(rasValue_annual_late_tmin,rowMeans(rasValue_late_tmin[,(ncol(rasValue_late_tmin)-11):ncol(rasValue_late_tmin)])) #calculating annual means
}

# plot the temperature data of the last year & the sampling coordinates
plot(s) # very low scale is because of some ESP data points - removed later (in ocean?)
points(Coordinates_spatial)

# convert to degrees, as data is saved a bit unusual (°C/10)
rasValue_late_tmin<-rasValue_late_tmin/10
rasValue_annual_late_tmin<-rasValue_annual_late_tmin/10


# add coordinates and pidn to temperature data (rasValue)
combinePointValue_late_tmin=cbind(as.character(climate$pidn),coordinates,rasValue_late_tmin) 

# create sensible column names
column_names<-c()
for (j in 1:17) {
  for (i in 1:12) {
    column_names<-c(column_names,paste("CHELSA",1999+j,i,"tmin", sep="_"))
  }
}
column_names<-c("Plotcode","x","y",column_names)
colnames(combinePointValue_late_tmin)<-column_names

# add coordinates and pidn to temperature data (rasValue_annual)
combinePointValue_annual_late_tmin=cbind(as.character(climate$pidn),as.data.frame(cbind(coordinates,rasValue_annual_late_tmin)))

# create sensible column names
column_names<-c()
for (j in 1:17) {
  column_names<-c(column_names,paste("CHELSA","tmin",1999+j, sep="_"))
}
column_names<-c("Plotcode","x","y",column_names)
colnames(combinePointValue_annual_late_tmin)<-column_names

# transform into data frame
MIREN_annual_late_tmin<-as.data.frame(combinePointValue_annual_late_tmin)

# merge tmin and tmax by coordinates and pids to calculate mean
MIREN_annual_late <- left_join(MIREN_annual_late_tmax, MIREN_annual_late_tmin, by = c("Plotcode", "x", "y"))

# take the mean of tmin and tmax
aa <- split.default(MIREN_annual_late[,-c(1:3)], str_remove(names(MIREN_annual_late[,-c(1:3)]), "\\D+")) %>% map_dfc(rowMeans)
MIREN_annual_late <- bind_cols(MIREN_annual_late, aa) %>%
  dplyr::select(!c(4:37)) 

# delete ESP.EM.11.1.2008, ESP.EM.11.2.2008 and ESP.EM.11.3.2008 as the temperatures are very unlikely (> -3000 °C...) - maybe they're too close to the sea? (no problem in CHELSA timeseries...)
MIREN_annual_late <- MIREN_annual_late[-c(3421:3424),]


### CALCULATE CHANGE IN TEMPERATURE ##########################################################################################

MIREN_annual <- MIREN_annual_late

MIREN_annual <- distinct(MIREN_annual)

# calculate the change in temperature separately for all transects (until 2016)
MIREN_annual$change<-rep(NA,nrow(MIREN_annual))
MIREN_annual$change_p<-rep(NA,nrow(MIREN_annual))
for (i in 1:nrow(MIREN_annual)){
  MIREN_annual$change[i]<-coef(lm(t(MIREN_annual[i,4:20])~seq(0,16,1)))[2]*17 # results in mean yearly temperature increase per Year * 12
  MIREN_annual$change_p[i]<-summary(lm(t(MIREN_annual[i,4:20])~seq(0,16,1)))$coefficients[2,4]
}

# get a region & road column for plotting & grouping
MIREN_annual <- MIREN_annual %>%
  mutate(Region = str_extract(Plotcode, "^..."),
         Road = str_extract(Plotcode, "\\.\\D\\D\\."),
         Road = str_extract(Road, "[[:upper:]]{2}"))


# calculate mean temperature in Region 
MIREN_annual_reg <- MIREN_annual %>%
  relocate(Region, .after = `2016`) %>%
  relocate(Road, .after = Region) %>%
  dplyr::select(!c(change, change_p)) %>%
  group_by(Region) %>%
  summarise(across(c(4:20), mean))

# calculate the change in temperature separately for all regions (until 2016): first turn into long data frame
MIREN_annual_reg_long <- pivot_longer(MIREN_annual_reg, names_to = "Year", values_to = "temperatures", cols = 2:18) %>%
  mutate(Year = as.numeric(Year),
         temperatures = as.numeric(temperatures)) 

# model change in temperature for all region separately
estimates_temp_increase <- data.frame()
levels <- unique(MIREN_annual_reg_long$Region)
x <- MIREN_annual_reg_long

for (i in 1:11){
  reg <- levels(as.factor(x$Region))[i]
  mod <- lm(temperatures ~Year, data = x[x$Region == reg,])
  estimates_temp_increase[i, 1] <- levels(as.factor(x$Region))[i]
  estimates_temp_increase[i, 2] <- summary(mod)$coef[2] 
  estimates_temp_increase[i, 3] <- summary(mod)$coef[2] * 17 # results in increase over 17 Years
  estimates_temp_increase[i, 4] <- summary(mod)$coef[4]
  estimates_temp_increase[i, 5] <- summary(mod)$coef[8]
  estimates_temp_increase[i, 6] <- length(mod$fitted)
}

colnames(estimates_temp_increase) <- c("Region", "estimate", "increase_in_17_Years",  "sd", "p_value", "N")


# check whether spread differences can be explained by temperature increase
spread_temp <- left_join(estimates_spread_freq, estimates_temp_increase[, c(1:4)], by = "Region")

mod <- lm(estimate.x~estimate.y, data = spread_temp)
mod1 <- lm(estimate.x~1, data = spread_temp)

anova(mod, mod1)
summary(mod)


###############################################################################################################################
## PLOTTING FIGURE 1 ##########################################################################################################
###############################################################################################################################


# generate a set of 100 species for presentation
# first generate range shifts falling between -1500 and +1500 m (i.e. along a hypothetical gradient from 500 to 2000 masl)
alt_min <- 525
alt_max <- 2000
alt <- seq(alt_min, alt_max, 25)
poss.shifts <- c(dist(alt), dist(alt)*-1, rep(0,length(alt))) #i.e. all possible upward, downward or zero shifts for every initial Elevation

set.seed(132)
shifts <- sample(poss.shifts,100)
range(shifts)
hist(shifts,9)

### PANEL C: HISTOGRAM ########################################################################################################

# plot a histogram of these species shifts
pdf(file="Fig1C.pdf",width= 4.5,height= 2.5, useDingbats=FALSE)
par(mfrow=c(1,1),xaxs="i",yaxs="i",tck=0.02, mar=c(4,6,1,1), oma=c(0,0,0,0),bty="l", cex=0.9)
hist(shifts, 9, main="", xlab="Change in upper Elevation limit (t2-t1) (m)")

dev.off()

### PANEL D: RANDOMLY SAMPLED SHIFTS ##########################################################################################

# next generate the observed elevation at timepoint 1 for these species and their randomly chosen shift
timepoint_1 <- numeric(0)
for (i in 1:length(shifts)){												#for every species (initial elevation)....
  if (sign(shifts[i])==1) #if spread is upwards
  {alt.poss <- alt[alt <= max(alt)-shifts[i]]  	#poss starting Elevations are all those below the edges of outer triangle given the spread
  timepoint_1[i] <- sample(alt.poss,1)}	else			#sample new mid
  {alt.poss <- alt[alt >= min(alt)-shifts[i]]  	#alts constrained to edges of outer triangle
  timepoint_1[i] <- sample(alt.poss,1)}		
}


# now resample the initial elevations to produce null expectation for relationship between initial elevation limit and elevation shifts
runs=1000
fitted.sim <- data.frame(matrix(data=NA,nrow=length(alt),ncol=runs,byrow=FALSE)) #empty dataframe to contain fitted values for each alt
names(fitted.sim) <- as.factor(seq(1:runs))
slopes <- numeric(0)


for (j in 1:runs){
  timepoint_1.new <- numeric(0)
  
  for (i in 1:length(shifts)){												#for every species (initial Elevation)....
    #if (abs(r$spread[i])==length(alt)) timepoint_1.new[i] <- r$timepoint_1[i] else	#if the spread (up or down) is equal to the length of the gradient, the spread value can't be moved to another position along the gradient
    if (sign(shifts[i])==1) #if spread is upwards
    {alt.poss <- alt[alt <= max(alt)-shifts[i]]  	#poss starting Elevations are all those below the edges of outer triangle given the spread
    timepoint_1.new[i] <- sample(alt.poss,1)}	else			#sample new mid
    {alt.poss <- alt[alt >= min(alt)-shifts[i]]  	#alts constrained to edges of outer triangle
    timepoint_1.new[i] <- sample(alt.poss,1)}		
  }
  
  mod <- lm(shifts~timepoint_1.new)
  fitted.sim[,j] <- as.vector(predict(mod, list(timepoint_1.new=alt),type="response"))
  slopes[j] <- coef(mod)[2]
}


mean <- numeric(0)
upper <- numeric(0)
lower <- numeric(0)
up95 <- numeric(0)
lo05 <- numeric(0)

for (i in 1:dim(fitted.sim)[1]){				#vectors with upper and lower 95%CI for each altitude
  mean[i] <- sum(fitted.sim[i,])/runs
  lower[i] <- as.numeric(quantile(as.numeric(fitted.sim[i,]),prob=c(0.025,0.975))[1])
  upper[i] <- as.numeric(quantile(as.numeric(fitted.sim[i,]),prob=c(0.025,0.975))[2])
  lo05[i] <- as.numeric(quantile(as.numeric(fitted.sim[i,]),prob=c(0.05,0.95))[1])
  up95[i] <- as.numeric(quantile(as.numeric(fitted.sim[i,]),prob=c(0.05,0.95))[2])}

# make a new data frame excluding top and bottom 10% of GRADIENT: first for species at bottom and top in timepoint 1
gradient <- alt_max-alt_min # gradient is 1475m, 10% of it is 147.5
cutoff_low <- alt_min + 147.5 # get Elevation of lowest 10% of gradient
cutoff_high <- alt_max - 147.5 # get Elevation of highest 10% of gradient

alt_small <- alt[alt >= cutoff_low & alt <= cutoff_high]

# make new data frame with elevation at timepoint 2
dat <- data.frame(shifts, timepoint_1)
dat <- dat %>%
  mutate(timepoint_2 = timepoint_1 + shifts)

# filter out top & bottom 10% of gradient for both timepoint 1 and timepoint 2
dat <- dat %>%
  filter(timepoint_1 >= cutoff_low & timepoint_1 <= cutoff_high,
         timepoint_2 >= cutoff_low & timepoint_2 <= cutoff_high)

# repeat the resampling, but after each resampling before modelling, get rid of all species occurring in the top or bottom 10% of the gradient at either time point 1 or 2
runs=1000
fitted.sim_small <- data.frame(matrix(data=NA,nrow=length(alt),ncol=runs,byrow=FALSE)) #empty dataframe to contain fitted values for each alt
names(fitted.sim_small) <- as.factor(seq(1:runs))
slopes_small <- numeric(0)


for (j in 1:runs){
  timepoint_1.new <- numeric(0)
  
  for (i in 1:length(shifts)){												#for every species (initial elevation)....
    #if (abs(r$spread[i])==length(alt)) timepoint_1.new[i] <- r$timepoint_1[i] else	#if the spread (up or down) is equal to the length of the gradient, the spread value can't be moved to another position along the gradient
    if (sign(shifts[i])==1) #if spread is upwards
    {alt.poss <- alt[alt <= max(alt)-shifts[i]]  	#poss starting elevations are all those below the edges of outer triangle given the spread
    timepoint_1.new[i] <- sample(alt.poss,1)}	else			#sample new mid
    {alt.poss <- alt[alt >= min(alt)-shifts[i]]  	#alts constrained to edges of outer triangle
    timepoint_1.new[i] <- sample(alt.poss,1)}		
  }
  
  # make new data frame with elevation at timepoint 2
  dat1 <- data.frame(shifts, timepoint_1.new)
  dat1 <- dat1 %>%
    mutate(timepoint_2 = timepoint_1.new + shifts)
  
  # filter 
  dat1 <- dat1 %>%
    filter(timepoint_1.new >= cutoff_low & timepoint_1.new <= cutoff_high,
           timepoint_2 >= cutoff_low & timepoint_2 <= cutoff_high)
  
  
  
  mod <- lm(shifts~timepoint_1.new, data = dat1)
  fitted.sim_small[,j] <- as.vector(predict(mod, list(timepoint_1.new=alt),type="response"))
  slopes_small[j] <- coef(mod)[2]
}

mean_sm <- numeric(0)
upper_sm <- numeric(0)
lower_sm <- numeric(0)
up95_sm <- numeric(0)
lo05_sm <- numeric(0)

# vectors with upper and lower 95%CI for each altitude
for (i in 1:dim(fitted.sim_small)[1]){
  mean_sm[i] <- sum(fitted.sim_small[i,])/runs
  lower_sm[i] <- as.numeric(quantile(as.numeric(fitted.sim_small[i,]),prob=c(0.025,0.975))[1])
  upper_sm[i] <- as.numeric(quantile(as.numeric(fitted.sim_small[i,]),prob=c(0.025,0.975))[2])
  lo05_sm[i] <- as.numeric(quantile(as.numeric(fitted.sim_small[i,]),prob=c(0.05,0.95))[1])
  up95_sm[i] <- as.numeric(quantile(as.numeric(fitted.sim_small[i,]),prob=c(0.05,0.95))[2])}


# "triangle plots" showing the relation of max alt to range sizes within the range of potential combinations (complete gradient)
pdf(file="Fig1D_complete.pdf",width= 4.3,height= 3.9, useDingbats=FALSE)
par(mfrow=c(1,1),xaxs="i",yaxs="i",tck=0.02, mar=c(4,5,1,1), oma=c(0,0,0,0),bty="l", cex=0.9)
xlimit <- c(min(alt)-(max(alt)-min(alt))/20,max(alt)+(max(alt)-min(alt))/20)
ylimit <- c(min(shifts)+(min(shifts)/10),max(shifts)+(max(shifts)/10))

Plot(dat$timepoint_1, dat$shifts,ylab="Change in upper Elevation limit (t2-t1)", xlab="Upper Elevation limit (m a.s.l.) at timepoint 1",xlim=xlimit,ylim=ylimit,cex=1, pch=21,bg="grey", frame.Plot=T, axes=T,type="n")

abline(0,0,col="dark grey")

polygon(c(min(alt),	min(alt),			max(alt),			max(alt)),
        c(max(alt)-min(alt),	max(alt)+1000,	max(alt)*2+1000,	0),
        col=rgb(0,0,0,0.1),border=FALSE)

polygon(c(min(alt),	min(alt),			max(alt),			max(alt)),
        c(0,					max(alt)*-1-1000,	max(alt)*-1-1000,	(max(alt)-min(alt))*-1),
        col=rgb(0,0,0,0.1),border=FALSE)

points(timepoint_1, shifts,cex=1, pch=21,bg="grey")

# add regression line
x<-timepoint_1
mod <- lm(shifts ~ x)
xs1 <- seq(min(timepoint_1),max(timepoint_1),10)
ys <- predict(mod,list(x=xs1),type="response")
fitted <- predict(mod,list(x=alt),type="response")
lines(xs1,ys,lwd=2, col=2)

points(alt,lower,type="l",lty=2,lwd=1.5, col=2)
points(alt,upper,type="l",lty=2,lwd=1.5, col=2)

# color the 80% blue
points(dat$timepoint_1, dat$shifts,cex=1, pch=21,bg=4)

# fit and draw a new regression based on 80% of gradient
x<- dat$timepoint_1
mod2 <- lm(dat$shifts ~ x)
xs1 <- seq(min(dat$timepoint_1),max(dat$timepoint_1), 10)
ys <- predict(mod2,list(x=xs1),type="response")
fitted <- predict(mod2,list(x=alt_small),type="response")
lines(xs1,ys,lwd=2, col=4)

# shorten lower_sm and upper_sm to length of small altitude gradient
xy <- head(lower_sm, -6)
lower_sm1 <- tail(xy, 48)

xy <- head(upper_sm, -6)
upper_sm1 <- tail(xy, 48)

points(alt_small,lower_sm1,type="l",lty=2,lwd=1.5, col=4)
points(alt_small,upper_sm1,type="l",lty=2,lwd=1.5, col=4)


dev.off()

### PANEL B: SHIFTS EXAMPLE SPECIES “##########################################################################################

pdf(file="Fig1B.pdf",width= 4,height= 3, useDingbats=FALSE)
par(mfrow=c(1,1),xaxs="i",yaxs="i",tck=0.02, mar=c(4,6,0.6,0.5), oma=c(0,0,0,0),bty="l", cex=0.9)

Bt1 <- c(750,1250,1900, 1000, 1750,800, 1400,2000)
Bt2 <- c(2000,2000,2000,1700,800,500,500,500)
Bshift <- Bt2-Bt1
alt <- c(500,2000)
shifts <- c(-1500,1500)
xlimit <- c(min(alt)-(max(alt)-min(alt))/20,max(alt)+(max(alt)-min(alt))/20)
ylimit <- c(min(shifts)+(min(shifts)/10),max(shifts)+(max(shifts)/10))

Plot(Bt1, Bshift,xlab="Upper Elevation limit at timepoint 1 (t1) (m)", ylab="Change in upper\n Elevation limit (t2-t1) (m)",xlim=xlimit,ylim=ylimit,cex=1, pch=21,bg="grey", frame.Plot=T, axes=T,type="n")
abline(0,0,col="dark grey")

polygon(c(min(alt),	min(alt),			max(alt),			max(alt)),
        c(max(alt)-min(alt),	max(alt)+1000,	max(alt)*2+1000,	0),
        col=rgb(0,0,0,0.1),border=FALSE)

polygon(c(min(alt),	min(alt),			max(alt),			max(alt)),
        c(0,					max(alt)*-1-1000,	max(alt)*-1-1000,	(max(alt)-min(alt))*-1),
        col=rgb(0,0,0,0.1),border=FALSE)

points(Bt1, Bshift,cex=1.5, pch=4)
dev.off()

