A kinship2::pedigree.shrink() equivalent (Track B of
docs/planning/kinship2-supplement-full-reproduction-plan.md §4):
trims a pedigree down to the individuals needed to keep it genetically
informative within a genotyping-cost budget (maxBits), given which
individuals are genotyped (genotyped) and, optionally, which are
affected by a trait of interest (affected).
Arguments
- ped
a pedigree
data.frame. The fieldsid,sireanddamare required; an optionalaffectedlogical column is used as the default source foraffected(see below).sire/damareNAfor a founder, and may beNAfor only one of the two (partial parentage) – see the non-informative-trim tier above for how that case is handled.- genotyped
a logical vector, the same length as
nrow(ped)and in the same row order,TRUEwhere a genotype (or other available biological sample) exists for that individual.NAis not allowed, matching kinship2's ownavailvalidation.- affected
NULL(default) or a logical vector the same length asnrow(ped). WhenNULL, defaults toped$affectedif that column exists, or to all-FALSE(unaffected) if it does not – ensuring the affected-priority trim can always make progress even with no recorded affected status.- maxBits
numeric, default
16L. The bit-size budget the affected-priority trim reduces toward.
Value
A list:
- ped
The shrunk pedigree
data.frame, with all ofped's original columns; a promoted founder'ssire/damare set toNA.- idTrimmed
Character vector, every
idremoved, in removal order.- idList
A list with elements
unavail,noninformandaffected– character vectors (character(0)when empty) groupingidTrimmedby which tier removed each id.- bitSize
Numeric vector: the pedigree's bit size before any trimming, after tiers 1-2, then one further value per affected- priority round.
- genotyped
The final
genotypedvector, aligned toped$id.- pedSizeOriginal, pedSizeIntermed, pedSizeFinal
Integer row counts: original, after tiers 1-2, and final.
Details
Ported from kinship2's own pedigree.shrink() orchestrator and its 5
internal helpers (bitSize, findUnavailable –
excludeUnavailFounders/excludeStrayMarryin –,
findAvailNonInform, findAvailAffected,
pedigree.trim), all deparsed directly from the installed
kinship2 namespace (1.9.6.2), over this package's own
id/sire/dam data-frame pedigree representation
(kinship2 uses an S3 pedigree object with integer row indices
instead). Three tiers, applied in order:
Unavailable trim. Iteratively removes terminal (leaf) individuals who are not genotyped, then removes any founder couple with exactly one child together and no other mate, when both parents are themselves founders and neither is genotyped (the couple's shared child is promoted to founder status rather than removed), then removes any remaining childless founder ("stray marry-in") regardless of genotyped status – matching kinship2's own
excludeStrayMarryin, which does not consult availability at all.Non-informative trim. Removes a genotyped, non-parent individual whose own
sireanddamare both known and both genotyped, when the individual is notaffected(anNAaffectedstatus counts as unaffected here, matching kinship2's ownall(x == 0, na.rm = TRUE)rule) – they add no genotype information beyond what their parents already supply. A single-known- parent individual (one ofsire/damknown, the otherNA) is never trimmed by this tier: kinship2's ownpedigree()constructor forbids that input shape entirely ("Subjects must have both a father and mother, or have neither", confirmed against the installed namespace), so its algorithm never has to define this case – this package's pedigrees allow partial parentage as ordinary data (seegetIdsWithOneParent), so a literal port would divide a zero-length vector and error. This is a deliberate, documented package-specific extension, not a kinship2 behavior.Affected-priority trim. While the pedigree's bit size (
2 * nNonFounder - nFounder) still exceedsmaxBits, removes one genotyped, non-parent individual at a time – tryingNA-affected candidates first, then unaffected, then affected – choosing whichever single candidate's removal (including any cascade through tiers 1-2 above) minimizes the resulting bit size. Ties are broken deterministically by lowestid, compared as a string (ratified design decision D-B2) – kinship2's own reference implementation breaks ties viarunif()against the global RNG state, so the same input can produce a different answer run-to-run; a live, multi-seed comparison against the installedkinship2confirmed this is a genuine difference in reference behavior, not a hypothetical one. Unlike kinship2's ownidTrimmed/idList$affectfields, which record only the single trial candidate per round even when its removal cascades further (confirmed live: a fixture exists where kinship2's ownpedSizeFinaldrops by 2 in one round butidTrimmednames only 1) –shrinkPedigree()'sidTrimmed/idList$affectedrecord every id actually removed each round, sopedSizeOriginal - pedSizeFinalalways equalslength(idTrimmed). This does not change which individuals survive, only the completeness of the returned audit trail.
References
Sinnwell JP, Therneau TM, Schaid DJ (2014). "The kinship2 R Package for Pedigree Data." Human Heredity, 78(2), 91-93.
Examples
library(nprcgenekeepr)
ped <- nprcgenekeepr::examplePedigree[, c("id", "sire", "dam")]
genotyped <- rep(TRUE, nrow(ped))
result <- shrinkPedigree(ped, genotyped, maxBits = 16L)
nrow(ped)
#> [1] 3694
nrow(result$ped)
#> [1] 0
