(
log_means: &[f64],
n_bins: usize,
)
| 302 | } |
| 303 | |
| 304 | pub fn _get_mean_bins( |
| 305 | log_means: &[f64], |
| 306 | n_bins: usize, |
| 307 | ) -> anyhow::Result<(Vec<usize>, Vec<usize>)> { |
| 308 | // Use quantile-based binning instead of equal-width binning |
| 309 | // This ensures each bin has roughly the same number of genes |
| 310 | |
| 311 | let mut sorted_means: Vec<(usize, f64)> = log_means |
| 312 | .iter() |
| 313 | .enumerate() |
| 314 | .map(|(i, &mean)| (i, mean)) |
| 315 | .collect(); |
| 316 | |
| 317 | // Sort by log_means values |
| 318 | sorted_means.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)); |
| 319 | |
| 320 | let n_genes = log_means.len(); |
| 321 | let genes_per_bin = n_genes / n_bins; |
| 322 | let remainder = n_genes % n_bins; |
| 323 | |
| 324 | let mut bin_indices = vec![0; log_means.len()]; |
| 325 | let mut mean_bins = vec![0; n_bins]; |
| 326 | |
| 327 | let mut current_gene_idx = 0; |
| 328 | |
| 329 | (0..n_bins).for_each(|bin_idx| { |
| 330 | // Calculate how many genes should be in this bin |
| 331 | // First 'remainder' bins get one extra gene |
| 332 | let genes_in_this_bin = if bin_idx < remainder { |
| 333 | genes_per_bin + 1 |
| 334 | } else { |
| 335 | genes_per_bin |
| 336 | }; |
| 337 | |
| 338 | // Assign genes to this bin |
| 339 | for _ in 0..genes_in_this_bin { |
| 340 | if current_gene_idx < sorted_means.len() { |
| 341 | let original_idx = sorted_means[current_gene_idx].0; |
| 342 | bin_indices[original_idx] = bin_idx; |
| 343 | current_gene_idx += 1; |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | mean_bins[bin_idx] = genes_in_this_bin; |
| 348 | }); |
| 349 | |
| 350 | Ok((mean_bins, bin_indices)) |
| 351 | } |
| 352 | |
| 353 | pub fn _calculate_dispersion_stats( |
| 354 | log_dispersions: &[f64], |
nothing calls this directly
no outgoing calls
no test coverage detected