knitr::opts_chunk$set(cache = TRUE, dev = c("png", "pdf"))
library(here)
source(here("src", "model_linear_validation_grampians.R"))
Warning: Column `taxon` joining factor and character vector, coercing into
character vector
Warning: Column `ibra_subregion` joining factor and character vector, coercing
into character vector

Warning: Removed 60 rows containing missing values (geom_point).
Warning: Removed 3 rows containing missing values (geom_point).
Warning: Removed 60 rows containing missing values (geom_point).
Warning: Removed 3 rows containing missing values (geom_point).

Warning: Removed 30 rows containing missing values (geom_point).
Warning: Removed 3 rows containing missing values (geom_point).
Warning: Removed 30 rows containing missing values (geom_point).
Warning: Removed 3 rows containing missing values (geom_point).

Warning: Column `taxon` joining factor and character vector, coercing into
character vector
Warning: Column `ibra_subregion` joining factor and character vector, coercing
into character vector

library(geometry) # use dev version from github for inhulln()
library(ks) # use own fork with bugfix in kde
library(ggplot2)
vars <- c("mlq", "twi", "r1k", "tn")
regions <- unique(mds$ibra_subregion)
taxa <- unique(mds$taxon)
taxa_x_region <- unique(mds[, c("taxon", "ibra_subregion")])
traits <- sapply(
taxa,
function(x) unique(mds[mds$taxon == x, c("sla", "sm", "mh")]),
simplify = FALSE
)
environs <- sapply(
unique(mds$ibra_subregion),
function(x) {
as.matrix(
mds[
mds$ibra_subregion == x &
mds$taxon ==
taxa_x_region[taxa_x_region$ibra_subregion == x, "taxon"][1],
c("y", vars)
]
)
}
)
environ_extents <- sapply(
environs, function(x) apply(x[, vars], 2, range), simplify = FALSE
)
chulls <- sapply(environs, function(x) convhulln(x[, vars]))
empirical_niche_optima <- function(taxon, region) {
environ <- as.matrix(
mds[mds$ibra_subregion == region & mds$taxon == taxon, c("y", vars)]
)
environ_extent <- environ_extents[[region]]
gridsize <- rep(30, length(vars))
d <- lapply(
0:1,
function(x) {
try(
kde(
environ[environ[, "y"] == x, vars],
xmin = as.vector(environ_extent[1, ]),
xmax = as.vector(environ_extent[2, ]),
gridsize = gridsize
),
silent = TRUE
)
}
)
if (class(d[[2]]) == "try-error") return(NA)
chull <- chulls[[region]]
empirical_niche_optima <- arrayInd(
which.max(
d[[2]]$estimate / d[[1]]$estimate *
inhulln(chull, as.matrix(do.call(expand.grid, d[[1]]$eval.points)))
),
gridsize
)
empirical_niche_optima <- sapply(
1:4, function(i) d[[1]]$eval.points[[i]][empirical_niche_optima[, i]]
)
names(empirical_niche_optima) <- vars
empirical_niche_optima
}
file <- paste0(eucs_cache_path(), "/empirical_nich_optima.rds")
if (!file_test("-f", file)) {
empirical_niche_optima <- parallel::mcmapply(
empirical_niche_optima, taxa_x_region[, 1], taxa_x_region[, 2],
mc.cores = 3
)
saveRDS(empirical_niche_optima, file, compress = "xz")
} else {
empirical_niche_optima <- readRDS(file)
}
chull_vertices <- mapply(
function(x, y) unique(t(sapply(x, function(z) y[z, vars]))),
chulls,
environs
)
predicted_niche_optima <- function(taxon, region) {
traits <- traits[[taxon]]
chull_verts <- chull_vertices[[region]]
predicted_niche_optima <- as.vector(
chull_verts[
which.max(
predict(
model_grampians,
data.frame(
traits[rep(1, nrow(chull_verts)), ],
chull_verts,
row.names = NULL
),
re.form = ~0
)
),
]
)
names(predicted_niche_optima) <- vars
predicted_niche_optima
}
predicted_niche_optima <- mapply(
predicted_niche_optima, taxa_x_region[, 1], taxa_x_region[, 2],
SIMPLIFY = FALSE
)
rand_dist <- mapply(
function(x, y) sqrt(sum((x - y)^2)),
lapply(
taxa_x_region$ibra_subregion,
function(x) {
ans <- rep(-999, 4)
while (!inhulln(chulls[[x]], rbind(ans))) {
ans <- runif(
4,
as.vector(environ_extents[[x]][1, ]),
as.vector(environ_extents[[x]][2, ])
)
}
ans
}
),
empirical_niche_optima
)
max_dist <- sapply(chull_vertices, function(x) max(dist(x)))
names(max_dist) <- regions
distance <- data.frame(
taxa_x_region,
relative_distance = mapply(
function(x, y) sqrt(sum((x - y)^2)),
predicted_niche_optima,
empirical_niche_optima
) / max_dist[taxa_x_region[[2]]],
random_distance = rand_dist / max_dist[taxa_x_region[[2]]]
)
ggplot(distance) + aes(relative_distance) + geom_histogram() +
facet_wrap(~ibra_subregion)

