use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use std::collections::BTreeMap;
use crate::core::math::stats::*;
use crate::enrichment::gsea::*;
use crate::enrichment::oae::*;
use crate::prelude::*;
pub type GeneMap = FxHashMap<String, FxHashSet<String>>;
pub type AncestorMap = FxHashMap<String, Vec<String>>;
pub type LevelMap = FxHashMap<String, Vec<String>>;
type GoIntermediaryRes<T> = (T, usize, Vec<i32>);
#[derive(Clone, Debug)]
pub struct GoElimLevelResults<T> {
pub go_ids: Vec<String>,
pub pvals: Vec<T>,
pub odds_ratios: Vec<T>,
pub hits: Vec<usize>,
pub gene_set_lengths: Vec<usize>,
}
#[derive(Clone, Debug)]
pub struct GoElimFinalResults<T> {
pub go_ids: Vec<String>,
pub pvals: Vec<T>,
pub fdr: Vec<T>,
pub odds_ratios: Vec<T>,
pub hits: Vec<usize>,
pub gs_length: Vec<usize>,
}
#[derive(Clone, Debug)]
pub struct GoElimLevelResultsGsea<T> {
pub go_ids: Vec<String>,
pub es: Vec<T>,
pub nes: Vec<Option<T>>,
pub size: Vec<usize>,
pub pvals: Vec<T>,
pub n_more_extreme: Vec<usize>,
pub ge_zero: Vec<usize>,
pub le_zero: Vec<usize>,
pub leading_edge: Vec<Vec<i32>>,
}
#[derive(Clone, Debug)]
pub struct GeneOntology<'a> {
pub go_to_gene: GeneMap,
pub ancestors: &'a AncestorMap,
pub levels: &'a LevelMap,
}
impl<'a> GeneOntology<'a> {
pub fn new(gene_map: GeneMap, ancestor_map: &'a AncestorMap, levels_map: &'a LevelMap) -> Self {
GeneOntology {
go_to_gene: gene_map,
ancestors: ancestor_map,
levels: levels_map,
}
}
pub fn get_ancestors(&self, id: &String) -> Option<&Vec<String>> {
self.ancestors.get(id)
}
pub fn get_level_ids(&self, id: &String) -> Option<&Vec<String>> {
self.levels.get(id)
}
pub fn remove_genes(&mut self, ids: &[String], genes_to_remove: &FxHashSet<String>) {
for id in ids.iter() {
if let Some(gene_set) = self.go_to_gene.get_mut(id) {
gene_set.retain(|gene| !genes_to_remove.contains(gene));
}
}
}
pub fn get_genes_list(&self, ids: &[String]) -> FxHashMap<String, &FxHashSet<String>> {
let mut to_ret = FxHashMap::with_capacity_and_hasher(ids.len(), FxBuildHasher);
for id in ids.iter() {
if let Some(gene_set) = self.go_to_gene.get(id) {
to_ret.insert(id.clone(), gene_set);
}
}
to_ret
}
pub fn get_genes(&self, id: &String) -> Option<&FxHashSet<String>> {
self.go_to_gene.get(id)
}
}
#[derive(Clone, Debug)]
pub struct GeneOntologyRandomPerm<'a, T> {
pub random_perm: &'a Vec<Vec<T>>,
}
impl<'a, T> GeneOntologyRandomPerm<'a, T>
where
T: BixverseFloat,
{
pub fn new(perm_es: &'a Vec<Vec<T>>) -> Self {
GeneOntologyRandomPerm {
random_perm: perm_es,
}
}
pub fn get_gsea_res_simple<'b>(
&self,
pathway_scores: &'b [T],
pathway_sizes: &'b [usize],
) -> GseaResults<'b, T> {
let gsea_batch_res: GseaBatchResults<T> =
calc_gsea_stats_wrapper(pathway_scores, pathway_sizes, self.random_perm);
let gsea_res: GseaResults<'_, T> =
calculate_nes_es_pval(pathway_scores, pathway_sizes, &gsea_batch_res);
gsea_res
}
}
pub fn finalise_go_res<T>(
go_res: &[GoElimLevelResults<T>],
min_overlap: Option<usize>,
fdr_threshold: Option<T>,
) -> GoElimFinalResults<T>
where
T: BixverseFloat,
{
let n = go_res.iter().map(|x| x.go_ids.len()).sum::<usize>();
let mut go_ids: Vec<Vec<String>> = Vec::with_capacity(n);
let mut pvals: Vec<Vec<T>> = Vec::with_capacity(n);
let mut hits: Vec<Vec<usize>> = Vec::with_capacity(n);
let mut odds_ratios: Vec<Vec<T>> = Vec::with_capacity(n);
let mut gs_lengths: Vec<Vec<usize>> = Vec::with_capacity(n);
for res in go_res {
go_ids.push(res.go_ids.clone());
pvals.push(res.pvals.clone());
hits.push(res.hits.clone());
odds_ratios.push(res.odds_ratios.clone());
gs_lengths.push(res.gene_set_lengths.clone());
}
let go_ids = flatten_vector(go_ids);
let pvals = flatten_vector(pvals);
let fdr = calc_fdr(&pvals);
let hits = flatten_vector(hits);
let odds_ratios = flatten_vector(odds_ratios);
let gs_lengths = flatten_vector(gs_lengths);
let to_keep: Vec<usize> = (0..n)
.filter(|i| {
if let Some(min_overlap) = min_overlap
&& hits[*i] < min_overlap
{
return false;
}
if let Some(fdr_threshold) = fdr_threshold
&& fdr[*i] > fdr_threshold
{
return false;
}
true
})
.collect();
GoElimFinalResults {
go_ids: to_keep.iter().map(|i| go_ids[*i].clone()).collect(),
pvals: to_keep.iter().map(|i| pvals[*i]).collect(),
fdr: to_keep.iter().map(|i| fdr[*i]).collect(),
odds_ratios: to_keep.iter().map(|i| odds_ratios[*i]).collect(),
hits: to_keep.iter().map(|i| hits[*i]).collect(),
gs_length: to_keep.iter().map(|i| gs_lengths[*i]).collect(),
}
}
pub fn process_ontology_level<T>(
target_set: &FxHashSet<String>,
level: &String,
go_obj: &mut GeneOntology,
min_genes: usize,
gene_universe_length: usize,
elim_threshold: T,
) -> GoElimLevelResults<T>
where
T: BixverseFloat,
{
let binding: Vec<String> = Vec::new();
let level_ids = go_obj.get_level_ids(level).unwrap_or(&binding);
let level_data = go_obj.get_genes_list(level_ids);
let level_data_final: FxHashMap<_, _> = level_data
.iter()
.filter(|(_, value)| value.len() >= min_genes)
.map(|(key, value)| (key.clone(), value))
.collect();
let trials = target_set.len();
let size = level_data_final.len();
let mut go_ids = Vec::with_capacity(size);
let mut hits_vec = Vec::with_capacity(size);
let mut pvals = Vec::with_capacity(size);
let mut odds_ratios = Vec::with_capacity(size);
let mut gene_set_lengths = Vec::with_capacity(size);
for (key, value) in level_data_final {
let gene_set_length = value.len();
let hits = target_set.intersection(value).count();
let q = hits as i64 - 1;
let pval: T = if q > 0 {
hypergeom_pval(
q as usize,
gene_set_length,
gene_universe_length - gene_set_length,
trials,
)
} else {
T::one()
};
let odds_ratio = hypergeom_odds_ratio(
hits,
gene_set_length - hits,
trials - hits,
gene_universe_length - gene_set_length - trials + hits,
);
go_ids.push(key.clone());
hits_vec.push(hits);
pvals.push(pval);
odds_ratios.push(odds_ratio);
gene_set_lengths.push(gene_set_length);
}
let res = GoElimLevelResults {
go_ids,
pvals,
odds_ratios,
hits: hits_vec,
gene_set_lengths,
};
let go_to_remove: &Vec<_> = &res
.go_ids
.iter()
.zip(&res.pvals)
.filter(|(_, pval)| pval <= &&elim_threshold)
.map(|(string, _)| string.clone())
.collect();
for term in go_to_remove.iter() {
if let Some(ancestors) = go_obj.get_ancestors(term) {
let ancestors_final: Vec<String> = ancestors.to_vec();
if let Some(genes_to_remove) = go_obj.get_genes(term) {
let genes_to_remove = genes_to_remove.clone();
go_obj.remove_genes(&ancestors_final, &genes_to_remove);
}
}
}
res
}
#[allow(clippy::too_many_arguments)]
pub fn process_ontology_level_fgsea_simple<T>(
stats: &[T],
stat_name_indices: &FxHashMap<&String, usize>,
level: &String,
go_obj: &mut GeneOntology,
go_random_perms: &GeneOntologyRandomPerm<T>,
gsea_params: &GseaParams<T>,
elim_threshold: T,
) -> GoElimLevelResultsGsea<T>
where
T: BixverseFloat,
{
let binding: Vec<String> = Vec::new();
let go_ids = go_obj.get_level_ids(level).unwrap_or(&binding);
let mut level_data_es: BTreeMap<String, GoIntermediaryRes<T>> = BTreeMap::new();
for go_id in go_ids {
if let Some(genes) = go_obj.get_genes(go_id)
&& genes.len() >= gsea_params.min_size
&& genes.len() <= gsea_params.max_size
{
let mut indices: Vec<i32> = genes
.iter()
.filter_map(|gene| stat_name_indices.get(gene).map(|&i| i as i32))
.collect();
indices.sort();
if !indices.is_empty() {
let es_res =
calc_gsea_stats(stats, &indices, gsea_params.gsea_param, true, false, false);
let size = indices.len();
level_data_es.insert(go_id.clone(), (es_res.es, size, es_res.leading_edge));
}
}
}
let mut pathway_scores: Vec<T> = Vec::with_capacity(level_data_es.len());
let mut pathway_sizes: Vec<usize> = Vec::with_capacity(level_data_es.len());
let mut leading_edge_indices: Vec<Vec<i32>> = Vec::with_capacity(level_data_es.len());
for v in level_data_es.values() {
pathway_scores.push(v.0);
pathway_sizes.push(v.1);
leading_edge_indices.push(v.2.clone());
}
let level_res: GseaResults<'_, T> =
go_random_perms.get_gsea_res_simple(&pathway_scores, &pathway_sizes);
let go_to_remove: Vec<&String> = level_res
.pvals
.iter()
.zip(level_data_es.keys())
.filter(|(pval, _)| **pval <= elim_threshold) .map(|(_, go_id)| go_id)
.collect();
for term in go_to_remove.iter() {
if let Some(ancestors) = go_obj.get_ancestors(term) {
let ancestors_final: Vec<String> = ancestors.to_vec();
if let Some(genes_to_remove) = go_obj.get_genes(term) {
let genes_to_remove = genes_to_remove.clone();
go_obj.remove_genes(&ancestors_final, &genes_to_remove);
}
}
}
GoElimLevelResultsGsea {
go_ids: level_data_es.into_keys().collect(),
es: pathway_scores.clone(),
nes: level_res.nes,
size: pathway_sizes.clone(),
pvals: level_res.pvals,
n_more_extreme: level_res.n_more_extreme,
ge_zero: level_res.ge_zero,
le_zero: level_res.le_zero,
leading_edge: leading_edge_indices,
}
}