Journal of Biogeography SUPPORTING INFORMATION Climatic drivers of trait assembly in woody plants in Japan Takayuki Shiono, Buntarou Kusumoto, Ryo Maeshiro, Shin-jiro Fujii, Lars Götzenberger, Francesco de Bello and Yasuhiro Kubota Appendix S3 R code for calculating standardized effect sizes of the functional structure indices by using three null models. For null model 1, the null distributions were generated by sampling the grid cell-specific number of species without replacement from the pool of all 773 species in the Japanese archipelago. This null model and sampling scheme standardized the value of the functional trait structure indices by removing the sampling effect that results from differences in the number of species in the grid cells. For null model 2, the null distributions were generated by randomization of the grid cell–species matrix while keeping the number of species in the grid cells and the frequency of species occurrence across the grid cells using a quasi-swap algorithm. This scheme removed the sampling effects of species in the grid cells while considering the geographical coverage of each species: common (or rare) species remained common (or rare). For null model 3, we considered the influence of historical dispersal limitation as a result of geographical vicariance on the regional species pools. The Japanese archipelago represents a transitional region between the Palaeotropical (Indo-Pacific) and Holarctic kingdoms, and it is divided into three biogeographical regions by the Tsugaru Strait and the Tokara Strait (Fig. 1): the northern, central and southern regions. Therefore, the current species distribution was affected by historical dispersal limitations at a biogeographical scale and, as a consequence, the species pool differs among the three regions: certain species are absent from a given regional species pool as a result of vicariance, not because they could not survive under the current climatic conditions in that region. Such biogeographical features might invalidate the creation of a simple null sample that assumes a single species pool across the Japanese archipelago. To overcome this problem, we conducted an analysis with null model 3 that assumed three species pools (north, centre and south) in which only species that were distributed in each biogeographical region were defined as candidate community members of the grid cells that we surveyed. The null distributions of null model 3 were generated by sampling the grid cell-specific number of species without replacement from the each species pool. ##################################################################### # Packages ##################################################################### library( ade4 ) library( vegan ) library( geometry ) ##################################################################### # Sample data ##################################################################### ## These sample data are just for checking the code. ## Nonetheless, the fundamental structures of the sample data are the same as those of the original data. # Species pools------------------------------------------------------## We defined species pools in two ways, i.e. overall and regional pools, ## to obtain the null distributions of functional structure indices. #Overall ## All 15 species contained in dataset. Spool_all <c( "spA","spB","spC","spD","spE","spF","spG","spH","spI","spJ", "spK","spL","spM","spN","spO" ) ## Regional species pools in the Japanese archipelago, divided into three regions: 'North', 'Centre', 'South' ## In the manuscript, these were defined based on the locations of grid cells and geographical ranges of each species. #Regional pool1: North Spool_r1 <- c( "spA","spB","spC","spD","spE" ) #Regional pool2: Centre Spool_r2 <c( "spC","spD","spE","spF","spG","spH","spI","spJ","spK","spL" ) #Regional pool3: South Spool_r3 <- c( "spH","spI","spJ","spK","spL","spM","spN","spO" ) # Species presence/absence matrix (places x species)-----------------## In this supplemental code, we assumed nine grid cells with 15 species. spa.vec <- c( 0,1,1,1,1,0,0,0,0,0,0,0,0,0,0, 1,0,1,1,1,0,0,0,0,0,0,0,0,0,0, 1,1,1,0,1,0,0,0,0,0,0,0,0,0,0, 0,0,1,1,1,1,0,0,1,0,0,1,0,0,0, 0,0,1,0,1,0,1,1,1,0,1,0,0,0,0, 0,0,0,1,1,1,0,1,0,1,0,1,0,0,0, 0,0,0,0,0,0,0,0,1,0,1,0,1,1,1, 0,0,0,0,0,0,0,0,1,1,0,1,0,1,1, 0,0,0,0,0,0,0,1,1,1,0,1,0,0,1 ) Sp_matrix <- matrix( spa.vec, nrow = 9, ncol = 15, dimnames = list(1:9, Spool_all), byrow = TRUE ) #Species functional trait matrix (species x traits) ------------------SLA <- c( 7.4, 13.4, 38.9, 14.8, 20.0, 23.1, 18.4, 16.3, 14.2, 10.2, 17.7, 15.7, 9.0, 6.0, 11.4 ) WD <- c( 0.55, 0.46, 0.47, 0.50, 0.57, 0.55, 0.56, 0.53, 0.52, 0.49, 0.28, 0.31, 0.65, 0.44, 0.59 ) Trait_matrix <- cbind( SLA, WD ) rownames( Trait_matrix ) <- Spool_all ##################################################################### # Function to calculate functional structure index ##################################################################### ## We defined the function to calculate each functional structure indices. ## Input data are as follows. ## spm : species presence/absence matrix ## trm : trait matrix ## trait : name(s) of a focal trait # Community mean (CM)------------------------------------------------calc.cm <- function( spm, trm, trait ){ res <- NULL for( i in 1:length(trait) ){ trait_tab <- t( t(spm) * trm[colnames(spm), trait[i]] ) res.i <- apply( trait_tab, 1, function(x) mean(x[x != 0]) ) res <- cbind( res, res.i ) } dimnames( res )[[2]] <- trait return( res ) } # Functional richness (FRic)-----------------------------------------calc.ric <- function( spm, trm, trait ){ if( length(trait) < 2 ){ rtrait_tab <- t( t(spm) * c(scale(trm[colnames(spm), trait])) ) res <- apply( rtrait_tab, 1, function(x) range(x[x != 0])[2] - range(x[x != 0])[1] ) } if( length(trait) >= 2 ){ res <- NULL for( i in 1:nrow(spm) ){ trm2 <- apply( trm, 2, function(x) scale(x) * spm[i, ] ) convhull<- convhulln( trm2, options = "FA" ) res <- c( res, convhull$vol ) } } return( res ) } # Functional divergence (RaoQ)---------------------------------------## This function will return RaoQ values with warning messages if a grid cell contains some species having equivalent trait values as one another. calc.rao <- function( spm, trm, trait ){ tr.dist <- dist( scale(trm[colnames(spm), trait]) ) res <- unlist( divc(data.frame(t(spm), check.names = FALSE), tr.dist, scale=FALSE) ) names( res ) <- rownames( spm ) return( res ) } ##################################################################### # Calculation of functional structure index by using observed data ##################################################################### ## Observed values of functional structure indices for single traits were calculated. TRAIT <- c("WD","SLA") #chose focal trait CM.obs <- calc.cm( Sp_matrix, Trait_matrix, TRAIT ) FRic.obs <- calc.ric( Sp_matrix, Trait_matrix, TRAIT ) RaoQ.obs <- calc.rao( Sp_matrix, Trait_matrix, TRAIT ) ##################################################################### # Calculation of functional structure index by using null models ##################################################################### ## Simulated values of functional structure indices for single traits were calculated using three null models. Ncom <- nrow( Sp_matrix ) #Number of grid-cells Nsp <- apply( Sp_matrix, 1, sum ) N <- 100 #times of iteration #Number of species in each grid-cell # Null model1: overall model-----------------------------------------## Sampling the grid cell-specific number of species without replacement from the overall species pool by keeping the species number in each grid cell. CM.sim_n1 <- array( NA, dim = c(Ncom, length(TRAIT), N), dimnames = list(rownames(Sp_matrix), TRAIT, 1:N) ) FRic.sim_n1 <- NULL RaoQ.sim_n1 <- NULL for( i in 1:N ){ sim.mat <- matrix( NA, nrow = Ncom, ncol = ncol(Sp_matrix), dimnames = dimnames(Sp_matrix) ) for( j in 1:Ncom ){ rsmp <- sample( Spool_all, Nsp[j] ) sim.mat[ j, ]<- as.numeric( colnames(sim.mat) %in% rsmp ) } cm.sim <- calc.cm( sim.mat, Trait_matrix, TRAIT ) fr.sim <- calc.ric( sim.mat, Trait_matrix, TRAIT ) rao.sim <- calc.rao( sim.mat, Trait_matrix, TRAIT ) CM.sim_n1[ , , i ] <- cm.sim FRic.sim_n1 <- rbind( FRic.sim_n1, fr.sim ) RaoQ.sim_n1 <- rbind( RaoQ.sim_n1, rao.sim ) } # Null model2: fixed to fixed model----------------------------------------## Randomizing presence/absence matrix by keeping margins (the number of species in the grid cells and ## the frequency of species occurrence across the grid cells) using a quasi-swap algorithm. CM.sim_n2 <- array( NA, dim = c(Ncom, length(TRAIT), N), dimnames = list(rownames(Sp_matrix), TRAIT, 1:N ) ) FRic.sim_n2 <- NULL RaoQ.sim_n2 <- NULL for( i in 1:N ){ sim.mat cm.sim fr.sim rao.sim <- commsimulator( Sp_matrix, method="quasiswap" ) <- calc.cm( sim.mat, Trait_matrix, TRAIT ) <- calc.ric( sim.mat, Trait_matrix, TRAIT ) <- calc.rao( sim.mat, Trait_matrix, TRAIT ) CM.sim_n2[ , , i ] <- cm.sim FRic.sim_n2 <- rbind( FRic.sim_n2, fr.sim ) RaoQ.sim_n2 <- rbind( RaoQ.sim_n2, rao.sim ) } # Null model3: dispersal limited model-------------------------------------## Sampling the grid cell-specific number of species without replacement from each of the three species pools. ## In this example of the supplemental code, we defined the relationship between grid cells and regional species pools as follows: North = { 1, 2, 3 }; Centre = { 4, 5, 6 }; South = { 7, 8, 9} CM.sim_n3 <- array( NA, dim = c(Ncom, length(TRAIT), N), dimnames = list(rownames(Sp_matrix), TRAIT, 1:N) ) FRic.sim_n3 <- NULL RaoQ.sim_n3 <- NULL region <rep( c("North", "Centre", "South"), rep(3, 3) ) for( i in 1:N ){ sim.mat <- matrix( NA, nrow = Ncom, ncol = ncol(Sp_matrix), dimnames = dimnames(Sp_matrix) ) for( j in 1:Ncom ){ switch( region[j], "North" = rsmp <- sample( Spool_r1, Nsp[j] ), "Centre" = rsmp <- sample( Spool_r2, Nsp[j] ), "South" = rsmp <- sample( Spool_r3, Nsp[j] ) ) sim.mat[ j, ]<- as.numeric( colnames(sim.mat) %in% rsmp ) } cm.sim <- calc.cm( sim.mat, Trait_matrix, TRAIT ) fr.sim <- calc.ric( sim.mat, Trait_matrix, TRAIT ) rao.sim <- calc.rao( sim.mat, Trait_matrix, TRAIT ) CM.sim_n3[ , , i ] <- cm.sim FRic.sim_n3 <- rbind( FRic.sim_n3, fr.sim ) RaoQ.sim_n3 <- rbind( RaoQ.sim_n3, rao.sim ) } ##################################################################### #Calculation of standard effect size (SES) of functional structure indices ##################################################################### ## SES values of each index were calculated for each grid cell as (the observed value - the mean of the null distribution)/the standard deviation of the null distribution. # SES_null1----------------------------------------------------------#mean & SD of simulated values cm_sim.m_n1 <- apply( CM.sim_n1, c(1, 2), mean ) cm_sim.SD_n1 <- apply( CM.sim_n1, c(1, 2), sd ) fr_sim.m_n1 fr_sim.SD_n1 <- apply( FRic.sim_n1, 2, mean ) <- apply( FRic.sim_n1, 2, sd ) rao_sim.m_n1 <- apply( RaoQ.sim_n1, 2, mean ) rao_sim.SD_n1 <- apply( RaoQ.sim_n1, 2, sd ) #SES SES.CM_n1 SES.FRic_n1 SES.RaoQ_n1 <- ( CM.obs - cm_sim.m_n1 ) / cm_sim.SD_n1 <- ( FRic.obs - fr_sim.m_n1 ) / fr_sim.SD_n1 <- ( RaoQ.obs - rao_sim.m_n1 ) / rao_sim.SD_n1 # SES_null2----------------------------------------------------------#mean & SD of simulated values cm_sim.m_n2 <- apply( CM.sim_n2, c(1, 2), mean ) cm_sim.SD_n2 <- apply( CM.sim_n2, c(1, 2), sd ) fr_sim.m_n2 fr_sim.SD_n2 <- apply( FRic.sim_n2, 2, mean ) <- apply( FRic.sim_n2, 2, sd ) rao_sim.m_n2 <- apply( RaoQ.sim_n2, 2, mean ) rao_sim.SD_n2 <- apply( RaoQ.sim_n2, 2, sd ) #SES SES.CM_n2 SES.FRic_n2 SES.RaoQ_n2 <- ( CM.obs - cm_sim.m_n2 ) / cm_sim.SD_n2 <- ( FRic.obs - fr_sim.m_n2 ) / fr_sim.SD_n2 <- ( RaoQ.obs - rao_sim.m_n2 ) / rao_sim.SD_n2 # SES_null3----------------------------------------------------------#mean & SD of simulated values cm_sim.m_n3 <- apply( CM.sim_n3, c(1, 2), mean ) cm_sim.SD_n3 <- apply( CM.sim_n3, c(1, 2), sd ) fr_sim.m_n3 fr_sim.SD_n3 <- apply( FRic.sim_n3, 2, mean ) <- apply( FRic.sim_n3, 2, sd ) rao_sim.m_n3 <- apply( RaoQ.sim_n3, 2, mean ) rao_sim.SD_n3 <- apply( RaoQ.sim_n3, 2, sd ) #SES SES.CM_n3 SES.FRic_n3 SES.RaoQ_n3 <- ( CM.obs - cm_sim.m_n3 ) / cm_sim.SD_n3 <- ( FRic.obs - fr_sim.m_n3 ) / fr_sim.SD_n3 <- ( RaoQ.obs - rao_sim.m_n3 ) / rao_sim.SD_n3