celldetective 1.1.1.post4__py3-none-any.whl → 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. celldetective/__init__.py +2 -1
  2. celldetective/extra_properties.py +62 -34
  3. celldetective/gui/__init__.py +1 -0
  4. celldetective/gui/analyze_block.py +2 -1
  5. celldetective/gui/classifier_widget.py +8 -7
  6. celldetective/gui/control_panel.py +50 -6
  7. celldetective/gui/layouts.py +5 -4
  8. celldetective/gui/neighborhood_options.py +10 -8
  9. celldetective/gui/plot_signals_ui.py +39 -11
  10. celldetective/gui/process_block.py +413 -95
  11. celldetective/gui/retrain_segmentation_model_options.py +17 -4
  12. celldetective/gui/retrain_signal_model_options.py +106 -6
  13. celldetective/gui/signal_annotator.py +25 -5
  14. celldetective/gui/signal_annotator2.py +2708 -0
  15. celldetective/gui/signal_annotator_options.py +3 -1
  16. celldetective/gui/survival_ui.py +15 -6
  17. celldetective/gui/tableUI.py +235 -39
  18. celldetective/io.py +537 -421
  19. celldetective/measure.py +919 -969
  20. celldetective/models/pair_signal_detection/blank +0 -0
  21. celldetective/neighborhood.py +426 -354
  22. celldetective/relative_measurements.py +648 -0
  23. celldetective/scripts/analyze_signals.py +1 -1
  24. celldetective/scripts/measure_cells.py +28 -8
  25. celldetective/scripts/measure_relative.py +103 -0
  26. celldetective/scripts/segment_cells.py +5 -5
  27. celldetective/scripts/track_cells.py +4 -1
  28. celldetective/scripts/train_segmentation_model.py +23 -18
  29. celldetective/scripts/train_signal_model.py +33 -0
  30. celldetective/signals.py +402 -8
  31. celldetective/tracking.py +8 -2
  32. celldetective/utils.py +93 -0
  33. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/METADATA +8 -8
  34. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/RECORD +38 -34
  35. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/WHEEL +1 -1
  36. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/LICENSE +0 -0
  37. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/entry_points.txt +0 -0
  38. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.0.dist-info}/top_level.txt +0 -0
@@ -8,17 +8,15 @@ from mahotas.features import haralick
8
8
  from scipy.ndimage import zoom
9
9
  import os
10
10
  import subprocess
11
- from celldetective.utils import rename_intensity_column, create_patch_mask, remove_redundant_features
11
+ from celldetective.utils import contour_of_instance_segmentation, rename_intensity_column, create_patch_mask, remove_redundant_features, extract_identity_col
12
12
  from scipy.spatial.distance import cdist
13
- from celldetective.measure import contour_of_instance_segmentation
14
13
  from celldetective.io import locate_labels, get_position_pickle, get_position_table
15
14
  import re
16
15
 
17
16
  abs_path = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], 'celldetective'])
18
17
 
19
18
 
20
- def set_live_status(setA,setB,status, not_status_option):
21
-
19
+ def set_live_status(setA, setB, status, not_status_option):
22
20
  """
23
21
  Updates the live status for cells in two datasets based on specified status columns and options.
24
22
 
@@ -58,35 +56,37 @@ def set_live_status(setA,setB,status, not_status_option):
58
56
  if status[0] is None or status[0]=='live_status':
59
57
  setA.loc[:,'live_status'] = 1
60
58
  status[0] = 'live_status'
61
- elif status[0] is not None and isinstance(not_status_option,list):
62
- setA.loc[setA[status[0]]==2,status[0]] = 1 #already happened events become event
59
+ elif status[0] is not None and isinstance(not_status_option, list):
60
+ setA.loc[setA[status[0]] == 2, status[0]] = 1 # already happened events become event
63
61
  if not_status_option[0]:
64
62
  setA.loc[:,'not_'+status[0]] = [not a if a==0 or a==1 else np.nan for a in setA.loc[:,status[0]].values]
65
63
  status[0] = 'not_'+status[0]
66
64
  if status[1] is None or status[1]=='live_status':
67
65
  setB.loc[:,'live_status'] = 1
68
66
  status[1] = 'live_status'
69
- elif status[1] is not None and isinstance(not_status_option,list):
70
- setB.loc[setB[status[1]]==2,status[1]] = 1 #already happened events become event
67
+ elif status[1] is not None and isinstance(not_status_option, list):
68
+ setB.loc[setB[status[1]] == 2, status[1]] = 1 # already happened events become event
71
69
  if not_status_option[1]:
72
- setB.loc[:,'not_'+status[1]] = [not a if a==0 or a==1 else np.nan for a in setB.loc[:,status[1]].values]
73
- status[1] = 'not_'+status[1]
70
+ setB.loc[:, 'not_' + status[1]] = [not a if a == 0 or a == 1 else np.nan for a in
71
+ setB.loc[:, status[1]].values]
72
+ status[1] = 'not_' + status[1]
74
73
 
75
74
  assert status[0] in list(setA.columns)
76
75
  assert status[1] in list(setB.columns)
77
-
76
+
78
77
  setA = setA.reset_index(drop=True)
79
- setB = setB.reset_index(drop=True)
78
+ setB = setB.reset_index(drop=True)
80
79
 
81
80
  return setA, setB, status
82
81
 
83
- def compute_attention_weight(dist_matrix, cut_distance, opposite_cell_status, opposite_cell_ids, axis=1, include_dead_weight=True):
84
-
82
+
83
+ def compute_attention_weight(dist_matrix, cut_distance, opposite_cell_status, opposite_cell_ids, axis=1,
84
+ include_dead_weight=True):
85
85
  """
86
86
  Computes the attention weight for each cell based on its proximity to cells of an opposite type within a specified distance.
87
87
 
88
88
  This function calculates the attention weight for cells by considering the distance to the cells of an opposite type
89
- within a given cutoff distance. It optionally considers only the 'live' opposite cells based on their status. The function
89
+ within a given cutoff distance. It optionally considers only the 'live' opposite cells based on their status. The function
90
90
  returns two arrays: one containing the attention weights and another containing the IDs of the closest opposite cells.
91
91
 
92
92
  Parameters
