///|
pub fn summarize(document : VcfDocument) -> VcfStats {
let contigs : Array[String] = []
let filters : Array[String] = []
let info_keys : Array[String] = []
let variant_types : Array[String] = []
let mut multiallelic_count = 0
for record in document.records {
contigs.push(record.chrom)
if record.alt_alleles.length() > 1 {
multiallelic_count = multiallelic_count + 1
}
collect_filters(record, filters)
collect_info_keys(record, info_keys)
collect_variant_types(record, variant_types)
}
{
record_count: document.records.length(),
contig_counts: count_keys(contigs),
filter_counts: count_keys(filters),
info_counts: count_keys(info_keys),
variant_type_counts: count_keys(variant_types),
sample_count: document.header.samples.length(),
multiallelic_count,
}
}
///|
pub fn format_stats(stats : VcfStats) -> String {
let builder = StringBuilder()
builder.write_string("records\t\{stats.record_count}\n")
builder.write_string("samples\t\{stats.sample_count}\n")
builder.write_string("multiallelic\t\{stats.multiallelic_count}\n")
write_counts(builder, "contig", stats.contig_counts)
write_counts(builder, "filter", stats.filter_counts)
write_counts(builder, "info", stats.info_counts)
write_counts(builder, "variant", stats.variant_type_counts)
builder.to_string()
}
///|
fn collect_filters(record : VcfRecord, filters : Array[String]) -> Unit {
if record.filters.length() == 0 {
filters.push(".")
} else {
for item in record.filters {
filters.push(item)
}
}
}
///|
fn collect_info_keys(record : VcfRecord, info_keys : Array[String]) -> Unit {
for pair in record.info {
let (key, _) = pair
info_keys.push(key)
}
}
///|
fn collect_variant_types(
record : VcfRecord,
variant_types : Array[String],
) -> Unit {
if record.alt_alleles.length() == 0 {
variant_types.push("NO_ALT")
} else {
for alt in record.alt_alleles {
variant_types.push(classify_variant(record.ref_allele, alt))
}
}
}
///|
fn classify_variant(ref_allele : String, alt_allele : String) -> String {
if alt_allele.has_prefix("<") ||
alt_allele.contains("[") ||
alt_allele.contains("]") {
"SYMBOLIC"
} else if ref_allele.length() == 1 && alt_allele.length() == 1 {
"SNV"
} else if ref_allele.length() < alt_allele.length() {
"INS"
} else if ref_allele.length() > alt_allele.length() {
"DEL"
} else if ref_allele.length() == alt_allele.length() {
"MNV"
} else {
"COMPLEX"
}
}
///|
fn count_keys(keys : Array[String]) -> Array[(String, Int)] {
let counts : Array[(String, Int)] = []
for key in keys {
if !has_count(counts, key) {
counts.push((key, count_matching(keys, key)))
}
}
counts
}
///|
fn has_count(counts : Array[(String, Int)], key : String) -> Bool {
for pair in counts {
let (item_key, _) = pair
if item_key == key {
return true
}
}
false
}
///|
fn count_matching(keys : Array[String], needle : String) -> Int {
let mut count = 0
for key in keys {
if key == needle {
count = count + 1
}
}
count
}
///|
fn write_counts(
builder : StringBuilder,
label : String,
counts : Array[(String, Int)],
) -> Unit {
for pair in counts {
let (key, count) = pair
builder.write_string("\{label}\t\{key}\t\{count}\n")
}
}