88from numba .typed import List
99
1010from 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 )
207206def 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
268263def 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
0 commit comments