@@ -117,34 +117,36 @@ def compute_attention_weight(dist_matrix, cut_distance, opposite_cell_status, op
117
117
  closest_opposite = np.empty(dist_matrix.shape[axis])
118
118
 
119
119
  for i in range(dist_matrix.shape[axis]):
120
- if axis==1:
121
- row = dist_matrix[:,i]
122
- elif axis==0:
123
- row = dist_matrix[i,:]
124
- row[row==0.] = 1.0E06
125
- nbr_opposite = len(row[row<=cut_distance])
126
-
120
+ if axis == 1:
121
+ row = dist_matrix[:, i]
122
+ elif axis == 0:
123
+ row = dist_matrix[i, :]
124
+ row[row == 0.] = 1.0E06
125
+ nbr_opposite = len(row[row <= cut_distance])
126
+
127
127
  if not include_dead_weight:
128
- stat = opposite_cell_status[np.where(row<=cut_distance)[0]]
129
- nbr_opposite = len(stat[stat==1])
130
- index_subpop = np.argmin(row[opposite_cell_status==1])
131
- closest_opposite[i] = opposite_cell_ids[opposite_cell_status==1][index_subpop]
128
+ stat = opposite_cell_status[np.where(row <= cut_distance)[0]]
129
+ nbr_opposite = len(stat[stat == 1])
130
+ index_subpop = np.argmin(row[opposite_cell_status == 1])
131
+ closest_opposite[i] = opposite_cell_ids[opposite_cell_status == 1][index_subpop]
132
132
  else:
133
133
  closest_opposite[i] = opposite_cell_ids[np.argmin(row)]
134
-
135
- if nbr_opposite>0:
136
- weight = 1./float(nbr_opposite)
134
+
135
+ if nbr_opposite > 0:
136
+ weight = 1. / float(nbr_opposite)
137
137
  weights[i] = weight
138
138
 
139
139
  return weights, closest_opposite
140
140
 
141
- def distance_cut_neighborhood(setA, setB, distance, mode='two-pop', status=None, not_status_option=None, compute_cum_sum=True,
141
+
142
+ def distance_cut_neighborhood(setA, setB, distance, mode='two-pop', status=None, not_status_option=None,
143
+ compute_cum_sum=True,
142
144
  attention_weight=True, symmetrize=True, include_dead_weight=True,
143
- column_labels={'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y'}):
144
-
145
+ column_labels={'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X',
146
+ 'y': 'POSITION_Y'}):
145
147
  """
146
148
 
147
- Match neighbors in set A and B within a circle of radius d.
149
+ Match neighbors in set A and B within a circle of radius d.
148
150
 
149
151
  Parameters
150
152
  ----------
@@ -154,7 +156,7 @@ def distance_cut_neighborhood(setA, setB, distance, mode='two-pop', status=None,
154
156
  Cut-distance in pixels to match neighboring pairs.
155
157
  mode: str
156
158
  neighboring mode, between 'two-pop' (e.g. target-effector) and 'self' (target-target or effector-effector).
157
- status: None or status
159
+ status: None or status
158
160
  name to look for cells to ignore (because they are dead). By default all cells are kept.
159
161
  compute_cum_sum: bool,
160
162
  compute cumulated time of presence of neighbours (only if trajectories available for both sets)
@@ -170,112 +172,115 @@ def distance_cut_neighborhood(setA, setB, distance, mode='two-pop', status=None,
170
172
  if setA is not None and setB is not None:
171
173
  setA, setB, status = set_live_status(setA, setB, status, not_status_option)
172
174
  else:
173
- return None,None
175
+ return None, None
174
176
 
175
- # Check distance option
177
+ # Check distance option
176
178
  if not isinstance(distance, list):
177
179
  distance = [distance]
178
-
180
+
179
181
  for d in distance:
180
182
  # loop over each provided distance
181
-
182
- if mode=='two-pop':
183
+
184
+ if mode == 'two-pop':
183
185
  neigh_col = f'neighborhood_2_circle_{d}_px'
184
- elif mode=='self':
186
+ elif mode == 'self':
185
187
  neigh_col = f'neighborhood_self_circle_{d}_px'
186
-
188
+
187
189
  cl = []
188
- for s in [setA,setB]:
190
+ for s in [setA, setB]:
189
191
 
190
192
  # Check whether data can be tracked
191
193
  temp_column_labels = column_labels.copy()
192
194
 
193
- if not 'TRACK_ID' in list(s.columns):
194
- temp_column_labels.update({'track': 'ID'})
195
- compute_cum_sum = False # if no tracking data then cum_sum is not relevant
195
+ id_col = extract_identity_col(s)
196
+ temp_column_labels.update({'track': id_col})
197
+ if id_col=='ID':
198
+ compute_cum_sum = False # if no tracking data then cum_sum is not relevant
196
199
  cl.append(temp_column_labels)
197
200
 
198
201
  # Remove nan tracks (cells that do not belong to a track)
199
202
  s[neigh_col] = np.nan
200
203
  s[neigh_col] = s[neigh_col].astype(object)
201
- s.dropna(subset=[cl[-1]['track']],inplace=True)
204
+ s.dropna(subset=[cl[-1]['track']], inplace=True)
202
205
 
203
206
  # Loop over each available timestep
204
- timeline = np.unique(np.concatenate([setA[cl[0]['time']].to_numpy(), setB[cl[1]['time']].to_numpy()])).astype(int)
207
+ timeline = np.unique(np.concatenate([setA[cl[0]['time']].to_numpy(), setB[cl[1]['time']].to_numpy()])).astype(
208
+ int)
205
209
  for t in tqdm(timeline):
206
210
 
207
- index_A = list(setA.loc[setA[cl[0]['time']]==t].index)
208
- coordinates_A = setA.loc[setA[cl[0]['time']]==t,[cl[0]['x'], cl[0]['y']]].to_numpy()
209
- ids_A = setA.loc[setA[cl[0]['time']]==t,cl[0]['track']].to_numpy()
210
- status_A = setA.loc[setA[cl[0]['time']]==t,status[0]].to_numpy()
211
+ index_A = list(setA.loc[setA[cl[0]['time']] == t].index)
212
+ coordinates_A = setA.loc[setA[cl[0]['time']] == t, [cl[0]['x'], cl[0]['y']]].to_numpy()
213
+ ids_A = setA.loc[setA[cl[0]['time']] == t, cl[0]['track']].to_numpy()
214
+ status_A = setA.loc[setA[cl[0]['time']] == t, status[0]].to_numpy()
211
215
 
212
- index_B = list(setB.loc[setB[cl[1]['time']]==t].index)
213
- coordinates_B = setB.loc[setB[cl[1]['time']]==t,[cl[1]['x'], cl[1]['y']]].to_numpy()
214
- ids_B = setB.loc[setB[cl[1]['time']]==t,cl[1]['track']].to_numpy()
215
- status_B = setB.loc[setB[cl[1]['time']]==t,status[1]].to_numpy()
216
+ index_B = list(setB.loc[setB[cl[1]['time']] == t].index)
217
+ coordinates_B = setB.loc[setB[cl[1]['time']] == t, [cl[1]['x'], cl[1]['y']]].to_numpy()
218
+ ids_B = setB.loc[setB[cl[1]['time']] == t, cl[1]['track']].to_numpy()
219
+ status_B = setB.loc[setB[cl[1]['time']] == t, status[1]].to_numpy()
216
220
 
217
221
  if len(ids_A) > 0 and len(ids_B) > 0:
218
-
222
+
219
223
  # compute distance matrix
220
224
  dist_map = cdist(coordinates_A, coordinates_B, metric="euclidean")
221
-
225
+
222
226
  if attention_weight:
223
- weights, closest_A = compute_attention_weight(dist_map, d, status_A, ids_A, axis=1, include_dead_weight=include_dead_weight)
224
-
227
+ weights, closest_A = compute_attention_weight(dist_map, d, status_A, ids_A, axis=1,
228
+ include_dead_weight=include_dead_weight)
229
+
225
230
  # Target centric
226
231
  for k in range(dist_map.shape[0]):
227
-
228
- col = dist_map[k,:]
229
- col[col==0.] = 1.0E06
230
-
231
- neighs_B = np.array([ids_B[i] for i in np.where((col<=d))[0]])
232
- status_neigh_B = np.array([status_B[i] for i in np.where((col<=d))[0]])
233
- dist_B = [round(col[i],2) for i in np.where((col<=d))[0]]
234
- if len(dist_B)>0:
235
- closest_B_cell = neighs_B[np.argmin(dist_B)]
236
-
232
+
233
+ col = dist_map[k, :]
234
+ col[col == 0.] = 1.0E06
235
+
236
+ neighs_B = np.array([ids_B[i] for i in np.where((col <= d))[0]])
237
+ status_neigh_B = np.array([status_B[i] for i in np.where((col <= d))[0]])
238
+ dist_B = [round(col[i], 2) for i in np.where((col <= d))[0]]
239
+ if len(dist_B) > 0:
240
+ closest_B_cell = neighs_B[np.argmin(dist_B)]
241
+
237
242
  if symmetrize and attention_weight:
238
243
  n_neighs = float(len(neighs_B))
239
244
  if not include_dead_weight:
240
- n_neighs_alive = len(np.where(status_neigh_B==1)[0])
245
+ n_neighs_alive = len(np.where(status_neigh_B == 1)[0])
241
246
  neigh_count = n_neighs_alive
242
247
  else:
243
248
  neigh_count = n_neighs
244
- if neigh_count>0:
245
- weight_A = 1./neigh_count
249
+ if neigh_count > 0:
250
+ weight_A = 1. / neigh_count
246
251
  else:
247
252
  weight_A = np.nan
248
253
 
249
- if not include_dead_weight and status_A[k]==0:
254
+ if not include_dead_weight and status_A[k] == 0:
250
255
  weight_A = 0
251
-
256
+
252
257
  neighs = []
253
258
  setA.at[index_A[k], neigh_col] = []
254
259
  for n in range(len(neighs_B)):
255
-
260
+
256
261
  # index in setB
257
- n_index = np.where(ids_B==neighs_B[n])[0][0]
262
+ n_index = np.where(ids_B == neighs_B[n])[0][0]
258
263
  # Assess if neigh B is closest to A
259
264
  if attention_weight:
260
- if closest_A[n_index]==ids_A[k]:
265
+ if closest_A[n_index] == ids_A[k]:
261
266
  closest = True
262
267
  else:
263
268
  closest = False
264
-
269
+
265
270
  if symmetrize:
266
271
  # Load neighborhood previous data
267
272
  sym_neigh = setB.loc[index_B[n_index], neigh_col]
268
- if neighs_B[n]==closest_B_cell:
269
- closest_b=True
273
+ if neighs_B[n] == closest_B_cell:
274
+ closest_b = True
270
275
  else:
271
- closest_b=False
276
+ closest_b = False
272
277
  if isinstance(sym_neigh, list):
273
278
  sym_neigh.append({'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k]})
274
279
  else:
275
- sym_neigh = [{'id': ids_A[k], 'distance': dist_B[n],'status': status_A[k]}]
280
+ sym_neigh = [{'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k]}]
276
281
  if attention_weight:
277
282
  sym_neigh[-1].update({'weight': weight_A, 'closest': closest_b})
278
-
283
+
279
284
  # Write the minimum info about neighborhing cell B
280
285
  neigh_dico = {'id': neighs_B[n], 'distance': dist_B[n], 'status': status_neigh_B[n]}
281
286
  if attention_weight:
@@ -283,33 +288,42 @@ def distance_cut_neighborhood(setA, setB, distance, mode='two-pop', status=None,
283
288
 
284
289
  if compute_cum_sum:
285
290
  # Compute the integrated presence of the neighboring cell B
286
- assert cl[1]['track'] == 'TRACK_ID','The set B does not seem to contain tracked data. The cumulative time will be meaningless.'
287
- past_neighs = [[ll['id'] for ll in l] if len(l)>0 else [None] for l in setA.loc[(setA[cl[0]['track']]==ids_A[k])&(setA[cl[0]['time']]<=t), neigh_col].to_numpy()]
291
+ assert cl[1][
292
+ 'track'] == 'TRACK_ID', 'The set B does not seem to contain tracked data. The cumulative time will be meaningless.'
293
+ past_neighs = [[ll['id'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
294
+ (setA[cl[0]['track']] == ids_A[k]) & (setA[cl[0]['time']] <= t), neigh_col].to_numpy()]
288
295
  past_neighs = [item for sublist in past_neighs for item in sublist]
289
-
296
+
290
297
  if attention_weight:
291
- past_weights = [[ll['weight'] for ll in l] if len(l)>0 else [None] for l in setA.loc[(setA[cl[0]['track']]==ids_A[k])&(setA[cl[0]['time']]<=t), neigh_col].to_numpy()]
298
+ past_weights = [[ll['weight'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
299
+ (setA[cl[0]['track']] == ids_A[k]) & (
300
+ setA[cl[0]['time']] <= t), neigh_col].to_numpy()]
292
301
  past_weights = [item for sublist in past_weights for item in sublist]
293
302
 
294
- cum_sum = len(np.where(past_neighs==neighs_B[n])[0])
295
- neigh_dico.update({'cumulated_presence': cum_sum+1})
296
-
303
+ cum_sum = len(np.where(past_neighs == neighs_B[n])[0])
304
+ neigh_dico.update({'cumulated_presence': cum_sum + 1})
305
+
297
306
  if attention_weight:
298
- cum_sum_weighted = np.sum([w if l==neighs_B[n] else 0 for l,w in zip(past_neighs, past_weights)])
307
+ cum_sum_weighted = np.sum(
308
+ [w if l == neighs_B[n] else 0 for l, w in zip(past_neighs, past_weights)])
299
309
  neigh_dico.update({'cumulated_presence_weighted': cum_sum_weighted + weights[n_index]})
300
310
 
301
311
  if symmetrize:
302
312
  setB.at[index_B[n_index], neigh_col] = sym_neigh
303
-
313
+
304
314
  neighs.append(neigh_dico)
305
-
315
+
306
316
  setA.at[index_A[k], neigh_col] = neighs
307
-
317
+
308
318
  return setA, setB
309
319
 
310
- def compute_neighborhood_at_position(pos, distance, population=['targets','effectors'], theta_dist=None, img_shape=(2048,2048), return_tables=False, clear_neigh=False, event_time_col=None,
311
- neighborhood_kwargs={'mode': 'two-pop','status': None, 'not_status_option': None,'include_dead_weight': True,"compute_cum_sum": False,"attention_weight": True, 'symmetrize': True}):
312
-
320
+
321
+ def compute_neighborhood_at_position(pos, distance, population=['targets', 'effectors'], theta_dist=None,
322
+ img_shape=(2048, 2048), return_tables=False, clear_neigh=False,
323
+ event_time_col=None,
324
+ neighborhood_kwargs={'mode': 'two-pop', 'status': None, 'not_status_option': None,
325
+ 'include_dead_weight': True, "compute_cum_sum": False,
326
+ "attention_weight": True, 'symmetrize': True}):
313
327
  """
314
328
  Computes neighborhood metrics for specified cell populations within a given position, based on distance criteria and additional parameters.
315
329
 
@@ -347,12 +361,12 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
347
361
  ------
348
362
  AssertionError
349
363
  If the specified position path does not exist or if the number of distances and edge thresholds do not match.
350
-
364
+
351
365
  """
352
366
 
353
- pos = pos.replace('\\','/')
367
+ pos = pos.replace('\\', '/')
354
368
  pos = rf"{pos}"
355
- assert os.path.exists(pos),f'Position {pos} is not a valid path.'
369
+ assert os.path.exists(pos), f'Position {pos} is not a valid path.'
356
370
 
357
371
  if isinstance(population, str):
358
372
  population = [population, population]
@@ -363,17 +377,27 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
363
377
  theta_dist = [theta_dist]
364
378
 
365
379
  if theta_dist is None:
366
- theta_dist = [0.9*d for d in distance]
367
- assert len(theta_dist)==len(distance),'Incompatible number of distances and number of edge thresholds.'
380
+ theta_dist = [0.9 * d for d in distance]
381
+ assert len(theta_dist) == len(distance), 'Incompatible number of distances and number of edge thresholds.'
368
382
 
369
- if population[0]==population[1]:
383
+ if population[0] == population[1]:
370
384
  neighborhood_kwargs.update({'mode': 'self'})
371
- if population[1]!=population[0]:
385
+ if population[1] != population[0]:
372
386
  neighborhood_kwargs.update({'mode': 'two-pop'})
373
387
 
374
388
  df_A, path_A = get_position_table(pos, population=population[0], return_path=True)
375
389
  df_B, path_B = get_position_table(pos, population=population[1], return_path=True)
376
390
 
391
+ if clear_neigh:
392
+ if os.path.exists(path_A.replace('.csv','.pkl')):
393
+ os.remove(path_A.replace('.csv','.pkl'))
394
+ if os.path.exists(path_B.replace('.csv','.pkl')):
395
+ os.remove(path_B.replace('.csv','.pkl'))
396
+ df_pair, pair_path = get_position_table(pos, population='pairs', return_path=True)
397
+ if df_pair is not None:
398
+ os.remove(pair_path)
399
+
400
+
377
401
  df_A_pkl = get_position_pickle(pos, population=population[0], return_path=False)
378
402
  df_B_pkl = get_position_pickle(pos, population=population[1], return_path=False)
379
403
 
@@ -382,12 +406,9 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
382
406
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
383
407
  cols = list(pkl_columns[neigh_columns]) + ['FRAME']
384
408
 
385
- if 'TRACK_ID' in list(pkl_columns):
386
- cols.append('TRACK_ID')
387
- on_cols = ['TRACK_ID','FRAME']
388
- else:
389
- cols.append('ID')
390
- on_cols = ['ID','FRAME']
409
+ id_col = extract_identity_col(df_A_pkl)
410
+ cols.append(id_col)
411
+ on_cols = [id_col, 'FRAME']
391
412
 
392
413
  print(f'Recover {cols} from the pickle file...')
393
414
  try:
@@ -401,12 +422,9 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
401
422
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
402
423
  cols = list(pkl_columns[neigh_columns]) + ['FRAME']
403
424
 
404
- if 'TRACK_ID' in list(pkl_columns):
405
- cols.append('TRACK_ID')
406
- on_cols = ['TRACK_ID','FRAME']
407
- else:
408
- cols.append('ID')
409
- on_cols = ['ID','FRAME']
425
+ id_col = extract_identity_col(df_B_pkl)
426
+ cols.append(id_col)
427
+ on_cols = [id_col, 'FRAME']
410
428
 
411
429
  print(f'Recover {cols} from the pickle file...')
412
430
  try:
@@ -418,17 +436,17 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
418
436
  unwanted = df_A.columns[df_A.columns.str.contains('neighborhood')]
419
437
  df_A = df_A.drop(columns=unwanted)
420
438
  unwanted = df_B.columns[df_B.columns.str.contains('neighborhood')]
421
- df_B = df_B.drop(columns=unwanted)
439
+ df_B = df_B.drop(columns=unwanted)
422
440
 
423
- df_A, df_B = distance_cut_neighborhood(df_A,df_B, distance,**neighborhood_kwargs)
424
- if df_A is None or df_B is None:
441
+ df_A, df_B = distance_cut_neighborhood(df_A, df_B, distance, **neighborhood_kwargs)
442
+ if df_A is None or df_B is None or len(df_A)==0:
425
443
  return None
426
444
 
427
- for td,d in zip(theta_dist, distance):
445
+ for td, d in zip(theta_dist, distance):
428
446
 
429
- if neighborhood_kwargs['mode']=='two-pop':
447
+ if neighborhood_kwargs['mode'] == 'two-pop':
430
448
  neigh_col = f'neighborhood_2_circle_{d}_px'
431
- elif neighborhood_kwargs['mode']=='self':
449
+ elif neighborhood_kwargs['mode'] == 'self':
432
450
  neigh_col = f'neighborhood_self_circle_{d}_px'
433
451
 
434
452
  # edge_filter_A = (df_A['POSITION_X'] > td)&(df_A['POSITION_Y'] > td)&(df_A['POSITION_Y'] < (img_shape[0] - td))&(df_A['POSITION_X'] < (img_shape[1] - td))
@@ -438,28 +456,37 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
438
456
 
439
457
  print('Count neighborhood...')
440
458
  df_A = compute_neighborhood_metrics(df_A, neigh_col, metrics=['inclusive','exclusive','intermediate'], decompose_by_status=True)
441
- if neighborhood_kwargs['symmetrize']:
442
- df_B = compute_neighborhood_metrics(df_B, neigh_col, metrics=['inclusive','exclusive','intermediate'], decompose_by_status=True)
459
+ # if neighborhood_kwargs['symmetrize']:
460
+ # df_B = compute_neighborhood_metrics(df_B, neigh_col, metrics=['inclusive','exclusive','intermediate'], decompose_by_status=True)
443
461
  print('Done...')
444
-
445
- if 'TRACK_ID' in list(df_A.columns):
446
- print('Estimate average neighborhood before/after event...')
447
- df_A = mean_neighborhood_before_event(df_A, neigh_col, event_time_col)
448
- df_A = mean_neighborhood_after_event(df_A, neigh_col, event_time_col)
449
- print('Done...')
450
462
 
451
- df_A.to_pickle(path_A.replace('.csv','.pkl'))
452
- if not population[0]==population[1]:
453
- df_B.to_pickle(path_B.replace('.csv','.pkl'))
463
+ if 'TRACK_ID' in list(df_A.columns):
464
+ if not np.all(df_A['TRACK_ID'].isnull()):
465
+ print('Estimate average neighborhood before/after event...')
466
+ df_A = mean_neighborhood_before_event(df_A, neigh_col, event_time_col)
467
+ if event_time_col is not None:
468
+ df_A = mean_neighborhood_after_event(df_A, neigh_col, event_time_col)
469
+ print('Done...')
470
+
471
+ df_A.to_pickle(path_A.replace('.csv', '.pkl'))
472
+ if not population[0] == population[1]:
473
+ # Remove neighborhood column
474
+ for td, d in zip(theta_dist, distance):
475
+ if neighborhood_kwargs['mode'] == 'two-pop':
476
+ neigh_col = f'neighborhood_2_circle_{d}_px'
477
+ elif neighborhood_kwargs['mode'] == 'self':
478
+ neigh_col = f'neighborhood_self_circle_{d}_px'
479
+ df_B = df_B.drop(columns=[neigh_col])
480
+ df_B.to_pickle(path_B.replace('.csv', '.pkl'))
454
481
 
455
482
  unwanted = df_A.columns[df_A.columns.str.startswith('neighborhood_')]
456
483
  df_A2 = df_A.drop(columns=unwanted)
457
484
  df_A2.to_csv(path_A, index=False)
458
485
 
459
- if not population[0]==population[1]:
486
+ if not population[0] == population[1]:
460
487
  unwanted = df_B.columns[df_B.columns.str.startswith('neighborhood_')]
461
488
  df_B_csv = df_B.drop(unwanted, axis=1, inplace=False)
462
- df_B_csv.to_csv(path_B,index=False)
489
+ df_B_csv.to_csv(path_B, index=False)
463
490
 
464
491
  if return_tables:
465
492
  return df_A, df_B
@@ -508,7 +535,7 @@ def compute_neighborhood_metrics(neigh_table, neigh_col, metrics=['inclusive','e
508
535
  >>> neigh_col = 'neighborhood_info'
509
536
  >>> updated_neigh_table = compute_neighborhood_metrics(neigh_table, neigh_col, metrics=['inclusive'], decompose_by_status=True)
510
537
  # Computes the inclusive count of neighbors for each cell, decomposed by cell status.
511
-
538
+
512
539
  """
513
540
 
514
541
  neigh_table = neigh_table.reset_index(drop=True)
@@ -516,14 +543,13 @@ def compute_neighborhood_metrics(neigh_table, neigh_col, metrics=['inclusive','e
516
543
  groupbycols = ['position']
517
544
  else:
518
545
  groupbycols = []
519
- if 'TRACK_ID' in list(neigh_table.columns):
520
- groupbycols.append('TRACK_ID')
521
- else:
522
- groupbycols.append('ID')
546
+
547
+ id_col = extract_identity_col(neigh_table)
548
+ groupbycols.append(id_col)
523
549
 
524
550
  neigh_table.sort_values(by=groupbycols+['FRAME'],inplace=True)
525
551
 
526
- for tid,group in neigh_table.groupby(groupbycols):
552
+ for tid, group in neigh_table.groupby(groupbycols):
527
553
  group = group.dropna(subset=neigh_col)
528
554
  indices = list(group.index)
529
555
  neighbors = group[neigh_col].to_numpy()
@@ -569,49 +595,52 @@ def compute_neighborhood_metrics(neigh_table, neigh_col, metrics=['inclusive','e
569
595
  if 'intermediate' in metrics:
570
596
  n_intermediate[t] = np.sum(weights_at_t)
571
597
  if 'exclusive' in metrics:
572
- n_exclusive[t] = sum([c==1.0 for c in closest_at_t])
598
+ n_exclusive[t] = sum([c == 1.0 for c in closest_at_t])
573
599
 
574
600
  if decompose_by_status:
575
601
 
576
602
  if 'inclusive' in metrics:
577
- n_inclusive_status_0[t] = sum([s==0.0 for s in status_at_t])
578
- n_inclusive_status_1[t] = sum([s==1.0 for s in status_at_t])
603
+ n_inclusive_status_0[t] = sum([s == 0.0 for s in status_at_t])
604
+ n_inclusive_status_1[t] = sum([s == 1.0 for s in status_at_t])
579
605
 
580
606
  if 'intermediate' in metrics:
581
607
  weights_at_t = np.array(weights_at_t)
582
-
608
+
583
609
  # intermediate
584
- weights_status_1 = weights_at_t[np.array([s==1.0 for s in status_at_t],dtype=bool)]
585
- weights_status_0 = weights_at_t[np.array([s==0.0 for s in status_at_t],dtype=bool)]
610
+ weights_status_1 = weights_at_t[np.array([s == 1.0 for s in status_at_t], dtype=bool)]
611
+ weights_status_0 = weights_at_t[np.array([s == 0.0 for s in status_at_t], dtype=bool)]
586
612
  n_intermediate_status_1[t] = np.sum(weights_status_1)
587
613
  n_intermediate_status_0[t] = np.sum(weights_status_0)
588
614
 
589
615
  if 'exclusive' in metrics:
590
- n_exclusive_status_0[t] = sum([c==1.0 if s==0.0 else False for c,s in zip(closest_at_t,status_at_t)])
591
- n_exclusive_status_1[t] = sum([c==1.0 if s==1.0 else False for c,s in zip(closest_at_t,status_at_t)])
616
+ n_exclusive_status_0[t] = sum(
617
+ [c == 1.0 if s == 0.0 else False for c, s in zip(closest_at_t, status_at_t)])
618
+ n_exclusive_status_1[t] = sum(
619
+ [c == 1.0 if s == 1.0 else False for c, s in zip(closest_at_t, status_at_t)])
592
620
 
593
621
  if 'inclusive' in metrics:
594
- neigh_table.loc[indices, 'inclusive_count_'+neigh_col] = n_inclusive
622
+ neigh_table.loc[indices, 'inclusive_count_' + neigh_col] = n_inclusive
595
623
  if 'intermediate' in metrics:
596
- neigh_table.loc[indices, 'intermediate_count_'+neigh_col] = n_intermediate
624
+ neigh_table.loc[indices, 'intermediate_count_' + neigh_col] = n_intermediate
597
625
  if 'exclusive' in metrics:
598
- neigh_table.loc[indices, 'exclusive_count_'+neigh_col] = n_exclusive
626
+ neigh_table.loc[indices, 'exclusive_count_' + neigh_col] = n_exclusive
599
627
 
600
628
  if decompose_by_status:
601
629
  if 'inclusive' in metrics:
602
- neigh_table.loc[indices, 'inclusive_count_s0_'+neigh_col] = n_inclusive_status_0
603
- neigh_table.loc[indices, 'inclusive_count_s1_'+neigh_col] = n_inclusive_status_1
630
+ neigh_table.loc[indices, 'inclusive_count_s0_' + neigh_col] = n_inclusive_status_0
631
+ neigh_table.loc[indices, 'inclusive_count_s1_' + neigh_col] = n_inclusive_status_1
604
632
  if 'intermediate' in metrics:
605
- neigh_table.loc[indices, 'intermediate_count_s0_'+neigh_col] = n_intermediate_status_0
606
- neigh_table.loc[indices, 'intermediate_count_s1_'+neigh_col] = n_intermediate_status_1
607
- if 'exclusive' in metrics:
608
- neigh_table.loc[indices, 'exclusive_count_s0_'+neigh_col] = n_exclusive_status_0
609
- neigh_table.loc[indices, 'exclusive_count_s1_'+neigh_col] = n_exclusive_status_1
633
+ neigh_table.loc[indices, 'intermediate_count_s0_' + neigh_col] = n_intermediate_status_0
634
+ neigh_table.loc[indices, 'intermediate_count_s1_' + neigh_col] = n_intermediate_status_1
635
+ if 'exclusive' in metrics:
636
+ neigh_table.loc[indices, 'exclusive_count_s0_' + neigh_col] = n_exclusive_status_0
637
+ neigh_table.loc[indices, 'exclusive_count_s1_' + neigh_col] = n_exclusive_status_1
610
638
 
611
639
  return neigh_table
612
640
 
613
- def mean_neighborhood_before_event(neigh_table, neigh_col, event_time_col, metrics=['inclusive','exclusive','intermediate']):
614
-
641
+
642
+ def mean_neighborhood_before_event(neigh_table, neigh_col, event_time_col,
643
+ metrics=['inclusive', 'exclusive', 'intermediate']):
615
644
  """
616
645
  Computes the mean neighborhood metrics for each cell track before a specified event time.
617
646
 
@@ -645,53 +674,59 @@ def mean_neighborhood_before_event(neigh_table, neigh_col, event_time_col, metri
645
674
  groupbycols = ['position']
646
675
  else:
647
676
  groupbycols = []
648
- if 'TRACK_ID' in list(neigh_table.columns):
649
- groupbycols.append('TRACK_ID')
650
- else:
651
- groupbycols.append('ID')
677
+
678
+ id_col = extract_identity_col(neigh_table)
679
+ groupbycols.append(id_col)
652
680
 
653
681
  neigh_table.sort_values(by=groupbycols+['FRAME'],inplace=True)
654
682
  suffix = '_before_event'
655
-
683
+
656
684
  if event_time_col is None:
657
685
  print('No event time was provided... Estimating the mean neighborhood over the whole observation time...')
658
- neigh_table.loc[:,'event_time_temp'] = neigh_table['FRAME'].max()
686
+ neigh_table.loc[:, 'event_time_temp'] = neigh_table['FRAME'].max()
659
687
  event_time_col = 'event_time_temp'
660
688
  suffix = ''
661
-
662
- for tid,group in neigh_table.groupby(groupbycols):
689
+
690
+ for tid, group in neigh_table.groupby(groupbycols):
663
691
 
664
692
  group = group.dropna(subset=neigh_col)
665
693
  indices = list(group.index)
666
694
 
667
695
  event_time_values = group[event_time_col].to_numpy()
668
- if len(event_time_values)>0:
696
+ if len(event_time_values) > 0:
669
697
  event_time = event_time_values[0]
670
698
  else:
671
699
  continue
672
700
 
673
- if event_time<0.:
701
+ if event_time < 0.:
674
702
  event_time = group['FRAME'].max()
675
703
 
676
704
  if 'intermediate' in metrics:
677
- valid_counts_intermediate = group.loc[group['FRAME']<=event_time,'intermediate_count_s1_'+neigh_col].to_numpy()
678
- if len(valid_counts_intermediate[valid_counts_intermediate==valid_counts_intermediate])>0:
679
- neigh_table.loc[indices, f'mean_count_intermediate_{neigh_col}{suffix}'] = np.nanmean(valid_counts_intermediate)
705
+ valid_counts_intermediate = group.loc[
706
+ group['FRAME'] <= event_time, 'intermediate_count_s1_' + neigh_col].to_numpy()
707
+ if len(valid_counts_intermediate[valid_counts_intermediate == valid_counts_intermediate]) > 0:
708
+ neigh_table.loc[indices, f'mean_count_intermediate_{neigh_col}{suffix}'] = np.nanmean(
709
+ valid_counts_intermediate)
680
710
  if 'inclusive' in metrics:
681
- valid_counts_inclusive = group.loc[group['FRAME']<=event_time,'inclusive_count_s1_'+neigh_col].to_numpy()
682
- if len(valid_counts_inclusive[valid_counts_inclusive==valid_counts_inclusive])>0:
683
- neigh_table.loc[indices, f'mean_count_inclusive_{neigh_col}{suffix}'] = np.nanmean(valid_counts_inclusive)
711
+ valid_counts_inclusive = group.loc[
712
+ group['FRAME'] <= event_time, 'inclusive_count_s1_' + neigh_col].to_numpy()
713
+ if len(valid_counts_inclusive[valid_counts_inclusive == valid_counts_inclusive]) > 0:
714
+ neigh_table.loc[indices, f'mean_count_inclusive_{neigh_col}{suffix}'] = np.nanmean(
715
+ valid_counts_inclusive)
684
716
  if 'exclusive' in metrics:
685
- valid_counts_exclusive = group.loc[group['FRAME']<=event_time,'exclusive_count_s1_'+neigh_col].to_numpy()
686
- if len(valid_counts_exclusive[valid_counts_exclusive==valid_counts_exclusive])>0:
687
- neigh_table.loc[indices, f'mean_count_exclusive_{neigh_col}{suffix}'] = np.nanmean(valid_counts_exclusive)
717
+ valid_counts_exclusive = group.loc[
718
+ group['FRAME'] <= event_time, 'exclusive_count_s1_' + neigh_col].to_numpy()
719
+ if len(valid_counts_exclusive[valid_counts_exclusive == valid_counts_exclusive]) > 0:
720
+ neigh_table.loc[indices, f'mean_count_exclusive_{neigh_col}{suffix}'] = np.nanmean(
721
+ valid_counts_exclusive)
688
722
 
689
- if event_time_col=='event_time_temp':
723
+ if event_time_col == 'event_time_temp':
690
724
  neigh_table = neigh_table.drop(columns='event_time_temp')
691
725
  return neigh_table
692
726
 
693
- def mean_neighborhood_after_event(neigh_table, neigh_col, event_time_col, metrics=['inclusive','exclusive','intermediate']):
694
-
727
+
728
+ def mean_neighborhood_after_event(neigh_table, neigh_col, event_time_col,
729
+ metrics=['inclusive', 'exclusive', 'intermediate']):
695
730
  """
696
731
  Computes the mean neighborhood metrics for each cell track after a specified event time.
697
732
 
@@ -725,55 +760,62 @@ def mean_neighborhood_after_event(neigh_table, neigh_col, event_time_col, metric
725
760
  groupbycols = ['position']
726
761
  else:
727
762
  groupbycols = []
728
- if 'TRACK_ID' in list(neigh_table.columns):
729
- groupbycols.append('TRACK_ID')
730
- else:
731
- groupbycols.append('ID')
763
+
764
+ id_col = extract_identity_col(neigh_table)
765
+ groupbycols.append(id_col)
732
766
 
733
767
  neigh_table.sort_values(by=groupbycols+['FRAME'],inplace=True)
734
768
  suffix = '_after_event'
735
-
769
+
736
770
  if event_time_col is None:
737
- neigh_table.loc[:,'event_time_temp'] = None #neigh_table['FRAME'].max()
771
+ neigh_table.loc[:, 'event_time_temp'] = None # neigh_table['FRAME'].max()
738
772
  event_time_col = 'event_time_temp'
739
773
  suffix = ''
740
-
741
- for tid,group in neigh_table.groupby(groupbycols):
774
+
775
+ for tid, group in neigh_table.groupby(groupbycols):
742
776
 
743
777
  group = group.dropna(subset=neigh_col)
744
778
  indices = list(group.index)
745
779
 
746
780
  event_time_values = group[event_time_col].to_numpy()
747
- if len(event_time_values)>0:
781
+ if len(event_time_values) > 0:
748
782
  event_time = event_time_values[0]
749
783
  else:
750
784
  continue
751
785
 
752
- if event_time is not None and (event_time>=0.):
786
+ if event_time is not None and (event_time >= 0.):
753
787
 
754
788
  if 'intermediate' in metrics:
755
- valid_counts_intermediate = group.loc[group['FRAME']>event_time,'intermediate_count_s1_'+neigh_col].to_numpy()
756
- if len(valid_counts_intermediate[valid_counts_intermediate==valid_counts_intermediate])>0:
757
- neigh_table.loc[indices, f'mean_count_intermediate_{neigh_col}{suffix}'] = np.nanmean(valid_counts_intermediate)
789
+ valid_counts_intermediate = group.loc[
790
+ group['FRAME'] > event_time, 'intermediate_count_s1_' + neigh_col].to_numpy()
791
+ if len(valid_counts_intermediate[valid_counts_intermediate == valid_counts_intermediate]) > 0:
792
+ neigh_table.loc[indices, f'mean_count_intermediate_{neigh_col}{suffix}'] = np.nanmean(
793
+ valid_counts_intermediate)
758
794
  if 'inclusive' in metrics:
759
- valid_counts_inclusive = group.loc[group['FRAME']>event_time,'inclusive_count_s1_'+neigh_col].to_numpy()
760
- if len(valid_counts_inclusive[valid_counts_inclusive==valid_counts_inclusive])>0:
761
- neigh_table.loc[indices, f'mean_count_inclusive_{neigh_col}{suffix}'] = np.nanmean(valid_counts_inclusive)
795
+ valid_counts_inclusive = group.loc[
796
+ group['FRAME'] > event_time, 'inclusive_count_s1_' + neigh_col].to_numpy()
797
+ if len(valid_counts_inclusive[valid_counts_inclusive == valid_counts_inclusive]) > 0:
798
+ neigh_table.loc[indices, f'mean_count_inclusive_{neigh_col}{suffix}'] = np.nanmean(
799
+ valid_counts_inclusive)
762
800
  if 'exclusive' in metrics:
763
- valid_counts_exclusive = group.loc[group['FRAME']>event_time,'exclusive_count_s1_'+neigh_col].to_numpy()
764
- if len(valid_counts_exclusive[valid_counts_exclusive==valid_counts_exclusive])>0:
765
- neigh_table.loc[indices, f'mean_count_exclusive_{neigh_col}{suffix}'] = np.nanmean(valid_counts_exclusive)
801
+ valid_counts_exclusive = group.loc[
802
+ group['FRAME'] > event_time, 'exclusive_count_s1_' + neigh_col].to_numpy()
803
+ if len(valid_counts_exclusive[valid_counts_exclusive == valid_counts_exclusive]) > 0:
804
+ neigh_table.loc[indices, f'mean_count_exclusive_{neigh_col}{suffix}'] = np.nanmean(
805
+ valid_counts_exclusive)
766
806
 
767
- if event_time_col=='event_time_temp':
807
+ if event_time_col == 'event_time_temp':
768
808
  neigh_table = neigh_table.drop(columns='event_time_temp')
769
809
 
770
810
  return neigh_table
771
811
 
812
+
772
813
  # New functions for direct cell-cell contact neighborhood
773
814
 
774
815
  def sign(num):
775
816
  return -1 if num < 0 else 1
776
817
 
818
+
777
819
  def contact_neighborhood(labelsA, labelsB=None, border=3, connectivity=2):
778
820
 
779
821
  labelsA = labelsA.astype(float)
@@ -781,50 +823,50 @@ def contact_neighborhood(labelsA, labelsB=None, border=3, connectivity=2):
781
823
  labelsB = labelsB.astype(float)
782
824
 
783
825
  print(f"Border = {border}")
784
-
826
+
785
827
  if border > 0:
786
828
  print(labelsA.shape, border * (-1))
787
829
  labelsA_edge = contour_of_instance_segmentation(label=labelsA, distance=border * (-1)).astype(float)
788
- labelsA[np.where(labelsA_edge>0)] = labelsA_edge[np.where(labelsA_edge>0)]
830
+ labelsA[np.where(labelsA_edge > 0)] = labelsA_edge[np.where(labelsA_edge > 0)]
789
831
  if labelsB is not None:
790
832
  labelsB_edge = contour_of_instance_segmentation(label=labelsB, distance=border * (-1)).astype(float)
791
- labelsB[np.where(labelsB_edge>0)] = labelsB_edge[np.where(labelsB_edge>0)]
792
-
833
+ labelsB[np.where(labelsB_edge > 0)] = labelsB_edge[np.where(labelsB_edge > 0)]
834
+
793
835
  if labelsB is not None:
794
- labelsA[labelsA!=0] = -labelsA[labelsA!=0]
836
+ labelsA[labelsA != 0] = -labelsA[labelsA != 0]
795
837
  labelsAB = merge_labels(labelsA, labelsB)
796
838
  labelsBA = merge_labels(labelsB, labelsA)
797
839
  label_cases = [labelsAB, labelsBA]
798
840
  else:
799
841
  label_cases = [labelsA]
800
-
842
+
801
843
  coocurrences = []
802
844
  for lbl in label_cases:
803
845
  coocurrences.extend(find_contact_neighbors(lbl, connectivity=connectivity))
804
-
805
- unique_pairs = np.unique(coocurrences,axis=0)
806
-
846
+
847
+ unique_pairs = np.unique(coocurrences, axis=0)
848
+
807
849
  if labelsB is not None:
808
- neighs = np.unique([tuple(sorted(p)) for p in unique_pairs if p[0]!=p[1] and sign(p[0])!=sign(p[1])],axis=0)
850
+ neighs = np.unique([tuple(sorted(p)) for p in unique_pairs if p[0] != p[1] and sign(p[0]) != sign(p[1])],
851
+ axis=0)
809
852
  else:
810
- neighs = np.unique([tuple(sorted(p)) for p in unique_pairs if p[0]!=p[1]],axis=0)
811
-
853
+ neighs = np.unique([tuple(sorted(p)) for p in unique_pairs if p[0] != p[1]], axis=0)
854
+
812
855
  return neighs
813
856
 
814
857
  def merge_labels(labelsA, labelsB):
815
-
816
858
  labelsA = labelsA.astype(float)
817
859
  labelsB = labelsB.astype(float)
818
-
860
+
819
861
  labelsAB = labelsA.copy()
820
- labelsAB[np.where(labelsB!=0)] = labelsB[np.where(labelsB!=0)]
821
-
862
+ labelsAB[np.where(labelsB != 0)] = labelsB[np.where(labelsB != 0)]
863
+
822
864
  return labelsAB
823
865
 
866
+
824
867
  def find_contact_neighbors(labels, connectivity=2):
825
-
826
- assert labels.ndim==2,"Wrong dimension for labels..."
827
- g, nodes = pixel_graph(labels, mask=labels.astype(bool),connectivity=connectivity)
868
+ assert labels.ndim == 2, "Wrong dimension for labels..."
869
+ g, nodes = pixel_graph(labels, mask=labels.astype(bool), connectivity=connectivity)
828
870
  g.eliminate_zeros()
829
871
 
830
872
  coo = g.tocoo()
@@ -833,18 +875,19 @@ def find_contact_neighbors(labels, connectivity=2):
833
875
 
834
876
  center_values = labels.ravel()[center_coords]
835
877
  neighbor_values = labels.ravel()[neighbor_coords]
836
- touching_masks = np.column_stack((center_values, neighbor_values))
837
-
878
+ touching_masks = np.column_stack((center_values, neighbor_values))
879
+
838
880
  return touching_masks
839
881
 
840
882
 
841
- def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-pop', status=None, not_status_option=None, compute_cum_sum=True,
883
+ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-pop', status=None,
884
+ not_status_option=None, compute_cum_sum=True,
842
885
  attention_weight=True, symmetrize=True, include_dead_weight=True,
843
- column_labels={'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y', 'mask_id': 'class_id'}):
844
-
886
+ column_labels={'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y',
887
+ 'mask_id': 'class_id'}):
845
888
  """
846
889
 
847
- Match neighbors in set A and B within a circle of radius d.
890
+ Match neighbors in set A and B within a circle of radius d.
848
891
 
849
892
  Parameters
850
893
  ----------
@@ -854,7 +897,7 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
854
897
  Cut-distance in pixels to match neighboring pairs.
855
898
  mode: str
856
899
  neighboring mode, between 'two-pop' (e.g. target-effector) and 'self' (target-target or effector-effector).
857
- status: None or status
900
+ status: None or status
858
901
  name to look for cells to ignore (because they are dead). By default all cells are kept.
859
902
  compute_cum_sum: bool,
860
903
  compute cumulated time of presence of neighbours (only if trajectories available for both sets)
@@ -870,57 +913,59 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
870
913
  if setA is not None and setB is not None:
871
914
  setA, setB, status = set_live_status(setA, setB, status, not_status_option)
872
915
  else:
873
- return None,None
916
+ return None, None
874
917
 
875
- # Check distance option
918
+ # Check distance option
876
919
  if not isinstance(distance, list):
877
920
  distance = [distance]
878
-
921
+
879
922
  for d in distance:
880
923
  # loop over each provided distance
881
-
882
- if mode=='two-pop':
924
+
925
+ if mode == 'two-pop':
883
926
  neigh_col = f'neighborhood_2_contact_{d}_px'
884
- elif mode=='self':
927
+ elif mode == 'self':
885
928
  neigh_col = f'neighborhood_self_contact_{d}_px'
886
-
929
+
887
930
  cl = []
888
- for s in [setA,setB]:
931
+ for s in [setA, setB]:
889
932
 
890
933
  # Check whether data can be tracked
891
934
  temp_column_labels = column_labels.copy()
892
935
 
893
- if not 'TRACK_ID' in list(s.columns):
894
- temp_column_labels.update({'track': 'ID'})
895
- compute_cum_sum = False # if no tracking data then cum_sum is not relevant
936
+ id_col = extract_identity_col(s)
937
+ temp_column_labels.update({'track': id_col})
938
+ if id_col=='ID':
939
+ compute_cum_sum = False # if no tracking data then cum_sum is not relevant
896
940
  cl.append(temp_column_labels)
897
941
 
898
942
  # Remove nan tracks (cells that do not belong to a track)
899
943
  s[neigh_col] = np.nan
900
944
  s[neigh_col] = s[neigh_col].astype(object)
901
- s.dropna(subset=[cl[-1]['track']],inplace=True)
945
+ s.dropna(subset=[cl[-1]['track']], inplace=True)
902
946
 
903
947
  # Loop over each available timestep
904
- timeline = np.unique(np.concatenate([setA[cl[0]['time']].to_numpy(), setB[cl[1]['time']].to_numpy()])).astype(int)
948
+ timeline = np.unique(np.concatenate([setA[cl[0]['time']].to_numpy(), setB[cl[1]['time']].to_numpy()])).astype(
949
+ int)
905
950
  for t in tqdm(timeline):
906
951
 
907
- index_A = list(setA.loc[setA[cl[0]['time']]==t].index)
908
- coordinates_A = setA.loc[setA[cl[0]['time']]==t,[cl[0]['x'], cl[0]['y']]].to_numpy()
909
- ids_A = setA.loc[setA[cl[0]['time']]==t,cl[0]['track']].to_numpy()
910
- mask_ids_A = setA.loc[setA[cl[0]['time']]==t,cl[0]['mask_id']].to_numpy()
911
- status_A = setA.loc[setA[cl[0]['time']]==t,status[0]].to_numpy()
912
-
913
- index_B = list(setB.loc[setB[cl[1]['time']]==t].index)
914
- coordinates_B = setB.loc[setB[cl[1]['time']]==t,[cl[1]['x'], cl[1]['y']]].to_numpy()
915
- ids_B = setB.loc[setB[cl[1]['time']]==t,cl[1]['track']].to_numpy()
916
- mask_ids_B = setB.loc[setB[cl[1]['time']]==t,cl[1]['mask_id']].to_numpy()
917
- status_B = setB.loc[setB[cl[1]['time']]==t,status[1]].to_numpy()
918
-
952
+ index_A = list(setA.loc[setA[cl[0]['time']] == t].index)
953
+ coordinates_A = setA.loc[setA[cl[0]['time']] == t, [cl[0]['x'], cl[0]['y']]].to_numpy()
954
+ ids_A = setA.loc[setA[cl[0]['time']] == t, cl[0]['track']].to_numpy()
955
+ mask_ids_A = setA.loc[setA[cl[0]['time']] == t, cl[0]['mask_id']].to_numpy()
956
+ status_A = setA.loc[setA[cl[0]['time']] == t, status[0]].to_numpy()
957
+
958
+ index_B = list(setB.loc[setB[cl[1]['time']] == t].index)
959
+ coordinates_B = setB.loc[setB[cl[1]['time']] == t, [cl[1]['x'], cl[1]['y']]].to_numpy()
960
+ ids_B = setB.loc[setB[cl[1]['time']] == t, cl[1]['track']].to_numpy()
961
+ mask_ids_B = setB.loc[setB[cl[1]['time']] == t, cl[1]['mask_id']].to_numpy()
962
+ status_B = setB.loc[setB[cl[1]['time']] == t, status[1]].to_numpy()
963
+
919
964
  print(f"Frame {t}")
920
- print(f"{mask_ids_A=}",f"{mask_ids_B}")
965
+ print(f"{mask_ids_A=}", f"{mask_ids_B}")
921
966
 
922
967
  if len(ids_A) > 0 and len(ids_B) > 0:
923
-
968
+
924
969
  # compute distance matrix
925
970
  dist_map = cdist(coordinates_A, coordinates_B, metric="euclidean")
926
971
 
@@ -931,21 +976,21 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
931
976
  lblB = labelsB
932
977
 
933
978
  print(f"Distance {d} for contact as border")
934
- contact_pairs = contact_neighborhood(labelsA[t], labelsB=lblB, border=d, connectivity=2)
979
+ contact_pairs = contact_neighborhood(labelsA[t], labelsB=lblB, border=d, connectivity=2)
935
980
 
936
981
  print(t, f"{np.unique(labelsA[t])=}")
937
982
  print(f"Frame {t}: found the following contact pairs: {contact_pairs}...")
938
983
  # Put infinite distance to all non-contact pairs (something like this)
939
- plot_map=False
984
+ plot_map = False
940
985
 
941
- if len(contact_pairs)>0:
986
+ if len(contact_pairs) > 0:
942
987
  mask = np.ones_like(dist_map).astype(bool)
943
-
988
+
944
989
  indices_to_keep = []
945
990
  for cp in contact_pairs:
946
-
947
- if np.any(cp<0):
948
- if cp[0]<0:
991
+
992
+ if np.any(cp < 0):
993
+ if cp[0] < 0:
949
994
  mask_A = cp[1]
950
995
  mask_B = np.abs(cp[0])
951
996
  else:
@@ -957,8 +1002,8 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
957
1002
 
958
1003
  try:
959
1004
 
960
- idx_A = np.where(mask_ids_A==int(mask_A))[0][0]
961
- idx_B = np.where(mask_ids_B==int(mask_B))[0][0]
1005
+ idx_A = np.where(mask_ids_A == int(mask_A))[0][0]
1006
+ idx_B = np.where(mask_ids_B == int(mask_B))[0][0]
962
1007
  print(idx_A, idx_B)
963
1008
  indices_to_keep.append([idx_A,idx_B])
964
1009
  except Exception as e:
@@ -966,17 +1011,17 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
966
1011
  pass
967
1012
 
968
1013
  print(f'Indices to keep: {indices_to_keep}...')
969
- if len(indices_to_keep)>0:
1014
+ if len(indices_to_keep) > 0:
970
1015
  indices_to_keep = np.array(indices_to_keep)
971
- mask[indices_to_keep[:,0],indices_to_keep[:,1]] = False
972
- if mode=='self':
973
- mask[indices_to_keep[:,1],indices_to_keep[:,0]] = False
1016
+ mask[indices_to_keep[:, 0], indices_to_keep[:, 1]] = False
1017
+ if mode == 'self':
1018
+ mask[indices_to_keep[:, 1], indices_to_keep[:, 0]] = False
974
1019
  dist_map[mask] = 1.0E06
975
1020
  plot_map=True
976
1021
  else:
977
1022
  dist_map[:,:] = 1.0E06
978
1023
  else:
979
- dist_map[:,:] = 1.0E06
1024
+ dist_map[:, :] = 1.0E06
980
1025
 
981
1026
  # PROCEED all the same?? --> I guess so
982
1027
  # if plot_map:
@@ -988,62 +1033,63 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
988
1033
 
989
1034
  d_filter = 1.0E05
990
1035
  if attention_weight:
991
- weights, closest_A = compute_attention_weight(dist_map, d_filter, status_A, ids_A, axis=1, include_dead_weight=include_dead_weight)
992
-
1036
+ weights, closest_A = compute_attention_weight(dist_map, d_filter, status_A, ids_A, axis=1,
1037
+ include_dead_weight=include_dead_weight)
1038
+
993
1039
  # Target centric
994
1040
  for k in range(dist_map.shape[0]):
995
-
996
- col = dist_map[k,:]
997
- col[col==0.] = 1.0E06
998
-
999
- neighs_B = np.array([ids_B[i] for i in np.where((col<=d_filter))[0]])
1000
- status_neigh_B = np.array([status_B[i] for i in np.where((col<=d_filter))[0]])
1001
- dist_B = [round(col[i],2) for i in np.where((col<=d_filter))[0]]
1002
- if len(dist_B)>0:
1003
- closest_B_cell = neighs_B[np.argmin(dist_B)]
1004
-
1041
+
1042
+ col = dist_map[k, :]
1043
+ col[col == 0.] = 1.0E06
1044
+
1045
+ neighs_B = np.array([ids_B[i] for i in np.where((col <= d_filter))[0]])
1046
+ status_neigh_B = np.array([status_B[i] for i in np.where((col <= d_filter))[0]])
1047
+ dist_B = [round(col[i], 2) for i in np.where((col <= d_filter))[0]]
1048
+ if len(dist_B) > 0:
1049
+ closest_B_cell = neighs_B[np.argmin(dist_B)]
1050
+
1005
1051
  if symmetrize and attention_weight:
1006
1052
  n_neighs = float(len(neighs_B))
1007
1053
  if not include_dead_weight:
1008
- n_neighs_alive = len(np.where(status_neigh_B==1)[0])
1054
+ n_neighs_alive = len(np.where(status_neigh_B == 1)[0])
1009
1055
  neigh_count = n_neighs_alive
1010
1056
  else:
1011
1057
  neigh_count = n_neighs
1012
- if neigh_count>0:
1013
- weight_A = 1./neigh_count
1058
+ if neigh_count > 0:
1059
+ weight_A = 1. / neigh_count
1014
1060
  else:
1015
1061
  weight_A = np.nan
1016
1062
 
1017
- if not include_dead_weight and status_A[k]==0:
1063
+ if not include_dead_weight and status_A[k] == 0:
1018
1064
  weight_A = 0
1019
-
1065
+
1020
1066
  neighs = []
1021
1067
  setA.at[index_A[k], neigh_col] = []
1022
1068
  for n in range(len(neighs_B)):
1023
-
1069
+
1024
1070
  # index in setB
1025
- n_index = np.where(ids_B==neighs_B[n])[0][0]
1071
+ n_index = np.where(ids_B == neighs_B[n])[0][0]
1026
1072
  # Assess if neigh B is closest to A
1027
1073
  if attention_weight:
1028
- if closest_A[n_index]==ids_A[k]:
1074
+ if closest_A[n_index] == ids_A[k]:
1029
1075
  closest = True
1030
1076
  else:
1031
1077
  closest = False
1032
-
1078
+
1033
1079
  if symmetrize:
1034
1080
  # Load neighborhood previous data
1035
1081
  sym_neigh = setB.loc[index_B[n_index], neigh_col]
1036
- if neighs_B[n]==closest_B_cell:
1037
- closest_b=True
1082
+ if neighs_B[n] == closest_B_cell:
1083
+ closest_b = True
1038
1084
  else:
1039
- closest_b=False
1085
+ closest_b = False
1040
1086
  if isinstance(sym_neigh, list):
1041
1087
  sym_neigh.append({'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k]})
1042
1088
  else:
1043
- sym_neigh = [{'id': ids_A[k], 'distance': dist_B[n],'status': status_A[k]}]
1089
+ sym_neigh = [{'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k]}]
1044
1090
  if attention_weight:
1045
1091
  sym_neigh[-1].update({'weight': weight_A, 'closest': closest_b})
1046
-
1092
+
1047
1093
  # Write the minimum info about neighborhing cell B
1048
1094
  neigh_dico = {'id': neighs_B[n], 'distance': dist_B[n], 'status': status_neigh_B[n]}
1049
1095
  if attention_weight:
@@ -1051,33 +1097,43 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
1051
1097
 
1052
1098
  if compute_cum_sum:
1053
1099
  # Compute the integrated presence of the neighboring cell B
1054
- assert cl[1]['track'] == 'TRACK_ID','The set B does not seem to contain tracked data. The cumulative time will be meaningless.'
1055
- past_neighs = [[ll['id'] for ll in l] if len(l)>0 else [None] for l in setA.loc[(setA[cl[0]['track']]==ids_A[k])&(setA[cl[0]['time']]<=t), neigh_col].to_numpy()]
1100
+ assert cl[1][
1101
+ 'track'] == 'TRACK_ID', 'The set B does not seem to contain tracked data. The cumulative time will be meaningless.'
1102
+ past_neighs = [[ll['id'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
1103
+ (setA[cl[0]['track']] == ids_A[k]) & (setA[cl[0]['time']] <= t), neigh_col].to_numpy()]
1056
1104
  past_neighs = [item for sublist in past_neighs for item in sublist]
1057
-
1105
+
1058
1106
  if attention_weight:
1059
- past_weights = [[ll['weight'] for ll in l] if len(l)>0 else [None] for l in setA.loc[(setA[cl[0]['track']]==ids_A[k])&(setA[cl[0]['time']]<=t), neigh_col].to_numpy()]
1107
+ past_weights = [[ll['weight'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
1108
+ (setA[cl[0]['track']] == ids_A[k]) & (
1109
+ setA[cl[0]['time']] <= t), neigh_col].to_numpy()]
1060
1110
  past_weights = [item for sublist in past_weights for item in sublist]
1061
1111
 
1062
- cum_sum = len(np.where(past_neighs==neighs_B[n])[0])
1063
- neigh_dico.update({'cumulated_presence': cum_sum+1})
1064
-
1112
+ cum_sum = len(np.where(past_neighs == neighs_B[n])[0])
1113
+ neigh_dico.update({'cumulated_presence': cum_sum + 1})
1114
+
1065
1115
  if attention_weight:
1066
- cum_sum_weighted = np.sum([w if l==neighs_B[n] else 0 for l,w in zip(past_neighs, past_weights)])
1116
+ cum_sum_weighted = np.sum(
1117
+ [w if l == neighs_B[n] else 0 for l, w in zip(past_neighs, past_weights)])
1067
1118
  neigh_dico.update({'cumulated_presence_weighted': cum_sum_weighted + weights[n_index]})
1068
1119
 
1069
1120
  if symmetrize:
1070
1121
  setB.at[index_B[n_index], neigh_col] = sym_neigh
1071
-
1122
+
1072
1123
  neighs.append(neigh_dico)
1073
-
1124
+
1074
1125
  setA.at[index_A[k], neigh_col] = neighs
1075
-
1126
+
1076
1127
  return setA, setB
1077
1128
 
1078
- def compute_contact_neighborhood_at_position(pos, distance, population=['targets','effectors'], theta_dist=None, img_shape=(2048,2048), return_tables=False, clear_neigh=False, event_time_col=None,
1079
- neighborhood_kwargs={'mode': 'two-pop','status': None, 'not_status_option': None,'include_dead_weight': True,"compute_cum_sum": False,"attention_weight": True, 'symmetrize': True}):
1080
-
1129
+
1130
+ def compute_contact_neighborhood_at_position(pos, distance, population=['targets', 'effectors'], theta_dist=None,
1131
+ img_shape=(2048, 2048), return_tables=False, clear_neigh=False,
1132
+ event_time_col=None,
1133
+ neighborhood_kwargs={'mode': 'two-pop', 'status': None,
1134
+ 'not_status_option': None,
1135
+ 'include_dead_weight': True, "compute_cum_sum": False,
1136
+ "attention_weight": True, 'symmetrize': True}):
1081
1137
  """
1082
1138
  Computes neighborhood metrics for specified cell populations within a given position, based on distance criteria and additional parameters.
1083
1139
 
@@ -1115,12 +1171,12 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1115
1171
  ------
1116
1172
  AssertionError
1117
1173
  If the specified position path does not exist or if the number of distances and edge thresholds do not match.
1118
-
1174
+
1119
1175
  """
1120
1176
 
1121
- pos = pos.replace('\\','/')
1177
+ pos = pos.replace('\\', '/')
1122
1178
  pos = rf"{pos}"
1123
- assert os.path.exists(pos),f'Position {pos} is not a valid path.'
1179
+ assert os.path.exists(pos), f'Position {pos} is not a valid path.'
1124
1180
 
1125
1181
  if isinstance(population, str):
1126
1182
  population = [population, population]
@@ -1131,12 +1187,12 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1131
1187
  theta_dist = [theta_dist]
1132
1188
 
1133
1189
  if theta_dist is None:
1134
- theta_dist = [0 for d in distance] #0.9*d
1135
- assert len(theta_dist)==len(distance),'Incompatible number of distances and number of edge thresholds.'
1190
+ theta_dist = [0 for d in distance] # 0.9*d
1191
+ assert len(theta_dist) == len(distance), 'Incompatible number of distances and number of edge thresholds.'
1136
1192
 
1137
- if population[0]==population[1]:
1193
+ if population[0] == population[1]:
1138
1194
  neighborhood_kwargs.update({'mode': 'self'})
1139
- if population[1]!=population[0]:
1195
+ if population[1] != population[0]:
1140
1196
  neighborhood_kwargs.update({'mode': 'two-pop'})
1141
1197
 
1142
1198
  df_A, path_A = get_position_table(pos, population=population[0], return_path=True)
@@ -1144,6 +1200,15 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1144
1200
  if df_A is None or df_B is None:
1145
1201
  return None
1146
1202
 
1203
+ if clear_neigh:
1204
+ if os.path.exists(path_A.replace('.csv','.pkl')):
1205
+ os.remove(path_A.replace('.csv','.pkl'))
1206
+ if os.path.exists(path_B.replace('.csv','.pkl')):
1207
+ os.remove(path_B.replace('.csv','.pkl'))
1208
+ df_pair, pair_path = get_position_table(pos, population='pairs', return_path=True)
1209
+ if df_pair is not None:
1210
+ os.remove(pair_path)
1211
+
1147
1212
  df_A_pkl = get_position_pickle(pos, population=population[0], return_path=False)
1148
1213
  df_B_pkl = get_position_pickle(pos, population=population[1], return_path=False)
1149
1214
 
@@ -1152,12 +1217,9 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1152
1217
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
1153
1218
  cols = list(pkl_columns[neigh_columns]) + ['FRAME']
1154
1219
 
1155
- if 'TRACK_ID' in list(pkl_columns):
1156
- cols.append('TRACK_ID')
1157
- on_cols = ['TRACK_ID','FRAME']
1158
- else:
1159
- cols.append('ID')
1160
- on_cols = ['ID','FRAME']
1220
+ id_col = extract_identity_col(df_A_pkl)
1221
+ cols.append(id_col)
1222
+ on_cols = [id_col, 'FRAME']
1161
1223
 
1162
1224
  print(f'Recover {cols} from the pickle file...')
1163
1225
  try:
@@ -1171,12 +1233,9 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1171
1233
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
1172
1234
  cols = list(pkl_columns[neigh_columns]) + ['FRAME']
1173
1235
 
1174
- if 'TRACK_ID' in list(pkl_columns):
1175
- cols.append('TRACK_ID')
1176
- on_cols = ['TRACK_ID','FRAME']
1177
- else:
1178
- cols.append('ID')
1179
- on_cols = ['ID','FRAME']
1236
+ id_col = extract_identity_col(df_B_pkl)
1237
+ cols.append(id_col)
1238
+ on_cols = [id_col, 'FRAME']
1180
1239
 
1181
1240
  print(f'Recover {cols} from the pickle file...')
1182
1241
  try:
@@ -1185,7 +1244,7 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1185
1244
  print(f'Failure to merge pickle and csv files: {e}')
1186
1245
 
1187
1246
  labelsA = locate_labels(pos, population=population[0])
1188
- if population[1]==population[0]:
1247
+ if population[1] == population[0]:
1189
1248
  labelsB = None
1190
1249
  else:
1191
1250
  labelsB = locate_labels(pos, population=population[1])
@@ -1194,18 +1253,18 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1194
1253
  unwanted = df_A.columns[df_A.columns.str.contains('neighborhood')]
1195
1254
  df_A = df_A.drop(columns=unwanted)
1196
1255
  unwanted = df_B.columns[df_B.columns.str.contains('neighborhood')]
1197
- df_B = df_B.drop(columns=unwanted)
1256
+ df_B = df_B.drop(columns=unwanted)
1198
1257
 
1199
1258
  print(f"Distance: {distance} for mask contact")
1200
- df_A, df_B = mask_contact_neighborhood(df_A, df_B, labelsA, labelsB, distance,**neighborhood_kwargs)
1201
- if df_A is None or df_B is None:
1259
+ df_A, df_B = mask_contact_neighborhood(df_A, df_B, labelsA, labelsB, distance, **neighborhood_kwargs)
1260
+ if df_A is None or df_B is None or len(df_A)==0:
1202
1261
  return None
1203
1262
 
1204
- for td,d in zip(theta_dist, distance):
1263
+ for td, d in zip(theta_dist, distance):
1205
1264
 
1206
- if neighborhood_kwargs['mode']=='two-pop':
1265
+ if neighborhood_kwargs['mode'] == 'two-pop':
1207
1266
  neigh_col = f'neighborhood_2_contact_{d}_px'
1208
- elif neighborhood_kwargs['mode']=='self':
1267
+ elif neighborhood_kwargs['mode'] == 'self':
1209
1268
  neigh_col = f'neighborhood_self_contact_{d}_px'
1210
1269
 
1211
1270
  df_A.loc[df_A['class_id'].isnull(),neigh_col] = np.nan
@@ -1215,26 +1274,34 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1215
1274
  # df_A.loc[~edge_filter_A, neigh_col] = np.nan
1216
1275
  # df_B.loc[~edge_filter_B, neigh_col] = np.nan
1217
1276
 
1218
- df_A = compute_neighborhood_metrics(df_A, neigh_col, metrics=['inclusive','intermediate'], decompose_by_status=True)
1219
- if neighborhood_kwargs['symmetrize']:
1220
- df_B = compute_neighborhood_metrics(df_B, neigh_col, metrics=['inclusive','intermediate'], decompose_by_status=True)
1221
-
1222
- df_A = mean_neighborhood_before_event(df_A, neigh_col, event_time_col, metrics=['inclusive','intermediate'])
1223
- if event_time_col is not None:
1224
- df_A = mean_neighborhood_after_event(df_A, neigh_col, event_time_col, metrics=['inclusive','intermediate'])
1225
-
1226
- df_A.to_pickle(path_A.replace('.csv','.pkl'))
1227
- if not population[0]==population[1]:
1228
- df_B.to_pickle(path_B.replace('.csv','.pkl'))
1277
+ df_A = compute_neighborhood_metrics(df_A, neigh_col, metrics=['inclusive', 'intermediate'],
1278
+ decompose_by_status=True)
1279
+ if 'TRACK_ID' in list(df_A.columns):
1280
+ if not np.all(df_A['TRACK_ID'].isnull()):
1281
+ df_A = mean_neighborhood_before_event(df_A, neigh_col, event_time_col, metrics=['inclusive','intermediate'])
1282
+ if event_time_col is not None:
1283
+ df_A = mean_neighborhood_after_event(df_A, neigh_col, event_time_col, metrics=['inclusive', 'intermediate'])
1284
+ print('Done...')
1285
+
1286
+ df_A.to_pickle(path_A.replace('.csv', '.pkl'))
1287
+ if not population[0] == population[1]:
1288
+ # Remove neighborhood column
1289
+ for td, d in zip(theta_dist, distance):
1290
+ if neighborhood_kwargs['mode'] == 'two-pop':
1291
+ neigh_col = f'neighborhood_2_contact_{d}_px'
1292
+ elif neighborhood_kwargs['mode'] == 'self':
1293
+ neigh_col = f'neighborhood_self_contact_{d}_px'
1294
+ df_B = df_B.drop(columns=[neigh_col])
1295
+ df_B.to_pickle(path_B.replace('.csv', '.pkl'))
1229
1296
 
1230
1297
  unwanted = df_A.columns[df_A.columns.str.startswith('neighborhood_')]
1231
1298
  df_A2 = df_A.drop(columns=unwanted)
1232
1299
  df_A2.to_csv(path_A, index=False)
1233
1300
 
1234
- if not population[0]==population[1]:
1301
+ if not population[0] == population[1]:
1235
1302
  unwanted = df_B.columns[df_B.columns.str.startswith('neighborhood_')]
1236
1303
  df_B_csv = df_B.drop(unwanted, axis=1, inplace=False)
1237
- df_B_csv.to_csv(path_B,index=False)
1304
+ df_B_csv.to_csv(path_B, index=False)
1238
1305
 
1239
1306
  if return_tables:
1240
1307
  return df_A, df_B
@@ -1249,10 +1316,15 @@ if __name__ == "__main__":
1249
1316
 
1250
1317
  print('None')
1251
1318
  pos = "/home/torro/Documents/Experiments/NKratio_Exp/W5/500"
1252
-
1253
- test,_ = compute_neighborhood_at_position(pos, [62], population=['targets','effectors'], theta_dist=None, img_shape=(2048,2048), return_tables=True, clear_neigh=True,
1254
- neighborhood_kwargs={'mode': 'two-pop','status': ['class', None],'not_status_option': [True, False],'include_dead_weight': True,"compute_cum_sum": False,"attention_weight": True, 'symmetrize': False})
1255
-
1256
- #test = compute_neighborhood_metrics(test, 'neighborhood_self_circle_150_px', metrics=['inclusive','exclusive','intermediate'], decompose_by_status=True)
1319
+
1320
+ test, _ = compute_neighborhood_at_position(pos, [62], population=['targets', 'effectors'], theta_dist=None,
1321
+ img_shape=(2048, 2048), return_tables=True, clear_neigh=True,
1322
+ neighborhood_kwargs={'mode': 'two-pop', 'status': ['class', None],
1323
+ 'not_status_option': [True, False],
1324
+ 'include_dead_weight': True,
1325
+ "compute_cum_sum": False, "attention_weight": True,
1326
+ 'symmetrize': False})
1327
+
1328
+ # test = compute_neighborhood_metrics(test, 'neighborhood_self_circle_150_px', metrics=['inclusive','exclusive','intermediate'], decompose_by_status=True)
1257
1329
  print(test.columns)
1258
1330
  #print(segment(None,'test'))