///|
pub(all) struct SampleStats {
sample_count : Int
called_count : Int
missing_count : Int
invalid_count : Int
alt_allele_count : Int
total_dosage : Int?
heterozygous_count : Int
homozygous_reference_count : Int
homozygous_alternate_count : Int
} derive(Debug, Eq)
///|
pub fn summarize_sample_calls(
calls : Array[SampleCall],
key : String,
) -> SampleStats {
let mut called_count = 0
let mut missing_count = 0
let mut invalid_count = 0
let mut alt_allele_count = 0
let mut total_dosage = 0
let mut has_dosage = true
let mut heterozygous_count = 0
let mut homozygous_reference_count = 0
let mut homozygous_alternate_count = 0
for sample in calls {
match sample_value(sample, key) {
Some(VcfValue::String(raw)) => {
let genotype = decode_genotype(raw)
match genotype_class(genotype) {
GenotypeClass::Invalid => invalid_count = invalid_count + 1
GenotypeClass::Missing => missing_count = missing_count + 1
GenotypeClass::Heterozygous => {
called_count = called_count + 1
heterozygous_count = heterozygous_count + 1
}
GenotypeClass::Reference => {
called_count = called_count + 1
homozygous_reference_count = homozygous_reference_count + 1
}
GenotypeClass::HomozygousAlternate => {
called_count = called_count + 1
homozygous_alternate_count = homozygous_alternate_count + 1
}
MultiAlternate => called_count = called_count + 1
}
alt_allele_count = alt_allele_count + genotype_alt_count(genotype)
if genotype.invalid {
has_dosage = false
}
match genotype_dosage(genotype) {
Some(value) => total_dosage = total_dosage + value
None => ()
}
}
Some(VcfValue::Missing) | None => missing_count = missing_count + 1
_ => invalid_count = invalid_count + 1
}
}
{
sample_count: calls.length(),
called_count,
missing_count,
invalid_count,
alt_allele_count,
total_dosage: if has_dosage {
Some(total_dosage)
} else {
None
},
heterozygous_count,
homozygous_reference_count,
homozygous_alternate_count,
}
}
///|
pub fn summarize_document_samples(
document : VcfDocument,
key : String,
) -> Array[(String, SampleStats)] {
let grouped : Array[(String, Array[SampleCall])] = []
for record in document.records {
for sample in record.samples {
let mut found = false
for index, pair in grouped {
if pair.0 == sample.name {
grouped[index].1.push(sample)
found = true
break
}
}
if !found {
grouped.push((sample.name, [sample]))
}
}
}
let result : Array[(String, SampleStats)] = []
for pair in grouped {
result.push((pair.0, summarize_sample_calls(pair.1, key)))
}
result
}
///|
pub fn sample_call_rate(stats : SampleStats) -> Double {
if stats.sample_count == 0 {
0.0
} else {
stats.called_count.to_double() / stats.sample_count.to_double()
}
}
///|
pub fn sample_alt_frequency(stats : SampleStats) -> Double? {
if stats.sample_count == 0 {
None
} else {
Some(
stats.alt_allele_count.to_double() / (stats.sample_count * 2).to_double(),
)
}
}
///|
pub fn sample_missing_rate(stats : SampleStats) -> Double {
if stats.sample_count == 0 {
0.0
} else {
stats.missing_count.to_double() / stats.sample_count.to_double()
}
}
///|
pub fn sample_is_high_quality(
stats : SampleStats,
minimum_call_rate : Double,
) -> Bool {
sample_call_rate(stats) >= minimum_call_rate && stats.invalid_count == 0
}
///|
pub fn merge_sample_stats(
left : SampleStats,
right : SampleStats,
) -> SampleStats {
{
sample_count: left.sample_count + right.sample_count,
called_count: left.called_count + right.called_count,
missing_count: left.missing_count + right.missing_count,
invalid_count: left.invalid_count + right.invalid_count,
alt_allele_count: left.alt_allele_count + right.alt_allele_count,
total_dosage: match (left.total_dosage, right.total_dosage) {
(Some(a), Some(b)) => Some(a + b)
_ => None
},
heterozygous_count: left.heterozygous_count + right.heterozygous_count,
homozygous_reference_count: left.homozygous_reference_count +
right.homozygous_reference_count,
homozygous_alternate_count: left.homozygous_alternate_count +
right.homozygous_alternate_count,
}
}