Registers an observed phenotype: the quantity written to ind_phenotype
when add_phenotype() is called. For simple (non-composite) traits,
phenotype_name equals trait_name and define_trait() must already have
been called for that name. For composite traits (e.g. weaning weight
assembled from direct and maternal components), no define_trait() call is
needed for the composite name itself.
Writes one row to phenotype_meta. If residual_var is supplied, also
writes an unconditional diagonal entry to phenotype_var_comp
(effect_name = "residual"). If components is supplied, writes one row
per component to phenotype_components.
Usage
define_phenotype(
pop,
phenotype_name,
type = c("continuous", "count", "categorical", "derived_formula"),
mean = 0,
expressed_sex = c("both", "M", "F"),
repeatable = FALSE,
min_value = NULL,
max_value = NULL,
prevalence = NULL,
thresholds = NULL,
cat_values = NULL,
cat_names = NULL,
store_liability = FALSE,
residual_var = NULL,
components = NULL,
formula_tgv = NULL,
formula = NULL,
missing_component_action = c("skip", "error"),
condition_change_action = c("error", "independent"),
overwrite = FALSE
)Arguments
- pop
A
tidybreed_popobject.- phenotype_name
Character. Name of the observed phenotype; equals
trait_namefor simple (non-composite) traits.- type
Character. One of
"continuous","count","categorical", or"derived_formula"."derived_formula"phenotypes are computed atadd_phenotype()time by evaluating theformulaexpression over already- recorded phenotype values for the same individuals; they have no genetic value, no residual variance, and no QTL of their own.- mean
Numeric. The intercept. Default
0. A record ismean+ the genetic value as stored + random effects + residual; nothing is added to make the realised mean hitmean. Generated effects have mean 0 in expectation at their base allele frequencies (Hardy-Weinberg and linkage equilibrium), someanis the base phenotypic mean in expectation; a finite, selected or non-equilibrium base differs by its sample genetic mean, and hand-written functional terms by their implied mean (seead_terms()). For a particular realised base mean, setmeanto the target minus the base individuals' mean ofind_tgv_total, measured afteradd_founders().- expressed_sex
Character. Who receives a phenotype record:
"both"(default),"M", or"F".- repeatable
Logical. Whether an individual can have multiple records for this phenotype (e.g. repeated test-day or litter-size records). Default
FALSE.- min_value, max_value
Numeric. Clipping bounds for count traits.
NULLmeans no limit.- prevalence
Numeric between 0 and 1. For categorical traits with one threshold (two categories), the fraction expected strictly above the threshold. Mutually exclusive with
thresholds. The threshold ismean + qnorm(1 - prevalence) * sqrt(V), whereVis the variance of the whole liability:the trait's stored genetic targets (
trait_var_comp, population-wide rows): the sum of theadditive,dominanceandadditive_by_additivediagonals, each counted only if the trait's model has terms of that kind;the stored variance of every named random effect (
define_effect_random();normalanduniformare centred with that variance);the unconditional residual variance.
This is a Gaussian approximation at a reference population in Hardy-Weinberg and linkage equilibrium. It is exact only when the liability is normal: a few large QTL make the genetic value discrete, and the realised prevalence then differs even in an infinite reference population. Summing the component targets also assumes they are orthogonal (no covariance between additive, dominance and A x A values), which holds for statistical coding at one base, not under LD. Fixed effects shift the liability and are not included: the prevalence is for records whose fixed effects are 0. A selected or line-specific population also differs. For an exact fraction in a known population, compute cutpoints from it and pass
thresholds.add_phenotype()refuses the threshold, before any draw or write, when no stored target can describe the liability:a term not owned by
"generated". A generator (define_additive_effects()) always calibrates its terms to the stored target; terms written withdefine_genome_effect_terms()carry values nothing checked against one;a kind of term with no stored target, or terms outside the three kinds (an
indicatorsurface, other interactions);generated variants for two parent-of-origin scopes at one line (paternal-only plus maternal-only, or common plus a parent-only fallback). Each was calibrated to the target alone; together they have a different variance;
a
gammarandom effect (its mean issqrt(variance));a residual with conditional strata (the marginal variance would depend on the levels' frequencies);
a total variance of 0, where no cutpoint gives a fraction.
Not valid for composite phenotypes (
componentsorformula_tgv): their genetic liability combines several traits and contributors, which no stored variance describes. Givethresholdsinstead.- thresholds
Numeric vector of length K−1 for K ordered categories: finite liability cutpoints in strictly ascending order. A record is in category
k + 1when its liability is strictly above cutpointk; a liability exactly on a cutpoint stays in the lower category. Mutually exclusive withprevalence.- cat_values
Numeric vector of length K. Phenotype value stored in
ind_phenotypefor each category. Defaults to1, 2, ..., K.- cat_names
Character vector of length K. Human-readable label per category, e.g.
c("Alive", "Dead"). Must not contain commas.- store_liability
Logical. When
TRUE, the underlying liability value is written to the reservedliability_valuecolumn inind_phenotype. Only meaningful for categorical traits.- residual_var
Numeric or
NULL. Scalar residual variance. When supplied, writes a 1 × 1 unconditional residual block for this phenotype tophenotype_var_comp(effect_name = "residual"). It is an error when the phenotype already belongs to a multi-phenotype residual block — that block is redeclared as a whole withdefine_residual_cov()— or when the phenotype's existing residual has realized draws inind_phenotype. Withoverwrite = TRUEand noresidual_var,phenotype_var_compis left untouched. For heterogeneous or correlated residuals usedefine_residual_cov().- components
A data frame or
tibblewith one row per genetic component. Columns:source_trait_name(required): component trait name intrait_meta.contributor_type(required):"self","dam","sire", or"group".weight(optional, default1.0): scalar multiplier.weight_type(optional, default"fixed"):"fixed"or"covariate"(weight * covariate). Nothing else is implemented, and anything else is rejected here rather than atadd_phenotype()time.covariate_name(optional): covariate key.covariate_table(optional, default"ind_meta"): table containing the covariate column; it must have exactly one row per individual.poly_order(optional): polynomial basis order.poly_scale_min,poly_scale_max(optional): Legendre scaling bounds.component_names(optional, default"total"): which genetic value of the contributor this row reads fromind_tgv."total"is the total genetic value (theind_tgv_totalview: additive, dominance and every other component). A comma-separated list ofind_tgv.component_namevalues ("additive","dominance","indicator","interaction", e.g."additive"or"additive,dominance") reads their sum; a listed component the trait's model has no terms for contributes 0. Anything else,"total"mixed with other names, or a duplicate is an error.group_column(optional): column defining group membership.group_table(optional, default"ind_meta"): table containinggroup_column.aggregation(optional, default"sum"):"sum"or"mean"for group contributors.
NULL(default) → simple single-self trait;phenotype_componentsnot written. Mutually exclusive withformula_tgv.- formula_tgv
Character. DSL shorthand for assembling a composite genetic value from component traits already in
trait_meta. A bare trait symbol (e.g."WWD") is the individual's own ("self") value; contributor roles are given as calls:self(trait),dam(trait),sire(trait): one positional trait;group_sum(trait, col),group_mean(trait, col): the sum or mean over the individual's group-mates (the other individuals with the same value ofcol; a group of one gives0).
Every call takes an optional named
component =: which genetic value of the contributor to read fromind_tgv—"total"(the default, theind_tgv_totalview: additive, dominance and every other component), or one component,"additive"(the breeding value for generated effects),"dominance","indicator"or"interaction"; a component the trait's model has no terms for reads 0. The group calls also take a namedtable =(default"ind_meta"): the table holdingcol, with exactly one row per individual. Both must be named, e.g."WWD + dam(WWM, component = \"additive\")"or"ADG_direct + group_sum(ADG_social, pen, table = \"pens\")".References combine with
+,-,*,/,^, parentheses, numbers and the math functions listed underformula. Anything else — another function, an unknown or positional extra argument, a component outside that list, acolortablethat is not a plain identifier, or a table or column that does not exist yet — is an error here, before anything is written. Mutually exclusive withcomponents. Not valid withtype = "derived_formula". A constant expression gives every individual that value. Atadd_phenotype(), a result that isInf,-InforNaN(division by zero, overflow, a function outside its domain) is an error naming the individuals, and nothing is written; a missing contributor ismissing_component_action's business, as before.- formula
Character. Arithmetic expression evaluated over already- recorded phenotype values to produce a derived phenotype (e.g.
"ADFI / ADG"for feed conversion ratio). Phenotype names referenceind_phenotyperecords (pheno_number = 1) produced earlier in the sameadd_phenotype()call — component phenotypes must already be recorded, andadd_phenotype()topologically sorts multiplederived_formulaphenotypes so dependencies are computed first. Operators+,-,*,/,^and parentheses are supported, plus the math functionssqrt,log,log2,log10,exp,abs,round,ceiling,floor,sign,trunc,sin,cos,tan,asin,acos,atan. Non-finite results (Inf/NaN) are converted toNAwith a warning. Required whentype = "derived_formula". Not valid otherwise.- missing_component_action
Character. What to do when an individual is missing one or more required composite components (e.g. no group assignment for a
"group"contributor, or a missing dam/sire genetic value)."skip"(default) excludes the individual fromind_phenotypeand emits a warning with a count."error"stops with an informative message listing affected individuals. Stored inphenotype_metaso the behaviour is consistent across alladd_phenotype()calls for this phenotype. Note: this is unrelated tonull_class_action(set viadefine_effect_fixed_class()), which handlesNULLlevels for fixed-class covariate effects, and does not affect random-effect draws (new levels always get a fresh draw).- condition_change_action
Character. Applies only when this phenotype is in a residual covariance block with a
condition_column(seedefine_residual_cov()) and a correlated phenotype's residual was stored under a different condition level than the one the current record resolves to — e.g. an animal moved farms between the two records."error"(default) stops, because no covariance is defined between the two strata."independent"drops the incompatible stored residual from the conditioning set (stored residuals from the same stratum still condition the draw) and warns with a count. Stored inphenotype_meta; every phenotype in one residual block must carry the same value (D6), so this argument only sets it while the phenotype is still a block of one. Once the block has two or more members the value is block-scoped — change it withdefine_condition_change_action(), which writes every member in one transaction and leaves the rest of theirphenotype_metarows alone. An immutable condition column such assexnever triggers either action.- overwrite
Logical. If
TRUEand a phenotype with the same name already exists, replace its rows inphenotype_metaandphenotype_components. DefaultFALSEerrors on duplicate.
Examples
if (FALSE) { # \dontrun{
# ── Simple continuous trait ──────────────────────────────────────────────
pop <- pop |>
define_trait("ADG") |>
get_table("genome_meta") |>
define_additive_effects("ADG", G = 100) |>
define_phenotype("ADG",
type = "continuous",
mean = 850,
residual_var = 120)
# ── Count trait with clipping bounds ────────────────────────────────────
pop <- pop |>
define_trait("NW") |>
get_table("genome_meta") |>
define_additive_effects("NW", G = 2) |>
define_phenotype("NW",
type = "count",
mean = 10,
min_value = 1,
max_value = 30,
residual_var = 8)
# ── Categorical trait (binary via prevalence) ────────────────────────────
pop <- pop |>
define_trait("mort") |>
get_table("genome_meta") |>
define_additive_effects("mort", G = 0.05) |>
define_phenotype("mort",
type = "categorical",
prevalence = 0.05,
cat_names = c("Alive", "Dead"))
# ── Maternal composite via components data frame: WW = WWD (self) + WWM (dam) ──
pop <- pop |>
define_phenotype("WW",
type = "continuous",
mean = 230,
residual_var = 180,
components = tibble::tribble(
~source_trait_name, ~contributor_type,
"WWD", "self",
"WWM", "dam"
))
# ── Maternal composite via formula_tgv shorthand (equivalent to above) ──
pop <- pop |>
define_phenotype("WW2",
type = "continuous",
mean = 230,
residual_var = 180,
formula_tgv = "WWD + dam(WWM)")
# ── SGE (social genetic effects): ADG = direct (self) + social group sum ──
pop <- pop |>
define_phenotype("ADG_sge",
type = "continuous",
mean = 850,
residual_var = 100,
formula_tgv = "ADG_direct + group_sum(ADG_social, pen_id)")
# ── Derived formula: FCR computed from already-recorded ADFI and ADG ────
# (Define ADFI and ADG first, then derive FCR — no genetic value or residual needed)
pop <- pop |>
define_phenotype("FCR",
type = "derived_formula",
formula = "ADFI / ADG")
} # }
