Conspecific/heterospecific correlation growth and fecundity

James S. Clark

2022-10-04

source('treeCorFunctions.r')
cols      <- c("#3288BD", "#D53E4F", "#a65628")
blueGreen <- colorRampPalette( c('#67a9cf','#3690c0','#02818a','#016450') )

This set of algorithms determines tree-to-tree correlation in growth and fecundity, and it summarizes correlations by conspecific and heterospecific pairs of individuals. For fecundity, it extracts annual estimates from trees on MASTIF sites. For diameter growth, it uses raw diameter data from MASTIF and other sites with repeated censuses. For fecundity, only seed-producing individuals are included. For growth, comparisons are restricted to individuals with a minimal number of overlapping measurements.

tree growth

After loading census data in the MASTIF format treeData (tree-years by variables), the function minCensus extracts trees that have been measured at least minCens times. In the MASTIF format, locations are held in a separate data.frame xytree. Locations are concatenated to treeData in the column minCensus.

If minCens is too small, say 3 measurements, there will be many trees, but pairwise correlations will be poorly supported–results will be noisy. If minCens is too large, few trees will qualify, and the distribution of pairwise correlations will contain few values. The value for minCens has to balance these concerns. Diameter growth needs at least 4 censuses. Fecundity needs at least 3 censuses.

minCensus( tdata, xy, minCens = 4 )
# tdata   - tree-years by variables, including plot, tree, year, diam
# xy      - trees by variables, including plot, tree, x, y
# minCens - at least 4 needed for growth, three for fecundity

tile to competitive neighborhoods

Competition is local, so we need to approximate a tree’s neighborhood. The inventory plot is partitioned into neighborhoods of area wide^2. Each tree is assigned to one neighborhood or tile. The function getTile appends a column to treeData with an integer value for the competition neighborhood.

getTile( tdata, wide = 40 )
# tdata - tree-years by variables, including plot, tree, year, diam, x, y
# wide  - wide of tile (m)

individual (pairwise) correlation

Correlations are compiled for individuals having i) overlappling sample histories of measurement of at least minCens years and ii) are within the same neighborhood (tile). The function treeCor takes data for one neighborhood and compiles the individual pairwise correlations as a matrix. For growth, it returns this matrix with NA values for pairs that do not have the same measurement history. Diameter measurements (cm) are transformed into growth (cm/yr) prior to correlation. For fecundity, it removes individuals that are not reproductive.

treeCor( tdata, varName = 'diam', minCens = 4 )
# tdata - tree-years by variables for one tile, including treeID, year, species, diam, x, y, 
#         and varName, which can be 'diam' or 'fecEstMu' (fecundity)

The function treeCor returns several objects:

Object Structure variables
tileData tile by variables mean intra/inter cor, n, richness, diversity, density
corTile spp by spp mean pairwise correlation
covTile spp by spp mean pairwise covariance
ncomp spp by spp no. of pairwise comparisons
xmu tile by spp mean response (‘diam’ or ‘fecEstMu’)
xvar tile by spp variance of response
ntree by spp no. trees

compiling tile correlations

The function tileCorrelation takes the data.frame treeData, which includes the tile column, and returns a summary matrix of correlation data. The rows are tile numbers. The columns include the mean intra- and inter-specific correlations for that tile ('intraMu', 'interMu') and the 90% interval ('intra05', 'intra95', 'inter05', 'inter95').

