Skip to content

Commit 8eb9f7f

Browse files
author
Ian
committed
massive upgrade to library, removed duplicate steps
1 parent 9ab45b6 commit 8eb9f7f

18 files changed

Lines changed: 1092 additions & 226 deletions

File tree

.github/workflows/publish.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,22 @@ on:
66
tags:
77
- "v*"
88
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v2
13+
- uses: actions-rs/toolchain@v1
14+
with:
15+
toolchain: stable
16+
override: true
17+
- name: Install system dependencies
18+
run: |
19+
sudo apt-get update
20+
- name: Run tests
21+
run: cargo test --verbose
22+
923
publish:
24+
needs: test
1025
runs-on: ubuntu-latest
1126
steps:
1227
- uses: actions/checkout@v2

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22
.idea
3-
.idea/
3+
.idea/
4+
.fleet/

Cargo.lock

Lines changed: 33 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "single-statistics"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
edition = "2024"
55
license-file = "LICENSE.md"
66
readme = "README.md"
@@ -13,6 +13,7 @@ homepage = "https://singlerust.com"
1313
[dependencies]
1414
anyhow = "1.0.98"
1515
nalgebra-sparse = "0.10.0"
16+
ndarray = {version = "0.16.1", features = ["rayon"]}
1617
num-traits = "0.2.19"
1718
rayon = "1.10.0"
1819
single-utilities = "0.6.0"

src/enrichment/aucell.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Following the general implementation presented here, But adapted to nalgebra_sparse and multithreading: https://github.com/scverse/decoupler/blob/main/src/decoupler/mt/_aucell.py
2+
3+
use std::mem::offset_of;
4+
5+
use nalgebra_sparse::CsrMatrix;
6+
use ndarray::Array2;
7+
use single_utilities::traits::{FloatOps, FloatOpsTS};
8+
9+
fn validate_n_up<T>(n_genes: usize, n_up: Option<T>) -> anyhow::Result<usize>
10+
where
11+
T: FloatOps {
12+
let n_up_value = match n_up {
13+
Some(val) => {
14+
let n_up_int = num_traits::Float::ceil(val).to_usize().unwrap();
15+
n_up_int
16+
},
17+
None => {
18+
let n_up_float = T::from(0.05).unwrap() * T::from(n_genes).unwrap();
19+
let n_up_ceil = num_traits::Float::ceil(n_up_float);
20+
let n_up_int = n_up_ceil.to_usize().unwrap();
21+
n_up_int.clamp(2, n_genes)
22+
},
23+
};
24+
25+
if n_up_value <= 1 || n_up_value > n_genes {
26+
return Err(anyhow::anyhow!(
27+
"For n_genes={}, n_up={} must be between 1 and {}",
28+
n_genes, n_up_value, n_genes
29+
));
30+
}
31+
Ok(n_up_value)
32+
}
33+
34+
fn get_gene_set(
35+
connectivity: &[usize],
36+
starts: &[usize],
37+
offsets: &[usize],
38+
source_idx: usize
39+
) -> Vec<usize> {
40+
let start = starts[source_idx];
41+
let offset = offsets[source_idx];
42+
connectivity[start..start + offset].to_vec()
43+
}
44+
45+
fn rank_data<T>(values: &[T]) -> Vec<usize>
46+
where
47+
T: FloatOps
48+
{
49+
let mut indexed_values: Vec<(usize, T)> = values
50+
.iter()
51+
.enumerate()
52+
.map(|(i, &v)| (i,v))
53+
.collect();
54+
55+
indexed_values.sort_by(|a, b| {
56+
b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)
57+
});
58+
59+
let mut ranks = vec![0; values.len()];
60+
for (rank, (original_idx, _)) in indexed_values.iter().enumerate() {
61+
ranks[*original_idx] = rank + 1;
62+
}
63+
64+
ranks
65+
}
66+
67+
68+
69+
fn compute_chunk_size(n_cells: usize, n_genes: usize) -> usize {
70+
let n_cores = rayon::current_num_threads();
71+
72+
let base_chunk_size = if n_genes > 20000 {
73+
200
74+
} else if n_genes > 10000 {
75+
500
76+
} else {
77+
1000
78+
};
79+
80+
let min_chunks = n_cores;
81+
let max_chunk_size = (n_cells + min_chunks - 1) / min_chunks;
82+
base_chunk_size.min(max_chunk_size).max(1)
83+
}
84+
85+
pub(crate) fn aucell_compute<T>(
86+
matrix: &CsrMatrix<T>,
87+
connectivity: &[usize],
88+
starts: &[usize],
89+
offsets: &[usize],
90+
n_up: Option<T>
91+
) -> anyhow::Result<Array2<T>>
92+
where
93+
T: FloatOpsTS {
94+
let n_cells = matrix.nrows();
95+
let n_genes = matrix.ncols();
96+
let n_sources = starts.len();
97+
98+
if connectivity.is_empty() || starts.is_empty() || offsets.is_empty() {
99+
return Err(anyhow::anyhow!("Connectivity arrays cannot be empty!!"))
100+
}
101+
102+
if starts.len() != offsets.len() {
103+
return Err(anyhow::anyhow!("Starts and offsets must have the same length!"))
104+
}
105+
106+
todo!()
107+
}

src/enrichment/gsea.rs

Whitespace-only changes.

src/enrichment/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mod gsea;
2+
mod aucell;
3+
mod ora;

src/enrichment/ora.rs

Whitespace-only changes.

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
pub mod testing;
1+
pub mod testing;
2+
pub mod enrichment;

src/testing/correction/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ where
4242
let n_t = T::from(n).unwrap();
4343
let adjusted = p_values
4444
.iter()
45-
.map(|&p| num_traits::Float::min((p * n_t), T::one()))
45+
.map(|&p| num_traits::Float::min(p * n_t, T::one()))
4646
.collect();
4747

4848
Ok(adjusted)
@@ -149,7 +149,7 @@ where
149149

150150
// Calculate adjustment and take minimum of current and previous
151151
let adjustment =
152-
num_traits::Float::min((p_val * c_n * n_f64 / T::from(rank).unwrap()), T::one());
152+
num_traits::Float::min(p_val * c_n * n_f64 / T::from(rank).unwrap(), T::one());
153153
current_min = num_traits::Float::min(adjustment, current_min);
154154
adjusted_p_values[orig_idx] = current_min;
155155
}
@@ -264,7 +264,7 @@ where
264264
let denominator_t = T::from(n - i).unwrap_or(one);
265265

266266
let hochberg_value =
267-
num_traits::Float::min((indexed_p_values[i].1 * n_t / denominator_t), one);
267+
num_traits::Float::min(indexed_p_values[i].1 * n_t / denominator_t, one);
268268
adjusted_p_values[current_index] =
269269
num_traits::Float::min(hochberg_value, adjusted_p_values[prev_index]);
270270
}

0 commit comments

Comments
 (0)