nongenetic_feature: Non-genetic feature model (S3)

View source: R/nongenetic_feature.R

nongenetic_featureR Documentation

Non-genetic feature model (S3)

Description

Constructor for a single non-genetic forensic feature (sex, age, region, hair colour, eye colour, pigmentation, birth date, or a user-defined "custom" feature). It is the non-genetic counterpart of marker_model(): it bundles the recorded observation, the reference/observation sub-model, the population reference, and the observation-error model into a single object that the per-feature engines (arriving in milestones F6.3–F6.5) consume.

Like marker_model(), this constructor only validates and stores its inputs; no conditional probability table is built here. The point of F6.1 is to fix the structural contract so that the genetic and non-genetic paths are symmetric: every non-genetic feature reduces to a CPT under H1 (missing person, with observation error) and a CPT under H2 (population marginal), then LR = P(D | H1) / P(D | H2) — exactly the shape of the per-marker genetic path.

Usage

nongenetic_feature(
  type,
  observed,
  model = NULL,
  db_or_freqs = NULL,
  error = NULL,
  tol = 1e-06
)

Arguments

type

Character scalar. One of "sex", "age", "region", "hair", "eyes", "pigmentation", "birthdate", "custom".

observed

The recorded observation for the unidentified person. For categorical features a length-one value matching a category of db_or_freqs (numeric labels are matched as characters). For age a single finite numeric. For birthdate either a single Date / a "YYYY-MM-DD" string, or a single finite numeric day discrepancy.

model

Either NULL (a type-appropriate default is filled in) or a named list describing the reference/observation sub-model. For categorical features the field reference is one of "marginal" (use db_or_freqs as H2, the default) or "uniform". For age, reference is "uniform" (with a length-2 increasing numeric range, default c(1, 80)) or "empirical". For birthdate, search is "open" (default) or "closed", with a strictly increasing numeric cuts vector (default c(-120, -30, 30, 120, 240, 360)). For "custom", model must be a named list with a character class field in c("categorical", "continuous", "date").

db_or_freqs

The population reference. Categorical: a named numeric vector (>= 2 categories, entries in ⁠[0, 1]⁠, summing to 1 within tol). Continuous: NULL when reference = "uniform", otherwise a numeric sample (length >= 2, finite). Date: NULL for an open search, otherwise a non-negative numeric vector of bin frequencies of length length(cuts) + 1 or a data.frame of declared dates.

error

The observation-error model under H1. Categorical: a scalar in ⁠[0, 1)⁠ (symmetric misclassification) or a square row-stochastic numeric matrix whose dimension equals the number of categories. The matrix is positional: row i / column j are the i-th / j-th category in the order of db_or_freqs (any dimnames are informational only). Continuous: a scalar in ⁠[0, 1)⁠. Date: a numeric Dirichlet alpha vector, all strictly positive, of length length(cuts) + 1. Defaults are class-appropriate except for "custom", where error is required.

tol

Numeric tolerance for the sum-to-one and row-stochastic checks. Defaults to 1e-6.

Value

An object of class "nongenetic_feature": a list with components type, feature_class, observed, model, error, db_or_freqs, and categories (the category labels for categorical features, NULL otherwise).

Feature classes

Each type maps to one of three internal feature classes that determine how db_or_freqs and error are interpreted:

categorical

sex, region, hair, eyes, pigmentation. db_or_freqs is a named numeric vector of population category frequencies (the H2 marginal); error is either a scalar symmetric misclassification rate or a full row-stochastic confusion matrix E with E[true, observed]. Discrete support, so the categorical KL is identical to the genetic engine.

continuous

age. The reference is either "uniform" over a numeric range or "empirical" from a sample passed in db_or_freqs; error is a scalar mis-binning rate.

date

birthdate. A Dirichlet model over signed declared-minus-actual day discrepancy bins (cuts); error is the Dirichlet alpha vector. search = "open" uses a uniform H2; search = "closed" uses database bin frequencies.

type = "custom" requires model to declare its class (one of the three above); validation then follows that class.

References

Marsico FL, et al. (2023). "Likelihood ratios for non-genetic evidence in missing person cases." Forensic Science International: Genetics, 66, 102891. \Sexpr[results=rd]{tools:::Rd_expr_doi("10.1016/j.fsigen.2023.102891")}

See Also

marker_model() for the genetic counterpart; the legacy non-genetic functions lr_sex(), lr_age(), lr_hair_color(), lr_birthdate(), lr_pigmentation() whose behaviour this framework generalises and which serve as its regression oracles.

Examples

# Categorical: biological sex, missing person female, 5% error
f_sex <- nongenetic_feature(
  type = "sex",
  observed = "F",
  db_or_freqs = c(F = 0.5, M = 0.5),
  error = 0.05
)
print(f_sex)

# Categorical hair colour with a full confusion matrix
E <- error_matrix_hair()
f_hair <- nongenetic_feature(
  type = "hair",
  observed = 1,
  db_or_freqs = c("1" = 0.3, "2" = 0.2, "3" = 0.25, "4" = 0.15, "5" = 0.1),
  error = E
)

# Continuous: age, uniform reference over [1, 80]
f_age <- nongenetic_feature(
  type = "age",
  observed = 42,
  model = list(reference = "uniform", range = c(1, 80)),
  error = 0.05
)

# Date: birth-date discrepancy, open search, default Dirichlet
f_bd <- nongenetic_feature(
  type = "birthdate",
  observed = 45,
  error = c(1, 4, 60, 11, 6, 4, 4)
)

mispitools documentation built on Aug. 26, 2026, 1:08 a.m.