celldetective 1.1.1.post4__py3-none-any.whl → 1.2.1__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 (41) 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 +15 -9
  6. celldetective/gui/control_panel.py +50 -6
  7. celldetective/gui/layouts.py +5 -4
  8. celldetective/gui/neighborhood_options.py +13 -9
  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 +29 -9
  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 +222 -60
  18. celldetective/io.py +536 -420
  19. celldetective/measure.py +919 -969
  20. celldetective/models/pair_signal_detection/blank +0 -0
  21. celldetective/models/segmentation_effectors/ricm-bimodal/config_input.json +130 -0
  22. celldetective/models/segmentation_effectors/ricm-bimodal/ricm-bimodal +0 -0
  23. celldetective/models/segmentation_effectors/ricm-bimodal/training_instructions.json +37 -0
  24. celldetective/neighborhood.py +428 -354
  25. celldetective/relative_measurements.py +648 -0
  26. celldetective/scripts/analyze_signals.py +1 -1
  27. celldetective/scripts/measure_cells.py +28 -8
  28. celldetective/scripts/measure_relative.py +103 -0
  29. celldetective/scripts/segment_cells.py +5 -5
  30. celldetective/scripts/track_cells.py +4 -1
  31. celldetective/scripts/train_segmentation_model.py +23 -18
  32. celldetective/scripts/train_signal_model.py +33 -0
  33. celldetective/signals.py +405 -8
  34. celldetective/tracking.py +8 -2
  35. celldetective/utils.py +178 -17
  36. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.1.dist-info}/METADATA +8 -8
  37. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.1.dist-info}/RECORD +41 -34
  38. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.1.dist-info}/WHEEL +1 -1
  39. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.1.dist-info}/LICENSE +0 -0
  40. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.1.dist-info}/entry_points.txt +0 -0
  41. {celldetective-1.1.1.post4.dist-info → celldetective-1.2.1.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,16 +377,28 @@ 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)
390
+ if df_A is None or df_B is None:
391
+ return None
392
+
393
+ if clear_neigh:
394
+ if os.path.exists(path_A.replace('.csv','.pkl')):
395
+ os.remove(path_A.replace('.csv','.pkl'))
396
+ if os.path.exists(path_B.replace('.csv','.pkl')):
397
+ os.remove(path_B.replace('.csv','.pkl'))
398
+ df_pair, pair_path = get_position_table(pos, population='pairs', return_path=True)
399
+ if df_pair is not None:
400
+ os.remove(pair_path)
401
+
376
402
 
377
403
  df_A_pkl = get_position_pickle(pos, population=population[0], return_path=False)
378
404
  df_B_pkl = get_position_pickle(pos, population=population[1], return_path=False)
@@ -382,12 +408,9 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
382
408
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
383
409
  cols = list(pkl_columns[neigh_columns]) + ['FRAME']
384
410
 
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']
411
+ id_col = extract_identity_col(df_A_pkl)
412
+ cols.append(id_col)
413
+ on_cols = [id_col, 'FRAME']
391
414
 
392
415
  print(f'Recover {cols} from the pickle file...')
393
416
  try:
@@ -401,12 +424,9 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
401
424
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
402
425
  cols = list(pkl_columns[neigh_columns]) + ['FRAME']
403
426
 
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']
427
+ id_col = extract_identity_col(df_B_pkl)
428
+ cols.append(id_col)
429
+ on_cols = [id_col, 'FRAME']
410
430
 
411
431
  print(f'Recover {cols} from the pickle file...')
412
432
  try:
@@ -418,17 +438,17 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
418
438
  unwanted = df_A.columns[df_A.columns.str.contains('neighborhood')]
419
439
  df_A = df_A.drop(columns=unwanted)
420
440
  unwanted = df_B.columns[df_B.columns.str.contains('neighborhood')]
421
- df_B = df_B.drop(columns=unwanted)
441
+ df_B = df_B.drop(columns=unwanted)
422
442
 
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:
443
+ df_A, df_B = distance_cut_neighborhood(df_A, df_B, distance, **neighborhood_kwargs)
444
+ if df_A is None or df_B is None or len(df_A)==0:
425
445
  return None
426
446
 
427
- for td,d in zip(theta_dist, distance):
447
+ for td, d in zip(theta_dist, distance):
428
448
 
429
- if neighborhood_kwargs['mode']=='two-pop':
449
+ if neighborhood_kwargs['mode'] == 'two-pop':
430
450
  neigh_col = f'neighborhood_2_circle_{d}_px'
431
- elif neighborhood_kwargs['mode']=='self':
451
+ elif neighborhood_kwargs['mode'] == 'self':
432
452
  neigh_col = f'neighborhood_self_circle_{d}_px'
433
453
 
434
454
  # 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 +458,37 @@ def compute_neighborhood_at_position(pos, distance, population=['targets','effec
438
458
 
439
459
  print('Count neighborhood...')
440
460
  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)
461
+ # if neighborhood_kwargs['symmetrize']:
462
+ # df_B = compute_neighborhood_metrics(df_B, neigh_col, metrics=['inclusive','exclusive','intermediate'], decompose_by_status=True)
443
463
  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
