use rayon::prelude::*;
use rustc_hash::FxHashSet;
use std::time::Instant;
use crate::prelude::*;
pub fn get_top_genes_perc(
f_path: &str,
top_n_values: &[usize],
cell_indices: &[usize],
verbose: usize,
) -> Result<Vec<Vec<f32>>, BixverseErrors> {
let verbosity = parse_verbosity_level(verbose);
let start_reading = Instant::now();
let reader = ParallelSparseReader::new(f_path)?;
let cell_chunks = reader.read_cells_parallel(cell_indices)?;
let end_read = start_reading.elapsed();
if verbosity.normal_verbosity() {
println!("Load in data: {:.2?}", end_read);
}
let start_calculations = Instant::now();
let mut results: Vec<Vec<f32>> = Vec::with_capacity(top_n_values.len());
for &top_n in top_n_values {
let proportions: Vec<f32> = cell_chunks
.par_iter()
.map(|chunk| {
let mut gene_counts: Vec<u32> = chunk.data_raw.iter().collect();
if gene_counts.len() <= top_n {
1.0
} else {
gene_counts.select_nth_unstable_by(top_n, |a, b| b.cmp(a));
let top_sum = gene_counts[..top_n].iter().map(|&x| x as f32).sum::<f32>();
top_sum / chunk.library_size as f32
}
})
.collect();
results.push(proportions);
}
let end_calculations = start_calculations.elapsed();
if verbosity.normal_verbosity() {
println!(
"Finished the top genes proportion calculations: {:.2?}",
end_calculations
);
}
Ok(results)
}
pub fn get_top_genes_perc_streaming(
f_path: &str,
top_n_values: &[usize],
cell_indices: &[usize],
verbose: usize,
) -> Result<Vec<Vec<f32>>, BixverseErrors> {
let verbosity = parse_verbosity_level(verbose);
let start_total = Instant::now();
let reader = ParallelSparseReader::new(f_path)?;
let mut results: Vec<Vec<f32>> = vec![Vec::new(); top_n_values.len()];
const CELL_BATCH_SIZE: usize = 100000;
if verbosity.normal_verbosity() {
println!("Using a streaming approach for top gene percentage calculations.");
}
for batch_start in (0..cell_indices.len()).step_by(CELL_BATCH_SIZE) {
let batch_end = (batch_start + CELL_BATCH_SIZE).min(cell_indices.len());
let cell_batch = &cell_indices[batch_start..batch_end];
let cell_chunks = reader.read_cells_parallel(cell_batch)?;
for (top_idx, &top_n) in top_n_values.iter().enumerate() {
let proportions: Vec<f32> = cell_chunks
.par_iter()
.map(|chunk| {
let mut gene_counts: Vec<u32> = chunk.data_raw.iter().collect();
if gene_counts.len() <= top_n {
1.0
} else {
gene_counts.select_nth_unstable_by(top_n, |a, b| b.cmp(a));
let top_sum = gene_counts[..top_n].iter().map(|&x| x as f32).sum::<f32>();
top_sum / chunk.library_size as f32
}
})
.collect();
results[top_idx].extend(proportions);
}
if verbosity.detailed_verbosity() && batch_start % (CELL_BATCH_SIZE * 5) == 0 {
let progress = ((batch_start + 1) as f32 / cell_indices.len() as f32) * 100.0;
println!(
" Reading cells and calculating proportions: {:.1}%",
progress
);
}
}
let end_total = start_total.elapsed();
if verbosity.normal_verbosity() {
println!(
"Finished the top genes proportion calculations: {:.2?}",
end_total
);
}
Ok(results)
}
pub fn get_gene_set_perc(
f_path: &str,
gene_indices: Vec<Vec<u32>>,
cell_indices: &[usize],
verbose: usize,
) -> Result<Vec<Vec<f32>>, BixverseErrors> {
let verbosity = parse_verbosity_level(verbose);
let start_reading = Instant::now();
let reader = ParallelSparseReader::new(f_path)?;
let cell_chunks = reader.read_cells_parallel(cell_indices)?;
let end_read = start_reading.elapsed();
if verbosity.normal_verbosity() {
println!("Load in data: {:.2?}", end_read);
}
let start_calculations = Instant::now();
let mut results: Vec<Vec<f32>> = Vec::with_capacity(gene_indices.len());
for gene_set in gene_indices {
let hash_gene_set: FxHashSet<&u32> = gene_set.iter().collect();
let percentage: &Vec<f32> = &cell_chunks
.par_iter()
.map(|chunk| {
let total_sum = chunk
.indices
.iter()
.zip(chunk.data_raw.iter())
.filter(|(col_idx, _)| hash_gene_set.contains(col_idx))
.map(|(_, val)| val)
.sum::<u32>() as f32;
let lib_size = chunk.library_size as f32;
total_sum / lib_size
})
.collect();
results.push(percentage.clone());
}
let end_calculations = start_calculations.elapsed();
if verbosity.normal_verbosity() {
println!(
"Finished the gene set proportion calculations: {:.2?}",
end_calculations
);
}
Ok(results)
}
pub fn get_gene_set_perc_streaming(
f_path: &str,
gene_indices: Vec<Vec<u32>>,
cell_indices: &[usize],
verbose: usize,
) -> Result<Vec<Vec<f32>>, BixverseErrors> {
let verbosity = parse_verbosity_level(verbose);
let start_total = Instant::now();
let reader = ParallelSparseReader::new(f_path)?;
let mut results: Vec<Vec<f32>> = vec![Vec::new(); gene_indices.len()];
let hash_gene_sets: Vec<FxHashSet<&u32>> =
gene_indices.iter().map(|gs| gs.iter().collect()).collect();
const CELL_BATCH_SIZE: usize = 100000;
if verbosity.normal_verbosity() {
println!("Using a streaming approach for gene set percentage calculation.");
}
for batch_start in (0..cell_indices.len()).step_by(CELL_BATCH_SIZE) {
let batch_end = (batch_start + CELL_BATCH_SIZE).min(cell_indices.len());
let cell_batch = &cell_indices[batch_start..batch_end];
let cell_chunks = reader.read_cells_parallel(cell_batch)?;
for (gs_idx, hash_gene_set) in hash_gene_sets.iter().enumerate() {
let percentage: &Vec<f32> = &cell_chunks
.par_iter()
.map(|chunk| {
let total_sum = chunk
.indices
.iter()
.zip(chunk.data_raw.iter())
.filter(|(col_idx, _)| hash_gene_set.contains(col_idx))
.map(|(_, val)| val)
.sum::<u32>() as f32;
let lib_size = chunk.library_size as f32;
total_sum / lib_size
})
.collect();
results[gs_idx].extend(percentage);
}
if verbosity.detailed_verbosity() && batch_start % (CELL_BATCH_SIZE * 5) == 0 {
let progress = ((batch_start + 1) as f32 / cell_indices.len() as f32) * 100.0;
println!(
" Reading cells and calculating proportions: {:.1}%",
progress
);
}
}
let end_total = start_total.elapsed();
if verbosity.normal_verbosity() {
println!(
"Finished the gene set proportion calculations: {:.2?}",
end_total
);
}
Ok(results)
}