
Calculate the Correlated Fitness Surface
Source:R/correlated_fitness_surface.R
correlated_fitness_surface.RdFits a model for individual fitness based on multiple individual phenotypes (w ~ z1 + z2 + interactions). Traits MUST be standardized BEFORE calling this function.
Usage
correlated_fitness_surface(
data,
fitness_col,
trait_cols,
grid_n = 60,
method = "auto",
scale_traits = FALSE,
group = NULL,
group_effect = TRUE,
k = NULL,
mask = TRUE,
too_far = NULL,
bs = c("tp", "cr", "ps"),
smoothing = c("REML", "GCV.Cp", "ML"),
clamp = TRUE,
level = 0.95,
by_group = FALSE,
count_family = c("poisson", "quasipoisson", "nb")
)Arguments
- data
A data frame containing fitness and trait measurements.
- fitness_col
A string specifying the name of the fitness column.
- trait_cols
A character vector of exactly length 2 specifying the trait column names.
- grid_n
Integer specifying the resolution of the prediction grid. Default is 60.
- method
A string specifying the modeling method:
"auto","gam", or"tps".- scale_traits
Deprecated. Logical. Set to
FALSEto avoid double standardization.- group
Optional string naming a grouping column, such as species or year. The result's
groupstable then gives each group's mean trait values and the highest point of the surface within that group's own convex hull, and the plot functions draw both.- group_effect
Logical; with
TRUE(the default) the GAM includesgroupas a fixed effect and predicts the surface at the reference level, as the gradient models do for year or site; rows with no group label are one more level. WithFALSEone surface is fitted to everyone and the group is only used for thegroupstable and the overlay, which is what a community surface of several species needs (Beausoleil et al. 2023).- k
Basis dimension for the GAM smooth. The default
NULLsets it from the data asmin(30, max(10, floor(sqrt(n1 * n2)))), wheren1andn2are the numbers of distinct values of each trait; it is always capped at one less than the number of observations. Pass an integer to override.- mask
Logical; if
TRUE(the default) grid points outside the convex hull of the observed trait pairs getNAfitness, so the surface is only drawn where there are data. The grid column.insiderecords which points were kept,.fit_allholds the unmasked predictions, and the result'shullis the polygon the plot functions use to cover the outside.- too_far
Optional distance rule for blanking the grid, on top of
mask. Grid points farther than this from the nearest individual getNAfitness, with distances measured after scaling the grid to the unit square, as inmgcv::vis.gam. Beausoleil et al. (2023) used 0.15. The defaultNULLapplies no distance rule. The distance to the nearest individual is kept in the grid column.dist.- bs
Basis for the GAM smooth:
"tp"(thin plate, the default),"cr"or"ps".- smoothing
How the GAM's smoothing parameter is chosen:
"REML"(the default),"GCV.Cp"or"ML".- clamp
Logical; with
method = "tps"andTRUE(the default), predictions are held inside the range of the fitness type, 0 to 1 for survival and at least 0 for counts, asadaptive_landscape()does. The GAM respects the range through its link and is unaffected.- level
Confidence level of the band around a GAM surface. Default is 0.95.
- by_group
Logical; if
TRUEfit a separate surface to each level ofgroupand return a named list of surfaces, each with its own shape, grid and hull. The defaultFALSEfits one surface, with the group as a fixed effect or only marked on it (seegroup_effect).- count_family
Family for count fitness in the GAM:
"poisson"(the default);"quasipoisson", which estimates the dispersion, so the standard errors and peak comparisons allow for overdispersed counts; or"nb", a negative binomial. The smoothing parameter is chosen again with the dispersion estimated, so the fitted surface changes too, usually becoming smoother, and can change a lot where there are few individuals. The result'sdispersionis the Pearson dispersion of the fit, and a Poisson fit warns when it is above 1.5 (a rule of thumb).
Value
A list containing the fitted model, grid predictions, and metadata.
With method = "gam" the grid also carries .se, the standard
error of the fitted fitness, and .fit_lo and .fit_hi, the
band at level, worked out on the scale of the link and blanked
where the fit is; the thin-plate spline gives none.
peaks lists the local maxima of the kept surface, highest first,
with interior TRUE when every neighbouring cell is kept and
lower, and FALSE when the maximum sits against the edge of the
data, which may only be where the data end. original_data
holds the rows that were analysed. With a group the groups
data frame has one row per group with its size, mean traits and the
highest point of the surface within its own range, flagged
peak_interior when that point is an interior maximum of the
surface and peak_edge when it lies at the edge of the data; with
both FALSE the surface keeps rising past the group's range. For
count fitness count_family and dispersion record the family
used and the Pearson dispersion of the fit.
Details
The family follows the fitness column: 0/1 fitness gets a binomial
family, non-negative whole numbers with more than two values (lifespan in
years, offspring) a Poisson family with a log link or the one set by
count_family, and anything else a Gaussian family. With method = "auto" binary and count fitness use
the GAM and continuous fitness the thin-plate spline.
By default the GAM uses a thin-plate smooth with the smoothing
parameter chosen by REML; bs and smoothing are there to match
another study's smoother and do not apply to method = "tps". With
"cr" or "ps", which are one-dimensional bases, the two
traits enter as a tensor product smooth.
Predicting a fitted surface over the full rectangle of the grid
extrapolates into corners that no individual occupies; mask = TRUE
leaves those blank rather than showing a fitted value there. The hull
still fills gaps between separate clusters of individuals, such as
several species on one surface; too_far blanks those too.
Examples
prep <- prepare_selection_data(bumpus, "survival", c("total_length", "weight"))
surf <- correlated_fitness_surface(prep, "survival", c("total_length", "weight"), grid_n = 30)
#> Data type: binary; method: gam; n = 136; k = 29
#> GAM fitting with 136 observations
#> Trying formula: main
#> Success with formula: main
#> Predictions range: 0.0472 to 0.703
#> Masked 424 of 900 grid points outside the data
surf$grid[which.max(surf$grid$.fit), ]
#> total_length weight .fit .se .fit_lo .fit_hi .fit_all
#> 72 -0.5207948 -1.590066 0.7030432 0.1353279 0.3992416 0.8940022 0.7030432
#> .se_all .fit_lo_all .fit_hi_all .inside
#> 72 0.1353279 0.3992416 0.8940022 TRUE
# two lakes of pupfish on one surface: cells far from any fish blank, each
# lake's mean and local peak marked, the lake kept out of the model
pup <- rbind(crescent_pond_pupfish, little_lake_pupfish)
pup <- pup[pup$density == "H", ]
prep2 <- prepare_selection_data(pup, "survival", c("nose", "noseangle"))
surf2 <- correlated_fitness_surface(prep2, "survival", c("nose", "noseangle"), grid_n = 30,
too_far = 0.15, group = "lake", group_effect = FALSE)
#> Data type: binary; method: gam; n = 1671; k = 30
#> Grouping variable: lake (2 groups); one surface for all groups, the group only marks means and peaks
#> GAM fitting with 1671 observations
#> Trying formula: main
#> Success with formula: main
#> Predictions range: 0.0843 to 0.2206
#> Masked 390 of 900 grid points outside the data
surf2$groups
#> group n mean_nose mean_noseangle peak_nose peak_noseangle peak_fit
#> 1 CP 796 -0.005094234 -0.01971555 4.6037 2.438193 0.2049404
#> 2 LL 875 0.004634298 0.01793552 4.6037 1.455267 0.2013855
#> peak_interior peak_edge
#> 1 FALSE TRUE
#> 2 FALSE TRUE