464
 
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'))
465
+ if 'TRACK_ID' in list(df_A.columns):
466
+ if not np.all(df_A['TRACK_ID'].isnull()):
467
+ print('Estimate average neighborhood before/after event...')
468
+ df_A = mean_neighborhood_before_event(df_A, neigh_col, event_time_col)
469
+ if event_time_col is not None:
470
+ df_A = mean_neighborhood_after_event(df_A, neigh_col, event_time_col)
471
+ print('Done...')
472
+
473
+ df_A.to_pickle(path_A.replace('.csv', '.pkl'))
474
+ if not population[0] == population[1]:
475
+ # Remove neighborhood column
476
+ for td, d in zip(theta_dist, distance):
477
+ if neighborhood_kwargs['mode'] == 'two-pop':
478
+ neigh_col = f'neighborhood_2_circle_{d}_px'
479
+ elif neighborhood_kwargs['mode'] == 'self':
480
+ neigh_col = f'neighborhood_self_circle_{d}_px'
481
+ df_B = df_B.drop(columns=[neigh_col])
482
+ df_B.to_pickle(path_B.replace('.csv', '.pkl'))
454
483
 
455
484
  unwanted = df_A.columns[df_A.columns.str.startswith('neighborhood_')]
456
485
  df_A2 = df_A.drop(columns=unwanted)
457
486
  df_A2.to_csv(path_A, index=False)
458
487
 
459
- if not population[0]==population[1]:
488
+ if not population[0] == population[1]:
460
489
  unwanted = df_B.columns[df_B.columns.str.startswith('neighborhood_')]
461
490
  df_B_csv = df_B.drop(unwanted, axis=1, inplace=False)
462
- df_B_csv.to_csv(path_B,index=False)
491
+ df_B_csv.to_csv(path_B, index=False)
463
492
 
464
493
  if return_tables:
465
494
  return df_A, df_B
@@ -508,7 +537,7 @@ def compute_neighborhood_metrics(neigh_table, neigh_col, metrics=['inclusive','e
508
537
  >>> neigh_col = 'neighborhood_info'
509
538
  >>> updated_neigh_table = compute_neighborhood_metrics(neigh_table, neigh_col, metrics=['inclusive'], decompose_by_status=True)
510
539
  # Computes the inclusive count of neighbors for each cell, decomposed by cell status.
511
-
540
+
512
541
  """
513
542
 
514
543
  neigh_table = neigh_table.reset_index(drop=True)
@@ -516,14 +545,13 @@ def compute_neighborhood_metrics(neigh_table, neigh_col, metrics=['inclusive','e
516
545
  groupbycols = ['position']
517
546
  else:
518
547
  groupbycols = []
519
- if 'TRACK_ID' in list(neigh_table.columns):
520
- groupbycols.append('TRACK_ID')
521
- else:
522
- groupbycols.append('ID')
548
+
549
+ id_col = extract_identity_col(neigh_table)
550
+ groupbycols.append(id_col)
523
551
 
524
552
  neigh_table.sort_values(by=groupbycols+['FRAME'],inplace=True)
525
553
 
526
- for tid,group in neigh_table.groupby(groupbycols):
554
+ for tid, group in neigh_table.groupby(groupbycols):
527
555
  group = group.dropna(subset=neigh_col)
528
556
  indices = list(group.index)
529
557
  neighbors = group[neigh_col].to_numpy()
@@ -569,49 +597,52 @@ def compute_neighborhood_metrics(neigh_table, neigh_col, metrics=['inclusive','e
569
597
  if 'intermediate' in metrics:
570
598
  n_intermediate[t] = np.sum(weights_at_t)
571
599
  if 'exclusive' in metrics:
572
- n_exclusive[t] = sum([c==1.0 for c in closest_at_t])
600
+ n_exclusive[t] = sum([c == 1.0 for c in closest_at_t])
573
601
 
574
602
  if decompose_by_status:
575
603
 
576
604
  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])
605
+ n_inclusive_status_0[t] = sum([s == 0.0 for s in status_at_t])
606
+ n_inclusive_status_1[t] = sum([s == 1.0 for s in status_at_t])
579
607
 
580
608
  if 'intermediate' in metrics:
581
609
  weights_at_t = np.array(weights_at_t)
582
-
610
+
583
611
  # 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)]
612
+ weights_status_1 = weights_at_t[np.array([s == 1.0 for s in status_at_t], dtype=bool)]
613
+ weights_status_0 = weights_at_t[np.array([s == 0.0 for s in status_at_t], dtype=bool)]
586
614
  n_intermediate_status_1[t] = np.sum(weights_status_1)
587
615
  n_intermediate_status_0[t] = np.sum(weights_status_0)
588
616
 
589
617
  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)])
618
+ n_exclusive_status_0[t] = sum(
619
+ [c == 1.0 if s == 0.0 else False for c, s in zip(closest_at_t, status_at_t)])
620
+ n_exclusive_status_1[t] = sum(
621
+ [c == 1.0 if s == 1.0 else False for c, s in zip(closest_at_t, status_at_t)])
592
622
 
593
623
  if 'inclusive' in metrics:
594
- neigh_table.loc[indices, 'inclusive_count_'+neigh_col] = n_inclusive
624
+ neigh_table.loc[indices, 'inclusive_count_' + neigh_col] = n_inclusive
595
625
  if 'intermediate' in metrics:
596
- neigh_table.loc[indices, 'intermediate_count_'+neigh_col] = n_intermediate
626
+ neigh_table.loc[indices, 'intermediate_count_' + neigh_col] = n_intermediate
597
627
  if 'exclusive' in metrics:
598
- neigh_table.loc[indices, 'exclusive_count_'+neigh_col] = n_exclusive
628
+ neigh_table.loc[indices, 'exclusive_count_' + neigh_col] = n_exclusive
599
629
 
600
630
  if decompose_by_status:
601
631
  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
632
+ neigh_table.loc[indices, 'inclusive_count_s0_' + neigh_col] = n_inclusive_status_0
633
+ neigh_table.loc[indices, 'inclusive_count_s1_' + neigh_col] = n_inclusive_status_1
604
634
  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
635
+ neigh_table.loc[indices, 'intermediate_count_s0_' + neigh_col] = n_intermediate_status_0
636
+ neigh_table.loc[indices, 'intermediate_count_s1_' + neigh_col] = n_intermediate_status_1
637
+ if 'exclusive' in metrics:
638
+ neigh_table.loc[indices, 'exclusive_count_s0_' + neigh_col] = n_exclusive_status_0
639
+ neigh_table.loc[indices, 'exclusive_count_s1_' + neigh_col] = n_exclusive_status_1
610
640
 
611
641
  return neigh_table
612
642
 
613
- def mean_neighborhood_before_event(neigh_table, neigh_col, event_time_col, metrics=['inclusive','exclusive','intermediate']):
614
-
643
+
644
+ def mean_neighborhood_before_event(neigh_table, neigh_col, event_time_col,
645
+ metrics=['inclusive', 'exclusive', 'intermediate']):
615
646
  """
616
647
  Computes the mean neighborhood metrics for each cell track before a specified event time.
617
648
 
@@ -645,53 +676,59 @@ def mean_neighborhood_before_event(neigh_table, neigh_col, event_time_col, metri
645
676
  groupbycols = ['position']
646
677
  else:
647
678
  groupbycols = []
648
- if 'TRACK_ID' in list(neigh_table.columns):
649
- groupbycols.append('TRACK_ID')
650
- else:
651
- groupbycols.append('ID')
679
+
680
+ id_col = extract_identity_col(neigh_table)
681
+ groupbycols.append(id_col)
652
682
 
653
683
  neigh_table.sort_values(by=groupbycols+['FRAME'],inplace=True)
654
684
  suffix = '_before_event'
655
-
685
+
656
686
  if event_time_col is None:
657
687
  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()
688
+ neigh_table.loc[:, 'event_time_temp'] = neigh_table['FRAME'].max()
659
689
  event_time_col = 'event_time_temp'
660
690
  suffix = ''
661
-
662
- for tid,group in neigh_table.groupby(groupbycols):
691
+
692
+ for tid, group in neigh_table.groupby(groupbycols):
663
693
 
664
694
  group = group.dropna(subset=neigh_col)
665
695
  indices = list(group.index)
666
696
 
667
697
  event_time_values = group[event_time_col].to_numpy()
668
- if len(event_time_values)>0:
698
+ if len(event_time_values) > 0:
669
699
  event_time = event_time_values[0]
670
700
  else:
671
701
  continue
672
702
 
673
- if event_time<0.:
703
+ if event_time < 0.:
674
704
  event_time = group['FRAME'].max()
675
705
 
676
706
  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)
707
+ valid_counts_intermediate = group.loc[
708
+ group['FRAME'] <= event_time, 'intermediate_count_s1_' + neigh_col].to_numpy()
709
+ if len(valid_counts_intermediate[valid_counts_intermediate == valid_counts_intermediate]) > 0:
710
+ neigh_table.loc[indices, f'mean_count_intermediate_{neigh_col}{suffix}'] = np.nanmean(
711
+ valid_counts_intermediate)
680
712
  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)
713
+ valid_counts_inclusive = group.loc[
714
+ group['FRAME'] <= event_time, 'inclusive_count_s1_' + neigh_col].to_numpy()
715
+ if len(valid_counts_inclusive[valid_counts_inclusive == valid_counts_inclusive]) > 0:
716
+ neigh_table.loc[indices, f'mean_count_inclusive_{neigh_col}{suffix}'] = np.nanmean(
717
+ valid_counts_inclusive)
684
718
  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)
