celldetective 1.3.9.post5__py3-none-any.whl → 1.4.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 (94) hide show
  1. celldetective/__init__.py +0 -3
  2. celldetective/_version.py +1 -1
  3. celldetective/events.py +2 -4
  4. celldetective/exceptions.py +11 -0
  5. celldetective/extra_properties.py +132 -0
  6. celldetective/filters.py +7 -1
  7. celldetective/gui/InitWindow.py +37 -46
  8. celldetective/gui/__init__.py +3 -9
  9. celldetective/gui/about.py +19 -15
  10. celldetective/gui/analyze_block.py +34 -19
  11. celldetective/gui/base_annotator.py +786 -0
  12. celldetective/gui/base_components.py +23 -0
  13. celldetective/gui/classifier_widget.py +86 -94
  14. celldetective/gui/configure_new_exp.py +163 -46
  15. celldetective/gui/control_panel.py +76 -146
  16. celldetective/gui/{signal_annotator.py → event_annotator.py} +533 -1438
  17. celldetective/gui/generic_signal_plot.py +11 -13
  18. celldetective/gui/gui_utils.py +54 -23
  19. celldetective/gui/help/neighborhood.json +2 -2
  20. celldetective/gui/json_readers.py +5 -4
  21. celldetective/gui/layouts.py +265 -31
  22. celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +433 -635
  23. celldetective/gui/plot_measurements.py +21 -17
  24. celldetective/gui/plot_signals_ui.py +125 -72
  25. celldetective/gui/process_block.py +283 -188
  26. celldetective/gui/processes/compute_neighborhood.py +594 -0
  27. celldetective/gui/processes/downloader.py +37 -34
  28. celldetective/gui/processes/measure_cells.py +19 -8
  29. celldetective/gui/processes/segment_cells.py +47 -11
  30. celldetective/gui/processes/track_cells.py +18 -13
  31. celldetective/gui/seg_model_loader.py +21 -62
  32. celldetective/gui/settings/__init__.py +7 -0
  33. celldetective/gui/settings/_settings_base.py +70 -0
  34. celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +54 -109
  35. celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +54 -92
  36. celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +10 -13
  37. celldetective/gui/settings/_settings_segmentation.py +49 -0
  38. celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +38 -92
  39. celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +78 -103
  40. celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +85 -116
  41. celldetective/gui/styles.py +2 -1
  42. celldetective/gui/survival_ui.py +49 -95
  43. celldetective/gui/tableUI.py +53 -25
  44. celldetective/gui/table_ops/__init__.py +0 -0
  45. celldetective/gui/table_ops/merge_groups.py +118 -0
  46. celldetective/gui/thresholds_gui.py +617 -1221
  47. celldetective/gui/viewers.py +107 -42
  48. celldetective/gui/workers.py +8 -4
  49. celldetective/io.py +137 -57
  50. celldetective/links/zenodo.json +145 -144
  51. celldetective/measure.py +94 -53
  52. celldetective/neighborhood.py +342 -268
  53. celldetective/preprocessing.py +56 -35
  54. celldetective/regionprops/_regionprops.py +16 -5
  55. celldetective/relative_measurements.py +50 -29
  56. celldetective/scripts/analyze_signals.py +4 -1
  57. celldetective/scripts/measure_cells.py +5 -5
  58. celldetective/scripts/measure_relative.py +20 -12
  59. celldetective/scripts/segment_cells.py +4 -10
  60. celldetective/scripts/segment_cells_thresholds.py +3 -3
  61. celldetective/scripts/track_cells.py +10 -8
  62. celldetective/scripts/train_segmentation_model.py +18 -6
  63. celldetective/signals.py +29 -14
  64. celldetective/tracking.py +14 -3
  65. celldetective/utils.py +91 -62
  66. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/METADATA +24 -16
  67. celldetective-1.4.1.dist-info/RECORD +123 -0
  68. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/WHEEL +1 -1
  69. tests/gui/__init__.py +0 -0
  70. tests/gui/test_new_project.py +228 -0
  71. tests/gui/test_project.py +99 -0
  72. tests/test_preprocessing.py +2 -2
  73. celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
  74. celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  75. celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
  76. celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
  77. celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  78. celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
  79. celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
  80. celldetective/models/signal_detection/NucCond/config_input.json +0 -1
  81. celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
  82. celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
  83. celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
  84. celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
  85. celldetective/models/signal_detection/NucCond/scores.npy +0 -0
  86. celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
  87. celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
  88. celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
  89. celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
  90. celldetective-1.3.9.post5.dist-info/RECORD +0 -129
  91. tests/test_qt.py +0 -103
  92. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/entry_points.txt +0 -0
  93. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info/licenses}/LICENSE +0 -0
  94. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/top_level.txt +0 -0
@@ -9,6 +9,281 @@ from celldetective.io import locate_labels, get_position_pickle, get_position_ta
9
9
 
10
10
  abs_path = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0], 'celldetective'])
11
11
 
