bioticVelocity {enmSdmX} | R Documentation |
Velocity of shifts in densities across a series of rasters
Description
Calculates metrics of "movement" of cell densities across a time series of rasters. Rasters could represent, for example, the probability of presence of a species through time. In this case, velocities would indicate rates and directions of range shift. The simplest metric is movement of the density-weighted centroid (i.e., range "center"), but many more are available to provide a nuanced indicator of velocity. See Details
for the types of metrics that can be calculated.
Usage
bioticVelocity(
x,
times = NULL,
atTimes = NULL,
elevation = NULL,
metrics = c("centroid", "nsCentroid", "ewCentroid", "nCentroid", "sCentroid",
"eCentroid", "wCentroid", "nsQuants", "ewQuants", "similarity", "summary"),
quants = c(0.05, 0.1, 0.5, 0.9, 0.95),
onlyInSharedCells = FALSE,
cores = 1,
warn = TRUE,
longitude = NULL,
latitude = NULL,
paths = NULL,
...
)
Arguments
x |
Either a
|
times |
Numeric vector with the same number of layers in |
atTimes |
Numeric, values of |
elevation |
Either |
metrics |
Biotic velocity metrics to calculate (default is to calculate them all). All metrics ignore
|
quants |
Numeric vector indicating the quantiles at which biotic velocity is calculated for the " |
onlyInSharedCells |
If |
cores |
Positive integer. Number of processor cores to use. Note that if the number of time steps at which velocity is calculated is small, using more cores may not always be faster. If you have issues when |
warn |
Logical, if |
longitude |
Numeric matrix or
|
latitude |
Numeric matrix or
|
paths |
This is used internally and rarely (never?) needs to be defined by a user (i.e., leave it as |
... |
Other arguments (not used). |
Details
Attention:
This function may yield erroneous velocities if the region of interest is near or spans a pole or the international date line. Results using the "Quant" and "quant" metrics may be somewhat counterintuitive if just one cell is >0, or one row or column has the same values with all other values equal to 0 or NA
because defining quantiles in these situations is not intuitive. Results may also be counterintuitive if some cells have negative values because they can "push" a centroid away from what would seem to be the center of mass as assessed by visual examination of a map.
Note:
For the nsQuants
and ewQuants
metrics it is assumed that the latitude/longitude assigned to a cell is at its exact center. For calculating the position of a quantile, density is interpolated linearly from one cell center to the center of the adjacent cell. If a desired quantile does not fall exactly on the cell center, it is calculated from the interpolated values. For quantiles that fall south/westward of the first row/column of cells, the cell border is assumed to be at 0.5 * cell length south/west of the cell center.
Value
A data frame with biotic velocities and related values. Fields are as follows:
-
timeFrom
: Start time of interval -
timeTo
: End time of interval -
timeMid
: Time point betweentimeFrom
andtimeTo
-
timeSpan
: Duration of interval
Depending on metrics
that are specified, additional fields are as follows. All measurements of velocity are in distance units (typically meters) per time unit (which is the same as the units used for times
and atTimes
). For example, if the rasters are in an Albers equal-area projection and times
are in years, then the output will be meters per year.
If
metrics
has'centroid'
: Columns namedcentroidVelocity
,centroidLong
,centroidLat
– Speed of weighted centroid, plus its longitude and latitude (in thetimeTo
period of each time step). Values are always >= 0.If
metrics
has'nsCentroid'
: Columns namednsCentroid
andnsCentroidLat
– Velocity of weighted centroid in north-south direction, plus its latitude (in thetimeTo
period of each time step). Positive values connote movement north, and negative values south.If
metrics
has'ewCentroid'
:ewCentroid
andewCentroidLong
– Velocity of weighted centroid in east-west direction, plus its longitude (in thetimeTo
period of each time step). Positive values connote movement east, and negative values west.If
metrics
has'nCentroid'
,'sCentroid'
,'eCentroid'
, and/or'wCentroid'
: Columns namednCentroidVelocity
andnCentroidAbund
,sCentroid
andsCentroidAbund
,eCentroid
andeCentroidAbund
, and/orwCentroid
andwCentroidAbund
– Speed of weighted centroid of all cells that fall north, south, east, or west of the landscape-wide centroid, plus a column indicating the total weight (abundance) of all such populations. Values are always >= 0.If
metrics
contains any ofnsQuants
orewQuants
: Columns namednsQuantVelocity_quant
Q andnsQuantLat_quant
Q, orewQuantVelocity_quant
Q andewQuantLat_quant
Q: Velocity of the Qth quantile weight in the north-south or east-west directions, plus the latitude or longitude thereof (in thetimeTo
period of each time step). Quantiles are cumulated starting from the south or the west, so the 0.05th quantile, for example, is in the far south or west of the range and the 0.95th in the far north or east. Positive values connote movement north or east, and negative values movement south or west.If
metrics
containssimilarity
, metrics of similarity are calculated for each pair of successive landscapes, defined below asx1
(raster intimeFrom
) andx2
(raster intimeTo
), with the number of shared non-NA
cells between them beingn
:A column named
simpleMeanDiff
:sum(x2 - x1, na.rm = TRUE) / n
A column named
meanAbsDiff
:sum(abs(x2 - x1), na.rm = TRUE) / n
A column named
rmsd
(root-mean square difference):sqrt(sum((x2 - x1)^2, na.rm = TRUE)) / n
A column named
godsoeEsp
:1 - sum(2 * (x1 * x2), na.rm=TRUE) / sum(x1 + x2, na.rm = TRUE)
, values of 1 ==> maximally similar, 0 ==> maximally dissimilar.A column named
schoenersD
:1 - (sum(abs(x1 - x2), na.rm = TRUE) / n)
, values of 1 ==> maximally similar, 0 ==> maximally dissimilar.A column named
warrensI
:1 - sqrt(sum((sqrt(x1) - sqrt(x2))^2, na.rm = TRUE) / n)
, values of 1 ==> maximally similar, 0 ==> maximally dissimilar.A column named
cor
: Pearson correlation betweenx1
andx2
.A column named
rankCor
: Spearman rank correlation betweenx1
andx2
.
If
metrics
containselevCentroid
: Columns namedelevCentroidVelocity
andelevCentroidElev
– Velocity of the centroid in elevation (up or down) and the elevation in the "to" timestep. Positive values of velocity connote movement upward, and negative values downward.If
metrics
containselevQuants
: Columns namedelevQuantVelocity_quant
Q andelevQuantVelocityElev_quant
Q – Velocity of the Nth quantile of mass in elevation (up or down) and the elevation of this quantile in the "to" timestep. Positive values of velocity connote movement upward, and negative values downward.If
metrics
containssummary
:A column named
propSharedCellsNotNA
: Proportion of cells that are notNA
in both the "from" and "to" time steps. The proportion is calculated using the total number of cells in a raster as the denominator (i.e., not total number of cells across two rasters).Columns named
timeFromPropNotNA
andtimeToPropNotNA
: Proportion of cells in the "from" time and "to" steps that are notNA
.A column named
mean
: Mean weight intimeTo
time step. In the same units as the values of the cells.Columns named
quantile_quant
Q: The Qth quantile(s) of weight in thetimeTo
time step. In the same units as the values of the cells.A column named
prevalence
: Proportion of non-NA
cells with weight >0 in thetimeTo
time step relative to all non-NA
cells. Unitless.
Examples
# NB These examples can take a few minutes to run.
# To illustrate calculation and interpretation of biotic velocity,
# we will calibrate a SDM for the Red-Bellied Lemur and project
# the model to the present and successive future climates. The time series
# of rasters is then used to calculate biotic velocity.
library(sf)
library(terra)
### process environmental rasters
#################################
# get rasters
rastFile <- system.file('extdata/madClim.tif', package='enmSdmX')
madClim <- rast(rastFile)
rastFile <- system.file('extdata/madClim2030.tif', package='enmSdmX')
madClim2030 <- rast(rastFile)
rastFile <- system.file('extdata/madClim2050.tif', package='enmSdmX')
madClim2050 <- rast(rastFile)
rastFile <- system.file('extdata/madClim2070.tif', package='enmSdmX')
madClim2070 <- rast(rastFile)
rastFile <- system.file('extdata/madClim2090.tif', package='enmSdmX')
madClim2090 <- rast(rastFile)
# The bioticVelocity() function needs rasters to be in equal-area
# projection, so we will project them here.
madAlbers <- getCRS('madAlbers') # Albers projection for Madagascar
madClim <- project(madClim, madAlbers)
madClim2030 <- project(madClim2030, madAlbers)
madClim2050 <- project(madClim2050, madAlbers)
madClim2070 <- project(madClim2070, madAlbers)
madClim2090 <- project(madClim2090, madAlbers)
# Coordinate reference systems:
wgs84 <- getCRS('WGS84') # WGS84
madAlbers <- getCRS('madAlbers') # Madagascar Albers
# lemur occurrence data
data(lemurs)
occs <- lemurs[lemurs$species == 'Eulemur fulvus', ]
occs <- vect(occs, geom=c('longitude', 'latitude'), crs=wgs84)
occs <- project(occs, madAlbers)
# eliminate cell duplicates
occs <- elimCellDuplicates(occs, madClim)
# extract environment at occurrences
occEnv <- extract(madClim, occs, ID = FALSE)
occEnv <- occEnv[complete.cases(occEnv), ]
# create background sites (using just 1000 to speed things up!)
bgEnv <- terra::spatSample(madClim, 3000)
bgEnv <- bgEnv[complete.cases(bgEnv), ]
bgEnv <- bgEnv[sample(nrow(bgEnv), 1000), ]
# collate occurrences and background sites
presBg <- data.frame(
presBg = c(
rep(1, nrow(occEnv)),
rep(0, nrow(bgEnv))
)
)
env <- rbind(occEnv, bgEnv)
env <- cbind(presBg, env)
### calibrate model
###################
predictors <- c('bio1', 'bio12')
# MaxEnt
mx <- trainMaxEnt(
data = env,
resp = 'presBg',
preds = predictors,
regMult = 1, # too few values for reliable model, but fast
cores = 2
)
### project to present and future climate
#########################################
predPresent <- predictEnmSdm(mx, madClim)
pred2030 <- predictEnmSdm(mx, madClim2030)
pred2050 <- predictEnmSdm(mx, madClim2050)
pred2070 <- predictEnmSdm(mx, madClim2070)
pred2090 <- predictEnmSdm(mx, madClim2090)
plot(predPresent, main = 'Present Suitability')
# plot change in suitability between present and 2090s
delta <- pred2090 - predPresent
plot(delta, main = 'Change in Suitability')
### calculate biotic velocity
#############################
series <- c(
predPresent,
pred2030,
pred2050,
pred2070,
pred2090
)
names(series) <- c('present', 't2030', 't2050', 't2070', 't2090')
plot(series)
times <- c(1985, 2030, 2050, 2070, 2090)
quants <- c(0.10, 0.90)
bv <- bioticVelocity(
x = series,
times = times,
quants = quants,
cores = 2
)
bv
### centroid velocities
# centroid (will always be >= 0)
# fastest centroid movement around 2060
plot(bv$timeMid, bv$centroidVelocity, type = 'l',
xlab = 'Year', ylab = 'Speed (m / y)', main = 'Centroid Speed')
# velocity northward/southward through time
# shows northward shift because always positive, fastest around 2060
plot(bv$timeMid, bv$nsCentroidVelocity, type = 'l',
xlab = 'Year', ylab = 'Velocity (m / y)', main = 'Centroid N/S Velocity')
# velocity eastward (positive)/westward (negative) through time
# movement eastward (positive) first, then westward (negative)
plot(bv$timeMid, bv$ewCentroidVelocity, type = 'l',
xlab = 'Year', ylab = 'Velocity (m / y)', main = 'Centroid E/W Velocity')
### map of centroid location through time
# shows centroid moves slightly northward through time
plot(delta, main = 'Centroid Location &\nChange in Suitability')
points(bv$centroidLong[1], bv$centroidLat[1], pch = 1)
points(bv$centroidLong[4], bv$centroidLat[4], pch = 16)
lines(bv$centroidLong, bv$centroidLat)
legend(
'bottomright',
legend = c(
'start (~1985)',
'stop (~2090)',
'trajectory'
),
pch = c(1, 16, NA),
lwd = c(NA, NA, 1)
)
### velocities of portions of range north/south/east/west of centroid
# positive ==> northward shift
# negative ==> southward shift
# portion of range north of centroid
# shows northward expansion because always positive
plot(bv$timeMid, bv$nCentroidVelocity, type = 'l',
xlab = 'Year', ylab = 'Velocity (m / y)',
main = 'Northern Part of Range')
# portion of range south of centroid
# shows northward contraction because always positive
plot(bv$timeMid, bv$sCentroidVelocity, type = 'l',
xlab = 'Year', ylab = 'Velocity (m / y)',
main = 'Southern Part of Range')
# portion of range east of centroid
# shows eastern portion moves farther east
plot(bv$timeMid, bv$eCentroidVelocity, type = 'l',
xlab = 'Year', ylab = 'Velocity (m / y)',
main = 'Eastern Part of Range')
# portion of range west of centroid
# shows western portion moves east
plot(bv$timeMid, bv$wCentroidVelocity, type = 'l',
xlab = 'Year', ylab = 'Velocity (m / y)',
main = 'Western Part of Range')
### velocities of range margins
# from south to north, 10th and 90th quantiles of density
# positive ==> northward shift
# negative ==> southward shift
# shows both northern and southern range margins shift northward
# because always positive... northern margin shift usually slower
ylim <- range(bv$nsQuantVelocity_quant0p1, bv$nsQuantVelocity_quant0p9)
plot(bv$timeMid, bv$nsQuantVelocity_quant0p1, type = 'l', ylim = ylim,
xlab = 'Year', ylab = 'Velocity (m / y)',
main = 'Northern/Southern Range Margins')
lines(bv$timeMid, bv$nsQuantVelocity_quant0p9, lty = 'dashed')
legend(
'bottomright',
legend = c('Southern Margin', 'Northern Margin'),
lty = c('solid', 'dashed')
)
# from east to west, 10th and 90th quantiles of density
# positive ==> eastward shift
# negative ==> westward shift
ylim <- range(bv$ewQuantVelocity_quant0p1, bv$ewQuantVelocity_quant0p9)
plot(bv$timeMid, bv$ewQuantVelocity_quant0p1, type = 'l', ylim = ylim,
xlab = 'Year', ylab = 'Velocity (m / y)',
main = 'Eastern/Western Range Margins')
lines(bv$timeMid, bv$ewQuantVelocity_quant0p9, lty = 'dashed')
legend(
'bottomright',
legend = c('Eastern Margin', 'Western Margin'),
lty = c('solid', 'dashed')
)
### summary statistics
# mean density across cells through time
plot(bv$timeMid, bv$mean, type = 'l',
xlab = 'Year', ylab = 'Mean Density',
main = 'Mean Density')
# sum of density across cells through time
plot(bv$timeMid, bv$sum, type = 'l',
xlab = 'Year', ylab = 'Sum of Density',
main = 'Sum of Density')
### change metrics
# average change in suitability from one time period to next
# shows average conditions getting worse
plot(bv$timeMid, bv$simpleMeanDiff, type = 'l',
xlab = 'Year', ylab = 'Mean Change in Suitability')
# average absolute change in suitability from one time period to next
# shows average absolute change declining
plot(bv$timeMid, bv$meanAbsDiff, type = 'l',
xlab = 'Year', ylab = 'Mean Absolute Change in Suitability')
# root-mean square difference from one time period to the next
# shows difference between successive rasters declines through time
plot(bv$timeMid, bv$rmsd, type = 'l',
xlab = 'Year', ylab = 'RMSD')
### raster similarity
# most indicate that successive rasters are similar through time
ylim <- range(bv$godsoeEsp, bv$schoenerD, bv$warrenI, bv$cor, bv$warrenI)
plot(bv$timeMid, bv$godsoeEsp, type = 'l', lty = 1, col = 1,
xlab = 'Year', ylab = 'Raster similarity', ylim = ylim)
lines(bv$timeMid, bv$schoenerD, lty = 2, col = 2)
lines(bv$timeMid, bv$warrenI, lty = 3, col = 3)
lines(bv$timeMid, bv$cor, lty = 4, col = 4)
lines(bv$timeMid, bv$rankCor, lty = 5, col = 5)
legend(
'right',
legend = c(
'Godsoe\'s ESP',
'Schoener\'s D',
'Warren\'s I',
'Correlation',
'Rank Correlation'
),
col = 1:5,
lty = 1:5
)
# values of 10th and 90th quantiles across cells through time
# shows most favorable cells becoming less favorable
# least favorable cells remain mainly unchanged
ylim <- range(bv$quantile_quant0p1, bv$quantile_quant0p9)
plot(bv$timeMid, bv$quantile_quant0p1, type = 'l', ylim = ylim,
xlab = 'Year', ylab = 'Quantile Value',
main = 'Quantiles across Cells')
lines(bv$timeMid, bv$quantile_quant0p9, lty = 'dashed')
legend(
'topright',
legend = c('10th quantile', '90th quantile'),
lty = c('solid', 'dashed')
)
### map of northern/southern range margins through time
# range of longitude shown in plot
madExtent <- ext(madClim)
xExtent <- as.vector(madExtent)[1:2]
plot(predPresent, main = 'North/South Range Margin Location')
lines(c(xExtent[1], xExtent[2]),
c(bv$nsQuantLat_quant0p9[1], bv$nsQuantLat_quant0p9[1]))
lines(c(xExtent[1], xExtent[2]),
c(bv$nsQuantLat_quant0p9[2], bv$nsQuantLat_quant0p9[2]), lty = 'dashed')
lines(c(xExtent[1], xExtent[2]),
c(bv$nsQuantLat_quant0p9[3], bv$nsQuantLat_quant0p9[3]), lty = 'dotdash')
lines(c(xExtent[1], xExtent[2]),
c(bv$nsQuantLat_quant0p9[4], bv$nsQuantLat_quant0p9[4]), lty = 'dotted')
lines(c(xExtent[1], xExtent[2]),
c(bv$nsQuantLat_quant0p1[1], bv$nsQuantLat_quant0p1[1]))
lines(c(xExtent[1], xExtent[2]),
c(bv$nsQuantLat_quant0p1[2], bv$nsQuantLat_quant0p1[2]), lty = 'dashed')
lines(c(xExtent[1], xExtent[2]),
c(bv$nsQuantLat_quant0p1[3], bv$nsQuantLat_quant0p1[3]), lty = 'dotdash')
lines(c(xExtent[1], xExtent[2]),
c(bv$nsQuantLat_quant0p1[4], bv$nsQuantLat_quant0p1[4]), lty = 'dotted')
legend(
'bottomright',
legend = c(
'1980s',
'2030s',
'2050s',
'2070s',
'2090s'
),
lty = c('solid', 'dashed', 'dotdash', 'dotted')
)
### map of eastern/western range margins through time
# range of longitude shown in plot
madExtent <- ext(madClim)
yExtent <- as.vector(madExtent)[3:4]
plot(predPresent, main = 'North/South Range Margin Location')
lines(c(bv$ewQuantLong_quant0p9[1], bv$ewQuantLong_quant0p9[1]),
c(yExtent[1], yExtent[2]))
lines(c(bv$ewQuantLong_quant0p9[2], bv$ewQuantLong_quant0p9[2]),
c(yExtent[1], yExtent[2]), lty = 'dashed')
lines(c(bv$ewQuantLong_quant0p9[3], bv$ewQuantLong_quant0p9[3]),
c(yExtent[1], yExtent[2]), lty = 'dotdash')
lines(c(bv$ewQuantLong_quant0p9[4], bv$ewQuantLong_quant0p9[4]),
c(yExtent[1], yExtent[2]), lty = 'dotted')
lines(c(bv$ewQuantLong_quant0p1[1], bv$ewQuantLong_quant0p1[1]),
c(yExtent[1], yExtent[2]))
lines(c(bv$ewQuantLong_quant0p1[2], bv$ewQuantLong_quant0p1[2]),
c(yExtent[1], yExtent[2]), lty = 'dashed')
lines(c(bv$ewQuantLong_quant0p1[3], bv$ewQuantLong_quant0p1[3]),
c(yExtent[1], yExtent[2]), lty = 'dotdash')
lines(c(bv$ewQuantLong_quant0p1[4], bv$ewQuantLong_quant0p1[4]),
c(yExtent[1], yExtent[2]), lty = 'dotted')
legend(
'bottomright',
legend = c(
'1980s',
'2030s',
'2050s',
'2070s',
'2090s'
),
lty = c('solid', 'dashed', 'dotdash', 'dotted')
)