///|
fn format_doubles(values : Array[Double]) -> String {
let mut result = "["
let mut index = 0
while index < values.length() {
if index > 0 {
result = result + ", "
}
result = result + values[index].to_string()
index = index + 1
}
result + "]"
}
///|
fn format_ints(values : Array[Int32]) -> String {
let mut result = "["
let mut index = 0
while index < values.length() {
if index > 0 {
result = result + ", "
}
result = result + values[index].to_string()
index = index + 1
}
result + "]"
}
///|
fn format_strings(values : Array[String]) -> String {
let mut result = "["
let mut index = 0
while index < values.length() {
if index > 0 {
result = result + ", "
}
result = result + values[index]
index = index + 1
}
result + "]"
}
///|
fn main {
println("=== Bioconductor scrapper Demo ===")
let counts = @src.scrapper_sample_counts()
let blocks = ["batch1", "batch1", "batch1", "batch2", "batch2", "batch2"]
let mitochondrial = @src.ScrapperNamedSubset::new("mito", [2, 4])
println("\n1. Batch-aware RNA quality control")
let qc = @src.scrapper_quick_rna_qc(counts, subsets=[mitochondrial], blocks~) catch {
_ => abort("failed to compute scrapper RNA QC")
}
println(" " + qc.summary())
println(" library sums: " + format_doubles(qc.metrics.sums))
println(" detected genes: " + format_ints(qc.metrics.detected))
println(
" mitochondrial proportions: " +
format_doubles(qc.metrics.subset_proportions[0]),
)
println("\n2. Library-size factors and log-normalization")
let size_factors = @src.scrapper_library_size_factors(
counts,
blocks~,
mode=@src.scrapper_center_per_block(),
) catch {
_ => abort("failed to compute library size factors")
}
let logcounts = @src.scrapper_normalize_counts(counts, size_factors) catch {
_ => abort("failed to normalize counts")
}
println(" size factors: " + format_doubles(size_factors))
println(" first normalized feature: " + format_doubles(logcounts[0]))
println("\n3. Mean-variance trend and highly variable genes")
let model = @src.scrapper_model_gene_variances(
logcounts,
mean_filter=false,
min_window_count=3,
) catch {
_ => abort("failed to model gene variances")
}
let highly_variable = model.highly_variable_genes(top=3) catch {
_ => abort("failed to choose highly variable genes")
}
println(" means: " + format_doubles(model.means))
println(" residual variances: " + format_doubles(model.residuals))
println(" selected 0-based genes: " + format_ints(highly_variable))
println("\n4. Multi-factor pseudo-bulk aggregation")
let aggregate = @src.scrapper_aggregate_across_cells(
counts,
[
@src.ScrapperFactor::new("cluster", ["T", "T", "B", "T", "B", "B"]),
@src.ScrapperFactor::new("batch", blocks),
],
compute_median=true,
) catch {
_ => abort("failed to aggregate cells")
}
let pseudo_bulk_means = aggregate.means() catch {
_ => abort("failed to compute pseudo-bulk means")
}
println(" " + aggregate.summary())
println(" groups: " + format_strings(aggregate.group_names))
println(" cells per group: " + format_ints(aggregate.counts))
println(" first feature means: " + format_doubles(pseudo_bulk_means[0]))
println("\n5. Immutable SingleCellExperiment integration")
let sce = @src.SingleCellExperiment::new(
counts,
["Gene1", "Gene2", "Gene3", "Gene4", "Gene5", "Gene6"],
["Cell1", "Cell2", "Cell3", "Cell4", "Cell5", "Cell6"],
)
let normalized_sce = @src.scrapper_normalize_rna_counts_sce(
sce,
blocks~,
mode=@src.scrapper_center_per_block(),
) catch {
_ => abort("failed to normalize SingleCellExperiment")
}
let (annotated_sce, sce_qc) = @src.scrapper_quick_rna_qc_sce(
normalized_sce,
subsets=[mitochondrial],
blocks~,
) catch {
_ => abort("failed to annotate SingleCellExperiment QC")
}
println(
" original logcounts rows: " +
@src.sce_get_assay(sce, "logcounts").length().to_string(),
)
println(
" copied logcounts rows: " +
@src.sce_get_assay(annotated_sce, "logcounts").length().to_string(),
)
println(
" copied QC metadata columns populated: " +
@src.sce_get_col_data(annotated_sce, "scrapper_keep").length().to_string(),
)
println(" SCE QC retained cells: " + sce_qc.retained_count().to_string())
println("\n=== Demo Complete ===")
}