celldetective 1.3.8.post1__py3-none-any.whl → 1.3.9__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 (31) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/extra_properties.py +113 -17
  3. celldetective/filters.py +12 -12
  4. celldetective/gui/btrack_options.py +1 -1
  5. celldetective/gui/control_panel.py +1 -1
  6. celldetective/gui/gui_utils.py +4 -4
  7. celldetective/gui/measurement_options.py +1 -1
  8. celldetective/gui/plot_signals_ui.py +23 -6
  9. celldetective/gui/process_block.py +1 -1
  10. celldetective/gui/processes/measure_cells.py +4 -4
  11. celldetective/gui/processes/segment_cells.py +3 -3
  12. celldetective/gui/processes/track_cells.py +4 -4
  13. celldetective/gui/signal_annotator.py +26 -6
  14. celldetective/gui/signal_annotator2.py +1 -1
  15. celldetective/gui/signal_annotator_options.py +1 -1
  16. celldetective/gui/survival_ui.py +4 -1
  17. celldetective/gui/thresholds_gui.py +6 -5
  18. celldetective/io.py +1 -44
  19. celldetective/measure.py +22 -16
  20. celldetective/regionprops/__init__.py +1 -0
  21. celldetective/regionprops/_regionprops.py +310 -0
  22. celldetective/regionprops/props.json +63 -0
  23. celldetective/scripts/measure_relative.py +2 -20
  24. celldetective/segmentation.py +14 -4
  25. celldetective/utils.py +182 -171
  26. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.dist-info}/METADATA +1 -1
  27. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.dist-info}/RECORD +31 -28
  28. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.dist-info}/LICENSE +0 -0
  29. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.dist-info}/WHEEL +0 -0
  30. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.dist-info}/entry_points.txt +0 -0
  31. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.dist-info}/top_level.txt +0 -0
celldetective/utils.py CHANGED
@@ -31,6 +31,55 @@ from scipy.stats import ks_2samp
31
31
  from cliffs_delta import cliffs_delta
32
32
  from stardist.models import StarDist2D
33
33
  from cellpose.models import CellposeModel
34
+ from pathlib import PosixPath, PurePosixPath
35
+
36
+ def get_config(experiment):
37
+
38
+ """
39
+ Retrieves the path to the configuration file for a given experiment.
40
+
41
+ Parameters
42
+ ----------
43
+ experiment : str
44
+ The file system path to the directory of the experiment project.
45
+
46
+ Returns
47
+ -------
48
+ str
49
+ The full path to the configuration file (`config.ini`) within the experiment directory.
50
+
51
+ Raises
52
+ ------
53
+ AssertionError
54
+ If the `config.ini` file does not exist in the specified experiment directory.
55
+
56
+ Notes
57
+ -----
58
+ - The function ensures that the provided experiment path ends with the appropriate file separator (`os.sep`)
59
+ before appending `config.ini` to locate the configuration file.
60
+ - The configuration file is expected to be named `config.ini` and located at the root of the experiment directory.
61
+
62
+ Example
63
+ -------
64
+ >>> experiment = "/path/to/experiment"
65
+ >>> config_path = get_config(experiment)
66
+ >>> print(config_path)
67
+ '/path/to/experiment/config.ini'
68
+
69
+ """
70
+
71
+ if isinstance(experiment, (PosixPath, PurePosixPath)):
72
+ experiment = str(experiment)
73
+
74
+ if not experiment.endswith(os.sep):
75
+ experiment += os.sep
76
+
77
+ config = experiment + 'config.ini'
78
+ config = rf"{config}"
79
+
80
+ assert os.path.exists(config), 'The experiment configuration could not be located...'
81
+ return config
82
+
34
83
 
35
84
  def _remove_invalid_cols(df):
36
85
 
@@ -38,8 +87,7 @@ def _remove_invalid_cols(df):
38
87
  Removes invalid columns from a DataFrame.
39
88
 
40
89
  This function identifies and removes columns in the DataFrame whose names
41
- start with "Unnamed", which often indicate extraneous or improperly
42
- formatted columns (e.g., leftover columns from improperly read CSV files).
90
+ start with "Unnamed", or that contain only NaN values.
43
91
 
44
92
  Parameters
45
93
  ----------
@@ -51,32 +99,22 @@ def _remove_invalid_cols(df):
51
99
  pandas.DataFrame
52
100
  A new DataFrame with the invalid columns removed. If no invalid
53
101
  columns are found, the original DataFrame is returned unchanged.
54
-
55
- Notes
56
- -----
57
- - This function does not modify the original DataFrame in place; instead,
58
- it returns a new DataFrame.
59
- - Columns starting with "Unnamed" are commonly introduced when saving
60
- or loading data files with misaligned headers.
61
102
  """
62
103
 
63
104
  invalid_cols = [c for c in list(df.columns) if c.startswith('Unnamed')]
64
105
  if len(invalid_cols)>0:
65
106
  df = df.drop(invalid_cols, axis=1)
107
+ df = df.dropna(axis=1, how='all')
66
108
  return df
67
109
 
68
- def _extract_coordinates_from_features(features, timepoint):
110
+ def _extract_coordinates_from_features(df, timepoint):
69
111
 
70
112
  """
71
- Extracts spatial coordinates and other relevant metadata from a features DataFrame.
72
-
73
- This function processes a DataFrame of features to extract and rename centroid
74
- coordinates, assign a unique identifier to each feature, and add a frame (timepoint)
75
- column. The resulting DataFrame is structured for use in trajectory analysis.
113
+ Re-format coordinates from a regionprops table to tracking/measurement table format.
76
114
 
77
115
  Parameters
78
116
  ----------
79
- features : pandas.DataFrame
117
+ df : pandas.DataFrame
80
118
  A DataFrame containing feature data, including columns for centroids
