Isodars are estimated from the spread of a species’ densities across
replicates, so a species needs enough replicates, enough individuals and
a consistent habitat preference for its niche breadth to be estimated
well. Many species in a typical community data set will not meet these
requirements. niche_breadth() never stops on such a
species: it returns an estimate where it can, and records each potential
problem as a logical flag_* column. This vignette explains
each flag and what to do about it.
nb <- niche_breadth(moorea_fish, species = "species", site = "site",
habitat = "habitat", abundance = "abundance",
n_boot = 0, seed = 1)
summary(nb)
#> Niche breadth for 325 species
#> DD niche breadth at: asymptote; bootstrap replicates: 0
#> with DI and DD estimates: 167
#> without warning flags: 32
#>
#> Estimates (quartiles):
#> min q25 median q75 max
#> nb_di 0 0 0.181 0.442 1
#> spillover_abundance 1 1 9.000 39.500 5085
#> nb_dd 0 0 0.001 0.125 1
#>
#> Flags raised (species):
#> few_sites 158 fewer sites than min_sites
#> -> group nearby sites (group_sites()) or treat as unreliable
#> fit_failed 98 isodars could not be fitted
#> -> see the 'note' column
#> undersampled 97 zeros in a habitat sampled at too few sites to call it absent
#> -> sample more sites, or name absent habitats with 'absent'
#> intercept_noise 96 isodar intercepts do not differ from sampling noise
#> -> no evidence that niche breadth changes with abundance; report DD niche breadth
#> poor_fit 90 a line does not describe the data well, or the pairwise isodars disagree
#> -> check the isodars (fit_isodars()); try the other method
#> low_abundance 85 small counts per site, or mostly zeros
#> -> group sites, or report as unreliable; estimates rest on few individuals
#> no_di 60 no total abundance occupies every used habitat
#> -> check the isodars; the least-favoured habitat may never fill
#> di_extrapolation 37 spillover abundance outside the range of observed site totals
#> -> DI niche breadth is extrapolated; interpret with care
#> dd_extrapolation 30 the data do not reach the abundances that determine DD niche breadth
#> -> DD niche breadth is extrapolated; interpret with care
#> few_pair_sites 5 a pair of used habitats shares fewer than min_pair_sites sites
#> -> use dim = "ndim" with ndim_method = "latent", or group sites
#> reversal 133 habitats change rank along the niche breadth curve
#> -> informational: compare species at a stated abundance (see iai_curve())
#> abandoned 113 a used habitat is abandoned at high density
#> -> informational: the habitat counts for DI but not for DD niche breadth
#>
#> See vignette("diagnostics") for details. Thresholds are set with niche_breadth(diagnostics = ).In a data set of 13 sites, most rare species cannot be analysed. This is expected: the same species would also give unreliable classic niche breadth values, but without any warning.
Thresholds are arguments of niche_breadth(), set through
diagnostics
(e.g. diagnostics = list(min_sites = 8)); the defaults are
given below.
few_sites: fewer than
min_sites (10) replicates with the species. Isodars from
few points are imprecise. Group nearby sites
(group_sites(); see
vignette("data-preparation")) or treat the species as
unreliable.few_pair_sites: some pair of used
habitats was sampled together at fewer than min_pair_sites
(5) sites, so its pairwise isodar is poorly estimated. Use the
n-dimensional isodar with ndim_method = "latent", or group
sites.low_abundance: the median site total
is below min_median_count (5) individuals, or more than
max_zero_share (half) of the counts in used habitats are
zeros. Densities are then dominated by sampling noise.undersampled: a habitat without
records was sampled at too few sites to call it absent. Sample more
sites, or decide on ecological grounds and name absent habitats with
absent.fit_failed: the isodars could not be
fitted; the reason is in the note column. Most often the
species has too few sites.disconnected: some habitats never
occur together at a site, so their relationship cannot be
estimated.poor_fit: for the n-dimensional
isodar, the line explains less than min_var_explained (50%)
of the variance; for pairwise isodars, the isodars of three habitats
contradict each other (the product of two slopes differs from the third
by more than a factor max_slope_ratio, 2, or in sign).
Habitat selection may not follow a single isodar, or the data are too
noisy. Look at the isodars with fit_isodars(), and try the
other method (dim = "ndim" or
"pairwise").no_di: no total abundance up to
max_search puts an individual in every used habitat: the
least-favoured habitat is predicted never to fill.intercept_noise: the isodar intercepts
do not differ from those expected by sampling alone (see below). The
data then give no evidence that niche breadth changes with abundance:
with zero intercepts, the isodars pass through the origin and DI and DD
niche breadth are equal. Report a single value, DD niche breadth, which
rests on the slopes only and is the better estimated. With few sites the
test has little power, so this flag does not show that the intercepts
are zero.di_extrapolation: the spillover
abundance lies outside the observed range of site totals by more than a
factor extrapolation_ratio (2).dd_extrapolation: the largest site
total is below twice the spillover abundance, so the data never reach
the halfway point of the curve towards DD niche breadth.Both are extrapolations of the fitted isodars; the estimates may be right, but rest on the assumption that the isodars hold beyond the data.
unstable_bootstrap (only with
n_boot > 0): more than max_failed_share
(10%) of bootstrap replicates failed, or a confidence interval is wider
than max_ci_width (0.5). Increase n_boot; if
the interval stays wide, the data cannot resolve the species’ niche
breadth.reversal: two habitats swap rank as
abundance increases; the abundances are in the reversals
column. Niche breadth can then change non-monotonically with abundance,
so compare species at a stated abundance.abandoned: a habitat used at low
abundance is predicted to be empty at high abundance; it counts for DI
but not DD niche breadth.When a species is rare, counts in its habitats are small and their
ratios noisy, which alone produces isodar intercepts.
niche_breadth() tests whether the observed intercepts
exceed this: it simulates n_null (199) data sets in which
each site’s individuals are redistributed among its habitats at random,
in proportion to the species’ overall density in each habitat (no
density dependence), refits the isodars, and compares the observed
intercepts with the simulated ones. intercept_p is the
smallest Holm-corrected p-value; the species is flagged when it exceeds
alpha (0.05). The test needs integer counts.
noisy <- nb[nb$flag_intercept_noise & !nb$flag_few_sites, ]
noisy[c("species", "nb_di", "nb_dd", "intercept_p")]
#> # A tibble: 64 × 4
#> species nb_di nb_dd intercept_p
#> <chr> <dbl> <dbl> <dbl>
#> 1 Acanthurus lineatus NA 0.0675 1
#> 2 Acanthurus nigroris NA 0.00533 0.24
#> 3 Acanthurus olivaceus NA 0.00127 0.15
#> 4 Anampses caeruleopunctatus 0.0537 0.0537 0.746
#> 5 Anampses melanurus 0.0519 0.0379 0.49
#> 6 Arothron meleagris 0.243 0.209 0.155
#> 7 Cantherhines dumerilii NA 0 0.172
#> 8 Cantherhines pardalis 0.00157 0.00152 0.887
#> 9 Canthigaster bennetti 0.213 0.126 0.06
#> 10 Caracanthus maculatus 0.288 0 0.15
#> # ℹ 54 more rows
#> Abundances are per site (equal effort in every site and habitat).Diagnostics are a starting point. For a flagged species, look at its isodars and curve:
sp <- "Acanthurus nigrofuscus"
nb[nb$species == sp, c("nb_di", "nb_dd", "var_explained", "flags")]
#> # A tibble: 1 × 4
#> nb_di nb_dd var_explained flags
#> <dbl> <dbl> <dbl> <chr>
#> 1 0.468 0.365 NA reversal, poor_fit
#> Abundances are per site (equal effort in every site and habitat).
fit <- fit_isodars(moorea_fish[moorea_fish$species == sp, ], site = "site",
habitat = "habitat", abundance = "abundance")
fit$fit[[1]]
#> # A tibble: 3 × 7
#> habitat_x habitat_y intercept slope sd n_sites flipped
#> <chr> <chr> <dbl> <dbl> <dbl> <int> <lgl>
#> 1 barrier_reef fringing_reef 0.553 0.916 59.3 13 FALSE
#> 2 outer_slope barrier_reef 49.6 0.265 66.9 13 TRUE
#> 3 outer_slope fringing_reef 86.6 0.0755 70.9 13 TRUEnb$reversals[[which(nb$species == sp)]]
#> total_abundance habitat_1 habitat_2 niche_breadth
#> 2 235.4562 barrier_reef outer_slope 0.9759597
#> 3 244.3702 fringing_reef outer_slope 0.9791916
#> 1 323.1334 fringing_reef barrier_reef 0.8452180When rare, this species lives on the fringing and barrier reefs: both
isodars against the outer slope have large intercepts. But the outer
slope fills fastest as abundance rises (the reefs gain only 0.27 and
0.08 individuals per individual on the outer slope), so it overtakes
both reefs at a few hundred individuals, the ranking of habitats changes
and niche breadth first rises, then falls. Here the pairwise isodars are
also somewhat inconsistent with each other (poor_fit).
Neither DI nor DD niche breadth alone describes such a species well; the
curve does.
check_coverage() and drop species or habitats that
are clearly undersampled.niche_breadth() with n_boot = 0 on all
species; look at summary().fit_failed, few_sites or
low_abundance; report DI and DD niche breadth separately
only where intercept_noise is not raised).n_boot = 999 or
more, and check unstable_bootstrap.