719
+ valid_counts_exclusive = group.loc[
720
+ group['FRAME'] <= event_time, 'exclusive_count_s1_' + neigh_col].to_numpy()
721
+ if len(valid_counts_exclusive[valid_counts_exclusive == valid_counts_exclusive]) > 0:
722
+ neigh_table.loc[indices, f'mean_count_exclusive_{neigh_col}{suffix}'] = np.nanmean(
723
+ valid_counts_exclusive)
688
724
 
689
- if event_time_col=='event_time_temp':
725
+ if event_time_col == 'event_time_temp':
690
726
  neigh_table = neigh_table.drop(columns='event_time_temp')
691
727
  return neigh_table
692
728
 
693
- def mean_neighborhood_after_event(neigh_table, neigh_col, event_time_col, metrics=['inclusive','exclusive','intermediate']):
694
-
729
+
730
+ def mean_neighborhood_after_event(neigh_table, neigh_col, event_time_col,
731
+ metrics=['inclusive', 'exclusive', 'intermediate']):
695
732
  """
696
733
  Computes the mean neighborhood metrics for each cell track after a specified event time.
697
734
 
@@ -725,55 +762,62 @@ def mean_neighborhood_after_event(neigh_table, neigh_col, event_time_col, metric
725
762
  groupbycols = ['position']
726
763
  else:
727
764
  groupbycols = []
728
- if 'TRACK_ID' in list(neigh_table.columns):
729
- groupbycols.append('TRACK_ID')
730
- else:
731
- groupbycols.append('ID')
765
+
766
+ id_col = extract_identity_col(neigh_table)
767
+ groupbycols.append(id_col)
732
768
 
733
769
  neigh_table.sort_values(by=groupbycols+['FRAME'],inplace=True)
734
770
  suffix = '_after_event'
735
-
771
+
736
772
  if event_time_col is None:
737
- neigh_table.loc[:,'event_time_temp'] = None #neigh_table['FRAME'].max()
773
+ neigh_table.loc[:, 'event_time_temp'] = None # neigh_table['FRAME'].max()
738
774
  event_time_col = 'event_time_temp'
739
775
  suffix = ''
740
-
741
- for tid,group in neigh_table.groupby(groupbycols):
776
+
777
+ for tid, group in neigh_table.groupby(groupbycols):
742
778
 
743
779
  group = group.dropna(subset=neigh_col)
744
780
  indices = list(group.index)
745
781
 
746
782
  event_time_values = group[event_time_col].to_numpy()
747
- if len(event_time_values)>0:
783
+ if len(event_time_values) > 0:
748
784
  event_time = event_time_values[0]
749
785
  else:
750
786
  continue
751
787
 
752
- if event_time is not None and (event_time>=0.):
788
+ if event_time is not None and (event_time >= 0.):
753
789
 
754
790
  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)
791
+ valid_counts_intermediate = group.loc[
792
+ group['FRAME'] > event_time, 'intermediate_count_s1_' + neigh_col].to_numpy()
793
+ if len(valid_counts_intermediate[valid_counts_intermediate == valid_counts_intermediate]) > 0:
794
+ neigh_table.loc[indices, f'mean_count_intermediate_{neigh_col}{suffix}'] = np.nanmean(
795
+ valid_counts_intermediate)
758
796
  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)
797
+ valid_counts_inclusive = group.loc[
798
+ group['FRAME'] > event_time, 'inclusive_count_s1_' + neigh_col].to_numpy()
799
+ if len(valid_counts_inclusive[valid_counts_inclusive == valid_counts_inclusive]) > 0:
800
+ neigh_table.loc[indices, f'mean_count_inclusive_{neigh_col}{suffix}'] = np.nanmean(
801
+ valid_counts_inclusive)
762
802
  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)
803
+ valid_counts_exclusive = group.loc[
804
+ group['FRAME'] > event_time, 'exclusive_count_s1_' + neigh_col].to_numpy()
805
+ if len(valid_counts_exclusive[valid_counts_exclusive == valid_counts_exclusive]) > 0:
806
+ neigh_table.loc[indices, f'mean_count_exclusive_{neigh_col}{suffix}'] = np.nanmean(
807
+ valid_counts_exclusive)
766
808
 
767
- if event_time_col=='event_time_temp':
809
+ if event_time_col == 'event_time_temp':
768
810
  neigh_table = neigh_table.drop(columns='event_time_temp')
769
811
 
770
812
  return neigh_table
771
813
 
814
+
772
815
  # New functions for direct cell-cell contact neighborhood
773
816
 
774
817
  def sign(num):
775
818
  return -1 if num < 0 else 1
776
819
 
820
+
777
821
  def contact_neighborhood(labelsA, labelsB=None, border=3, connectivity=2):
778
822
 
779
823
  labelsA = labelsA.astype(float)
@@ -781,50 +825,50 @@ def contact_neighborhood(labelsA, labelsB=None, border=3, connectivity=2):
781
825
  labelsB = labelsB.astype(float)
782
826
 
783
827
  print(f"Border = {border}")
784
-
828
+
785
829
  if border > 0:
786
830
  print(labelsA.shape, border * (-1))
787
831
  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)]
832
+ labelsA[np.where(labelsA_edge > 0)] = labelsA_edge[np.where(labelsA_edge > 0)]
789
833
  if labelsB is not None:
790
834
  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
-
835
+ labelsB[np.where(labelsB_edge > 0)] = labelsB_edge[np.where(labelsB_edge > 0)]
836
+
793
837
  if labelsB is not None:
794
- labelsA[labelsA!=0] = -labelsA[labelsA!=0]
838
+ labelsA[labelsA != 0] = -labelsA[labelsA != 0]
795
839
  labelsAB = merge_labels(labelsA, labelsB)
796
840
  labelsBA = merge_labels(labelsB, labelsA)
797
841
  label_cases = [labelsAB, labelsBA]
798
842
  else:
799
843
  label_cases = [labelsA]
800
-
844
+
801
845
  coocurrences = []
802
846
  for lbl in label_cases:
803
847
  coocurrences.extend(find_contact_neighbors(lbl, connectivity=connectivity))
804
-
805
- unique_pairs = np.unique(coocurrences,axis=0)
806
-
848
+
849
+ unique_pairs = np.unique(coocurrences, axis=0)
850
+
807
851
  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)
852
+ neighs = np.unique([tuple(sorted(p)) for p in unique_pairs if p[0] != p[1] and sign(p[0]) != sign(p[1])],
853
+ axis=0)
809
854
  else:
810
- neighs = np.unique([tuple(sorted(p)) for p in unique_pairs if p[0]!=p[1]],axis=0)
811
-
855
+ neighs = np.unique([tuple(sorted(p)) for p in unique_pairs if p[0] != p[1]], axis=0)
856
+
812
857
  return neighs
813
858
 
814
859
  def merge_labels(labelsA, labelsB):
815
-
816
860
  labelsA = labelsA.astype(float)
817
861
  labelsB = labelsB.astype(float)
818
-
862
+
819
863
  labelsAB = labelsA.copy()
820
- labelsAB[np.where(labelsB!=0)] = labelsB[np.where(labelsB!=0)]
821
-
864
+ labelsAB[np.where(labelsB != 0)] = labelsB[np.where(labelsB != 0)]
865
+
822
866
  return labelsAB
823
867
 
868
+
824
869
  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)
870
+ assert labels.ndim == 2, "Wrong dimension for labels..."
871
+ g, nodes = pixel_graph(labels, mask=labels.astype(bool), connectivity=connectivity)
828
872
  g.eliminate_zeros()
829
873
 
830
874
  coo = g.tocoo()
@@ -833,18 +877,19 @@ def find_contact_neighbors(labels, connectivity=2):
833
877
 
834
878
  center_values = labels.ravel()[center_coords]
835
879
  neighbor_values = labels.ravel()[neighbor_coords]
836
- touching_masks = np.column_stack((center_values, neighbor_values))
837
-
880
+ touching_masks = np.column_stack((center_values, neighbor_values))
881
+
838
882
  return touching_masks
839
883
 
840
884
 
841
- def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-pop', status=None, not_status_option=None, compute_cum_sum=True,
885
+ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-pop', status=None,
886
+ not_status_option=None, compute_cum_sum=True,
842
887
  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
-
888
+ column_labels={'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y',
889
+ 'mask_id': 'class_id'}):
845
890
  """