81
119
  (`'centroid-1'` and `'centroid-0'`) and feature classes (`'class_id'`).
82
120
  timepoint : int
@@ -103,7 +141,7 @@ def _extract_coordinates_from_features(features, timepoint):
103
141
  to `'POSITION_Y'`.
104
142
  """
105
143
 
106
- coords = features[['centroid-1', 'centroid-0', 'class_id']].copy()
144
+ coords = df[['centroid-1', 'centroid-0', 'class_id']].copy()
107
145
  coords['ID'] = np.arange(len(coords))
108
146
  coords.rename(columns={'centroid-1': 'POSITION_X', 'centroid-0': 'POSITION_Y'}, inplace=True)
109
147
  coords['FRAME'] = int(timepoint)
@@ -143,10 +181,13 @@ def _mask_intensity_measurements(df, mask_channels):
143
181
  does not modify the input DataFrame.
144
182
  """
145
183
 
184
+ if isinstance(mask_channels, str):
185
+ mask_channels = [mask_channels]
186
+
146
187
  if mask_channels is not None:
147
188
 
148
189
  cols_to_drop = []
149
- columns = df.columns
190
+ columns = list(df.columns)
150
191
 
151
192
  for mc in mask_channels:
152
193
  cols_to_remove = [c for c in columns if mc in c]
@@ -156,7 +197,7 @@ def _mask_intensity_measurements(df, mask_channels):
156
197
  df = df.drop(cols_to_drop, axis=1)
157
198
  return df
158
199
 
159
- def _rearrange_multichannel_frame(frame):
200
+ def _rearrange_multichannel_frame(frame, n_channels=None):
160
201
 
161
202
  """
162
203
  Rearranges the axes of a multi-channel frame to ensure the channel axis is at the end.
@@ -206,7 +247,10 @@ def _rearrange_multichannel_frame(frame):
206
247
 
207
248
  if frame.ndim == 3:
208
249
  # Systematically move channel axis to the end
209
- channel_axis = np.argmin(frame.shape)
250
+ if n_channels is not None and n_channels in list(frame.shape):
251
+ channel_axis = list(frame.shape).index(n_channels)
252
+ else:
253
+ channel_axis = np.argmin(frame.shape)
210
254
  frame = np.moveaxis(frame, channel_axis, -1)
211
255
 
212
256
  if frame.ndim==2:
@@ -1083,9 +1127,11 @@ def mask_edges(binary_mask, border_size):
1083
1127
  return binary_mask
1084
1128
 
1085
1129
  def demangle_column_name(name):
1086
- if name.startswith("BACKTICK_QUOTED_STRING_"):
1130
+ if name.startswith("BACKTICK_QUOTED_STRING_") and not '_MINUS_' in name:
1087
1131
  # Unquote backtick-quoted string.
1088
1132
  return name[len("BACKTICK_QUOTED_STRING_"):].replace("_DOT_", ".").replace("_SLASH_", "/")
1133
+ elif name.startswith("BACKTICK_QUOTED_STRING_") and '_MINUS_' in name:
1134
+ return name[len("BACKTICK_QUOTED_STRING_"):].replace("_DOT_", ".").replace("_SLASH_", "/").replace('_MINUS_','-')
1089
1135
  return name
1090
1136
 
1091
1137
  def extract_cols_from_query(query: str):
@@ -1792,6 +1838,21 @@ def ConfigSectionMap(path,section):
1792
1838
 
1793
1839
  dict1: dictionary
1794
1840
 
1841
+ Examples
1842
+ --------
1843
+ >>> config = "path/to/config_file.ini"
1844
+ >>> section = "Channels"
1845
+ >>> channel_dictionary = ConfigSectionMap(config,section)
1846
+ >>> print(channel_dictionary)
1847
+ # {'brightfield_channel': '0',
1848
+ # 'live_nuclei_channel': 'nan',
1849
+ # 'dead_nuclei_channel': 'nan',
1850
+ # 'effector_fluo_channel': 'nan',
1851
+ # 'adhesion_channel': '1',
1852
+ # 'fluo_channel_1': 'nan',
1853
+ # 'fluo_channel_2': 'nan',
1854
+ # 'fitc_channel': '2',
1855
+ # 'cy5_channel': '3'}
1795
1856
  """
1796
1857
 
1797
1858
  Config = configparser.ConfigParser(interpolation=None)
@@ -1846,14 +1907,16 @@ def _extract_channel_indices_from_config(config, channels_to_extract):
1846
1907
 
1847
1908
  Examples
1848
1909
  --------
1849
- >>> config = ConfigParser()
1850
- >>> config.read('example_config.ini')
1851
- >>> channels_to_extract = ['GFP', 'RFP']
1910
+ >>> config = "path/to/config_file.ini"
1911
+ >>> channels_to_extract = ['adhesion_channel', 'brightfield_channel']
1852
1912
  >>> channel_indices = _extract_channel_indices_from_config(config, channels_to_extract)
1853
1913
  >>> print(channel_indices)
1854
- # [1, 2] or None if an error occurs or the channels are not found.
1914
+ # [1, 0] or None if an error occurs or the channels are not found.
1855
1915
  """
1856
1916
 
1917
+ if isinstance(channels_to_extract, str):
1918
+ channels_to_extract = [channels_to_extract]
1919
+
1857
1920
  channels = []
1858
1921
  for c in channels_to_extract:
1859
1922
  try:
@@ -1870,44 +1933,13 @@ def _extract_channel_indices_from_config(config, channels_to_extract):
1870
1933
  def _extract_nbr_channels_from_config(config, return_names=False):
1871
1934
 
1872
1935
  """