tileCorrelation( tdata, wide, varName = 'diam', FULLCOR = F ){
  # tdata requires a tile column, 1, 2, ...
  # returns: 
  # corMat    - tile by mean pairwise correlation differences
  # covMatrix - species by species mean covariances (not a covariance matrix)
  # growth    - mean species growth

Here is an example for diameter growth for BCI data.

load( 'treeData_diam/BCI.rdata', verbose = T )

wide      <- 30
treeData  <- minCensus( treeData, xytree, minCens = 4 )
treeData  <- getTile( treeData, wide = wide )
bciCor    <- tileCorrelation( treeData, wide = wide, varName = 'diam' )
tileData  <- bciCor$tileData
covMatrix <- bciCor$covMatrix
corMatrix <- bciCor$corMatrix
comMatrix <- bciCor$comMatrix
xmu       <- bciCor$xmu
xse       <- bciCor$xse
ntree     <- bciCor$ntree

Here is the tiled stem map for BCI:

par( bty='n')
plot( treeData$x, treeData$y, cex = treeData$diam/200, 
      col = blueGreen(5)[ 2*(1 + treeData$tile %% 2) ],
      asp = 1, xlab = '', ylab = '')
Thirty-meter tiles for BCI. Correlations between trees are evaluated at this scale, defined by parameter `wide`.

Thirty-meter tiles for BCI. Correlations between trees are evaluated at this scale, defined by parameter wide.

The function tileCorrelation returns several objects:

Object Structure variables
tileData tile by variables mean intra/inter correlation, n, richness, diversity, density
corMatrix spp by spp mean pairwise correlation
covMatrix spp by spp mean pairwise covariance
comMatrix spp by spp no. of pairwise comparisons
xmu tile by spp mean response (‘diam’ or ‘fecEstMu’)
xse tile by spp SE of response
ntree tile by spp no. of trees

The value NA is used where there are insufficient comparisons.

This code gives the proportionate fractional dominance

library( corrplot )
corMatrix[ !is.finite(corMatrix) ] <- 0
covMatrix[ !is.finite(covMatrix) ] <- 0
corrplot( t(corMatrix), is.corr = T, type = 'lower', order = 'hclust', tl.pos = 'n',
          addgrid.col = NA)

The differences in scale for species makes the covariance matrix hard to plot (most values are white). Here is the matrix of correlations:

Mean pairwise correlations for BCI growth data, with high values (blue) clustered along the diagonal.

Mean pairwise correlations for BCI growth data, with high values (blue) clustered along the diagonal.

This code plots histograms:

par( mfrow=c(1,2), bty='n', omi = c(.2, .1, .1, .1) )
corHist( tileData[,2], ylab = 'Frequency', col = cols[2] )
corHist( tileData[,1], add = T )
legend( 'topright', c('intra','inter'), text.col = cols, bty='n')
title( 'a) Pairwise correlations', font.main = 6 )

corHist( tileData[,1] - tileData[,2], col = 'white', xlim = c(-.05, .2), ylim = c(0, 6) )
abline( v = 0, col = 'white', lwd=3)
abline( v = 0, lty=2, col = 'grey', lwd=3)
corHist( tileData[,1] - tileData[,2], col = cols[3], add = T )
title( 'b) Intra- minus inter-specific', font.main = 6 )
mtext('Correlation', 1, outer=T, line = -1.5, cex = 1.2)

Below are histograms for mean correlations between individuals of the same and different species within a neighborhood (tile) at BCI. At right is the histogram of correlation differences.

Histograms of mean tile correlations between tree growth rates for intra- and inter-specific comparisons (left) with their difference (right) for BCI.

Histograms of mean tile correlations between tree growth rates for intra- and inter-specific comparisons (left) with their difference (right) for BCI.

The next block uses the function getData to merge sites by biome. To change this grouping edit your version of the function groupSites( sites ) in the file treeCorFunctions.R. Here are comparisons for diameter data from multiple sites:

wide    <- 30
varName <- 'diam'
minCens <- 4
minDiam <- 5

tmp <- getData( varName, wide, minCens, minDiam = minDiam )
tileList <- tmp$tileList
corList  <- tmp$corList
covList  <- tmp$covList
comList  <- tmp$comList
     xmu <- tmp$xmu
     xse <- tmp$xse
     
tileList <- tileList[ sapply(tileList, nrow) > 10 ]

dlim    <- c(-.2, 1)
breaks  <- seq( -1.5, 1.5, by = .05 )
words   <- plotSetup(names(tileList))
np      <- nrow(words)
xlabels <- ylabels <- F

for( j in 1:np){
  
  ylabels <- F
  yaxt    <- 'n'
  if( j == 1 ){
    ylabels <- T
    yaxt <- 's'
  }
  
  tmp <- mergeCov( tileList[j], covList[j], corList[j], comList[j], cint = .04 )
  x   <- tmp$density
  tl  <- tmp$tileList
  st  <- tmp$stats
  
  corHist( tl[,2], col = cols[2], xlim = dlim, ylim = c(0, 3),
           breaks = breaks, xaxt = 'n', yaxt = yaxt )
  corHist( tl[,1], col = cols[1], breaks = breaks, add = T )
  title( words[j,1], font.main= 6, line = -.5, cex=.9 )
  title( words[j,2], font.main= 6, line = -1.5, cex=.9 )
  if( j == np )legend( 'right', c('intra','inter'), text.col = cols, bty='n')
  
  corHist( tl[,1] - tl[,2], col = 'white', xlim = dlim, ylim = c(0, 3), 
           breaks = breaks, xaxt = 'n', yaxt = yaxt )
  abline( v = 0, col = 'white', lwd=3)
  abline( v = 0, lty=2, col = 'grey', lwd=3)
  corHist( tl[,1] - tl[,2], col = cols[3], breaks = breaks, add = T )
  
  ylim <- c(.1,100)
  dif <- tl[,1] - tl[,2]
  wf  <- which( is.finite(dif) )
  plot( dif, tl[,'BA'], xaxt = 'n', ylim = ylim, yaxt = 'n',
        xlab = '', ylab = '', xlim = dlim, log = 'y',  cex=.8, 
        pch = 16, col = .getColor( cols[3], .4) )
  axis( 2, labels = ylabels)
  axis( 1, labels = F )
  abline( v = 0, lwd = 3, col = 'white' )
  abline( v = 0, lwd = 3, lty = 2, col = 'grey' )
  if( length(wf) > 5 )reg2plot( dif, tl[,'BA'], col = cols[1], p = .1, ylim = ylim )
  
  ylim <- c(.5,5)
  plot( dif, tl[,'diversity'], xaxt = 'n', yaxt = 'n', 
        xlab = '', ylab = '', ylim = ylim, log = 'y', xlim = dlim, cex=.8, 
        pch = 16, col = .getColor( cols[3], .3) )
  axis( 2, labels = ylabels)
  axis( 1, labels = T )
  abline( v = 0, lwd = 3, col = 'white' )
  abline( v = 0, lwd = 3, lty = 2, col = 'grey' )
  if( length(wf) > 5 )reg2plot( dif, tl[,'diversity'], col = cols[1], p = .1, ylim = ylim )
}

mtext( paste0( c( 'Diversity', 'BA (m2/ha)', 'Cor Difference', 'Correlation'  ),
               collapse = '         '), 2, outer=T, line = 1 )
mtext( 'Correlation', 1, outer = T, line = 3 )
Intra- and inter-specific comparisons (first row) for diameter growth, with the difference histogram (2nd row). The two rows at bottom plot the correlation difference against the tile density and diversity (Shannon entropy).

Intra- and inter-specific comparisons (first row) for diameter growth, with the difference histogram (2nd row). The two rows at bottom plot the correlation difference against the tile density and diversity (Shannon entropy).

Same as above with wide = 50 m.

Same as above with wide = 50 m.

Intra- and inter-specific comparisons (first row) for fecundity, with the difference histogram (2nd row). The two rows at bottom plot the correlation difference against the tile density and diversity (Shannon entropy).

Intra- and inter-specific comparisons (first row) for fecundity, with the difference histogram (2nd row). The two rows at bottom plot the correlation difference against the tile density and diversity (Shannon entropy).

Mean correlation difference by site for diameter growth.

Mean correlation difference by site for diameter growth.

fecundity estimates

Fecundity estimates from MASTIF can be evaluated using the same code. In the previous block, I change varName <- fecEstMu, and minCens <- 3. Here are plots:

Intra- and inter-specific comparisons (first row) for fecundity, with the difference histogram (2nd row). The two rows at bottom plot the correlation difference against the tile density and diversity (Shannon entropy).

Intra- and inter-specific comparisons (first row) for fecundity, with the difference histogram (2nd row). The two rows at bottom plot the correlation difference against the tile density and diversity (Shannon entropy).

Same as previous with wide = 50 m.

Same as previous with wide = 50 m.

Mean correlation difference by site for fecundity.

Mean correlation difference by site for fecundity.