Skip to content

Commit 7677064

Browse files
authored
Merge pull request #345 from lhock17/speed_up
reworked get_interaction_matrix and get_interaction_pvals to be faster with numba
2 parents b9ef6b6 + 4444702 commit 7677064

4 files changed

Lines changed: 55 additions & 83 deletions

File tree

stlearn/tl/cci/analysis.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,6 @@ def run_cci(
710710

711711
int_matrix = get_interaction_matrix(
712712
cell_data,
713-
neighbourhood_bcs,
714713
neighbourhood_indices,
715714
all_set,
716715
sig_bool,

stlearn/tl/cci/het.py

Lines changed: 52 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from numba.typed import List
99

1010
from stlearn.tl.cci.het_helpers import (
11-
add_unique_edges,
1211
edge_core,
1312
get_between_spot_edge_array,
1413
get_data_for_counting,
@@ -203,7 +202,7 @@ def count_interactions(
203202
return int_matrix if trans_dir else int_matrix.transpose()
204203

205204

206-
@jit(parallel=True)
205+
@njit(parallel=True)
207206
def get_interaction_pvals(
208207
int_matrix,
209208
n_perms,
@@ -216,24 +215,16 @@ def get_interaction_pvals(
216215
R_bool,
217216
cell_prop_cutoff,
218217
):
219-
""" Perturbs the cell labels to get background count frequency to estimate \
220-
p-values.
221-
"""
218+
"""Gets the p-values for the interaction counts."""
222219

223-
# Counting how many times permutation of spots cell data creates interaction
224-
# counts greater than that observed, in order to calculate p-values.
225220
shape_ = (n_perms, int_matrix.shape[0], int_matrix.shape[1])
226-
# Storing the instances where the count is greater randomly for each perm.
227-
# Allows for embarassing parallelisation.
228221
greater_counts = np.zeros(shape_, dtype=np.int64)
229222
indices = np.zeros((cell_data.shape[0]), dtype=np.int64)
230223
for i in range(cell_data.shape[0]):
231224
indices[i] = i
232225

233-
# If dealing with discrete data, no need to randomise columns indendently #
234226
discrete = np.all(np.logical_or(cell_data == 0, cell_data == 1))
235227
for i in prange(n_perms):
236-
# Permuting the cell data by swapping between spots for each column #
237228
if not discrete:
238229
perm_data = cell_data.copy()
239230
for j in range(cell_data.shape[1]):
@@ -243,95 +234,79 @@ def get_interaction_pvals(
243234
rand_indices = np.random.choice(indices, cell_data.shape[0], False)
244235
perm_data = cell_data[rand_indices, :]
245236

246-
# Calculating interactions for permuted labels #
247237
perm_matrix = get_interaction_matrix(
248238
perm_data,
249-
neighbourhood_bcs,
250239
neighbourhood_indices,
251240
all_set,
252241
sig_bool,
253242
L_bool,
254243
R_bool,
255244
cell_prop_cutoff,
256245
)
257-
# perm_greater = (perm_matrix >= int_matrix).astype(int)
258-
perm_greater = perm_matrix >= int_matrix
259-
greater_counts[i, :, :] = perm_greater
246+
for row in range(int_matrix.shape[0]):
247+
for col in range(int_matrix.shape[1]):
248+
greater_counts[i, row, col] = (
249+
perm_matrix[row, col] >= int_matrix[row, col]
250+
)
260251

261-
# Calculating the pvalues #
262-
total_greater_counts = greater_counts.sum(axis=0) # cts * ct counts
263-
int_pvals = total_greater_counts / n_perms
252+
# Numba parallel sums axis 0 efficiently
253+
out = np.zeros((int_matrix.shape[0], int_matrix.shape[1]), dtype=np.float64)
254+
for i in range(n_perms):
255+
for row in range(int_matrix.shape[0]):
256+
for col in range(int_matrix.shape[1]):
257+
out[row, col] += greater_counts[i, row, col]
258+
int_pvals = out / n_perms
264259
return int_pvals
265260

266261

267262
@njit
268263
def get_interaction_matrix(
269264
cell_data,
270-
neighbourhood_bcs,
271265
neighbourhood_indices,
272266
all_set,
273267
sig_bool,
274268
L_bool,
275269
R_bool,
276270
cell_prop_cutoff,
277271
):
278-
"""Gets the interaction count matrix."""
279-
# Now counting the interactions under 3 situations:
280-
# 1) sig spot with ligand, only neighbours with receptor relevant
281-
# 2) sig spot with receptor, only neighbours with ligand relevant
282-
# NOTE, A<->B is double counted, but on different side of matrix.
283-
# (if bidirectional interaction between two spots, counts as two seperate
284-
# interactions).
285-
LR_edges = get_interactions(
286-
cell_data,
287-
neighbourhood_bcs,
288-
neighbourhood_indices,
289-
all_set,
290-
sig_bool,
291-
L_bool,
292-
R_bool,
293-
cell_prop_cutoff=cell_prop_cutoff,
294-
# sig ligand->receptor mode
295-
)
296-
RL_edges = get_interactions(
297-
cell_data,
298-
neighbourhood_bcs,
299-
neighbourhood_indices,
300-
all_set,
301-
sig_bool,
302-
R_bool,
303-
L_bool,
304-
cell_prop_cutoff=cell_prop_cutoff,
305-
# sig receptor->ligand mode
306-
)
307-
308-
# Counting the number of unique interacting edges
309-
# between different cell type via indicate LR
310-
int_matrix = np.zeros((all_set.shape[0], all_set.shape[0]), dtype=np.int64)
311-
edge_i = 0
312-
for i in range(all_set.shape[0]):
313-
for j in range(all_set.shape[0]):
314-
RL_Atrans_Bedges = LR_edges[edge_i]
315-
LR_Atrans_Bedges = RL_edges[edge_i]
316-
edge_i += 1
317-
max_len = max([len(RL_Atrans_Bedges), len(LR_Atrans_Bedges)])
318-
if max_len == 0: # Nothing to count #
319-
continue
320-
321-
edge_starts = List()
322-
edge_ends = List()
323-
for k in range(max_len):
324-
if k < len(RL_Atrans_Bedges):
325-
edge_starts.append(RL_Atrans_Bedges[k][0])
326-
edge_ends.append(RL_Atrans_Bedges[k][1])
327-
if k < len(LR_Atrans_Bedges):
328-
edge_starts.append(LR_Atrans_Bedges[k][0])
329-
edge_ends.append(LR_Atrans_Bedges[k][1])
330-
Atrans_Bedges = List()
331-
Atrans_Bedges.append((edge_starts[0], edge_ends[0])) # for typing
332-
add_unique_edges(Atrans_Bedges, edge_starts, edge_ends)
333-
# Atrans_Bedges = np.unique(RL_Atrans_Bedges + LR_Atrans_Bedges)
334-
int_matrix[i, j] = len(Atrans_Bedges) - 1 # since added edge for type
272+
"""Gets the interaction matrix for a given cell data matrix."""
273+
274+
n_spots = cell_data.shape[0]
275+
n_types = all_set.shape[0]
276+
int_matrix = np.zeros((n_types, n_types), dtype=np.int64)
277+
278+
for t1 in range(n_types):
279+
for t2 in range(n_types):
280+
s = {np.int64(-1)}
281+
s.clear()
282+
283+
for i in range(n_spots):
284+
if not sig_bool[i]:
285+
continue
286+
if cell_data[i, t1] <= cell_prop_cutoff:
287+
continue
288+
289+
neighs = neighbourhood_indices[i][1]
290+
291+
for k in range(len(neighs)):
292+
n_idx = neighs[k]
293+
if cell_data[n_idx, t2] > cell_prop_cutoff:
294+
valid = False
295+
if L_bool[i] and R_bool[n_idx]:
296+
valid = True
297+
if R_bool[i] and L_bool[n_idx]:
298+
valid = True
299+
300+
if valid:
301+
u = np.int64(i)
302+
v = np.int64(n_idx)
303+
if u > v:
304+
tmp = u
305+
u = v
306+
v = tmp
307+
s.add((u << 32) | v)
308+
309+
int_matrix[t1, t2] = len(s)
335310

336311
return int_matrix
337312

tests/adds/test_row_annotations.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ def setUpClass(cls):
1818
f"{test_data_path()}/" + "v1_human_breast_cancer_block_a_section_1.csv"
1919
)
2020

21-
2221
def setUp(self):
2322
"""Set up test data with known clusters."""
2423
self.adata = self.__class__._base_adata.copy()
2524

26-
2725
def test_add_row_annotations(self):
28-
row_annotations.row_annotations(self.adata, self.__class__.annotations_path, "ID")
26+
row_annotations.row_annotations(
27+
self.adata, self.__class__.annotations_path, "ID"
28+
)
2929

3030
assert "annot_type" in self.adata.obs.columns
3131
assert "fine_annot_type" in self.adata.obs.columns
@@ -35,7 +35,6 @@ def test_add_row_annotations(self):
3535
fine_annotated = self.adata.obs["fine_annot_type"].dropna()
3636
assert len(fine_annotated) == len(annotated)
3737

38-
3938
def test_add_row_annotations_with_missing_column(self):
4039
with self.assertRaises(ValueError):
4140
row_annotations.row_annotations(

tests/tl/test_cci.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,6 @@ def test_get_interaction_matrix(self):
279279
# Get interaction matrix
280280
int_matrix = het.get_interaction_matrix(
281281
cell_data,
282-
self.neighbourhood_bcs,
283282
self.neighbourhood_indices,
284283
CELL_TYPE_LABELS,
285284
sig_bool,

0 commit comments

Comments
 (0)