1873
- Extracts the indices of specified channels from a configuration object.
1874
-
1875
- This function attempts to map required channel names to their respective indices as specified in a
1876
- configuration file. It supports two versions of configuration parsing: a primary method (V2) and a
1877
- fallback legacy method. If the required channels are not found using the primary method, the function
1878
- attempts to find them using the legacy configuration settings.
1879
-
1880
- Parameters
1881
- ----------
1882
- config : ConfigParser object
1883
- The configuration object parsed from a .ini or similar configuration file that includes channel settings.
1884
- channels_to_extract : list of str
1885
- A list of channel names for which indices are to be extracted from the configuration settings.
1886
-
1887
- Returns
1888
- -------
1889
- list of int or None
1890
- A list containing the indices of the specified channels as found in the configuration settings.
1891
- If a channel cannot be found, None is appended in its place. If an error occurs during the extraction
1892
- process, the function returns None.
1893
-
1894
- Notes
1895
- -----
1896
- - This function is designed to be flexible, accommodating changes in configuration file structure by
1897
- checking multiple sections for the required information.
1898
- - The configuration file is expected to contain either "Channels" or "MovieSettings" sections with mappings
1899
- from channel names to indices.
1900
- - An error message is printed if a required channel cannot be found, advising the user to check the
1901
- configuration file.
1902
1936
 
1903
1937
  Examples
1904
1938
  --------
1905
- >>> config = ConfigParser()
1906
- >>> config.read('example_config.ini')
1907
- >>> channels_to_extract = ['GFP', 'RFP']
1908
- >>> channel_indices = _extract_channel_indices_from_config(config, channels_to_extract)
1909
- >>> print(channel_indices)
1910
- # [1, 2] or None if an error occurs or the channels are not found.
1939
+ >>> config = "path/to/config_file.ini"
1940
+ >>> nbr_channels = _extract_channel_indices_from_config(config)
1941
+ >>> print(nbr_channels)
1942
+ # 4
1911
1943
  """
1912
1944
 
1913
1945
  # V2
@@ -2019,16 +2051,26 @@ def _get_img_num_per_channel(channels_indices, len_movie, nbr_channels):
2019
2051
 
2020
2052
  Examples
2021
2053
  --------
2022
- >>> channels_indices = [0, 2, None] # Indices for channels 1, 3, and a non-existing channel
2054
+ >>> channels_indices = [0] # Indices for channels 1, 3, and a non-existing channel
2055
+ >>> len_movie = 10 # Total frames for each channel
2056
+ >>> nbr_channels = 3 # Total channels in the movie
2057
+ >>> img_num_per_channel = _get_img_num_per_channel(channels_indices, len_movie, nbr_channels)
2058
+ >>> print(img_num_per_channel)
2059
+ # array([[ 0, 3, 6, 9, 12, 15, 18, 21, 24, 27]])
2060
+
2061
+ >>> channels_indices = [1,2] # Indices for channels 1, 3, and a non-existing channel
2023
2062
  >>> len_movie = 10 # Total frames for each channel
2024
2063
  >>> nbr_channels = 3 # Total channels in the movie
2025
2064
  >>> img_num_per_channel = _get_img_num_per_channel(channels_indices, len_movie, nbr_channels)
2026
2065
  >>> print(img_num_per_channel)
2027
- # [[ 0 3 6 9 12 15 18 21 24 27]
2028
- # [ 2 5 8 11 14 17 20 23 26 29]
2029
- # [-1 -1 -1 -1 -1 -1 -1 -1 -1 -1]]
2066
+ # array([[ 1, 4, 7, 10, 13, 16, 19, 22, 25, 28],
2067
+ # [ 2, 5, 8, 11, 14, 17, 20, 23, 26, 29]])
2068
+
2030
2069
  """
2031
2070
 
2071
+ if isinstance(channels_indices, (int, np.int_)):
2072
+ channels_indices = [channels_indices]
2073
+
2032
2074
  len_movie = int(len_movie)
2033
2075
  nbr_channels = int(nbr_channels)
2034
2076
 
@@ -2039,7 +2081,8 @@ def _get_img_num_per_channel(channels_indices, len_movie, nbr_channels):
2039
2081
  else:
2040
2082
  indices = [-1]*len_movie
2041
2083
  img_num_all_channels.append(indices)
2042
- img_num_all_channels = np.array(img_num_all_channels, dtype=int)
2084
+ img_num_all_channels = np.array(img_num_all_channels, dtype=int)
2085
+
2043
2086
  return img_num_all_channels
2044
2087
 
2045
2088
  def _extract_labels_from_config(config,number_of_wells):
@@ -2063,6 +2106,9 @@ def _extract_labels_from_config(config,number_of_wells):
2063
2106
  labels: string of the biological condition for each well
2064
2107
 
2065
2108
  """
2109
+
2110
+ # Deprecated, need to read metadata to extract concentration units and discard non essential fields
2111
+
2066
2112
 
2067
2113
  try:
2068
2114
  concentrations = ConfigSectionMap(config,"Labels")["concentrations"].split(",")
@@ -2082,20 +2128,15 @@ def _extract_labels_from_config(config,number_of_wells):
2082
2128
 
2083
2129
  return(labels)
2084
2130
 
2085
- def extract_experiment_channels(config):
2131
+
2132
+ def _extract_channels_from_config(config):
2086
2133
 
2087
2134
  """
2088
2135
  Extracts channel names and their indices from an experiment configuration.
2089
2136
 
2090
- This function attempts to parse channel information from a given configuration object, supporting
2091
- both a newer (V2) and a legacy format. It first tries to extract channel names and indices according
2092
- to the V2 format from the "Channels" section. If no channels are found or if the section does not
2093
- exist, it falls back to extracting specific channel information from the "MovieSettings" section
2094
- based on predefined channel names.
2095
-
2096
2137
  Parameters
2097
2138
  ----------