12
+ def _fill_distance_neighborhood_at_t(time_index, setA, setB, dist_map, attention_weight=None, include_dead_weight=False, symmetrize=False, compute_cum_sum=False,
13
+ weights=None, closest_A=None, neigh_col="", column_labelsA=None, column_labelsB=None, statusA=None, statusB=None, distance=10):
14
+
15
+ index_A = list(setA.loc[setA[column_labelsA['time']] == time_index].index)
16
+ index_B = list(setB.loc[setB[column_labelsB['time']] == time_index].index)
17
+
18
+ dataA = setA.loc[setA[column_labelsA['time']] == time_index, [column_labelsA['x'], column_labelsA['y'], column_labelsA['track'],statusA]].to_numpy()
19
+
20
+ ids_A = dataA[:, 2]
21
+ status_A = dataA[:, 3]
22
+
23
+ dataB = setB.loc[setB[column_labelsB['time']] == time_index, [column_labelsB['x'], column_labelsB['y'], column_labelsB['track'],statusB]].to_numpy()
24
+ ids_B = dataB[:, 2]
25
+ status_B = dataB[:, 3]
26
+
27
+ for k in range(dist_map.shape[0]):
28
+
29
+ col = dist_map[k, :]
30
+ col[col == 0.] = 1.0E06
31
+
32
+ neighs_B = np.array([ids_B[i] for i in np.where((col <= distance))[0]])
33
+ status_neigh_B = np.array([status_B[i] for i in np.where((col <= distance))[0]])
34
+ dist_B = [round(col[i], 2) for i in np.where((col <= distance))[0]]
35
+ if len(dist_B) > 0:
36
+ closest_B_cell = neighs_B[np.argmin(dist_B)]
37
+
38
+ if symmetrize and attention_weight:
39
+ n_neighs = float(len(neighs_B))
40
+ if not include_dead_weight:
41
+ n_neighs_alive = len(np.where(status_neigh_B == 1)[0])
42
+ neigh_count = n_neighs_alive
43
+ else:
44
+ neigh_count = n_neighs
45
+ if neigh_count > 0:
46
+ weight_A = 1. / neigh_count
47
+ else:
48
+ weight_A = np.nan
49
+
50
+ if not include_dead_weight and status_A[k] == 0:
51
+ weight_A = 0
52
+
53
+ neighs = []
54
+ setA.at[index_A[k], neigh_col] = []
55
+ for n in range(len(neighs_B)):
56
+
57
+ # index in setB
58
+ n_index = np.where(ids_B == neighs_B[n])[0][0]
59
+ # Assess if neigh B is closest to A
60
+ if attention_weight:
61
+ if closest_A[n_index] == ids_A[k]:
62
+ closest = True
63
+ else:
64
+ closest = False
65
+
66
+ if symmetrize:
67
+ # Load neighborhood previous data
68
+ sym_neigh = setB.loc[index_B[n_index], neigh_col]
69
+ if neighs_B[n] == closest_B_cell:
70
+ closest_b = True
71
+ else:
72
+ closest_b = False
73
+ if isinstance(sym_neigh, list):
74
+ sym_neigh.append({'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k]})
75
+ else:
76
+ sym_neigh = [{'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k]}]
77
+ if attention_weight:
78
+ sym_neigh[-1].update({'weight': weight_A, 'closest': closest_b})
79
+
80
+ # Write the minimum info about neighborhing cell B
81
+ neigh_dico = {'id': neighs_B[n], 'distance': dist_B[n], 'status': status_neigh_B[n]}
82
+ if attention_weight:
83
+ neigh_dico.update({'weight': weights[n_index], 'closest': closest})
84
+
85
+ if compute_cum_sum:
86
+ # Compute the integrated presence of the neighboring cell B
87
+ assert column_labelsB[
88
+ 'track'] == 'TRACK_ID', 'The set B does not seem to contain tracked data. The cumulative time will be meaningless.'
89
+ past_neighs = [[ll['id'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
90
+ (setA[column_labelsA['track']] == ids_A[k]) & (setA[column_labelsA['time']] <= time_index), neigh_col].to_numpy()]
91
+ past_neighs = [item for sublist in past_neighs for item in sublist]
92
+
93
+ if attention_weight:
94
+ past_weights = [[ll['weight'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
95
+ (setA[column_labelsA['track']] == ids_A[k]) & (
96
+ setA[column_labelsA['time']] <= time_index), neigh_col].to_numpy()]
97
+ past_weights = [item for sublist in past_weights for item in sublist]
98
+
99
+ cum_sum = len(np.where(past_neighs == neighs_B[n])[0])
100
+ neigh_dico.update({'cumulated_presence': cum_sum + 1})
101
+
102
+ if attention_weight:
103
+ cum_sum_weighted = np.sum(
104
+ [w if l == neighs_B[n] else 0 for l, w in zip(past_neighs, past_weights)])
105
+ neigh_dico.update({'cumulated_presence_weighted': cum_sum_weighted + weights[n_index]})
106
+
107
+ if symmetrize:
108
+ setB.at[index_B[n_index], neigh_col] = sym_neigh
109
+
110
+ neighs.append(neigh_dico)
111
+
112
+ setA.at[index_A[k], neigh_col] = neighs
113
+
114
+ def _fill_contact_neighborhood_at_t(time_index, setA, setB, dist_map, intersection_map=None, attention_weight=None, include_dead_weight=False, symmetrize=False, compute_cum_sum=False,
115
+ weights=None, closest_A=None, neigh_col="", column_labelsA=None, column_labelsB=None, statusA=None, statusB=None, d_filter=10):
116
+
117
+ index_A = list(setA.loc[setA[column_labelsA['time']] == time_index].index)
118
+ index_B = list(setB.loc[setB[column_labelsB['time']] == time_index].index)
119
+
120
+ dataA = setA.loc[setA[column_labelsA['time']] == time_index, [column_labelsA['x'], column_labelsA['y'], column_labelsA['track'], column_labelsA['mask_id'],
121
+ statusA]].to_numpy()
122
+
123
+ ids_A = dataA[:, 2]
124
+ status_A = dataA[:, 4]
125
+
126
+ dataB = setB.loc[setB[column_labelsB['time']] == time_index, [column_labelsB['x'], column_labelsB['y'], column_labelsB['track'], column_labelsB['mask_id'],
127
+ statusB]].to_numpy()
128
+ ids_B = dataB[:, 2]
129
+ status_B = dataB[:, 4]
130
+
131
+ for k in range(dist_map.shape[0]):
132
+
133
+ col = dist_map[k, :]
134
+ col_inter = intersection_map[k, :]
135
+ col[col == 0.] = 1.0E06
136
+
137
+ neighs_B = np.array([ids_B[i] for i in np.where((col <= d_filter))[0]])
138
+ status_neigh_B = np.array([status_B[i] for i in np.where((col <= d_filter))[0]])
139
+ dist_B = [round(col[i], 2) for i in np.where((col <= d_filter))[0]]
140
+ intersect_B = [round(col_inter[i], 2) for i in np.where((col <= d_filter))[0]]
141
+
142
+ if len(dist_B) > 0:
143
+ closest_B_cell = neighs_B[np.argmin(dist_B)]
144
+
145
+ if symmetrize and attention_weight:
146
+ n_neighs = float(len(neighs_B))
147
+ if not include_dead_weight:
148
+ n_neighs_alive = len(np.where(status_neigh_B == 1)[0])
149
+ neigh_count = n_neighs_alive
150
+ else:
151
+ neigh_count = n_neighs
152
+ if neigh_count > 0:
153
+ weight_A = 1. / neigh_count
154
+ else:
155
+ weight_A = np.nan
156
+
157
+ if not include_dead_weight and status_A[k] == 0:
158
+ weight_A = 0
159
+
160
+ neighs = []
161
+ setA.at[index_A[k], neigh_col] = []
162
+ for n in range(len(neighs_B)):
163
+
164
+ # index in setB
165
+ n_index = np.where(ids_B == neighs_B[n])[0][0]
166
+ # Assess if neigh B is closest to A
167
+ if attention_weight:
168
+ if closest_A[n_index] == ids_A[k]:
169
+ closest = True
170
+ else:
171
+ closest = False
172
+
173
+ if symmetrize:
174
+ # Load neighborhood previous data
175
+ sym_neigh = setB.loc[index_B[n_index], neigh_col]
176
+ if neighs_B[n] == closest_B_cell:
177
+ closest_b = True
178
+ else:
179
+ closest_b = False
180
+ if isinstance(sym_neigh, list):
181
+ sym_neigh.append({'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k],
182
+ 'intersection': intersect_B[n]})
183
+ else:
184
+ sym_neigh = [{'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k],
185
+ 'intersection': intersect_B[n]}]
186
+ if attention_weight:
187
+ sym_neigh[-1].update({'weight': weight_A, 'closest': closest_b})
188
+
189
+ # Write the minimum info about neighborhing cell B
190
+ neigh_dico = {'id': neighs_B[n], 'distance': dist_B[n], 'status': status_neigh_B[n],
191
+ 'intersection': intersect_B[n]}
192
+ if attention_weight:
193
+ neigh_dico.update({'weight': weights[n_index], 'closest': closest})
194
+
195
+ if compute_cum_sum:
196
+ # Compute the integrated presence of the neighboring cell B
197
+ assert column_labelsB[
198
+ 'track'] == 'TRACK_ID', 'The set B does not seem to contain tracked data. The cumulative time will be meaningless.'
199
+ past_neighs = [[ll['id'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
200
+ (setA[column_labelsA['track']] == ids_A[k]) & (
201
+ setA[column_labelsA['time']] <= time_index), neigh_col].to_numpy()]
202
+ past_neighs = [item for sublist in past_neighs for item in sublist]
203
+
204
+ if attention_weight:
205
+ past_weights = [[ll['weight'] for ll in l] if len(l) > 0 else [None] for l in
206
+ setA.loc[
207
+ (setA[column_labelsA['track']] == ids_A[k]) & (
208
+ setA[column_labelsA['time']] <= time_index), neigh_col].to_numpy()]
209
+ past_weights = [item for sublist in past_weights for item in sublist]
210
+
211
+ cum_sum = len(np.where(past_neighs == neighs_B[n])[0])
212
+ neigh_dico.update({'cumulated_presence': cum_sum + 1})
213
+
214
+ if attention_weight:
215
+ cum_sum_weighted = np.sum(
216
+ [w if l == neighs_B[n] else 0 for l, w in zip(past_neighs, past_weights)])
217
+ neigh_dico.update(
218
+ {'cumulated_presence_weighted': cum_sum_weighted + weights[n_index]})
219
+
220
+ if symmetrize:
221
+ setB.at[index_B[n_index], neigh_col] = sym_neigh
222
+
223
+ neighs.append(neigh_dico)
224
+
225
+ setA.at[index_A[k], neigh_col] = neighs
226
+
227
+
228
+ def _compute_mask_contact_dist_map(setA, setB, labelsA, labelsB=None, distance=10, mode="self", column_labelsA=None, column_labelsB=None):
229
+
230
+ coordinates_A = setA.loc[:, [column_labelsA['x'], column_labelsA['y']]].to_numpy()
231
+ coordinates_B = setB.loc[:, [column_labelsB['x'], column_labelsB['y']]].to_numpy()
232
+ ids_A = setA.loc[:, column_labelsA["track"]].to_numpy()
233
+ ids_B = setB.loc[:, column_labelsB["track"]].to_numpy()
234
+ mask_ids_A = setA.loc[:, column_labelsA["mask_id"]].to_numpy()
235
+ mask_ids_B = setB.loc[:, column_labelsB["mask_id"]].to_numpy()
236
+
237
+ # compute distance matrix
238
+ dist_map = cdist(coordinates_A, coordinates_B, metric="euclidean")
239
+ intersection_map = np.zeros_like(dist_map).astype(float)
240
+
241
+ # Do the mask contact computation
242
+ labelsA = np.where(np.isin(labelsA, mask_ids_A), labelsA.copy(), 0.)
243
+
244
+ if labelsB is not None:
245
+ labelsB = np.where(np.isin(labelsB, mask_ids_B), labelsB.copy(), 0.)
246
+
247
+ contact_pairs = contact_neighborhood(labelsA, labelsB=labelsB, border=distance, connectivity=2)
248
+
249
+ # Put infinite distance to all non-contact pairs (something like this)
250
+ flatA = labelsA.flatten()
251
+ if labelsB is not None:
252
+ flatB = labelsB.flatten()
253
+
254
+ if len(contact_pairs) > 0:
255
+ mask = np.ones_like(dist_map).astype(bool)
256
+
257
+ indices_to_keep = []
258
+ for cp in contact_pairs:
259
+
260
+ cp = np.abs(cp)
261
+ mask_A, mask_B = cp
262
+ idx_A = np.where(mask_ids_A == int(mask_A))[0][0]
263
+ idx_B = np.where(mask_ids_B == int(mask_B))[0][0]
264
+
265
+ intersection = 0
266
+ if labelsB is not None:
267
+ intersection = len(flatA[(flatA == int(mask_A)) & (flatB == int(mask_B))])
268
+
269
+ indices_to_keep.append([idx_A, idx_B, intersection])
270
+ print(f'Ref cell #{ids_A[idx_A]} matched with neigh. cell #{ids_B[idx_B]}...')
271
+ print(f'Computed intersection: {intersection} px...')
272
+
273
+ if len(indices_to_keep) > 0:
274
+ indices_to_keep = np.array(indices_to_keep)
275
+ mask[indices_to_keep[:, 0], indices_to_keep[:, 1]] = False
276
+ if mode == 'self':
277
+ mask[indices_to_keep[:, 1], indices_to_keep[:, 0]] = False
278
+ dist_map[mask] = 1.0E06
279
+ intersection_map[indices_to_keep[:, 0], indices_to_keep[:, 1]] = indices_to_keep[:, 2]
280
+ else:
281
+ dist_map[:, :] = 1.0E06
282
+ else:
283
+ dist_map[:, :] = 1.0E06
284
+
285
+ return dist_map, intersection_map
286
+
12
287
 
13
288
  def set_live_status(setA, setB, status, not_status_option):
14
289
  """
@@ -202,15 +477,12 @@ def distance_cut_neighborhood(setA, setB, distance, mode='two-pop', status=None,
202
477
  int)
203
478
  for t in tqdm(timeline):
204
479
 
205
- index_A = list(setA.loc[setA[cl[0]['time']] == t].index)
206
480
  coordinates_A = setA.loc[setA[cl[0]['time']] == t, [cl[0]['x'], cl[0]['y']]].to_numpy()
207
481
  ids_A = setA.loc[setA[cl[0]['time']] == t, cl[0]['track']].to_numpy()
208
482
  status_A = setA.loc[setA[cl[0]['time']] == t, status[0]].to_numpy()
209
483
 
210
- index_B = list(setB.loc[setB[cl[1]['time']] == t].index)
211
484
  coordinates_B = setB.loc[setB[cl[1]['time']] == t, [cl[1]['x'], cl[1]['y']]].to_numpy()
212
485
  ids_B = setB.loc[setB[cl[1]['time']] == t, cl[1]['track']].to_numpy()
213
- status_B = setB.loc[setB[cl[1]['time']] == t, status[1]].to_numpy()
214
486
 
215
487
  if len(ids_A) > 0 and len(ids_B) > 0:
216
488
 
@@ -218,96 +490,14 @@ def distance_cut_neighborhood(setA, setB, distance, mode='two-pop', status=None,
218
490
  dist_map = cdist(coordinates_A, coordinates_B, metric="euclidean")
219
491
 
220
492
  if attention_weight:
221
- weights, closest_A = compute_attention_weight(dist_map, d, status_A, ids_A, axis=1,
222
- include_dead_weight=include_dead_weight)
493
+ weights, closest_A = compute_attention_weight(dist_map, d, status_A, ids_A, axis=1, include_dead_weight=include_dead_weight)
223
494
 
224
- # Target centric
225
- for k in range(dist_map.shape[0]):
226
-
227
- col = dist_map[k, :]
228
- col[col == 0.] = 1.0E06
229
-
230
- neighs_B = np.array([ids_B[i] for i in np.where((col <= d))[0]])
231
- status_neigh_B = np.array([status_B[i] for i in np.where((col <= d))[0]])
232
- dist_B = [round(col[i], 2) for i in np.where((col <= d))[0]]
233
- if len(dist_B) > 0:
234
- closest_B_cell = neighs_B[np.argmin(dist_B)]
235
-
236
- if symmetrize and attention_weight:
237
- n_neighs = float(len(neighs_B))
238
- if not include_dead_weight:
239
- n_neighs_alive = len(np.where(status_neigh_B == 1)[0])
240
- neigh_count = n_neighs_alive
241
- else:
242
- neigh_count = n_neighs
243
- if neigh_count > 0:
244
- weight_A = 1. / neigh_count
245
- else:
246
- weight_A = np.nan
247
-
248
- if not include_dead_weight and status_A[k] == 0:
249
- weight_A = 0
250
-
251
- neighs = []
252
- setA.at[index_A[k], neigh_col] = []
253
- for n in range(len(neighs_B)):
254
-
255
- # index in setB
256
- n_index = np.where(ids_B == neighs_B[n])[0][0]
257
- # Assess if neigh B is closest to A
258
- if attention_weight:
259
- if closest_A[n_index] == ids_A[k]:
260
- closest = True
261
- else:
262
- closest = False
263
-
264
- if symmetrize:
265
- # Load neighborhood previous data
266
- sym_neigh = setB.loc[index_B[n_index], neigh_col]
267
- if neighs_B[n] == closest_B_cell:
268
- closest_b = True
269
- else:
270
- closest_b = False
271
- if isinstance(sym_neigh, list):
272
- sym_neigh.append({'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k]})
273
- else:
274
- sym_neigh = [{'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k]}]
275
- if attention_weight:
276
- sym_neigh[-1].update({'weight': weight_A, 'closest': closest_b})
277
-
278
- # Write the minimum info about neighborhing cell B
279
- neigh_dico = {'id': neighs_B[n], 'distance': dist_B[n], 'status': status_neigh_B[n]}
280
- if attention_weight:
281
- neigh_dico.update({'weight': weights[n_index], 'closest': closest})
282
-
283
- if compute_cum_sum:
284
- # Compute the integrated presence of the neighboring cell B
285
- assert cl[1][
286
- '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[
288
- (setA[cl[0]['track']] == ids_A[k]) & (setA[cl[0]['time']] <= t), neigh_col].to_numpy()]
289
- past_neighs = [item for sublist in past_neighs for item in sublist]
290
-
291
- if attention_weight:
292
- past_weights = [[ll['weight'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
293
- (setA[cl[0]['track']] == ids_A[k]) & (
294
- setA[cl[0]['time']] <= t), neigh_col].to_numpy()]
295
- past_weights = [item for sublist in past_weights for item in sublist]
296
-
297
- cum_sum = len(np.where(past_neighs == neighs_B[n])[0])
298
- neigh_dico.update({'cumulated_presence': cum_sum + 1})
299
-
300
- if attention_weight:
301
- cum_sum_weighted = np.sum(
302
- [w if l == neighs_B[n] else 0 for l, w in zip(past_neighs, past_weights)])
303
- neigh_dico.update({'cumulated_presence_weighted': cum_sum_weighted + weights[n_index]})
304
-
305
- if symmetrize:
306
- setB.at[index_B[n_index], neigh_col] = sym_neigh
307
-
308
- neighs.append(neigh_dico)
309
-
310
- setA.at[index_A[k], neigh_col] = neighs
495
+ _fill_distance_neighborhood_at_t(t, setA, setB, dist_map,
496
+ attention_weight=attention_weight,
497
+ include_dead_weight=include_dead_weight, symmetrize=symmetrize,
498
+ compute_cum_sum=compute_cum_sum, weights=weights, closest_A=closest_A,
499
+ neigh_col=neigh_col, column_labelsA=cl[0], column_labelsB=cl[1],
500
+ statusA=status[0], statusB=status[1], distance=d)
311
501
 
312
502
  return setA, setB
313
503
 
@@ -435,6 +625,7 @@ def compute_neighborhood_at_position(pos, distance, population=['targets', 'effe
435
625
  df_B = df_B.drop(columns=unwanted)
436
626
 
437
627
  df_A, df_B = distance_cut_neighborhood(df_A, df_B, distance, **neighborhood_kwargs)
628
+
438
629
  if df_A is None or df_B is None or len(df_A)==0:
439
630
  return None
440
631
 
@@ -442,6 +633,7 @@ def compute_neighborhood_at_position(pos, distance, population=['targets', 'effe
442
633
 
443
634
  if neighborhood_kwargs['mode'] == 'two-pop':
444
635
  neigh_col = f'neighborhood_2_circle_{d}_px'
636
+
445
637
  elif neighborhood_kwargs['mode'] == 'self':
446
638
  neigh_col = f'neighborhood_self_circle_{d}_px'
447
639
 
@@ -464,17 +656,27 @@ def compute_neighborhood_at_position(pos, distance, population=['targets', 'effe
464
656
  df_A = mean_neighborhood_after_event(df_A, neigh_col, event_time_col)
465
657
  print('Done...')
466
658
 
467
- df_A.to_pickle(path_A.replace('.csv', '.pkl'))
468
659
  if not population[0] == population[1]:
469
- # Remove neighborhood column
660
+ # Remove neighborhood column from neighbor table, rename with actual population name
470
661
  for td, d in zip(theta_dist, distance):
471
662
  if neighborhood_kwargs['mode'] == 'two-pop':
472
663
  neigh_col = f'neighborhood_2_circle_{d}_px'
664
+ new_neigh_col = neigh_col.replace('_2_',f'_({population[0]}-{population[1]})_')
665
+ df_A = df_A.rename(columns={neigh_col: new_neigh_col})
473
666
  elif neighborhood_kwargs['mode'] == 'self':
474
667
  neigh_col = f'neighborhood_self_circle_{d}_px'
475
668
  df_B = df_B.drop(columns=[neigh_col])
476
669
  df_B.to_pickle(path_B.replace('.csv', '.pkl'))
477
670
 
671
+ cols_to_rename = [c for c in list(df_A.columns) if c.startswith('intermediate_count_') or c.startswith('inclusive_count_') or c.startswith('exclusive_count_') or c.startswith('mean_count_')]
672
+ new_col_names = [c.replace('_2_',f'_({population[0]}-{population[1]})_') for c in cols_to_rename]
673
+ new_name_map = {}
674
+ for k,c in enumerate(cols_to_rename):
675
+ new_name_map.update({c: new_col_names[k]})
676
+ df_A = df_A.rename(columns=new_name_map)
677
+
678
+ df_A.to_pickle(path_A.replace('.csv', '.pkl'))
679
+
478
680
  unwanted = df_A.columns[df_A.columns.str.startswith('neighborhood_')]
479
681
  df_A2 = df_A.drop(columns=unwanted)
480
682
  df_A2.to_csv(path_A, index=False)
@@ -904,16 +1106,6 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
904
1106
  do not count dead cells when establishing attention weight
905
1107
  """
906
1108
 
907
- # Check live_status option
908
- # if setA is not None:
909
- # setA_id = extract_identity_col(setA)
910
- # if setA_id=="TRACK_ID":
911
- # setA = setA.loc[~setA['TRACK_ID'].isnull(),:].copy()
912
- # if setB is not None:
913
- # setB_id = extract_identity_col(setB)
914
- # if setB_id=="TRACK_ID":
915
- # setB = setB.loc[~setB['TRACK_ID'].isnull(),:].copy()
916
-
917
1109
  if setA is not None and setB is not None:
918
1110
  setA, setB, status = set_live_status(setA, setB, status, not_status_option)
919
1111
  else:
@@ -949,6 +1141,9 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
949
1141
  neigh_col = f'neighborhood_2_contact_{d}_px'
950
1142
  elif mode == 'self':
951
1143
  neigh_col = f'neighborhood_self_contact_{d}_px'
1144
+ else:
1145
+ print("Please provide a valid mode between `two-pop` and `self`...")
1146
+ return None
952
1147
 
953
1148
  setA[neigh_col] = np.nan
954
1149
  setA[neigh_col] = setA[neigh_col].astype(object)
@@ -961,163 +1156,30 @@ def mask_contact_neighborhood(setA, setB, labelsA, labelsB, distance, mode='two-
961
1156
  int)
962
1157
  for t in tqdm(timeline):
963
1158
 
964
- index_A = list(setA.loc[setA[cl[0]['time']] == t].index)
965
- dataA = setA.loc[setA[cl[0]['time']] == t, [cl[0]['x'], cl[0]['y'], cl[0]['track'], cl[0]['mask_id'], status[0]]].to_numpy()
966
- coordinates_A = dataA[:,[0,1]]; ids_A = dataA[:,2]; mask_ids_A = dataA[:,3]; status_A = dataA[:,4];
967
-
968
- index_B = list(setB.loc[setB[cl[1]['time']] == t].index)
969
- dataB = setB.loc[setB[cl[1]['time']] == t, [cl[1]['x'], cl[1]['y'], cl[1]['track'], cl[1]['mask_id'], status[1]]].to_numpy()
970
- coordinates_B = dataB[:,[0,1]]; ids_B = dataB[:,2]; mask_ids_B = dataB[:,3]; status_B = dataB[:,4]
971
-
972
- if len(coordinates_A) > 0 and len(coordinates_B) > 0:
973
-
974
- # compute distance matrix
975
- dist_map = cdist(coordinates_A, coordinates_B, metric="euclidean")
976
- intersection_map = np.zeros_like(dist_map).astype(float)
1159
+ setA_t = setA.loc[setA[cl[0]['time']] == t, :]
1160
+ setB_t = setB.loc[setB[cl[1]['time']] == t, :]
977
1161
 
978
- # Do the mask contact computation
979
- lblA = labelsA[t]
980
- lblA = np.where(np.isin(lblA, mask_ids_A), lblA, 0.)
981
-
982
- lblB = labelsB[t]
983
- if lblB is not None:
984
- lblB = np.where(np.isin(lblB, mask_ids_B), lblB, 0.)
985
-
986
- contact_pairs = contact_neighborhood(lblA, labelsB=lblB, border=d, connectivity=2)
987
-
988
- # Put infinite distance to all non-contact pairs (something like this)
989
- plot_map = False
990
- flatA = lblA.flatten()
991
- if lblB is not None:
992
- flatB = lblB.flatten()
993
-
994
- if len(contact_pairs) > 0:
995
- mask = np.ones_like(dist_map).astype(bool)
996
-
997
- indices_to_keep = []
998
- for cp in contact_pairs:
999
-
1000
- cp = np.abs(cp)
1001
- mask_A, mask_B = cp
1002
- idx_A = np.where(mask_ids_A == int(mask_A))[0][0]
1003
- idx_B = np.where(mask_ids_B == int(mask_B))[0][0]
1004
-
1005
- intersection = 0
1006
- if lblB is not None:
1007
- intersection = len(flatA[(flatA==int(mask_A))&(flatB==int(mask_B))])
1008
-
1009
- indices_to_keep.append([idx_A,idx_B, intersection])
1010
- print(f'Ref cell #{ids_A[idx_A]} matched with neigh. cell #{ids_B[idx_B]}...')
1011
- print(f'Computed intersection: {intersection} px...')
1012
-
1013
- if len(indices_to_keep) > 0:
1014
- indices_to_keep = np.array(indices_to_keep)
1015
- mask[indices_to_keep[:, 0], indices_to_keep[:, 1]] = False
1016
- if mode == 'self':
1017
- mask[indices_to_keep[:, 1], indices_to_keep[:, 0]] = False
1018
- dist_map[mask] = 1.0E06
1019
- intersection_map[indices_to_keep[:,0], indices_to_keep[:,1]] = indices_to_keep[:,2]
1020
- plot_map=True
1021
- else:
1022
- dist_map[:,:] = 1.0E06
1023
- else:
1024
- dist_map[:, :] = 1.0E06
1162
+ if len(setA_t) > 0 and len(setB_t) > 0:
1163
+ dist_map, intersection_map = _compute_mask_contact_dist_map(setA_t, setB_t, labelsA[t], labelsB[t],
1164
+ distance=d, mode=mode, column_labelsA=cl[0],
1165
+ column_labelsB=cl[1])
1025
1166
 
1026
1167
  d_filter = 1.0E05
1027
1168
  if attention_weight:
1169
+ status_A = setA_t[status[0]].to_numpy()
1170
+ ids_A = setA_t[cl[0]["track"]].to_numpy()
1028
1171
  weights, closest_A = compute_attention_weight(dist_map, d_filter, status_A, ids_A, axis=1,
1029
1172
  include_dead_weight=include_dead_weight)
1173
+ else:
1174
+ weights = None
1175
+ closest_A = None
1030
1176
 
1031
- # Target centric
1032
- for k in range(dist_map.shape[0]):
1033
-
1034
- col = dist_map[k, :]
1035
- col_inter = intersection_map[k, :]
1036
- col[col == 0.] = 1.0E06
1037
-
1038
- neighs_B = np.array([ids_B[i] for i in np.where((col <= d_filter))[0]])
1039
- status_neigh_B = np.array([status_B[i] for i in np.where((col <= d_filter))[0]])
1040
- dist_B = [round(col[i], 2) for i in np.where((col <= d_filter))[0]]
1041
- intersect_B = [round(col_inter[i], 2) for i in np.where((col <= d_filter))[0]]
1042
-
1043
- if len(dist_B) > 0:
1044
- closest_B_cell = neighs_B[np.argmin(dist_B)]
1045
-
1046
- if symmetrize and attention_weight:
1047
- n_neighs = float(len(neighs_B))
1048
- if not include_dead_weight:
1049
- n_neighs_alive = len(np.where(status_neigh_B == 1)[0])
1050
- neigh_count = n_neighs_alive
1051
- else:
1052
- neigh_count = n_neighs
1053
- if neigh_count > 0:
1054
- weight_A = 1. / neigh_count
1055
- else:
1056
- weight_A = np.nan
1057
-
1058
- if not include_dead_weight and status_A[k] == 0:
1059
- weight_A = 0
1060
-
1061
- neighs = []
1062
- setA.at[index_A[k], neigh_col] = []
1063
- for n in range(len(neighs_B)):
1064
-
1065
- # index in setB
1066
- n_index = np.where(ids_B == neighs_B[n])[0][0]
1067
- # Assess if neigh B is closest to A
1068
- if attention_weight:
1069
- if closest_A[n_index] == ids_A[k]:
1070
- closest = True
1071
- else:
1072
- closest = False
1073
-
1074
- if symmetrize:
1075
- # Load neighborhood previous data
1076
- sym_neigh = setB.loc[index_B[n_index], neigh_col]
1077
- if neighs_B[n] == closest_B_cell:
1078
- closest_b = True
1079
- else:
1080
- closest_b = False
1081
- if isinstance(sym_neigh, list):
1082
- sym_neigh.append({'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k], 'intersection': intersect_B[n]})
1083
- else:
1084
- sym_neigh = [{'id': ids_A[k], 'distance': dist_B[n], 'status': status_A[k], 'intersection': intersect_B[n]}]
1085
- if attention_weight:
1086
- sym_neigh[-1].update({'weight': weight_A, 'closest': closest_b})
1087
-
1088
- # Write the minimum info about neighborhing cell B
1089
- neigh_dico = {'id': neighs_B[n], 'distance': dist_B[n], 'status': status_neigh_B[n], 'intersection': intersect_B[n]}
1090
- if attention_weight:
1091
- neigh_dico.update({'weight': weights[n_index], 'closest': closest})
1092
-
1093
- if compute_cum_sum:
1094
- # Compute the integrated presence of the neighboring cell B
1095
- assert cl[1][
1096
- 'track'] == 'TRACK_ID', 'The set B does not seem to contain tracked data. The cumulative time will be meaningless.'
1097
- past_neighs = [[ll['id'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
1098
- (setA[cl[0]['track']] == ids_A[k]) & (setA[cl[0]['time']] <= t), neigh_col].to_numpy()]
1099
- past_neighs = [item for sublist in past_neighs for item in sublist]
1100
-
1101
- if attention_weight:
1102
- past_weights = [[ll['weight'] for ll in l] if len(l) > 0 else [None] for l in setA.loc[
1103
- (setA[cl[0]['track']] == ids_A[k]) & (
1104
- setA[cl[0]['time']] <= t), neigh_col].to_numpy()]
1105
- past_weights = [item for sublist in past_weights for item in sublist]
1106
-
1107
- cum_sum = len(np.where(past_neighs == neighs_B[n])[0])
1108
- neigh_dico.update({'cumulated_presence': cum_sum + 1})
1109
-
1110
- if attention_weight:
1111
- cum_sum_weighted = np.sum(
1112
- [w if l == neighs_B[n] else 0 for l, w in zip(past_neighs, past_weights)])
1113
- neigh_dico.update({'cumulated_presence_weighted': cum_sum_weighted + weights[n_index]})
1114
-
1115
- if symmetrize:
1116
- setB.at[index_B[n_index], neigh_col] = sym_neigh
1117
-
1118
- neighs.append(neigh_dico)
1119
-
1120
- setA.at[index_A[k], neigh_col] = neighs
1177
+ _fill_contact_neighborhood_at_t(t, setA, setB, dist_map, intersection_map=intersection_map,
1178
+ attention_weight=attention_weight,
1179
+ include_dead_weight=include_dead_weight, symmetrize=symmetrize,
1180
+ compute_cum_sum=compute_cum_sum, weights=weights, closest_A=closest_A,
1181
+ neigh_col=neigh_col, column_labelsA=cl[0], column_labelsB=cl[1],
1182
+ statusA=status[0], statusB=status[1], d_filter=d_filter)
1121
1183
 
1122
1184
  return setA, setB
1123
1185
 
@@ -1278,17 +1340,28 @@ def compute_contact_neighborhood_at_position(pos, distance, population=['targets
1278
1340
  df_A = mean_neighborhood_after_event(df_A, neigh_col, event_time_col, metrics=['inclusive', 'intermediate'])
1279
1341
  print('Done...')
1280
1342
 
1281
- df_A.to_pickle(path_A.replace('.csv', '.pkl'))
1282
1343
  if not population[0] == population[1]:
1283
- # Remove neighborhood column
1344
+ # Remove neighborhood column from neighbor table, rename with actual population name
1284
1345
  for td, d in zip(theta_dist, distance):
1285
1346
  if neighborhood_kwargs['mode'] == 'two-pop':
1286
1347
  neigh_col = f'neighborhood_2_contact_{d}_px'
1348
+ new_neigh_col = neigh_col.replace('_2_',f'_({population[0]}-{population[1]})_')
1349
+ df_A = df_A.rename(columns={neigh_col: new_neigh_col})
1287
1350
  elif neighborhood_kwargs['mode'] == 'self':
1288
1351
  neigh_col = f'neighborhood_self_contact_{d}_px'
1289
1352
  df_B = df_B.drop(columns=[neigh_col])
1290
1353
  df_B.to_pickle(path_B.replace('.csv', '.pkl'))
1291
1354
 
1355
+ cols_to_rename = [c for c in list(df_A.columns) if c.startswith('intermediate_count_') or c.startswith('inclusive_count_') or c.startswith('exclusive_count_') or c.startswith('mean_count_')]
1356
+ new_col_names = [c.replace('_2_',f'_({population[0]}-{population[1]})_') for c in cols_to_rename]
1357
+ new_name_map = {}
1358
+ for k,c in enumerate(cols_to_rename):
1359
+ new_name_map.update({c: new_col_names[k]})
1360
+ df_A = df_A.rename(columns=new_name_map)
1361
+
1362
+ print(f'{df_A.columns=}')
1363
+ df_A.to_pickle(path_A.replace('.csv', '.pkl'))
1364
+
1292
1365
  unwanted = df_A.columns[df_A.columns.str.startswith('neighborhood_')]
1293
1366
  df_A2 = df_A.drop(columns=unwanted)
1294
1367
  df_A2.to_csv(path_A, index=False)
@@ -1355,28 +1428,29 @@ def extract_neighborhood_in_pair_table(df, distance=None, reference_population="
1355
1428
  """
1356
1429
 
1357
1430
 
1358
- assert reference_population in ["targets", "effectors"], "Please set a valid reference population ('targets' or 'effectors')"
1431
+ #assert reference_population in ["targets", "effectors"], "Please set a valid reference population ('targets' or 'effectors')"
1359
1432
  if neighborhood_key is None:
1360
- assert neighbor_population in ["targets", "effectors"], "Please set a valid neighbor population ('targets' or 'effectors')"
1433
+ #assert neighbor_population in ["targets", "effectors"], "Please set a valid neighbor population ('targets' or 'effectors')"
1361
1434
  assert mode in ["circle", "contact"], "Please set a valid neighborhood computation mode ('circle' or 'contact')"
1362
- if reference_population==neighbor_population:
1363
- type = "self"
1364
- else:
1365
- type = "2"
1366
-
1435
+ type = '('+'-'.join([reference_population, neighbor_population])+')'
1367
1436
  neigh_col = f"neighborhood_{type}_{mode}_{distance}_px"
1368
1437
  else:
1369
1438
  neigh_col = neighborhood_key.replace('status_','')
1370
- if 'self' in neigh_col:
1371
- neighbor_population = reference_population
1439
+ if '_(' in neigh_col and ')_' in neigh_col:
1440
+ neighbor_population = neigh_col.split('_(')[-1].split(')_')[0].split('-')[-1]
1372
1441
  else:
1373
- if reference_population=="effectors":
1374
- neighbor_population=='targets'
1442
+ if 'self' in neigh_col:
1443
+ neighbor_population = reference_population
1375
1444
  else:
1376
- neighbor_population=='effectors'
1445
+ if reference_population=="effectors":
1446
+ neighbor_population='targets'
1447
+ else:
1448
+ neighbor_population='effectors'
1377
1449
 
1378
1450
  assert "status_"+neigh_col in list(df.columns),"The selected neighborhood does not appear in the data..."
1379
1451
 
1452
+ print(df[['reference_population','neighbor_population', "status_"+neigh_col]])
1453
+
1380
1454
  if contact_only:
1381
1455
  s_keep = [1]
1382
1456
  else: