two2tango

Simulation of associated virtual species

The model assumes an underlying Poisson point pattern and a Gaussian response, with intensity λ of species 1 and 2 given by:

λ1=peak1×exp(1/2×(tempμ1)2σ12+e1)
λ2=peak2×exp(1/2×(tempμ2)2σ22+e2)

where temp is the predictor, μ1 and μ2 are the niche mean for each species, and σ1 and σ2 represent the niche breath. The variables peak1 and peak2 are constants to that set the expected abundance at the mean.

Both species are associated through a residuals e1 and e2:

eijMVN(0,Σ)

Residuals have a multivariate (bivariate) normal distribution with mean zero and covariance matrix,

Σ=[var1,1cov1,2cov2,1var2,2]

The inverse of the covariance matrix is called the precision matrix, denoted by τ=Σ1.

Function

The function two2tango() needs the following arguments:

  • mu_1 = μ1 and mu_2 = μ2: the mean of the response curve (niche mean) for each species,
  • sigma_1 = σ1 and sigma_2 = σ2: the SD of the response curve (niche breath) for each species,
  • peak_1 = peak1 and peak_2 = peak2: constants that set the expected abundance at the mean,
  • var = var1,1 = var2,2: the variance of each species, which will always be set to 1,
  • cov1 = cov1,2 and cov2,1: the covariance of one species against the other, which needs to be symmetric.

Then it returns a list with two sf objects of POINT geometry, one for each species.

library(spatstat)
library(tmap)
tmap_mode("plot")
library(terra)
library(gstat)
library(sf)
library(tidyverse)

# functions
source('code/two2tango.R')
source('code/auxiliary.R')

Test the function

We will use as an example covariate the average annual temperature for Uruguay

uruguay <- geodata::gadm(country = 'UY', level=0, path = 'data/')
temperature <- geodata::worldclim_country('UY', var = 'tavg', path = 'data/')
temperature <- mean(temperature, na.rm=T) %>% mask(uruguay)
temp <- scale(temperature)

tm_shape(temp) +
  tm_raster(col.scale = tm_scale_continuous(midpoint = NA, values = 'brewer.rd_bu'),
            col.legend = tm_legend('temperature')) +
tm_shape(uruguay) +
  tm_borders() +
  tm_layout(frame=F, legend.frame = F)

Case 1

Species have the same niche and co-occur

μ1σ1peak1varcov
sp10.50.56010.9
sp20.50.56010.9
mu1 = 0.5
mu2 = 0.5
sigma1 = 0.5
sigma2 = 0.5
peak1 = 60
peak2 = 60
cov= 0.9

simulated_species <- two2tango(peak1=peak1, peak2=peak2,
                               mu1=mu1, sigma1=sigma1,
                               mu2=mu2, sigma2=sigma2,
                               cov=cov,
                               predictor = temp)

sp1 <- simulated_species[[1]] %>% mutate(species = 'sp1')
sp2 <- simulated_species[[2]] %>% mutate(species = 'sp2')
Code
tm_shape(temp) +
  tm_raster(col.scale = tm_scale_continuous(midpoint = NA, values = 'brewer.rd_bu'),
            col.legend = tm_legend('')) +
tm_shape(uruguay) + tm_borders() +
tm_shape(sp1) +
  tm_dots(fill='species',
          fill.scale = tm_scale_categorical(values='red'),
          fill.legend = tm_legend(''), size = 0.5) +
tm_shape(sp2) +
  tm_dots(fill='species',
          fill.scale = tm_scale_categorical(values='black'),
          fill.legend = tm_legend(''), size = 0.5) +
  tm_layout(frame=F, legend.frame = F)

Code
response.df <- tibble(x = seq(-3, 3, by = 0.01),
                      y1 = spec.response(x, mu1, peak1, sigma1),
                      y2 = spec.response(x, mu2, peak2, sigma2))

ggplot() +
    geom_line(data=response.df, aes(x=x, y=y1), col='red', linetype = 'dashed') +
    geom_point(data=response.df, aes(x=x, y=y1), col='red') +
    geom_line(data=response.df, aes(x=x, y=y2), col='black') +
    geom_line(data=response.df, aes(x=x, y=y2), col='black') +
    labs(y='Y') + theme_bw()

Case 2

Species have a different niche and negative co-occurrence

μ1σ1peak1varcov
sp10.250.5601-0.9
sp2-0.250.5601-0.9
mu1 = 0.25
mu2 = -0.25
sigma1 = 0.5
sigma2 = 0.5
peak1 = 60
peak2 = 60
cov= -0.9

simulated_species <- two2tango(peak1=peak1, peak2=peak2,
                               mu1=mu1, sigma1=sigma1,
                               mu2=mu2, sigma2=sigma2,
                               cov=cov,
                               predictor = temp)

sp1 <- simulated_species[[1]] %>% mutate(species = 'sp1')
sp2 <- simulated_species[[2]] %>% mutate(species = 'sp2')
Code
tm_shape(temp) +
  tm_raster(col.scale = tm_scale_continuous(midpoint = NA, values = 'brewer.rd_bu'),
            col.legend = tm_legend('')) +
tm_shape(uruguay) + tm_borders() +
tm_shape(sp1) +
  tm_dots(fill='species',
          fill.scale = tm_scale_categorical(values='red'),
          fill.legend = tm_legend(''), size = 0.5) +
tm_shape(sp2) +
  tm_dots(fill='species',
          fill.scale = tm_scale_categorical(values='black'),
          fill.legend = tm_legend(''), size = 0.5) +
  tm_layout(frame=F, legend.frame = F)

Code
response.df <- tibble(x = seq(-3, 3, by = 0.01),
                      y1 = spec.response(x, mu1, peak1, sigma1),
                      y2 = spec.response(x, mu2, peak2, sigma2))

ggplot() +
    geom_line(data=response.df, aes(x=x, y=y1), col='red', linetype = 'dashed') +
    geom_point(data=response.df, aes(x=x, y=y1), col='red') +
    geom_line(data=response.df, aes(x=x, y=y2), col='black') +
    geom_line(data=response.df, aes(x=x, y=y2), col='black') +
    labs(y='Y') + theme_bw()

Florencia Grattarola
Florencia Grattarola
Postdoc Researcher

Uruguayan biologist doing research in macroecology and biodiversity informatics.