2098
- config : ConfigParser object
2139
+ config : path to config file (.ini)
2099
2140
  The configuration object parsed from an experiment's .ini or similar configuration file.
2100
2141
 
2101
2142
  Returns
@@ -2105,23 +2146,17 @@ def extract_experiment_channels(config):
2105
2146
  the names of the channels as specified in the configuration, and `channel_indices` includes their
2106
2147
  corresponding indices. Both arrays are ordered according to the channel indices.
2107
2148
 
2108
- Notes
2109
- -----
2110
- - The function supports extracting a variety of channel types, including brightfield, live and dead nuclei
2111
- channels, effector fluorescence channels, adhesion channels, and generic fluorescence channels.
2112
- - If channel information cannot be parsed or if required fields are missing, the function returns empty arrays.
2113
- - This utility is particularly useful for preprocessing steps where specific channels of multi-channel
2114
- experimental data are needed for further analysis or model input.
2115
-
2116
2149
  Examples
2117
2150
  --------
2118
- >>> config = ConfigParser()
2119
- >>> config.read('experiment_config.ini')
2120
- >>> channel_names, channel_indices = extract_experiment_channels(config)
2121
- # Extracts and sorts channel information based on indices from the experiment configuration.
2151
+ >>> config = "path/to/config_file.ini"
2152
+ >>> channels, indices = _extract_channels_from_config(config)
2153
+ >>> print(channels)
2154
+ # array(['brightfield_channel', 'adhesion_channel', 'fitc_channel',
2155
+ # 'cy5_channel'], dtype='<U19')
2156
+ >>> print(indices)
2157
+ # array([0, 1, 2, 3])
2122
2158
  """
2123
2159
 
2124
- # V2
2125
2160
  channel_names = []
2126
2161
  channel_indices = []
2127
2162
  try:
@@ -2135,64 +2170,7 @@ def extract_experiment_channels(config):
2135
2170
  pass
2136
2171
  except:
2137
2172
  pass
2138
-
2139
-
2140
- if not channel_names:
2141
- # LEGACY
2142
- # Remap intensities to channel:
2143
- channel_names = []
2144
- channel_indices = []
2145
-
2146
- try:
2147
- brightfield_channel = int(ConfigSectionMap(config,"MovieSettings")["brightfield_channel"])
2148
- channel_names.append("brightfield_channel")
2149
- channel_indices.append(brightfield_channel)
2150
- #exp_channels.update({"brightfield_channel": brightfield_channel})
2151
- except:
2152
- pass
2153
- try:
2154
- live_nuclei_channel = int(ConfigSectionMap(config,"MovieSettings")["live_nuclei_channel"])
2155
- channel_names.append("live_nuclei_channel")
2156
- channel_indices.append(live_nuclei_channel)
2157
- #exp_channels.update({"live_nuclei_channel": live_nuclei_channel})
2158
- except:
2159
- pass
2160
- try:
2161
- dead_nuclei_channel = int(ConfigSectionMap(config,"MovieSettings")["dead_nuclei_channel"])
2162
- channel_names.append("dead_nuclei_channel")
2163
- channel_indices.append(dead_nuclei_channel)
2164
- #exp_channels.update({"dead_nuclei_channel": dead_nuclei_channel})
2165
- except:
2166
- pass
2167
- try:
2168
- effector_fluo_channel = int(ConfigSectionMap(config,"MovieSettings")["effector_fluo_channel"])
2169
- channel_names.append("effector_fluo_channel")
2170
- channel_indices.append(effector_fluo_channel)
2171
- #exp_channels.update({"effector_fluo_channel": effector_fluo_channel})
2172
- except:
2173
- pass
2174
- try:
2175
- adhesion_channel = int(ConfigSectionMap(config,"MovieSettings")["adhesion_channel"])
2176
- channel_names.append("adhesion_channel")
2177
- channel_indices.append(adhesion_channel)
2178
- #exp_channels.update({"adhesion_channel": adhesion_channel})
2179
- except:
2180
- pass
2181
- try:
2182
- fluo_channel_1 = int(ConfigSectionMap(config,"MovieSettings")["fluo_channel_1"])
2183
- channel_names.append("fluo_channel_1")
2184
- channel_indices.append(fluo_channel_1)
2185
- #exp_channels.update({"fluo_channel_1": fluo_channel_1})
2186
- except:
2187
- pass
2188
- try:
2189
- fluo_channel_2 = int(ConfigSectionMap(config,"MovieSettings")["fluo_channel_2"])
2190
- channel_names.append("fluo_channel_2")
2191
- channel_indices.append(fluo_channel_2)
2192
- #exp_channels.update({"fluo_channel_2": fluo_channel_2})
2193
- except:
2194
- pass
2195
-
2173
+
2196
2174
  channel_indices = np.array(channel_indices)
2197
2175
  channel_names = np.array(channel_names)
2198
2176
  reorder = np.argsort(channel_indices)
@@ -2201,61 +2179,94 @@ def extract_experiment_channels(config):
2201
2179
 
2202
2180
  return channel_names, channel_indices
2203
2181
 
2182
+
2183
+ def extract_experiment_channels(experiment):
2184
+
2185
+ """
2186
+ Extracts channel names and their indices from an experiment project.
2187
+
2188
+ Parameters
2189
+ ----------
2190
+ experiment : str
2191
+ The file system path to the directory of the experiment project.
2192
+
2193
+ Returns
2194
+ -------
2195
+ tuple
2196
+ A tuple containing two numpy arrays: `channel_names` and `channel_indices`. `channel_names` includes
2197
+ the names of the channels as specified in the configuration, and `channel_indices` includes their
2198
+ corresponding indices. Both arrays are ordered according to the channel indices.
2199
+
2200
+ Examples
2201
+ --------
2202
+ >>> experiment = "path/to/my_experiment"
2203
+ >>> channels, indices = extract_experiment_channels(experiment)
2204
+ >>> print(channels)
2205
+ # array(['brightfield_channel', 'adhesion_channel', 'fitc_channel',
2206
+ # 'cy5_channel'], dtype='<U19')
2207
+ >>> print(indices)
2208
+ # array([0, 1, 2, 3])
2209
+ """
2210
+
2211
+ config = get_config(experiment)
2212
+ return _extract_channels_from_config(config)
2213
+
2214
+
2204
2215
  def get_software_location():
2205
- return rf"{os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]}"
2206
2216
 
2207
- def remove_trajectory_measurements(trajectories, column_labels):
2217
+ """
2218
+ Get the installation folder of celldetective.
2208
2219
 