846
891
 
847
- Match neighbors in set A and B within a circle of radius d.
892
+ Match neighbors in set A and B within a circle of radius d.
848
893
 
849
894
  Parameters
850
895
  ----------
@@ -854,7 +899,7 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
854
899
  Cut-distance in pixels to match neighboring pairs.
855
900
  mode: str
856
901
  neighboring mode, between 'two-pop' (e.g. target-effector) and 'self' (target-target or effector-effector).
857
- status: None or status
902
+ status: None or status
858
903
  name to look for cells to ignore (because they are dead). By default all cells are kept.
859
904
  compute_cum_sum: bool,
860
905
  compute cumulated time of presence of neighbours (only if trajectories available for both sets)
@@ -870,57 +915,59 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
870
915
  if setA is not None and setB is not None:
871
916
  setA, setB, status = set_live_status(setA, setB, status, not_status_option)
872
917
  else:
873
- return None,None
918
+ return None, None
874
919
 
875
- # Check distance option
920
+ # Check distance option
876
921
  if not isinstance(distance, list):
877
922
  distance = [distance]
878
-
923
+
879
924
  for d in distance:
880
925
  # loop over each provided distance
881
-
882
- if mode=='two-pop':
926
+
927
+ if mode == 'two-pop':
883
928
  neigh_col = f'neighborhood_2_contact_{d}_px'
