
Report eligible individual mate pairs with kinship and genetic-value context
Source:R/reportMatePairs.R
reportMatePairs.RdIssue #151 Slice 1: composes the existing pair-eligibility pipeline
(kinMatrix2LongForm, filterPairs,
filterAge(), filterKinMatrix – all unmodified)
into a report of opposite-sex, minimum-age-eligible mate-pair candidates,
each row optionally enriched with marker-based kinship
(markerKinship) and per-parent genetic-value context
(reportGV's indivMeanKin/gu) when available.
No composite/blended ranking score is computed – callers sort/filter the
raw columns themselves (see
docs/planning/issue151-individual-mate-pair-analysis-plan.md
Section 3, decision D3).
Usage
reportMatePairs(
ped,
kmat,
markerKmat = NULL,
geneticValues = NULL,
minAge = 1L,
populationIds = NULL,
exclude = character(0L),
ancestryRules = NULL,
overriddenRules = NULL
)Arguments
- ped
Pedigree data.frame with at least
id,sire,dam,sex, andagecolumns.- kmat
Pedigree kinship matrix (e.g. as returned by
kinship),idxid, covering every id considered.- markerKmat
Optional genotype-only KING-robust kinship matrix (e.g. as returned by
markerKinship),idxid, which may cover only a subset ofped$id(not every animal is genotyped).NULL(the default) leavesmarkerKinshipasNAfor every pair.- geneticValues
Optional
reportGV-shaped list (only$report, withid,indivMeanKin, andgucolumns, is used).NULL(the default) leaves the four per-parent genetic-value columnsNAfor every pair.- minAge
Numeric scalar, the minimum age (in the same units as
ped$age) for an individual to be pair-eligible. Default1, mirroring Breeding Groups' own scalar convention. A missing (NA) age passes this screen (see Details) – usepopulationIdsto actually bound the candidate population.- populationIds
Optional character vector of ids to which the analysis is scoped, applied before the pair-reshape (D4, see Details).
NULL(the default) performs no scoping;character(0)scopes to nobody and returns a zero-row result.- exclude
Character vector of ids to drop entirely, regardless of any other eligibility screen. Default
character(0)(no caller-supplied exclusions).- ancestryRules
Optional data frame of ancestry compatibility rules in
checkAncestryRules's shape (ancestry1,ancestry2,severity); validated here.pedmust then carry anancestrycolumn, or the call stops.NULL(the default) applies no ancestry screen. A valid zero-rule table is accepted: the result then has the ancestry columns, with nothing moved.- overriddenRules
Optional data frame with
ancestry1andancestry2columns namingblockrules to override for this call (unordered, case-insensitive match, as inreportAncestryViolations); areasoncolumn is accepted and ignored. An override naming no rule inancestryRules, aflagrule, or the same rule twice is an error, as is giving overrides withoutancestryRules.NULLor zero rows: none.
Value
A list with two data.frames (three when ancestryRules is
given):
- pairs
One row per eligible opposite-sex pair surviving every screen:
sireId,damId,kinship,markerKinship(NAif unavailable),sireIndivMeanKin,sireGu,damIndivMeanKin,damGu(the latter fourNAifgeneticValuesis not supplied or the parent is absent from its report). WithancestryRules, three columns follow:ancestryRule(the matched rule as a sorted"LEVEL-LEVEL"string),ancestrySeverity("flag"or"block") andancestryStatus("violation", or"overridden"for an overridden block rule), allNAfor a pair no rule matches.- excluded
One row per pair dropped by the age, user-exclude or ancestry screen:
sireId,damId,reason("under minimum age","user-excluded"or, withancestryRules,"ancestry rule"). WithancestryRulesanancestryRulecolumn follows: the matched rule for an ancestry exclusion,NAotherwise.- ancestryCoverage
Only with
ancestryRules: one row per standardized ancestry level (CHINESE, INDIAN, HYBRID, JAPANESE, OTHER, UNKNOWN) withancestry,n(distinct animals at that level inpairsandexcluded) andcovered(TRUEwhen a rule names the level), so a level no rule covers is visible.
Details
Population scoping (D4). minAge alone does not bound the
candidate-pair table on real, imperfectly-curated data: a missing age
passes filterAge()'s screen rather than being excluded
by it (see that function's own semantics), so long-retired or
never-dated individuals can still appear. populationIds, applied
via filterKinMatrix before the pair-reshape, is the
mechanism that actually bounds both correctness and table size; the
default NULL performs no scoping (every individual in kmat
is a candidate). An explicit character(0) is a valid, distinct
input – "scope to nobody" – and returns a zero-row pairs frame
with the full column shape, not an error.
Sex. Only animals recorded as male ("M") or female
("F") are paired. An animal with any other sex (unknown,
hermaphrodite or missing) is left out before pairs are formed and appears
in neither pairs nor excluded; the Mate Pair Analysis tab
says how many animals were left out.
Exclusion transparency (D5). Two independent screens produce
excluded rows: the age screen ("under minimum age") and the
caller-supplied exclude id list ("user-excluded"). A pair
failing the age screen is reported with that reason and never reaches the
user-exclude screen, so each dropped pair carries exactly one reason from
this closed, enumerable vocabulary.
Graceful degradation. markerKmat and geneticValues
are both NULL-safe: when absent (or when a specific pair is not
covered by them), the corresponding columns are NA for that row –
the pair itself is never dropped or the call errored, mirroring
modMarkerGeneticsServer's own "not yet uploaded" contract.
Ancestry rules (issue #169). ancestryRules applies the
same center-configurable rules table as the Breeding Groups ancestry
guardrails (checkAncestryRules) to individual pairs; a rule
matches a pair whichever animal is the sire and whichever the dam. A
block rule moves the matching pair out of pairs and into
excluded with the reason "ancestry rule" (here "block" means
"not listed as eligible", not "never placed together"); a flag rule
keeps the pair in pairs and annotates it. Naming a block rule in
overriddenRules keeps its pairs in pairs, marked
"overridden" – never silently absent. The ancestry screen runs
last: a pair that already failed the age or user-exclude screen keeps that
reason. Rules only move and label pairs; every pair is either in
pairs or in excluded, with or without rules. An animal whose
ancestry no rule names, or is missing, matches nothing. With
ancestryRules = NULL (the default) the result is unchanged, and
ped needs no ancestry column.
Examples
library(nprcgenekeepr)
ped <- data.frame(
id = c("S1", "D1", "A", "B"),
sire = c(NA, NA, "S1", NA),
dam = c(NA, NA, "D1", NA),
sex = c("M", "F", "M", "F"),
age = c(10, 10, 5, 5),
stringsAsFactors = FALSE
)
ped$gen <- findGeneration(ped$id, ped$sire, ped$dam)
kmat <- kinship(ped$id, ped$sire, ped$dam, ped$gen)
result <- reportMatePairs(ped, kmat, minAge = 1)
result$pairs
#> sireId damId kinship markerKinship sireIndivMeanKin sireGu damIndivMeanKin
#> 1 S1 D1 0.00 NA NA NA NA
#> 2 S1 B 0.00 NA NA NA NA
#> 3 A D1 0.25 NA NA NA NA
#> 4 A B 0.00 NA NA NA NA
#> damGu
#> 1 NA
#> 2 NA
#> 3 NA
#> 4 NA
# Ancestry rules (issue #169): the example rules block INDIAN x CHINESE
# and INDIAN x HYBRID pairs and flag INDIAN x UNKNOWN / INDIAN x OTHER
ancPed <- qcStudbook(
read.csv(
system.file("extdata", "examples", "example_ancestry_pedigree.csv",
package = "nprcgenekeepr"
),
stringsAsFactors = FALSE, na.strings = c("", "NA")
),
minSireAge = 2, minDamAge = 2, reportChanges = FALSE,
reportErrors = FALSE
)
ancPed$gen <- findGeneration(ancPed$id, ancPed$sire, ancPed$dam)
ancKmat <- kinship(ancPed$id, ancPed$sire, ancPed$dam, ancPed$gen)
rules <- checkAncestryRules(readAncestryRules(
system.file("extdata", "examples", "example_ancestry_rules.csv",
package = "nprcgenekeepr"
)
))
ancResult <- reportMatePairs(ancPed, ancKmat, ancestryRules = rules)
ancResult$excluded
#> sireId damId reason ancestryRule
#> 1 C1 I2 ancestry rule CHINESE-INDIAN
#> 2 I1 C2 ancestry rule CHINESE-INDIAN
#> 3 A1 C2 ancestry rule CHINESE-INDIAN
#> 4 I1 H1 ancestry rule HYBRID-INDIAN
#> 5 A1 H1 ancestry rule HYBRID-INDIAN
ancResult$ancestryCoverage
#> ancestry n covered
#> 1 CHINESE 2 TRUE
#> 2 INDIAN 3 TRUE
#> 3 HYBRID 1 TRUE
#> 4 JAPANESE 2 FALSE
#> 5 OTHER 1 TRUE
#> 6 UNKNOWN 1 TRUE
# overriding one block rule keeps its pairs listed, marked "overridden"
overridden <- reportMatePairs(ancPed, ancKmat,
ancestryRules = rules,
overriddenRules = data.frame(ancestry1 = "CHINESE", ancestry2 = "INDIAN")
)
overridden$pairs[
which(overridden$pairs$ancestryStatus == "overridden"),
c("sireId", "damId", "ancestryRule", "ancestryStatus")
]
#> sireId damId ancestryRule ancestryStatus
#> 2 C1 I2 CHINESE-INDIAN overridden
#> 6 I1 C2 CHINESE-INDIAN overridden
#> 9 A1 C2 CHINESE-INDIAN overridden