2220
+ Returns
2221
+ -------
2222
+ str
2223
+ Path to the celldetective installation folder.
2209
2224
  """
2210
- Filters a DataFrame of trajectory measurements to retain only essential tracking and classification columns.
2211
2225
 
2212
- Given a DataFrame containing detailed trajectory measurements and metadata for tracked objects, this
2213
- function reduces the DataFrame to include only a predefined set of essential columns necessary for
2214
- further analysis or visualization. The set of columns to retain includes basic tracking information,
2215
- spatial coordinates, classification results, and certain metadata.
2226
+ return rf"{os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]}"
2227
+
2228
+ def remove_trajectory_measurements(trajectories, column_labels={'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y'}):
2229
+
2230
+ """
2231
+ Clear a measurement table, while keeping the tracking information.
2216
2232
 
2217
2233
  Parameters
2218
2234
  ----------
2219
2235
  trajectories : pandas.DataFrame
2220
- The DataFrame containing trajectory measurements and metadata for each tracked object.
2221
- column_labels : dict
2222
- A dictionary mapping standard column names to their corresponding column names in the `trajectories` DataFrame.
2223
- Expected keys include 'track', 'time', 'x', 'y', among others.
2236
+ The measurement table where each line is a cell at a timepoint and each column a tracking feature or measurement.
2237
+ column_labels : dict, optional
2238
+ The column labels to use in the output DataFrame. Default is {'track': "TRACK_ID", 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y'}.
2239
+
2224
2240
 
2225
2241
  Returns
2226
2242
  -------
2227
2243
  pandas.DataFrame
2228
- A filtered DataFrame containing only the essential columns as defined by `columns_to_keep` and present
2229
- in `trajectories`.
2230
-
2231
- Notes
2232
- -----
2233
- - The function dynamically adjusts the list of columns to retain based on their presence in the input DataFrame,
2234
- ensuring compatibility with DataFrames containing varying sets of measurements.
2235
- - Essential columns include tracking identifiers, time points, spatial coordinates (both pixel and physical units),
2236
- classification labels, state information, lineage metadata, and visualization attributes.
2244
+ A filtered DataFrame containing only the tracking columns.
2237
2245
 
2238
2246
  Examples
2239
2247
  --------
2240
- >>> column_labels = {
2241
- ... 'track': 'TRACK_ID', 'time': 'FRAME', 'x': 'POSITION_X', 'y': 'POSITION_Y'
2242
- ... }
2243
2248
  >>> trajectories_df = pd.DataFrame({
2244
2249
  ... 'TRACK_ID': [1, 1, 2],
2245
2250
  ... 'FRAME': [0, 1, 0],
2246
2251
  ... 'POSITION_X': [100, 105, 200],
2247
2252
  ... 'POSITION_Y': [150, 155, 250],
2248
- ... 'velocity': [0.5, 0.5, 0.2], # Additional column to be removed
2253
+ ... 'area': [10,100,100], # Additional column to be removed
2249
2254
  ... })
2250
- >>> filtered_df = remove_trajectory_measurements(trajectories_df, column_labels)
2251
- # `filtered_df` will contain only the essential columns as per `column_labels` and predefined essential columns.
2255
+ >>> filtered_df = remove_trajectory_measurements(trajectories_df)
2256
+ >>> print(filtered_df)
2257
+ # pd.DataFrame({
2258
+ # 'TRACK_ID': [1, 1, 2],
2259
+ # 'FRAME': [0, 1, 0],
2260
+ # 'POSITION_X': [100, 105, 200],
2261
+ # 'POSITION_Y': [150, 155, 250],
2262
+ # })
2252
2263
  """
2253
2264
 
2254
2265
  tracks = trajectories.copy()
2255
2266
 
2256
2267
  columns_to_keep = [column_labels['track'], column_labels['time'], column_labels['x'], column_labels['y'],column_labels['x']+'_um', column_labels['y']+'_um', 'class_id',
2257
2268
  't', 'state', 'generation', 'root', 'parent', 'ID', 't0', 'class', 'status', 'class_color', 'status_color', 'class_firstdetection', 't_firstdetection', 'velocity']
2258
- cols = tracks.columns
2269
+ cols = list(tracks.columns)
2259
2270
  for c in columns_to_keep:
2260
2271
  if c not in cols:
2261
2272
  columns_to_keep.remove(c)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: celldetective
3
- Version: 1.3.8.post1
3
+ Version: 1.3.9
4
4
  Summary: description
5
5
  Home-page: http://github.com/remyeltorro/celldetective
6
6
  Author: Rémy Torro
@@ -1,47 +1,47 @@
1
1
  celldetective/__init__.py,sha256=bi3SGTMo6s2qQBsJAaKy-a4xaGcTQVW8zsqaiX5XKeY,139
2
2
  celldetective/__main__.py,sha256=bxTlSvbKhqn3LW_azd2baDCnDsgb37PAP9DfuAJ1_5M,1844
3
- celldetective/_version.py,sha256=Gwdlkn6lk8YInJKr89NJRcSacpWsCYBBBhoLN4FGb2Y,28
3
+ celldetective/_version.py,sha256=SaWgUI6v92kVfF_Qdoxbfc38bwA34RuDGZmXMqa5g3c,22
4
4
  celldetective/events.py,sha256=UkjY_-THo6WviWWCLnDbma7jWOd_O9a60C4IOX2htG8,8254
5
- celldetective/extra_properties.py,sha256=y556D6EMjLGhtjDqRoOTRGa85XxTIe0K1Asb26VZXmo,5643
6
- celldetective/filters.py,sha256=DT3MyqNBSj3EMtb74oZUirdonKcwcowjtehTT72mrrk,4181
7
- celldetective/io.py,sha256=kD8jvaZJHndlYXM-NwVjesKs8tRQNYiZJCH5VHmk2uA,123067
8
- celldetective/measure.py,sha256=IfmyRaGzYjlTKh6uVBdn4qLEm6LgXQstb5NlB_Blc8I,58291
5
+ celldetective/extra_properties.py,sha256=PUs-lltSj_jSko_Gd-w483OxZW6oR3BAHQrw51DUxMg,8081
6
+ celldetective/filters.py,sha256=6pl2IGPK2FH8KPWbjzQEbT4C-ruqE1fQ8NQNN7Euvy8,4433
7
+ celldetective/io.py,sha256=HEy9KbidrPVbzzbC1kJcXR0TsvIxNgRiDk31ecdjxpE,121954
8
+ celldetective/measure.py,sha256=h1F08Vf7sy-20syMFZTUl1lHzE4h6cQvswJpwD9fcfE,58475
9
9
  celldetective/neighborhood.py,sha256=s-zVsfGnPlqs6HlDJCXRh21lLiPKbA_S1JC6uZvfG_0,56712
10
10
  celldetective/preprocessing.py,sha256=Wlt_PJua97CpMuWe_M65znUmz_yjYPqWIcs2ZK_RLgk,44109
11
11
  celldetective/relative_measurements.py,sha256=-GWig0lC5UWAcJSPlo9Sp45khGj80fxuQfFk-bdBca0,30117
12
- celldetective/segmentation.py,sha256=ul8E-cjjaWxaMZfjAtbrH7joUjxbZSeb77GL3Alp5Os,30779
12
+ celldetective/segmentation.py,sha256=WApzoU1s3Ntvp0eIw_pRZXNwCA8LDKC9YoyR52tioz4,30943
13
13
  celldetective/signals.py,sha256=nMyyGUpla8D2sUYKY1zjbWsAueVPI_gUalY0KXfWteI,111595
14
14
  celldetective/tracking.py,sha256=VBJLd-1EeJarOVPBNEomhVBh9UYAMdSnH0tmUiUoTrY,40242
15
- celldetective/utils.py,sha256=lX9W8H7y4z8B5DajUDqAoXlycJbIIzOmX7O-2b7eb04,108440
15
+ celldetective/utils.py,sha256=RoNkCGCs9V3EiIn63N20qNKBQ_-H9bogF5LgByQ2UZo,106316
16
16
  celldetective/datasets/segmentation_annotations/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  celldetective/datasets/signal_annotations/blank,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  celldetective/gui/InitWindow.py,sha256=TPKWYczhPHvPKWg654sXnE9xCEx6U-oWX0_OJgLTWbU,15044
19
19
  celldetective/gui/__init__.py,sha256=2_r2xfOj4_2xj0yBkCTIfzlF94AHKm-j6Pvpd7DddQc,989
20
20
  celldetective/gui/about.py,sha256=FJZrj6C-p6uqp_3UaprKosuW-Sw9_HPNQAvFbis9Gdk,1749
21
21
  celldetective/gui/analyze_block.py,sha256=h0sp7Tk3hZFM8w0yTidwIjZX4WRI-lQF3JCFObrLCPo,2761
22
- celldetective/gui/btrack_options.py,sha256=ZXaJuCdqB_BK1CFRbgZErlAlXMbzDF2DP7opLSd6III,44682
22
+ celldetective/gui/btrack_options.py,sha256=F2wrhuaNSaBr6vNApI22XSjdI5Cl3q138qCYBk-FDu4,44684
23
23
  celldetective/gui/classifier_widget.py,sha256=nHWmaWXse2CxxRFmR4mA0JRADr5i0MsWldaqJQIQ-i8,19992
24
24
  celldetective/gui/configure_new_exp.py,sha256=N4SdL-4Xo0XbTAkCtt1ywr74HmHc5eaPlHGv1XgxAX0,20772
25
- celldetective/gui/control_panel.py,sha256=1TWUCvEif19ZLy4pDUupPrGzIKnG6Tg6kWGp_R8OSG4,22045
25
+ celldetective/gui/control_panel.py,sha256=jFpedm03x6_jKabeadsNiigKnlh7sXCkulwe4gMCJaM,22042
26
26
  celldetective/gui/generic_signal_plot.py,sha256=Gv4KhA5vhbgVSj5jteE42T0aNCoQZtmIAUkEsMi6JNA,36309
27
- celldetective/gui/gui_utils.py,sha256=n9JVXBantJK3RFt8fQ7HJYKdCC4ezBMBwlgT5QT2I9o,39343
27
+ celldetective/gui/gui_utils.py,sha256=lF3YqMecN_xEyFN_MS0j2KWYZoCnOvMmZeMj4lTRFu4,39335
28
28
  celldetective/gui/json_readers.py,sha256=SkI_bR1MlAKd8NLBmQUVjJfZ7mKiU_FEjgC4dUwBACk,3698
29
29
  celldetective/gui/layouts.py,sha256=XsqiHR58DXsG5SSD5S8KOtUv4yw-y-s2_wZx_XsHeJs,52013
30
- celldetective/gui/measurement_options.py,sha256=cfiC4lq-XqY4Sa4Vkw5mys7JKaa9tE2BgeN8_k-SIDY,40355
30
+ celldetective/gui/measurement_options.py,sha256=sxtQPKSixVbrFcBC1XLLC4-h7ZHlgTLcPfVAW2RVV7w,40357
31
31
  celldetective/gui/neighborhood_options.py,sha256=FBNDvlzMPKp8s0Grxds90pCPHG1s27XrpMN0HV2gf0I,19839
32
32
  celldetective/gui/plot_measurements.py,sha256=n0pDUcYcsKlSMaUaBSVplGziuWp_7jKaeXdREs-MqyI,50848
33
- celldetective/gui/plot_signals_ui.py,sha256=u7b36pmjF8sf0Ctb9KCrnRcvAD3vlNaaIVkX3-rHCA0,17332
34
- celldetective/gui/process_block.py,sha256=3v4q5kAq5uY6w0f3zyImXi_iTp8Qv2EFvzNjTEvqG9E,71210
33
+ celldetective/gui/plot_signals_ui.py,sha256=7jCu-wLk_NDL4ThLZwhltODngre8iqPMUcEhJNNllo8,18189
34
+ celldetective/gui/process_block.py,sha256=dH0zHIEouL8ApAD4uN7IR9dSl8X8QqE-hXL6U7HZHaM,71212
35
35
  celldetective/gui/retrain_segmentation_model_options.py,sha256=7iawDN4kwq56Z-dX9kQe9tLW8B3YMrIW_D85LMAAYwk,23906
36
36
  celldetective/gui/retrain_signal_model_options.py,sha256=GCa0WKKsgmH2CFDHAKxPGbHtCE19p1_bbcWNasyZw5o,22482
37
37
  celldetective/gui/seg_model_loader.py,sha256=b1BiHuAf_ZqroE4jSEVCo7ASQv-xyWMPWU799alpbNM,19727
38
- celldetective/gui/signal_annotator.py,sha256=9l_qbIO9V862eGk6mVOCXo0jsG8Z3WP9kx7wCBFGvKU,89014
39
- celldetective/gui/signal_annotator2.py,sha256=qBDPk3UHmMoY4_i5KJt3Nr8kxjLOkYay_xNc6xrn_pE,106340
40
- celldetective/gui/signal_annotator_options.py,sha256=ekxy7Qtw5EGqXa82KdZezaSlmprSAGMW2ZZoE1SObRM,11013
38
+ celldetective/gui/signal_annotator.py,sha256=lf15PWZoWx2S1qZtrMsbBeZKtwVEKTjwwyIIG-XHdQA,89541
39
+ celldetective/gui/signal_annotator2.py,sha256=RhcMX32FH_zg8GgAofGIyda9H8euSFADau301qi1NU0,106342
40
+ celldetective/gui/signal_annotator_options.py,sha256=Tq20tybpelHFUqFCSemqH9es2FqM8S3I6Fab-u0FK0A,11015
41
41
  celldetective/gui/styles.py,sha256=SZy_ACkA6QB_4ANyY1V0m1QF66C0SVGssOrwW1Qt1Aw,5076
42
- celldetective/gui/survival_ui.py,sha256=J4ZYjX8ne0wT0ruQu9WL3WfLnRIAQalkaAR8ngb7PkI,14170
42
+ celldetective/gui/survival_ui.py,sha256=Q-530cAK-SsKPr8sWFTbhGekB2CFLWd0SNjtV1Wr0oo,14202
43
43
  celldetective/gui/tableUI.py,sha256=Yz_pHk1ERXRb0QsBPrvLEwAGpvVlawgn1b6uzz5wL_0,58022
44
- celldetective/gui/thresholds_gui.py,sha256=VjCVBCDsNh_LoXfEqUn32nsYQwO9-Elh3Gl1RBv7acc,48756
44
+ celldetective/gui/thresholds_gui.py,sha256=O7NkeaPV1nnQdTbduXd3lMttWPBzKcJ-9jI5OMr8H-0,48808
45
45
  celldetective/gui/viewers.py,sha256=HDLB6j1FJwgKR6dQwzeHmcDvDMbDIYwD2svd-VZhJFE,47806
46
46
  celldetective/gui/workers.py,sha256=P4qUMXuCtGcggGmJr3VitAPSfRG30wkJ1B0pfcdGbKg,4225
47
47
  celldetective/gui/help/DL-segmentation-strategy.json,sha256=PZD9xXjrwbX3TiudHJPuvcyZD28o4k-fVgeTd7dBKzI,1583
@@ -56,9 +56,9 @@ celldetective/gui/help/propagate-classification.json,sha256=F7Ir1mtgRVTXWLN7n3ny
56
56
  celldetective/gui/help/track-postprocessing.json,sha256=VaGd8EEkA33OL-EI3NXWZ8yHeWWyUeImDF5yAjsVYGA,3990
57
57
  celldetective/gui/help/tracking.json,sha256=yIAoOToqCSQ_XF4gwEZCcyXcvQ3mROju263ZPDvlUyY,776
58
58
  celldetective/gui/processes/downloader.py,sha256=SuMTuM82QOZBqLfj36I14fhZ2k3NmLp0PBcGUHxnpXI,3287
59
- celldetective/gui/processes/measure_cells.py,sha256=1nJNHhLKHBL7kVsmmPbWv0NHcar0D8Gihjj_AD1UxnI,12851
60
- celldetective/gui/processes/segment_cells.py,sha256=AVdRGuDnoQcrevk7itiEu5xluBeqaqXRSFVZAd7q91g,11313
61
- celldetective/gui/processes/track_cells.py,sha256=og23iqc7WyVbpqsdygltVjejLGUVIb5ocxAForGnhl0,9947
59
+ celldetective/gui/processes/measure_cells.py,sha256=MLkVLLuUtVVgwQB9a9N8eZ5TzF-Xtuhzb5wtcFRa1lU,12846
60
+ celldetective/gui/processes/segment_cells.py,sha256=49Bh3nejkrxlLNhlP3Y_6-flkBOhkqFMQmrc2qDj7Dk,11320
61
+ celldetective/gui/processes/track_cells.py,sha256=y8ohHAMcGtAjrJOP1WqvDfy_K85LbmdXoN7CA7BYpaY,9942
62
62
  celldetective/gui/processes/train_segmentation_model.py,sha256=bvcPG19hBjhNY9hd6Ch5_wk2FOJYQg97Azoz4RKeP-0,10776
63
63
  celldetective/gui/processes/train_signal_model.py,sha256=qqqkq9gdvNyvycYkmzWgRXWvsbEozyzNWP_POGvnlIs,3816
64
64
  celldetective/icons/logo-large.png,sha256=FXSwV3u6zEKcfpuSn4unnqB0oUnN9cHqQ9BCKWytrpg,36631
@@ -98,9 +98,12 @@ celldetective/models/tracking_configs/mcf7.json,sha256=iDjb8i6yxs0GleW39dvY3Ld5b
98
98
  celldetective/models/tracking_configs/no_z_motion.json,sha256=b4RWOJ0w6Y2e0vJYwKMyOexedeL2eA8fEDbSzbNmB4A,2702
99
99
  celldetective/models/tracking_configs/ricm.json,sha256=L-vmwCR1f89U-qnH2Ms0cBfPFR_dxIWoe2ccH8V-QBA,2727
100
100
  celldetective/models/tracking_configs/ricm2.json,sha256=DDjJ6ScYcDWvlsy7ujPID8v8H28vcNcMuZmNR8XmGxo,2718
101
+ celldetective/regionprops/__init__.py,sha256=ohe9vC7j4lnpKnGXidVo1PVfoQfC8TqCuHTNo8rXmdg,44
102
+ celldetective/regionprops/_regionprops.py,sha256=K1yHZDdLe81N9w73XMmRVLVsaMXJ3IE5VLP8ui6QsL4,9855
103
+ celldetective/regionprops/props.json,sha256=sCwACmbh0n-JAw9eve9yV85REukoMBJLsRjxCwTRMm8,2424
101
104
  celldetective/scripts/analyze_signals.py,sha256=YE05wZujl2hQFWkvqATBcCx-cAd_V3RxnvKoh0SB7To,2194
102
105
  celldetective/scripts/measure_cells.py,sha256=GvuHrSb3S-JnUjBaACx84BhUd_vUrwEHiSgDV9BoXXE,11766
103
- celldetective/scripts/measure_relative.py,sha256=L_NjIUfHSGupkAKLZkubBHJdZh0ugPhCTJem80b0zMM,4261
106
+ celldetective/scripts/measure_relative.py,sha256=wI7ibgWCsh5NVfcYum9LK0khL7cwKyCxwHFaXG4oWjM,3698
104
107
  celldetective/scripts/segment_cells.py,sha256=AZfHqig0WBMkAh9ho7JHmjM9vMO-EiPVviTHJcDj5Mw,6954
105
108
  celldetective/scripts/segment_cells_thresholds.py,sha256=lsW5hsHenNPiyIwZuZHipeCN2Wer69VcLko5rS96GAU,5118
106
109
  celldetective/scripts/track_cells.py,sha256=_OPfn9BsbvdpAU4dg36SXmOTx_BkDkZL4N05X0_Tig4,8795
@@ -118,9 +121,9 @@ tests/test_segmentation.py,sha256=k1b_zIZdlytEdJcHjAUQEO3gTBAHtv5WvrwQN2xD4kc,34
118
121
  tests/test_signals.py,sha256=No4cah6KxplhDcKXnU8RrA7eDla4hWw6ccf7xGnBokU,3599
119
122
  tests/test_tracking.py,sha256=8hebWSqEIuttD1ABn-6dKCT7EXKRR7-4RwyFWi1WPFo,8800
120
123
  tests/test_utils.py,sha256=NKRCAC1d89aBK5cWjTb7-pInYow901RrT-uBlIdz4KI,3692
121
- celldetective-1.3.8.post1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
122
- celldetective-1.3.8.post1.dist-info/METADATA,sha256=AZ3aJ7r1LW6e83-wvt7fTY0QqhlWrGYvpWKd8pUebno,10753
123
- celldetective-1.3.8.post1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
124
- celldetective-1.3.8.post1.dist-info/entry_points.txt,sha256=2NU6_EOByvPxqBbCvjwxlVlvnQreqZ3BKRCVIKEv3dg,62
125
- celldetective-1.3.8.post1.dist-info/top_level.txt,sha256=6rsIKKfGMKgud7HPuATcpq6EhdXwcg_yknBVWn9x4C4,20
126
- celldetective-1.3.8.post1.dist-info/RECORD,,
124
+ celldetective-1.3.9.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
125
+ celldetective-1.3.9.dist-info/METADATA,sha256=POcWapSbuacLCmplgT1JURcBQIQ25jNSigcN5ltnx5s,10747
126
+ celldetective-1.3.9.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
127
+ celldetective-1.3.9.dist-info/entry_points.txt,sha256=2NU6_EOByvPxqBbCvjwxlVlvnQreqZ3BKRCVIKEv3dg,62
128
+ celldetective-1.3.9.dist-info/top_level.txt,sha256=6rsIKKfGMKgud7HPuATcpq6EhdXwcg_yknBVWn9x4C4,20
129
+ celldetective-1.3.9.dist-info/RECORD,,