884
- elif mode=='self':
929
+ elif mode == 'self':
885
930
  neigh_col = f'neighborhood_self_contact_{d}_px'
886
-
931
+
887
932
  cl = []
888
- for s in [setA,setB]:
933
+ for s in [setA, setB]:
889
934
 
890
935
  # Check whether data can be tracked
891
936
  temp_column_labels = column_labels.copy()
892
937
 
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
938
+ id_col = extract_identity_col(s)
939
+ temp_column_labels.update({'track': id_col})
940
+ if id_col=='ID':
941
+ compute_cum_sum = False # if no tracking data then cum_sum is not relevant
896
942
  cl.append(temp_column_labels)
897
943
 
898
944
  # Remove nan tracks (cells that do not belong to a track)
899
945
  s[neigh_col] = np.nan
900
946
  s[neigh_col] = s[neigh_col].astype(object)
901
- s.dropna(subset=[cl[-1]['track']],inplace=True)
947
+ s.dropna(subset=[cl[-1]['track']], inplace=True)
902
948
 
903
949
  # 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)
950
+ timeline = np.unique(np.concatenate([setA[cl[0]['time']].to_numpy(), setB[cl[1]['time']].to_numpy()])).astype(
951
+ int)
905
952
  for t in tqdm(timeline):
906
953
 
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
-
954
+ index_A = list(setA.loc[setA[cl[0]['time']] == t].index)
955
+ coordinates_A = setA.loc[setA[cl[0]['time']] == t, [cl[0]['x'], cl[0]['y']]].to_numpy()
956
+ ids_A = setA.loc[setA[cl[0]['time']] == t, cl[0]['track']].to_numpy()
957
+ mask_ids_A = setA.loc[setA[cl[0]['time']] == t, cl[0]['mask_id']].to_numpy()
958
+ status_A = setA.loc[setA[cl[0]['time']] == t, status[0]].to_numpy()
959
+
960
+ index_B = list(setB.loc[setB[cl[1]['time']] == t].index)
961
+ coordinates_B = setB.loc[setB[cl[1]['time']] == t, [cl[1]['x'], cl[1]['y']]].to_numpy()
962
+ ids_B = setB.loc[setB[cl[1]['time']] == t, cl[1]['track']].to_numpy()
963
+ mask_ids_B = setB.loc[setB[cl[1]['time']] == t, cl[1]['mask_id']].to_numpy()
964
+ status_B = setB.loc[setB[cl[1]['time']] == t, status[1]].to_numpy()
965
+
919
966
  print(f"Frame {t}")
920
- print(f"{mask_ids_A=}",f"{mask_ids_B}")
967
+ print(f"{mask_ids_A=}", f"{mask_ids_B}")
921
968
 
922
969
  if len(ids_A) > 0 and len(ids_B) > 0:
923
-
970
+
924
971
  # compute distance matrix
925
972
  dist_map = cdist(coordinates_A, coordinates_B, metric="euclidean")
926
973
 
@@ -931,21 +978,21 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
931
978
  lblB = labelsB
932
979
 
933
980
  print(f"Distance {d} for contact as border")
934
- contact_pairs = contact_neighborhood(labelsA[t], labelsB=lblB, border=d, connectivity=2)
981
+ contact_pairs = contact_neighborhood(labelsA[t], labelsB=lblB, border=d, connectivity=2)
935
982
 
936
983
  print(t, f"{np.unique(labelsA[t])=}")
937
984
  print(f"Frame {t}: found the following contact pairs: {contact_pairs}...")