ggplot(distance) + aes(random_distance) + geom_histogram() +
facet_wrap(~ibra_subregion)

IycgLS0tCiMnIHRpdGxlOiAiTmljaGUgb3B0aW1hIHZhbGlkYXRpb24gb2YgbGluZWFyIG1vZGVsIgojJyBhdXRob3I6ICJXaWxsaWFtIEsuIE1vcnJpcyIKIycgZGF0ZTogImByIFN5cy5EYXRlKClgIgojJyBvdXRwdXQ6CiMnICAgcm1hcmtkb3duOjpodG1sX25vdGVib29rOgojJyAgICAgY29kZV9mb2xkaW5nOiBoaWRlCiMnIC0tLQoKIysgc2V0dXAsIG1lc3NhZ2U9RkFMU0UKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gVFJVRSwgZGV2ID0gYygicG5nIiwgInBkZiIpKQpsaWJyYXJ5KGhlcmUpCnNvdXJjZShoZXJlKCJzcmMiLCAibW9kZWxfbGluZWFyX3ZhbGlkYXRpb25fZ3JhbXBpYW5zLlIiKSkKbGlicmFyeShnZW9tZXRyeSkgIyB1c2UgZGV2IHZlcnNpb24gZnJvbSBnaXRodWIgZm9yIGluaHVsbG4oKQpsaWJyYXJ5KGtzKSAjIHVzZSBvd24gZm9yayB3aXRoIGJ1Z2ZpeCBpbiBrZGUKbGlicmFyeShnZ3Bsb3QyKQoKIysgdmFycwp2YXJzIDwtIGMoIm1scSIsICJ0d2kiLCAicjFrIiwgInRuIikKcmVnaW9ucyA8LSB1bmlxdWUobWRzJGlicmFfc3VicmVnaW9uKQp0YXhhIDwtIHVuaXF1ZShtZHMkdGF4b24pCnRheGFfeF9yZWdpb24gPC0gdW5pcXVlKG1kc1ssIGMoInRheG9uIiwgImlicmFfc3VicmVnaW9uIildKQoKIysgdHJhaXRzCnRyYWl0cyA8LSBzYXBwbHkoCiAgdGF4YSwKICBmdW5jdGlvbih4KSB1bmlxdWUobWRzW21kcyR0YXhvbiA9PSB4LCBjKCJzbGEiLCAic20iLCAibWgiKV0pLAogIHNpbXBsaWZ5ID0gRkFMU0UKKQoKIysgZW52CmVudmlyb25zIDwtIHNhcHBseSgKICB1bmlxdWUobWRzJGlicmFfc3VicmVnaW9uKSwKICBmdW5jdGlvbih4KSB7CiAgICBhcy5tYXRyaXgoCiAgICAgIG1kc1sKICAgICAgICBtZHMkaWJyYV9zdWJyZWdpb24gPT0geCAmCiAgICAgICAgbWRzJHRheG9uID09CiAgICAgICAgICB0YXhhX3hfcmVnaW9uW3RheGFfeF9yZWdpb24kaWJyYV9zdWJyZWdpb24gPT0geCwgInRheG9uIl1bMV0sCiAgICAgICAgYygieSIsIHZhcnMpCiAgICAgIF0KICAgICkKICB9CikKCiMrIGVudmV4dAplbnZpcm9uX2V4dGVudHMgPC0gc2FwcGx5KAogIGVudmlyb25zLCBmdW5jdGlvbih4KSBhcHBseSh4WywgdmFyc10sIDIsIHJhbmdlKSwgc2ltcGxpZnkgPSBGQUxTRQopCgojKwpjaHVsbHMgPC0gc2FwcGx5KGVudmlyb25zLCBmdW5jdGlvbih4KSBjb252aHVsbG4oeFssIHZhcnNdKSkKCiMrIGVtcHJpY25pY2hlb3B0CmVtcGlyaWNhbF9uaWNoZV9vcHRpbWEgPC0gZnVuY3Rpb24odGF4b24sIHJlZ2lvbikgewogIGVudmlyb24gPC0gYXMubWF0cml4KAogICAgbWRzW21kcyRpYnJhX3N1YnJlZ2lvbiA9PSByZWdpb24gJiBtZHMkdGF4b24gPT0gdGF4b24sIGMoInkiLCB2YXJzKV0KICApCiAgZW52aXJvbl9leHRlbnQgPC0gZW52aXJvbl9leHRlbnRzW1tyZWdpb25dXQogIGdyaWRzaXplIDwtIHJlcCgzMCwgbGVuZ3RoKHZhcnMpKQoKICBkIDwtIGxhcHBseSgKICAgIDA6MSwKICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgdHJ5KAogICAgICAgIGtkZSgKICAgICAgICAgIGVudmlyb25bZW52aXJvblssICJ5Il0gPT0geCwgdmFyc10sCiAgICAgICAgICB4bWluID0gYXMudmVjdG9yKGVudmlyb25fZXh0ZW50WzEsIF0pLAogICAgICAgICAgeG1heCA9IGFzLnZlY3RvcihlbnZpcm9uX2V4dGVudFsyLCBdKSwKICAgICAgICAgIGdyaWRzaXplID0gZ3JpZHNpemUKICAgICAgICApLAogICAgICAgIHNpbGVudCA9IFRSVUUKICAgICAgKQogICAgfQogICkKCiAgaWYgKGNsYXNzKGRbWzJdXSkgPT0gInRyeS1lcnJvciIpIHJldHVybihOQSkKCiAgY2h1bGwgPC0gY2h1bGxzW1tyZWdpb25dXQoKICBlbXBpcmljYWxfbmljaGVfb3B0aW1hIDwtIGFycmF5SW5kKAogICAgd2hpY2gubWF4KAogICAgICBkW1syXV0kZXN0aW1hdGUgLyBkW1sxXV0kZXN0aW1hdGUgKgogICAgICAgIGluaHVsbG4oY2h1bGwsIGFzLm1hdHJpeChkby5jYWxsKGV4cGFuZC5ncmlkLCBkW1sxXV0kZXZhbC5wb2ludHMpKSkKICAgICksCiAgICBncmlkc2l6ZQogICkKCiAgZW1waXJpY2FsX25pY2hlX29wdGltYSA8LSBzYXBwbHkoCiAgICAxOjQsIGZ1bmN0aW9uKGkpIGRbWzFdXSRldmFsLnBvaW50c1tbaV1dW2VtcGlyaWNhbF9uaWNoZV9vcHRpbWFbLCBpXV0KICApCgogIG5hbWVzKGVtcGlyaWNhbF9uaWNoZV9vcHRpbWEpIDwtIHZhcnMKICBlbXBpcmljYWxfbmljaGVfb3B0aW1hCn0KCmZpbGUgPC0gcGFzdGUwKGV1Y3NfY2FjaGVfcGF0aCgpLCAiL2VtcGlyaWNhbF9uaWNoX29wdGltYS5yZHMiKQoKaWYgKCFmaWxlX3Rlc3QoIi1mIiwgZmlsZSkpIHsKICBlbXBpcmljYWxfbmljaGVfb3B0aW1hIDwtIHBhcmFsbGVsOjptY21hcHBseSgKICAgIGVtcGlyaWNhbF9uaWNoZV9vcHRpbWEsIHRheGFfeF9yZWdpb25bLCAxXSwgdGF4YV94X3JlZ2lvblssIDJdLAogICAgbWMuY29yZXMgPSAzCiAgKQogIHNhdmVSRFMoZW1waXJpY2FsX25pY2hlX29wdGltYSwgZmlsZSwgY29tcHJlc3MgPSAieHoiKQp9ICBlbHNlIHsKICBlbXBpcmljYWxfbmljaGVfb3B0aW1hIDwtIHJlYWRSRFMoZmlsZSkKfQoKIysgY2h1bGx2ZXJ0CmNodWxsX3ZlcnRpY2VzIDwtIG1hcHBseSgKICBmdW5jdGlvbih4LCB5KSB1bmlxdWUodChzYXBwbHkoeCwgZnVuY3Rpb24oeikgeVt6LCB2YXJzXSkpKSwKICBjaHVsbHMsCiAgZW52aXJvbnMKKQoKIysgcHJlZG5pY2hlb3B0CnByZWRpY3RlZF9uaWNoZV9vcHRpbWEgPC0gZnVuY3Rpb24odGF4b24sIHJlZ2lvbikgewogIHRyYWl0cyA8LSB0cmFpdHNbW3RheG9uXV0KICBjaHVsbF92ZXJ0cyA8LSBjaHVsbF92ZXJ0aWNlc1tbcmVnaW9uXV0KCiAgcHJlZGljdGVkX25pY2hlX29wdGltYSA8LSBhcy52ZWN0b3IoCiAgICBjaHVsbF92ZXJ0c1sKICAgICAgd2hpY2gubWF4KAogICAgICAgIHByZWRpY3QoCiAgICAgICAgICBtb2RlbF9ncmFtcGlhbnMsCiAgICAgICAgICBkYXRhLmZyYW1lKAogICAgICAgICAgICB0cmFpdHNbcmVwKDEsIG5yb3coY2h1bGxfdmVydHMpKSwgXSwKICAgICAgICAgICAgY2h1bGxfdmVydHMsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IE5VTEwKICAgICAgICAgICksCiAgICAgICAgICByZS5mb3JtID0gfjAKICAgICAgICApCiAgICAgICksCiAgICBdCiAgKQoKICBuYW1lcyhwcmVkaWN0ZWRfbmljaGVfb3B0aW1hKSA8LSB2YXJzCiAgcHJlZGljdGVkX25pY2hlX29wdGltYQp9CgpwcmVkaWN0ZWRfbmljaGVfb3B0aW1hIDwtIG1hcHBseSgKICBwcmVkaWN0ZWRfbmljaGVfb3B0aW1hLCB0YXhhX3hfcmVnaW9uWywgMV0sIHRheGFfeF9yZWdpb25bLCAyXSwKICBTSU1QTElGWSA9IEZBTFNFCikKCiMrIHJhbmRkaXN0CnJhbmRfZGlzdCA8LSBtYXBwbHkoCiAgZnVuY3Rpb24oeCwgeSkgc3FydChzdW0oKHggLSB5KV4yKSksCiAgbGFwcGx5KAogICAgdGF4YV94X3JlZ2lvbiRpYnJhX3N1YnJlZ2lvbiwKICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgYW5zIDwtIHJlcCgtOTk5LCA0KQogICAgICB3aGlsZSAoIWluaHVsbG4oY2h1bGxzW1t4XV0sIHJiaW5kKGFucykpKSB7CiAgICAgICAgYW5zIDwtIHJ1bmlmKAogICAgICAgICAgNCwKICAgICAgICAgIGFzLnZlY3RvcihlbnZpcm9uX2V4dGVudHNbW3hdXVsxLCBdKSwKICAgICAgICAgIGFzLnZlY3RvcihlbnZpcm9uX2V4dGVudHNbW3hdXVsyLCBdKQogICAgICAgICkKICAgICAgfQogICAgICBhbnMKICAgIH0KICApLAogIGVtcGlyaWNhbF9uaWNoZV9vcHRpbWEKKQoKIysgbWF4ZGlzdAptYXhfZGlzdCA8LSBzYXBwbHkoY2h1bGxfdmVydGljZXMsIGZ1bmN0aW9uKHgpIG1heChkaXN0KHgpKSkKbmFtZXMobWF4X2Rpc3QpIDwtIHJlZ2lvbnMKCiMrIGRpc3RhbmNlCmRpc3RhbmNlIDwtIGRhdGEuZnJhbWUoCiAgdGF4YV94X3JlZ2lvbiwKICByZWxhdGl2ZV9kaXN0YW5jZSA9IG1hcHBseSgKICAgIGZ1bmN0aW9uKHgsIHkpIHNxcnQoc3VtKCh4IC0geSleMikpLAogICAgcHJlZGljdGVkX25pY2hlX29wdGltYSwKICAgIGVtcGlyaWNhbF9uaWNoZV9vcHRpbWEKICApIC8gbWF4X2Rpc3RbdGF4YV94X3JlZ2lvbltbMl1dXSwKICByYW5kb21fZGlzdGFuY2UgPSByYW5kX2Rpc3QgLyBtYXhfZGlzdFt0YXhhX3hfcmVnaW9uW1syXV1dCikKCiMrIHBsb3RkaXN0LCBmaWcud2lkdGg9MTAsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFCmdncGxvdChkaXN0YW5jZSkgKyBhZXMocmVsYXRpdmVfZGlzdGFuY2UpICsgZ2VvbV9oaXN0b2dyYW0oKSArCiAgZmFjZXRfd3JhcCh+aWJyYV9zdWJyZWdpb24pCgojKyBwbG90cmFuZGRpc3QsIGZpZy53aWR0aD0xMCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UKZ2dwbG90KGRpc3RhbmNlKSArIGFlcyhyYW5kb21fZGlzdGFuY2UpICsgZ2VvbV9oaXN0b2dyYW0oKSArCiAgZmFjZXRfd3JhcCh+aWJyYV9zdWJyZWdpb24pCg==