celldetective 1.2.2.post2__py3-none-any.whl → 1.3.0.post1__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.
- celldetective/__init__.py +1 -0
- celldetective/_version.py +1 -0
- celldetective/events.py +5 -0
- celldetective/gui/about.py +2 -1
- celldetective/gui/classifier_widget.py +43 -33
- celldetective/gui/control_panel.py +33 -23
- celldetective/gui/gui_utils.py +9 -3
- celldetective/gui/measurement_options.py +969 -969
- celldetective/gui/signal_annotator.py +65 -53
- celldetective/gui/tableUI.py +196 -26
- celldetective/io.py +4 -1
- celldetective/links/zenodo.json +359 -168
- celldetective/measure.py +46 -18
- celldetective/segmentation.py +17 -9
- celldetective/utils.py +82 -26
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/METADATA +3 -1
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/RECORD +22 -21
- tests/test_segmentation.py +5 -4
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/LICENSE +0 -0
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/WHEEL +0 -0
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/top_level.txt +0 -0
celldetective/measure.py
CHANGED
|
@@ -980,7 +980,6 @@ def blob_detection(image, label, threshold, diameter):
|
|
|
980
980
|
|
|
981
981
|
### Classification ####
|
|
982
982
|
|
|
983
|
-
|
|
984
983
|
def estimate_time(df, class_attr, model='step_function', class_of_interest=[2], r2_threshold=0.5):
|
|
985
984
|
|
|
986
985
|
"""
|
|
@@ -1027,6 +1026,7 @@ def estimate_time(df, class_attr, model='step_function', class_of_interest=[2],
|
|
|
1027
1026
|
df = df.sort_values(by=sort_cols,ignore_index=True)
|
|
1028
1027
|
df = df.reset_index(drop=True)
|
|
1029
1028
|
|
|
1029
|
+
|
|
1030
1030
|
for tid,group in df.loc[df[class_attr].isin(class_of_interest)].groupby(sort_cols):
|
|
1031
1031
|
|
|
1032
1032
|
indices = group.index
|
|
@@ -1034,21 +1034,19 @@ def estimate_time(df, class_attr, model='step_function', class_of_interest=[2],
|
|
|
1034
1034
|
|
|
1035
1035
|
group_clean = group.dropna(subset=status_col)
|
|
1036
1036
|
status_signal = group_clean[status_col].values
|
|
1037
|
+
if np.all(np.array(status_signal)==1):
|
|
1038
|
+
continue
|
|
1039
|
+
|
|
1037
1040
|
timeline = group_clean['FRAME'].values
|
|
1038
|
-
|
|
1039
1041
|
frames = group_clean['FRAME'].to_numpy()
|
|
1040
1042
|
status_values = group_clean[status_col].to_numpy()
|
|
1041
1043
|
t_first = group['t_firstdetection'].to_numpy()[0]
|
|
1042
1044
|
|
|
1043
1045
|
try:
|
|
1044
|
-
|
|
1045
|
-
popt, pcov = curve_fit(eval(model), timeline.astype(int), status_signal, p0=[df['FRAME'].max()//2, 0.8],maxfev=30000)
|
|
1046
|
+
popt, pcov = curve_fit(eval(model), timeline.astype(int), status_signal, p0=[max(timeline)//2, 0.8],maxfev=100000)
|
|
1046
1047
|
values = [eval(model)(t, *popt) for t in timeline]
|
|
1047
1048
|
r2 = r2_score(status_signal,values)
|
|
1048
|
-
|
|
1049
|
-
except Exception as e:
|
|
1050
|
-
|
|
1051
|
-
print(e)
|
|
1049
|
+
except Exception:
|
|
1052
1050
|
df.loc[indices, class_attr] = 2.0
|
|
1053
1051
|
df.loc[indices, class_attr.replace('class','t')] = -1
|
|
1054
1052
|
continue
|
|
@@ -1064,7 +1062,7 @@ def estimate_time(df, class_attr, model='step_function', class_of_interest=[2],
|
|
|
1064
1062
|
return df
|
|
1065
1063
|
|
|
1066
1064
|
|
|
1067
|
-
def interpret_track_classification(df, class_attr, irreversible_event=False, unique_state=False,r2_threshold=0.5):
|
|
1065
|
+
def interpret_track_classification(df, class_attr, irreversible_event=False, unique_state=False,r2_threshold=0.5, percentile_recovery=50):
|
|
1068
1066
|
|
|
1069
1067
|
"""
|
|
1070
1068
|
Interpret and classify tracked cells based on their status signals.
|
|
@@ -1123,15 +1121,15 @@ def interpret_track_classification(df, class_attr, irreversible_event=False, uni
|
|
|
1123
1121
|
|
|
1124
1122
|
if irreversible_event:
|
|
1125
1123
|
|
|
1126
|
-
df = classify_irreversible_events(df, class_attr, r2_threshold=
|
|
1127
|
-
|
|
1124
|
+
df = classify_irreversible_events(df, class_attr, r2_threshold=r2_threshold, percentile_recovery=percentile_recovery)
|
|
1125
|
+
|
|
1128
1126
|
elif unique_state:
|
|
1129
1127
|
|
|
1130
1128
|
df = classify_unique_states(df, class_attr, percentile=50)
|
|
1131
1129
|
|
|
1132
1130
|
return df
|
|
1133
1131
|
|
|
1134
|
-
def classify_irreversible_events(df, class_attr, r2_threshold=0.5, percentile_recovery=
|
|
1132
|
+
def classify_irreversible_events(df, class_attr, r2_threshold=0.5, percentile_recovery=50):
|
|
1135
1133
|
|
|
1136
1134
|
"""
|
|
1137
1135
|
Classify irreversible events in a tracked dataset based on the status of cells and transitions.
|
|
@@ -1179,6 +1177,12 @@ def classify_irreversible_events(df, class_attr, r2_threshold=0.5, percentile_re
|
|
|
1179
1177
|
stat_col = class_attr.replace('class','status')
|
|
1180
1178
|
|
|
1181
1179
|
for tid,track in df.groupby(sort_cols):
|
|
1180
|
+
|
|
1181
|
+
# Set status to 0.0 before first detection
|
|
1182
|
+
t_firstdetection = track['t_firstdetection'].values[0]
|
|
1183
|
+
indices_pre_detection = track.loc[track['FRAME']<=t_firstdetection,class_attr].index
|
|
1184
|
+
track.loc[indices_pre_detection,stat_col] = 0.0
|
|
1185
|
+
df.loc[indices_pre_detection,stat_col] = 0.0
|
|
1182
1186
|
|
|
1183
1187
|
track_valid = track.dropna(subset=stat_col)
|
|
1184
1188
|
indices_valid = track_valid[class_attr].index
|
|
@@ -1193,16 +1197,20 @@ def classify_irreversible_events(df, class_attr, r2_threshold=0.5, percentile_re
|
|
|
1193
1197
|
elif np.all([s==1 for s in status_values]):
|
|
1194
1198
|
# all positive, event already observed
|
|
1195
1199
|
df.loc[indices, class_attr] = 2
|
|
1196
|
-
df.loc[indices, class_attr.replace('class','status')] = 2
|
|
1200
|
+
#df.loc[indices, class_attr.replace('class','status')] = 2
|
|
1197
1201
|
else:
|
|
1198
1202
|
# ambiguity, possible transition
|
|
1199
1203
|
df.loc[indices, class_attr] = 2
|
|
1200
1204
|
|
|
1205
|
+
print("Classes after initial pass: ",df.loc[df['FRAME']==0,class_attr].value_counts())
|
|
1206
|
+
|
|
1201
1207
|
df.loc[df[class_attr]!=2, class_attr.replace('class', 't')] = -1
|
|
1202
1208
|
df = estimate_time(df, class_attr, model='step_function', class_of_interest=[2],r2_threshold=r2_threshold)
|
|
1203
|
-
|
|
1209
|
+
print("Classes after fit: ", df.loc[df['FRAME']==0,class_attr].value_counts())
|
|
1210
|
+
|
|
1204
1211
|
# Revisit class 2 cells to classify as neg/pos with percentile tolerance
|
|
1205
1212
|
df.loc[df[class_attr]==2,:] = classify_unique_states(df.loc[df[class_attr]==2,:].copy(), class_attr, percentile_recovery)
|
|
1213
|
+
print("Classes after unique state recovery: ",df.loc[df['FRAME']==0,class_attr].value_counts())
|
|
1206
1214
|
|
|
1207
1215
|
return df
|
|
1208
1216
|
|
|
@@ -1251,14 +1259,17 @@ def classify_unique_states(df, class_attr, percentile=50):
|
|
|
1251
1259
|
|
|
1252
1260
|
stat_col = class_attr.replace('class','status')
|
|
1253
1261
|
|
|
1262
|
+
|
|
1254
1263
|
for tid,track in df.groupby(sort_cols):
|
|
1255
1264
|
|
|
1265
|
+
|
|
1256
1266
|
track_valid = track.dropna(subset=stat_col)
|
|
1257
1267
|
indices_valid = track_valid[class_attr].index
|
|
1258
1268
|
|
|
1259
1269
|
indices = track[class_attr].index
|
|
1260
1270
|
status_values = track_valid[stat_col].to_numpy()
|
|
1261
1271
|
|
|
1272
|
+
|
|
1262
1273
|
frames = track_valid['FRAME'].to_numpy()
|
|
1263
1274
|
t_first = track['t_firstdetection'].to_numpy()[0]
|
|
1264
1275
|
perc_status = np.nanpercentile(status_values[frames>=t_first], percentile)
|
|
@@ -1324,10 +1335,14 @@ def classify_cells_from_query(df, status_attr, query):
|
|
|
1324
1335
|
|
|
1325
1336
|
|
|
1326
1337
|
# Initialize all states to 0
|
|
1327
|
-
|
|
1338
|
+
if not status_attr.startswith('status_'):
|
|
1339
|
+
status_attr = 'status_'+status_attr
|
|
1340
|
+
|
|
1341
|
+
df = df.copy()
|
|
1342
|
+
df.loc[:,status_attr] = 0
|
|
1343
|
+
|
|
1328
1344
|
cols = extract_cols_from_query(query)
|
|
1329
1345
|
cols_in_df = np.all([c in list(df.columns) for c in cols], axis=0)
|
|
1330
|
-
|
|
1331
1346
|
if query=='':
|
|
1332
1347
|
print('The provided query is empty...')
|
|
1333
1348
|
else:
|
|
@@ -1340,8 +1355,21 @@ def classify_cells_from_query(df, status_attr, query):
|
|
|
1340
1355
|
df.loc[selection, status_attr] = 1
|
|
1341
1356
|
else:
|
|
1342
1357
|
df.loc[:, status_attr] = np.nan
|
|
1343
|
-
|
|
1344
1358
|
except Exception as e:
|
|
1345
|
-
print("The query could not be understood. No filtering was applied. {e}...")
|
|
1359
|
+
print(f"The query could not be understood. No filtering was applied. {e}...")
|
|
1346
1360
|
return None
|
|
1361
|
+
return df.copy()
|
|
1362
|
+
|
|
1363
|
+
def classify_tracks_from_query(df, event_name, query, irreversible_event=True, unique_state=False, r2_threshold=0.5, percentile_recovery=50):
|
|
1364
|
+
|
|
1365
|
+
status_attr = "status_"+event_name
|
|
1366
|
+
df = classify_cells_from_query(df, status_attr, query)
|
|
1367
|
+
class_attr = "class_"+event_name
|
|
1368
|
+
|
|
1369
|
+
name_map = {status_attr: class_attr}
|
|
1370
|
+
df = df.drop(list(set(name_map.values()) & set(df.columns)), axis=1).rename(columns=name_map)
|
|
1371
|
+
df.reset_index(inplace=True, drop=True)
|
|
1372
|
+
|
|
1373
|
+
df = interpret_track_classification(df, class_attr, irreversible_event=irreversible_event, unique_state=unique_state, r2_threshold=r2_threshold, percentile_recovery=percentile_recovery)
|
|
1374
|
+
|
|
1347
1375
|
return df
|
celldetective/segmentation.py
CHANGED
|
@@ -28,7 +28,7 @@ import subprocess
|
|
|
28
28
|
abs_path = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],'celldetective'])
|
|
29
29
|
|
|
30
30
|
def segment(stack, model_name, channels=None, spatial_calibration=None, view_on_napari=False,
|
|
31
|
-
use_gpu=True, channel_axis=-1):
|
|
31
|
+
use_gpu=True, channel_axis=-1, cellprob_threshold=None, flow_threshold=None):
|
|
32
32
|
|
|
33
33
|
"""
|
|
34
34
|
|
|
@@ -50,10 +50,6 @@ def segment(stack, model_name, channels=None, spatial_calibration=None, view_on_
|
|
|
50
50
|
Whether to visualize the segmentation results using Napari. Default is False.
|
|
51
51
|
use_gpu : bool, optional
|
|
52
52
|
Whether to use GPU acceleration if available. Default is True.
|
|
53
|
-
time_flat_normalization : bool, optional
|
|
54
|
-
Whether to perform time-flat normalization on the stack before segmentation. Default is False.
|
|
55
|
-
time_flat_percentiles : tuple, optional
|
|
56
|
-
The percentiles used for time-flat normalization. Default is (0.0, 99.99).
|
|
57
53
|
|
|
58
54
|
Returns
|
|
59
55
|
-------
|
|
@@ -95,6 +91,9 @@ def segment(stack, model_name, channels=None, spatial_calibration=None, view_on_
|
|
|
95
91
|
assert len(channels)==stack.shape[-1],f'The channel names provided do not match with the expected number of channels in the stack: {stack.shape[-1]}.'
|
|
96
92
|
|
|
97
93
|
required_channels = input_config['channels']
|
|
94
|
+
channel_intersection = [ch for ch in channels if ch in required_channels]
|
|
95
|
+
assert len(channel_intersection)>0,'None of the channels required by the model can be found in the images to segment... Abort.'
|
|
96
|
+
|
|
98
97
|
channel_indices = _extract_channel_indices(channels, required_channels)
|
|
99
98
|
|
|
100
99
|
required_spatial_calibration = input_config['spatial_calibration']
|
|
@@ -108,10 +107,13 @@ def segment(stack, model_name, channels=None, spatial_calibration=None, view_on_
|
|
|
108
107
|
diameter = input_config['diameter']
|
|
109
108
|
# if diameter!=30:
|
|
110
109
|
# required_spatial_calibration = None
|
|
111
|
-
cellprob_threshold
|
|
112
|
-
|
|
110
|
+
if cellprob_threshold is None:
|
|
111
|
+
cellprob_threshold = input_config['cellprob_threshold']
|
|
112
|
+
if flow_threshold is None:
|
|
113
|
+
flow_threshold = input_config['flow_threshold']
|
|
113
114
|
|
|
114
115
|
scale = _estimate_scale_factor(spatial_calibration, required_spatial_calibration)
|
|
116
|
+
print(f"{spatial_calibration=} {required_spatial_calibration=} Scale = {scale}...")
|
|
115
117
|
|
|
116
118
|
if model_type=='stardist':
|
|
117
119
|
|
|
@@ -145,13 +147,19 @@ def segment(stack, model_name, channels=None, spatial_calibration=None, view_on_
|
|
|
145
147
|
channel_indices = np.array(channel_indices)
|
|
146
148
|
none_channel_indices = np.where(channel_indices==None)[0]
|
|
147
149
|
channel_indices[channel_indices==None] = 0
|
|
148
|
-
print(channel_indices)
|
|
149
150
|
|
|
150
|
-
frame = stack[t
|
|
151
|
+
frame = stack[t]
|
|
152
|
+
#frame = stack[t,:,:,channel_indices.astype(int)].astype(float)
|
|
151
153
|
if frame.ndim==2:
|
|
152
154
|
frame = frame[:,:,np.newaxis]
|
|
153
155
|
if frame.ndim==3 and np.array(frame.shape).argmin()==0:
|
|
154
156
|
frame = np.moveaxis(frame,0,-1)
|
|
157
|
+
|
|
158
|
+
frame_to_segment = np.zeros((frame.shape[0], frame.shape[1], len(required_channels))).astype(float)
|
|
159
|
+
for ch in channel_intersection:
|
|
160
|
+
idx = required_channels.index(ch)
|
|
161
|
+
frame_to_segment[:,:,idx] = frame[:,:,channels.index(ch)]
|
|
162
|
+
frame = frame_to_segment
|
|
155
163
|
template = frame.copy()
|
|
156
164
|
|
|
157
165
|
values = []
|
celldetective/utils.py
CHANGED
|
@@ -26,6 +26,8 @@ import re
|
|
|
26
26
|
from scipy.ndimage.morphology import distance_transform_edt
|
|
27
27
|
from scipy import ndimage
|
|
28
28
|
from skimage.morphology import disk
|
|
29
|
+
from scipy.stats import ks_2samp
|
|
30
|
+
from cliffs_delta import cliffs_delta
|
|
29
31
|
|
|
30
32
|
def contour_of_instance_segmentation(label, distance):
|
|
31
33
|
|
|
@@ -1183,27 +1185,10 @@ def _extract_channel_indices(channels, required_channels):
|
|
|
1183
1185
|
ch_idx = channels.index(c)
|
|
1184
1186
|
channel_indices.append(ch_idx)
|
|
1185
1187
|
except Exception as e:
|
|
1186
|
-
|
|
1187
|
-
channels = None
|
|
1188
|
-
break
|
|
1188
|
+
channel_indices.append(None)
|
|
1189
1189
|
else:
|
|
1190
1190
|
channel_indices.append(None)
|
|
1191
1191
|
|
|
1192
|
-
# if channels is not None:
|
|
1193
|
-
# channel_indices = []
|
|
1194
|
-
# for ch in required_channels:
|
|
1195
|
-
|
|
1196
|
-
# try:
|
|
1197
|
-
# idx = channels.index(ch)
|
|
1198
|
-
# except ValueError:
|
|
1199
|
-
# print('Mismatch between the channels required by the model and the provided channels.')
|
|
1200
|
-
# return None
|
|
1201
|
-
|
|
1202
|
-
# channel_indices.append(idx)
|
|
1203
|
-
# channel_indices = np.array(channel_indices)
|
|
1204
|
-
# else:
|
|
1205
|
-
# channel_indices = np.arange(len(required_channels))
|
|
1206
|
-
|
|
1207
1192
|
return channel_indices
|
|
1208
1193
|
|
|
1209
1194
|
def ConfigSectionMap(path,section):
|
|
@@ -2044,8 +2029,8 @@ def augmenter(x, y, flip=True, gauss_blur=True, noise_option=True, shift=True,
|
|
|
2044
2029
|
if channel_extinction:
|
|
2045
2030
|
assert extinction_probability <= 1.,'The extinction probability must be a number between 0 and 1.'
|
|
2046
2031
|
channel_off = [np.random.random() < extinction_probability for i in range(x.shape[-1])]
|
|
2047
|
-
|
|
2048
|
-
|
|
2032
|
+
channel_off[0] = False
|
|
2033
|
+
x[:,:,np.array(channel_off, dtype=bool)] = 0.
|
|
2049
2034
|
|
|
2050
2035
|
return x, y
|
|
2051
2036
|
|
|
@@ -2318,9 +2303,9 @@ def get_zenodo_files(cat=None):
|
|
|
2318
2303
|
for f in all_files_short:
|
|
2319
2304
|
if f.startswith('CP') or f.startswith('SD'):
|
|
2320
2305
|
category = os.sep.join(['models','segmentation_generic'])
|
|
2321
|
-
elif f.startswith('MCF7'):
|
|
2306
|
+
elif f.startswith('MCF7') or f.startswith('mcf7'):
|
|
2322
2307
|
category = os.sep.join(['models','segmentation_targets'])
|
|
2323
|
-
elif f.startswith('primNK'):
|
|
2308
|
+
elif f.startswith('primNK') or f.startswith('lymphocytes'):
|
|
2324
2309
|
category = os.sep.join(['models','segmentation_effectors'])
|
|
2325
2310
|
elif f.startswith('demo'):
|
|
2326
2311
|
category = 'demos'
|
|
@@ -2362,9 +2347,7 @@ def download_zenodo_file(file, output_dir):
|
|
|
2362
2347
|
if len(file_to_rename)>0 and not file_to_rename[0].endswith(os.sep) and not file.startswith('demo'):
|
|
2363
2348
|
os.rename(file_to_rename[0], os.sep.join([output_dir,file,file]))
|
|
2364
2349
|
|
|
2365
|
-
if file
|
|
2366
|
-
os.rename(os.sep.join([output_dir,file.replace('db_','')]), os.sep.join([output_dir,file]))
|
|
2367
|
-
if file=="db_primary_NK_w_mcf7":
|
|
2350
|
+
if file.startswith('db_'):
|
|
2368
2351
|
os.rename(os.sep.join([output_dir,file.replace('db_','')]), os.sep.join([output_dir,file]))
|
|
2369
2352
|
if file=='db-si-NucPI':
|
|
2370
2353
|
os.rename(os.sep.join([output_dir,'db2-NucPI']), os.sep.join([output_dir,file]))
|
|
@@ -2476,4 +2459,77 @@ def step_function(t, t_shift, dt):
|
|
|
2476
2459
|
array([0.26894142, 0.37754067, 0.5 , 0.62245933, 0.73105858, 0.81757448])
|
|
2477
2460
|
"""
|
|
2478
2461
|
|
|
2479
|
-
return 1/(1+np.exp(-(t-t_shift)/dt))
|
|
2462
|
+
return 1/(1+np.exp(-(t-t_shift)/dt))
|
|
2463
|
+
|
|
2464
|
+
|
|
2465
|
+
def test_2samp_generic(data, feature=None, groupby_cols=None, method="ks_2samp", *args, **kwargs):
|
|
2466
|
+
|
|
2467
|
+
"""
|
|
2468
|
+
Performs pairwise statistical tests between groups of data, comparing a specified feature using a chosen method.
|
|
2469
|
+
|
|
2470
|
+
The function applies two-sample statistical tests, such as the Kolmogorov-Smirnov (KS) test or Cliff's Delta,
|
|
2471
|
+
to compare distributions of a given feature across groups defined by `groupby_cols`. It returns the test results
|
|
2472
|
+
in a pivot table format with each group's pairwise comparison.
|
|
2473
|
+
|
|
2474
|
+
Parameters
|
|
2475
|
+
----------
|
|
2476
|
+
data : pandas.DataFrame
|
|
2477
|
+
The input dataset containing the feature to be tested.
|
|
2478
|
+
feature : str
|
|
2479
|
+
The name of the column representing the feature to compare between groups.
|
|
2480
|
+
groupby_cols : list or str
|
|
2481
|
+
The column(s) used to group the data. These columns define the groups that will be compared pairwise.
|
|
2482
|
+
method : str, optional, default="ks_2samp"
|
|
2483
|
+
The statistical test to use. Options:
|
|
2484
|
+
- "ks_2samp": Two-sample Kolmogorov-Smirnov test (default).
|
|
2485
|
+
- "cliffs_delta": Cliff's Delta for effect size between two distributions.
|
|
2486
|
+
*args, **kwargs :
|
|
2487
|
+
Additional arguments and keyword arguments for the selected test method.
|
|
2488
|
+
|
|
2489
|
+
Returns
|
|
2490
|
+
-------
|
|
2491
|
+
pivot : pandas.DataFrame
|
|
2492
|
+
A pivot table containing the pairwise test results (p-values or effect sizes).
|
|
2493
|
+
The rows and columns represent the unique groups defined by `groupby_cols`,
|
|
2494
|
+
and the values represent the test result (e.g., p-values or effect sizes) between each group.
|
|
2495
|
+
|
|
2496
|
+
Notes
|
|
2497
|
+
-----
|
|
2498
|
+
- The function compares all unique pairwise combinations of the groups based on `groupby_cols`.
|
|
2499
|
+
- For the "ks_2samp" method, the test compares the distributions using the Kolmogorov-Smirnov test.
|
|
2500
|
+
- For the "cliffs_delta" method, the function calculates the effect size between two distributions.
|
|
2501
|
+
- The results are returned in a symmetric pivot table where each cell represents the test result for the corresponding group pair.
|
|
2502
|
+
|
|
2503
|
+
"""
|
|
2504
|
+
|
|
2505
|
+
|
|
2506
|
+
assert groupby_cols is not None,"Please set a valid groupby_cols..."
|
|
2507
|
+
assert feature is not None,"Please set a feature to test..."
|
|
2508
|
+
|
|
2509
|
+
results = []
|
|
2510
|
+
|
|
2511
|
+
for lbl1,group1 in data.dropna(subset=feature).groupby(groupby_cols):
|
|
2512
|
+
for lbl2,group2 in data.dropna(subset=feature).groupby(groupby_cols):
|
|
2513
|
+
|
|
2514
|
+
dist1 = group1[feature].values
|
|
2515
|
+
dist2 = group2[feature].values
|
|
2516
|
+
if method=="ks_2samp":
|
|
2517
|
+
test = ks_2samp(list(dist1),list(dist2), alternative='less', mode='auto', *args, **kwargs)
|
|
2518
|
+
val = test.pvalue
|
|
2519
|
+
elif method=="cliffs_delta":
|
|
2520
|
+
test = cliffs_delta(list(dist1),list(dist2), *args, **kwargs)
|
|
2521
|
+
val = test[0]
|
|
2522
|
+
|
|
2523
|
+
results.append({"cdt1": lbl1, "cdt2": lbl2, "value": val})
|
|
2524
|
+
|
|
2525
|
+
results = pd.DataFrame(results)
|
|
2526
|
+
results['cdt1'] = results['cdt1'].astype(str)
|
|
2527
|
+
results['cdt2'] = results['cdt2'].astype(str)
|
|
2528
|
+
|
|
2529
|
+
pivot = results.pivot(index='cdt1', columns='cdt2', values='value')
|
|
2530
|
+
pivot.reset_index(inplace=True)
|
|
2531
|
+
pivot.columns.name = None
|
|
2532
|
+
pivot.set_index("cdt1",drop=True, inplace=True)
|
|
2533
|
+
pivot.index.name = None
|
|
2534
|
+
|
|
2535
|
+
return pivot
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: celldetective
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0.post1
|
|
4
4
|
Summary: description
|
|
5
5
|
Home-page: http://github.com/remyeltorro/celldetective
|
|
6
6
|
Author: Rémy Torro
|
|
@@ -38,6 +38,8 @@ Requires-Dist: matplotlib-scalebar
|
|
|
38
38
|
Requires-Dist: numpy==1.26.4
|
|
39
39
|
Requires-Dist: pytest
|
|
40
40
|
Requires-Dist: pytest-qt
|
|
41
|
+
Requires-Dist: h5py
|
|
42
|
+
Requires-Dist: cliffs-delta
|
|
41
43
|
|
|
42
44
|
# Celldetective
|
|
43
45
|
|
|
@@ -1,32 +1,33 @@
|
|
|
1
|
-
celldetective/__init__.py,sha256=
|
|
1
|
+
celldetective/__init__.py,sha256=bi3SGTMo6s2qQBsJAaKy-a4xaGcTQVW8zsqaiX5XKeY,139
|
|
2
2
|
celldetective/__main__.py,sha256=_H9B620ntENFx9RBvlV6ybxpvtHzCbv5NnIPmDBr9Z0,1127
|
|
3
|
-
celldetective/
|
|
3
|
+
celldetective/_version.py,sha256=psRKPA5pfVLLl8tIZkft2nS2RjrlphliNDLiwS7_UEA,28
|
|
4
|
+
celldetective/events.py,sha256=hPnGSeQtQEwLM7Ks9_7AnuXgEVjQXD_LZeoayE4v7x0,4847
|
|
4
5
|
celldetective/extra_properties.py,sha256=8DkxTvVs7gASsnnGurVZ3_zt6uR0pvvJhBKO2LC6hGk,5118
|
|
5
6
|
celldetective/filters.py,sha256=b0qKwHor1fvNA_dHovP17nQz8EsW5YlyhT2TJnayn08,3615
|
|
6
|
-
celldetective/io.py,sha256=
|
|
7
|
-
celldetective/measure.py,sha256=
|
|
7
|
+
celldetective/io.py,sha256=erGKB0ALcLe5ffuo7pkuACDBC2DR-ZBmJHUcKfCLkrM,86442
|
|
8
|
+
celldetective/measure.py,sha256=S2aEhAAdwNk2p2d8rGG78MoN1g19zTUxYQhCQ-febQI,56328
|
|
8
9
|
celldetective/neighborhood.py,sha256=GjF_fTmtcU8jq5XZT-DnDSp28VDTrsaHjIGJG7W2Ppk,52799
|
|
9
10
|
celldetective/preprocessing.py,sha256=iV5or20s8XPJXQZDsWzis7OBaqzPDyAo_cwcDVDuX5A,38188
|
|
10
11
|
celldetective/relative_measurements.py,sha256=qPHC6gtUaICNGKh6Qfh8y6NA4OUJauGcaj9_TUbrswg,24998
|
|
11
|
-
celldetective/segmentation.py,sha256=
|
|
12
|
+
celldetective/segmentation.py,sha256=NjAVaVpZufpJ-LIvBx5UFH6j5kjRKE6EVLjttDrqaqs,30826
|
|
12
13
|
celldetective/signals.py,sha256=485axzJwE3xsSsN4AmBQlZ2at7uWu9QRg0XrUHvLBUE,120552
|
|
13
14
|
celldetective/tracking.py,sha256=HGeOtfQ-nmpaea9_Q-sq2WcwZfdPXRH028JyFlt6eUs,37901
|
|
14
|
-
celldetective/utils.py,sha256=
|
|
15
|
+
celldetective/utils.py,sha256=4Nf6jGMdd6kOcF3z0Q_3J8LLZ7Y3fOJKYUwGzUlzk3Q,88768
|
|
15
16
|
celldetective/datasets/segmentation_annotations/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
17
|
celldetective/datasets/signal_annotations/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
18
|
celldetective/gui/InitWindow.py,sha256=M0v58BmOuZ05em2WSXHpDar3tp2AeS6pr44ZBtiYz-4,13683
|
|
18
19
|
celldetective/gui/__init__.py,sha256=2_r2xfOj4_2xj0yBkCTIfzlF94AHKm-j6Pvpd7DddQc,989
|
|
19
|
-
celldetective/gui/about.py,sha256=
|
|
20
|
+
celldetective/gui/about.py,sha256=FJZrj6C-p6uqp_3UaprKosuW-Sw9_HPNQAvFbis9Gdk,1749
|
|
20
21
|
celldetective/gui/analyze_block.py,sha256=sat8RECEeZxlaconZZIxI0IrIjwJo121PcBXqJmA1-o,24756
|
|
21
22
|
celldetective/gui/btrack_options.py,sha256=OrvbG5coZhLRk5jtXiLFJu34z1hWHN9kHkBiZ7XMZoI,39269
|
|
22
|
-
celldetective/gui/classifier_widget.py,sha256=
|
|
23
|
+
celldetective/gui/classifier_widget.py,sha256=cjCuBGvNPI9OQeBeLlLI2h9mDUHHslWkJRjn4JIuVOw,16098
|
|
23
24
|
celldetective/gui/configure_new_exp.py,sha256=Eyr-M4FH-4xrUJbIGNdKXAtXb_ULr8lCol26JSzXEww,20125
|
|
24
|
-
celldetective/gui/control_panel.py,sha256=
|
|
25
|
+
celldetective/gui/control_panel.py,sha256=JbS47IFeVHc1D2olWiUB_Lo9Bn20QJCPVwhXnRdlFec,18990
|
|
25
26
|
celldetective/gui/generic_signal_plot.py,sha256=OzlFr2RxspkyxW1YIhl_c3q63RXAngMPouYPhEtk0S0,29509
|
|
26
|
-
celldetective/gui/gui_utils.py,sha256=
|
|
27
|
+
celldetective/gui/gui_utils.py,sha256=afqJTeVyMa49QEbSmT0hAiXGd7EiKzckOD5tMvUr-aY,27901
|
|
27
28
|
celldetective/gui/json_readers.py,sha256=Su3angSobroeGrrumGgQcs3Cr_9l9p52-Hfm3qneVcI,3664
|
|
28
29
|
celldetective/gui/layouts.py,sha256=feeAYh7I21ZgH-x4I7lg2DAvz5PBm71D7MovCrtb9QE,47325
|
|
29
|
-
celldetective/gui/measurement_options.py,sha256=
|
|
30
|
+
celldetective/gui/measurement_options.py,sha256=0CAxM61NqfWVsBaHD1cuC04kMER96-Tgom1p8G5X7Qg,39249
|
|
30
31
|
celldetective/gui/neighborhood_options.py,sha256=BvWwsIX1KWogUgHWRZptqY3ZRmH1aj7r8tiLmbRFhW4,19783
|
|
31
32
|
celldetective/gui/plot_measurements.py,sha256=SBFkY3542hW4H_vllOCMxMOgBz09KUE2FLhhgI8avXk,51024
|
|
32
33
|
celldetective/gui/plot_signals_ui.py,sha256=dQINAOyOmAOHMfmjfCsx1hJ7HGeCgNCq8kESuADbQqo,15864
|
|
@@ -34,12 +35,12 @@ celldetective/gui/process_block.py,sha256=PdgKKEfr-GFad3P1y0n9LB_OaIBvuBuaQdBRtz
|
|
|
34
35
|
celldetective/gui/retrain_segmentation_model_options.py,sha256=eGGrimwmNfJXRPRpZ6NZ74TWx6TK70HoNRHnJl1_8XQ,22292
|
|
35
36
|
celldetective/gui/retrain_signal_model_options.py,sha256=XigNdGlNu3KWB_MYBcKQhfXjcWwVZNMmu0qmxNoo14E,21919
|
|
36
37
|
celldetective/gui/seg_model_loader.py,sha256=vWvPMU6nkTiQfI-x2WjQHrdJGFdV4a4Ne-4YIOq_YZ8,18153
|
|
37
|
-
celldetective/gui/signal_annotator.py,sha256=
|
|
38
|
+
celldetective/gui/signal_annotator.py,sha256=z205XhTZjKPo9L4lzt-g-uLiu9vk6V1w5zUlxu3bWqQ,87379
|
|
38
39
|
celldetective/gui/signal_annotator2.py,sha256=Y43JSCg3bO_QL7zsqdeQ-FY8TNq_5WiV1cHO-knK0QE,109375
|
|
39
40
|
celldetective/gui/signal_annotator_options.py,sha256=ztFFgA70SJ0QkntxYGsgDNCvSuSR5GjF7_J6pYVYc1g,11020
|
|
40
41
|
celldetective/gui/styles.py,sha256=fup0_U1Zk0IJAjyruHde_r-X7d7cTMcrpPLPl-HuKAM,4820
|
|
41
42
|
celldetective/gui/survival_ui.py,sha256=tzvtI0eC0bgd7Lfm20HqU7rfCJ60gy9a_Sc9cZLSKhs,9439
|
|
42
|
-
celldetective/gui/tableUI.py,sha256=
|
|
43
|
+
celldetective/gui/tableUI.py,sha256=sHmXjRhMYjYY4qCrUyMKZf4O-PorvJuMzHbVwshSNVs,46167
|
|
43
44
|
celldetective/gui/thresholds_gui.py,sha256=t1hTDdaJTJWHBt9vtRCE4B1TsVHhobQwCpKfntVgaQI,50510
|
|
44
45
|
celldetective/gui/viewers.py,sha256=mCyE9DaUX2rGq65oYEwyEfNtEUPrHdoFNg2MRp1kXs4,27701
|
|
45
46
|
celldetective/gui/help/DL-segmentation-strategy.json,sha256=59jVtn8pECbCqPQwJifgViVYTF1AxLQDIkNJMS7CJuk,1143
|
|
@@ -62,7 +63,7 @@ celldetective/icons/splash0.png,sha256=qVXsrYUinm5g6-vbHcqwyjh8SIqs9lEqPWnPa1Wij
|
|
|
62
63
|
celldetective/icons/survival2.png,sha256=8zsualD7d9VPAecoFA4Om9TFARErqpJzMg6U7XANXf4,4479
|
|
63
64
|
celldetective/icons/vignette_signals2.png,sha256=hsVOdQDpEfMGM45aaSeacEm3lvxbquRKKYutiS9qoS0,20743
|
|
64
65
|
celldetective/icons/vignette_signals2.svg,sha256=muGNcQudV1jG-bmFd9FwV-Wb8PcrRV5osdZ7pHR7Ekk,5947
|
|
65
|
-
celldetective/links/zenodo.json,sha256=
|
|
66
|
+
celldetective/links/zenodo.json,sha256=qxbRAWRRXgeM4GMPhcvuRFWviI_W0pwovQOq_k5I6Uw,30216
|
|
66
67
|
celldetective/models/pair_signal_detection/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
68
|
celldetective/models/segmentation_effectors/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
68
69
|
celldetective/models/segmentation_generic/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -87,13 +88,13 @@ tests/test_measure.py,sha256=FEUAs1rVHylvIvubCb0bJDNGZLVmkgXNgI3NaGQ1dA8,4542
|
|
|
87
88
|
tests/test_neighborhood.py,sha256=gk5FmoI7ANEczUtNXYRxc48KzkfYzemwS_eYaLq4_NI,2093
|
|
88
89
|
tests/test_preprocessing.py,sha256=FI-Wk-kc4wWmOQg_NLCUIZC1oti396wr5cC-BauBai0,1436
|
|
89
90
|
tests/test_qt.py,sha256=eYqOoff-vfvZAZM6H_IY19IqK7qzyETcyj54f9T0bQA,3906
|
|
90
|
-
tests/test_segmentation.py,sha256=
|
|
91
|
+
tests/test_segmentation.py,sha256=k1b_zIZdlytEdJcHjAUQEO3gTBAHtv5WvrwQN2xD4kc,3470
|
|
91
92
|
tests/test_signals.py,sha256=No4cah6KxplhDcKXnU8RrA7eDla4hWw6ccf7xGnBokU,3599
|
|
92
93
|
tests/test_tracking.py,sha256=8hebWSqEIuttD1ABn-6dKCT7EXKRR7-4RwyFWi1WPFo,8800
|
|
93
94
|
tests/test_utils.py,sha256=NKRCAC1d89aBK5cWjTb7-pInYow901RrT-uBlIdz4KI,3692
|
|
94
|
-
celldetective-1.
|
|
95
|
-
celldetective-1.
|
|
96
|
-
celldetective-1.
|
|
97
|
-
celldetective-1.
|
|
98
|
-
celldetective-1.
|
|
99
|
-
celldetective-1.
|
|
95
|
+
celldetective-1.3.0.post1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
96
|
+
celldetective-1.3.0.post1.dist-info/METADATA,sha256=QzPTXc4_K4xgHDJ60xC7CaHbejQ9wjSXGwOEGj1LYrc,9971
|
|
97
|
+
celldetective-1.3.0.post1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
98
|
+
celldetective-1.3.0.post1.dist-info/entry_points.txt,sha256=2NU6_EOByvPxqBbCvjwxlVlvnQreqZ3BKRCVIKEv3dg,62
|
|
99
|
+
celldetective-1.3.0.post1.dist-info/top_level.txt,sha256=6rsIKKfGMKgud7HPuATcpq6EhdXwcg_yknBVWn9x4C4,20
|
|
100
|
+
celldetective-1.3.0.post1.dist-info/RECORD,,
|
tests/test_segmentation.py
CHANGED
|
@@ -21,11 +21,12 @@ class TestDLMCF7Segmentation(unittest.TestCase):
|
|
|
21
21
|
with open(TEST_CONFIG_FILENAME) as config_file:
|
|
22
22
|
self.config = json.load(config_file)
|
|
23
23
|
self.channels = self.config['channels']
|
|
24
|
+
print(f'{self.channels=}')
|
|
24
25
|
self.spatial_calibration = self.config['spatial_calibration']
|
|
25
26
|
|
|
26
27
|
def test_correct_segmentation_with_multimodal_model(self):
|
|
27
28
|
|
|
28
|
-
labels = segment(self.stack, "
|
|
29
|
+
labels = segment(self.stack, "mcf7_nuc_multimodal", channels=self.channels, spatial_calibration=self.spatial_calibration, view_on_napari=False,
|
|
29
30
|
use_gpu=False)
|
|
30
31
|
np.testing.assert_array_equal(labels[0], labels[1])
|
|
31
32
|
|
|
@@ -39,11 +40,11 @@ class TestDLMCF7Segmentation(unittest.TestCase):
|
|
|
39
40
|
m.update_state(self.binary_label_true, label_binary)
|
|
40
41
|
score = m.result().numpy()
|
|
41
42
|
|
|
42
|
-
self.assertGreater(score,0.
|
|
43
|
+
self.assertGreater(score,0.85)
|
|
43
44
|
|
|
44
45
|
def test_correct_segmentation_with_transferred_model(self):
|
|
45
46
|
|
|
46
|
-
labels = segment(self.stack, "
|
|
47
|
+
labels = segment(self.stack, "mcf7_nuc_stardist_transfer", channels=self.channels, spatial_calibration=self.spatial_calibration, view_on_napari=False,
|
|
47
48
|
use_gpu=True)
|
|
48
49
|
np.testing.assert_array_equal(labels[0], labels[1])
|
|
49
50
|
|
|
@@ -57,7 +58,7 @@ class TestDLMCF7Segmentation(unittest.TestCase):
|
|
|
57
58
|
m.update_state(self.binary_label_true, label_binary)
|
|
58
59
|
score = m.result().numpy()
|
|
59
60
|
|
|
60
|
-
self.assertGreater(score,0.
|
|
61
|
+
self.assertGreater(score,0.85)
|
|
61
62
|
|
|
62
63
|
|
|
63
64
|
class TestThresholdMCF7Segmentation(unittest.TestCase):
|
|
File without changes
|
|
File without changes
|
{celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|