938
985
  # Put infinite distance to all non-contact pairs (something like this)
939
- plot_map=False
986
+ plot_map = False
940
987
 
941
- if len(contact_pairs)>0:
988
+ if len(contact_pairs) > 0:
942
989
  mask = np.ones_like(dist_map).astype(bool)
943
-
990
+
944
991
  indices_to_keep = []
945
992
  for cp in contact_pairs:
946
-
947
- if np.any(cp<0):
948
- if cp[0]<0:
993
+
994
+ if np.any(cp < 0):
995
+ if cp[0] < 0:
949
996
  mask_A = cp[1]
950
997
  mask_B = np.abs(cp[0])
951
998
  else:
@@ -957,8 +1004,8 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
957
1004
 
958
1005
  try:
959
1006
 
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]
1007
+ idx_A = np.where(mask_ids_A == int(mask_A))[0][0]
1008
+ idx_B = np.where(mask_ids_B == int(mask_B))[0][0]
962
1009
  print(idx_A, idx_B)
963
1010
  indices_to_keep.append([idx_A,idx_B])
964
1011
  except Exception as e:
@@ -966,17 +1013,17 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
966
1013
  pass
967
1014
 
968
1015
  print(f'Indices to keep: {indices_to_keep}...')
969
- if len(indices_to_keep)>0:
1016
+ if len(indices_to_keep) > 0:
970
1017
  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
1018
+ mask[indices_to_keep[:, 0], indices_to_keep[:, 1]] = False
1019
+ if mode == 'self':
1020
+ mask[indices_to_keep[:, 1], indices_to_keep[:, 0]] = False
974
1021
  dist_map[mask] = 1.0E06
975
1022
  plot_map=True
976
1023
  else:
977
1024
  dist_map[:,:] = 1.0E06
978
1025
  else:
979
- dist_map[:,:] = 1.0E06
1026
+ dist_map[:, :] = 1.0E06
980
1027
 
981
1028
  # PROCEED all the same?? --> I guess so
982
1029
  # if plot_map:
@@ -988,62 +1035,63 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
988
1035
 
989
1036
  d_filter = 1.0E05
990
1037
  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
-
1038
+ weights, closest_A = compute_attention_weight(dist_map, d_filter, status_A, ids_A, axis=1,
1039
+ include_dead_weight=include_dead_weight)
1040
+
993
1041
  # Target centric
994
1042
  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
-
1043
+
1044
+ col = dist_map[k, :]
1045
+ col[col == 0.] = 1.0E06
1046
+
1047
+ neighs_B = np.array([ids_B[i] for i in np.where((col <= d_filter))[0]])
1048
+ status_neigh_B = np.array([status_B[i] for i in np.where((col <= d_filter))[0]])
1049
+ dist_B = [round(col[i], 2) for i in np.where((col <= d_filter))[0]]
1050
+ if len(dist_B) > 0:
1051
+ closest_B_cell = neighs_B[np.argmin(dist_B)]
1052
+
1005
1053
  if symmetrize and attention_weight:
1006
1054
  n_neighs = float(len(neighs_B))
1007
1055
  if not include_dead_weight:
1008
- n_neighs_alive = len(np.where(status_neigh_B==1)[0])
1056
+ n_neighs_alive = len(np.where(status_neigh_B == 1)[0])
1009
1057
  neigh_count = n_neighs_alive
1010
1058
  else:
1011
1059
  neigh_count = n_neighs
1012
- if neigh_count>0:
1013
- weight_A = 1./neigh_count
1060
+ if neigh_count > 0:
1061
+ weight_A = 1. / neigh_count
1014
1062
  else:
1015
1063
  weight_A = np.nan
1016
1064
 
1017
- if not include_dead_weight and status_A[k]==0:
1065
+ if not include_dead_weight and status_A[k] == 0:
1018
1066
  weight_A = 0
1019
-
1067
+
1020
1068
  neighs = []
1021
1069
  setA.at[index_A[k], neigh_col] = []
1022
1070
  for n in range(len(neighs_B)):
1023
-
1071
+
1024
1072
  # index in setB
1025
- n_index = np.where(ids_B==neighs_B[n])[0][0]
1073
+ n_index = np.where(ids_B == neighs_B[n])[0][0]
1026
1074
  # Assess if neigh B is closest to A
1027
1075
  if attention_weight:
1028
- if closest_A[n_index]==ids_A[k]:
1076
+ if closest_A[n_index] == ids_A[k]:
1029
1077
  closest = True
1030
1078
  else:
1031
1079
  closest = False
1032
-
1080
+
1033
1081
  if symmetrize:
1034
1082
  # Load neighborhood previous data
1035
1083
  sym_neigh = setB.loc[index_B[n_index], neigh_col]
1036
- if neighs_B[n]==closest_B_cell:
1037
- closest_b=True
1084
+ if neighs_B[n] == closest_B_cell:
1085
+ closest_b = True
1038
1086
  else:
1039
- closest_b=False
1087
+ closest_b = False
1040
1088
  if isinstance(sym_neigh, list):
1041
1089
  sym_neigh.append({'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k]})
1042
1090
  else:
1043
- sym_neigh = [{'id': ids_A[k], 'distance': dist_B[n],'status': status_A[k]}]
1091
+ sym_neigh = [{'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k]}]
1044
1092
  if attention_weight:
1045
1093
  sym_neigh[-1].update({'weight': weight_A, 'closest': closest_b})
1046
-
1094
+
1047
1095
  # Write the minimum info about neighborhing cell B
1048
1096
  neigh_dico = {'id': neighs_B[n], 'distance': dist_B[n], 'status': status_neigh_B[n]}
1049
1097
  if attention_weight:
@@ -1051,33 +1099,43 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
1051
1099
 
1052
1100
  if compute_cum_sum:
1053
1101
  # 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()]
1102
+ assert cl[1][
1103
+ 'track'] == 'TRACK_ID', 'The set B does not seem to contain tracked data. The cumulative time will be meaningless.'
1104
+ past_neighs = [[ll['id'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
1105
+ (setA[cl[0]['track']] == ids_A[k]) & (setA[cl[0]['time']] <= t), neigh_col].to_numpy()]
1056
1106
  past_neighs = [item for sublist in past_neighs for item in sublist]
1057
-
1107
+
1058
1108
  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()]
1109
+ past_weights = [[ll['weight'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
1110
+ (setA[cl[0]['track']] == ids_A[k]) & (
1111
+ setA[cl[0]['time']] <= t), neigh_col].to_numpy()]
1060
1112
  past_weights = [item for sublist in past_weights for item in sublist]
1061
1113
 
1062
- cum_sum = len(np.where(past_neighs==neighs_B[n])[0])
1063
- neigh_dico.update({'cumulated_presence': cum_sum+1})
1064
-
1114
+ cum_sum = len(np.where(past_neighs == neighs_B[n])[0])
1115
+ neigh_dico.update({'cumulated_presence': cum_sum + 1})
1116
+
1065
1117
  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)])
1118
+ cum_sum_weighted = np.sum(
1119
+ [w if l == neighs_B[n] else 0 for l, w in zip(past_neighs, past_weights)])
1067
1120
  neigh_dico.update({'cumulated_presence_weighted': cum_sum_weighted + weights[n_index]})
1068
1121
 
1069
1122
  if symmetrize:
1070
1123
  setB.at[index_B[n_index], neigh_col] = sym_neigh
1071
-
1124
+
1072
1125
  neighs.append(neigh_dico)
1073
-
1126
+
1074
1127
  setA.at[index_A[k], neigh_col] = neighs
1075
-
1128
+
1076
1129
  return setA, setB
1077
1130
 
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
-
1131
+
1132
+ def compute_contact_neighborhood_at_position(pos, distance, population=['targets', 'effectors'], theta_dist=None,
1133
+ img_shape=(2048, 2048), return_tables=False, clear_neigh=False,
1134
+ event_time_col=None,
1135
+ neighborhood_kwargs={'mode': 'two-pop', 'status': None,
1136
+ 'not_status_option': None,
1137
+ 'include_dead_weight': True, "compute_cum_sum": False,
1138
+ "attention_weight": True, 'symmetrize': True}):
1081
1139
  """
1082
1140
  Computes neighborhood metrics for specified cell populations within a given position, based on distance criteria and additional parameters.
1083
1141
 
@@ -1115,12 +1173,12 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1115
1173
  ------
1116
1174
  AssertionError
1117
1175
  If the specified position path does not exist or if the number of distances and edge thresholds do not match.
1118
-
1176
+
1119
1177
  """
1120
1178
 
1121
- pos = pos.replace('\\','/')
1179
+ pos = pos.replace('\\', '/')
1122
1180
  pos = rf"{pos}"
1123
- assert os.path.exists(pos),f'Position {pos} is not a valid path.'
1181
+ assert os.path.exists(pos), f'Position {pos} is not a valid path.'
1124
1182
 
1125
1183
  if isinstance(population, str):
1126
1184
  population = [population, population]
@@ -1131,12 +1189,12 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1131
1189
  theta_dist = [theta_dist]
1132
1190
 
1133
1191
  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.'
1192
+ theta_dist = [0 for d in distance] # 0.9*d
1193
+ assert len(theta_dist) == len(distance), 'Incompatible number of distances and number of edge thresholds.'
1136
1194
 
1137
- if population[0]==population[1]:
1195
+ if population[0] == population[1]:
1138
1196
  neighborhood_kwargs.update({'mode': 'self'})
1139
- if population[1]!=population[0]:
1197
+ if population[1] != population[0]:
1140
1198
  neighborhood_kwargs.update({'mode': 'two-pop'})
1141
1199
 
1142
1200
  df_A, path_A = get_position_table(pos, population=population[0], return_path=True)
@@ -1144,6 +1202,15 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1144
1202
  if df_A is None or df_B is None:
1145
1203
  return None
1146
1204
 
1205
+ if clear_neigh:
1206
+ if os.path.exists(path_A.replace('.csv','.pkl')):
1207
+ os.remove(path_A.replace('.csv','.pkl'))
1208
+ if os.path.exists(path_B.replace('.csv','.pkl')):
1209
+ os.remove(path_B.replace('.csv','.pkl'))
1210
+ df_pair, pair_path = get_position_table(pos, population='pairs', return_path=True)
1211
+ if df_pair is not None:
1212
+ os.remove(pair_path)
1213
+
1147
1214
  df_A_pkl = get_position_pickle(pos, population=population[0], return_path=False)
1148
1215
  df_B_pkl = get_position_pickle(pos, population=population[1], return_path=False)
1149
1216
 
@@ -1152,12 +1219,9 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1152
1219
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
1153
1220
  cols = list(pkl_columns[neigh_columns]) + ['FRAME']
1154
1221
 
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']
1222
+ id_col = extract_identity_col(df_A_pkl)
1223
+ cols.append(id_col)
1224
+ on_cols = [id_col, 'FRAME']
1161
1225
 
1162
1226
  print(f'Recover {cols} from the pickle file...')
1163
1227
  try:
@@ -1171,12 +1235,9 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1171
1235
  neigh_columns = np.array([c.startswith('neighborhood') for c in pkl_columns])
1172
1236
  cols = list(pkl_columns[neigh_columns]) + ['FRAME']
1173
1237
 
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']
1238
+ id_col = extract_identity_col(df_B_pkl)
1239
+ cols.append(id_col)
1240
+ on_cols = [id_col, 'FRAME']
1180
1241
 
1181
1242
  print(f'Recover {cols} from the pickle file...')
1182
1243
  try:
@@ -1185,7 +1246,7 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1185
1246
  print(f'Failure to merge pickle and csv files: {e}')
1186
1247
 
1187
1248
  labelsA = locate_labels(pos, population=population[0])
1188
- if population[1]==population[0]:
1249
+ if population[1] == population[0]:
1189
1250
  labelsB = None
1190
1251
  else:
1191
1252
  labelsB = locate_labels(pos, population=population[1])
@@ -1194,18 +1255,18 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1194
1255
  unwanted = df_A.columns[df_A.columns.str.contains('neighborhood')]
1195
1256
  df_A = df_A.drop(columns=unwanted)
1196
1257
  unwanted = df_B.columns[df_B.columns.str.contains('neighborhood')]
1197
- df_B = df_B.drop(columns=unwanted)
1258
+ df_B = df_B.drop(columns=unwanted)
1198
1259
 
1199
1260
  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:
1261
+ df_A, df_B = mask_contact_neighborhood(df_A, df_B, labelsA, labelsB, distance, **neighborhood_kwargs)
1262
+ if df_A is None or df_B is None or len(df_A)==0:
1202
1263
  return None
1203
1264
 
1204
- for td,d in zip(theta_dist, distance):
1265
+ for td, d in zip(theta_dist, distance):
1205
1266
 
1206
- if neighborhood_kwargs['mode']=='two-pop':
1267
+ if neighborhood_kwargs['mode'] == 'two-pop':
1207
1268
  neigh_col = f'neighborhood_2_contact_{d}_px'
1208
- elif neighborhood_kwargs['mode']=='self':
1269
+ elif neighborhood_kwargs['mode'] == 'self':
1209
1270
  neigh_col = f'neighborhood_self_contact_{d}_px'
1210
1271
 
1211
1272
  df_A.loc[df_A['class_id'].isnull(),neigh_col] = np.nan
@@ -1215,26 +1276,34 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1215
1276
  # df_A.loc[~edge_filter_A, neigh_col] = np.nan
1216
1277
  # df_B.loc[~edge_filter_B, neigh_col] = np.nan
1217
1278
 
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'))
1279
+ df_A = compute_neighborhood_metrics(df_A, neigh_col, metrics=['inclusive', 'intermediate'],
1280
+ decompose_by_status=True)
1281
+ if 'TRACK_ID' in list(df_A.columns):
1282
+ if not np.all(df_A['TRACK_ID'].isnull()):
1283
+ df_A = mean_neighborhood_before_event(df_A, neigh_col, event_time_col, metrics=['inclusive','intermediate'])
1284
+ if event_time_col is not None:
1285
+ df_A = mean_neighborhood_after_event(df_A, neigh_col, event_time_col, metrics=['inclusive', 'intermediate'])
1286
+ print('Done...')
1287
+
1288
+ df_A.to_pickle(path_A.replace('.csv', '.pkl'))
1289
+ if not population[0] == population[1]:
1290
+ # Remove neighborhood column
1291
+ for td, d in zip(theta_dist, distance):
1292
+ if neighborhood_kwargs['mode'] == 'two-pop':
1293
+ neigh_col = f'neighborhood_2_contact_{d}_px'
1294
+ elif neighborhood_kwargs['mode'] == 'self':
1295
+ neigh_col = f'neighborhood_self_contact_{d}_px'
1296
+ df_B = df_B.drop(columns=[neigh_col])
1297
+ df_B.to_pickle(path_B.replace('.csv', '.pkl'))
1229
1298
 
1230
1299
  unwanted = df_A.columns[df_A.columns.str.startswith('neighborhood_')]
1231
1300
  df_A2 = df_A.drop(columns=unwanted)
1232
1301
  df_A2.to_csv(path_A, index=False)
1233
1302
 
1234
- if not population[0]==population[1]:
1303
+ if not population[0] == population[1]:
1235
1304
  unwanted = df_B.columns[df_B.columns.str.startswith('neighborhood_')]
1236
1305
  df_B_csv = df_B.drop(unwanted, axis=1, inplace=False)
1237
- df_B_csv.to_csv(path_B,index=False)
1306
+ df_B_csv.to_csv(path_B, index=False)
1238
1307
 
1239
1308
  if return_tables:
1240
1309
  return df_A, df_B
@@ -1249,10 +1318,15 @@ if __name__ == "__main__":
1249
1318
 
1250
1319
  print('None')
1251
1320
  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)
1321
+
1322
+ test, _ = compute_neighborhood_at_position(pos, [62], population=['targets', 'effectors'], theta_dist=None,
1323
+ img_shape=(2048, 2048), return_tables=True, clear_neigh=True,
1324
+ neighborhood_kwargs={'mode': 'two-pop', 'status': ['class', None],
1325
+ 'not_status_option': [True, False],
1326
+ 'include_dead_weight': True,
1327
+ "compute_cum_sum": False, "attention_weight": True,
1328
+ 'symmetrize': False})
1329
+
1330
+ # test = compute_neighborhood_metrics(test, 'neighborhood_self_circle_150_px', metrics=['inclusive','exclusive','intermediate'], decompose_by_status=True)
1257
1331
  print(test.columns)
1258
1332
  #print(segment(None,'test'))