fucciphase 0.0.1__py3-none-any.whl → 0.0.3__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.
fucciphase/phase.py CHANGED
@@ -1,5 +1,4 @@
1
1
  from enum import Enum
2
- from typing import List
3
2
 
4
3
  import dtaidistance.preprocessing
5
4
  import numpy as np
@@ -99,45 +98,40 @@ class NewColumns(str, Enum):
99
98
 
100
99
  def generate_cycle_phases(
101
100
  df: pd.DataFrame,
102
- channels: List[str],
101
+ channels: list[str],
103
102
  sensor: FUCCISensor,
104
- thresholds: List[float],
103
+ thresholds: list[float],
105
104
  estimate_percentage: bool = False,
106
105
  ) -> None:
107
- """Add a column in place to the dataframe with the phase of the cell cycle.
106
+ """Add cell-cycle phase (and optionally percentage) columns to the dataframe.
108
107
 
109
- The phase is determined using a threshold on the channel intensities
110
- assuming a FUCCI sensor.
108
+ The phase is determined using thresholds on the normalized channel
109
+ intensities, assuming a FUCCI-like sensor. For each row (spot) in the
110
+ dataframe, this function:
111
111
 
112
- The thresholds per channel must be between 0 and 1.
112
+ 1. Checks that normalized intensity columns exist for all requested channels.
113
+ 2. Uses :func:`estimate_cell_phase_from_max_intensity` to assign a
114
+ discrete phase label based on whether each channel is ON/OFF.
115
+ 3. Optionally calls :func:`estimate_cell_cycle_percentage` to estimate
116
+ a continuous cell-cycle percentage from the intensities and the
117
+ discrete phase.
113
118
 
114
- Example:
115
- channels = ["CH1", "CH2"]
116
- thresholds = [0.1, 0.1]
117
-
118
- The sensor needs to be calibrated for each cell line.
119
- For that, record the FUCCI intensities of multiple cell cycles
120
- by live-cell fluorescence microscopy.
121
- See the examples for more details.
122
-
123
- The thresholds need to be chosen based on the expected noise of the background and
124
- uncertainty in intensity computation.
125
- They give the ratio to the maximum intensity.
126
- E.g., a threshold of 0.1 means that all intensities below 0.1 times the maximum
127
- intensity are considered background signal.
119
+ The thresholds per channel must be between 0 and 1 and are interpreted
120
+ as fractions of the maximum intensity in that channel (e.g. 0.1 means
121
+ “10% of max”).
128
122
 
129
123
  Parameters
130
124
  ----------
131
- df : pd.DataFrame
132
- Dataframe with columns holding normalized intensities
133
- sensor: FUCCISensor
134
- FUCCI sensor with phase specifics
135
- channels: List[str]
136
- Names of channels
137
- thresholds: List[float]
138
- Thresholds to separate phases
139
- estimate_percentage: bool
140
- Estimate cell cycle percentages
125
+ df : pandas.DataFrame
126
+ Dataframe with columns holding normalized intensities.
127
+ channels : List[str]
128
+ Names of normalized channels to use for phase estimation.
129
+ sensor : FUCCISensor
130
+ FUCCI sensor with phase-specific information.
131
+ thresholds : List[float]
132
+ Thresholds (0-1) used to separate phases.
133
+ estimate_percentage : bool, optional
134
+ If True, also estimate a continuous cell-cycle percentage.
141
135
 
142
136
 
143
137
  Raises
@@ -163,7 +157,7 @@ def generate_cycle_phases(
163
157
  # check that all channels are present
164
158
  check_channels(sensor.fluorophores, channels)
165
159
 
166
- # compute phases
160
+ # compute discrete phases based on normalized intensities
167
161
  estimate_cell_phase_from_max_intensity(
168
162
  df,
169
163
  norm_channel_names,
@@ -172,28 +166,33 @@ def generate_cycle_phases(
172
166
  thresholds=thresholds,
173
167
  )
174
168
 
175
- # name of phase_column
169
+ # name of phase column
176
170
  phase_column = NewColumns.discrete_phase_max()
177
- # compute percentages
171
+ # optionally compute continuous cell-cycle percentages
178
172
  if estimate_percentage:
179
173
  estimate_cell_cycle_percentage(df, norm_channel_names, sensor, phase_column)
180
174
 
181
175
 
182
176
  def estimate_cell_cycle_percentage(
183
- df: pd.DataFrame, channels: List[str], sensor: FUCCISensor, phase_column: str
177
+ df: pd.DataFrame, channels: list[str], sensor: FUCCISensor, phase_column: str
184
178
  ) -> None:
185
179
  """Estimate cell cycle percentage from intensity pairs.
186
180
 
181
+ For each row in the dataframe, this function reads the normalized
182
+ intensities in ``channels`` together with the discrete phase label in
183
+ ``phase_column`` and queries the sensor for an estimated cell-cycle
184
+ percentage. The result is stored in the ``CELL_CYCLE_PERC`` column.
185
+
187
186
  Parameters
188
187
  ----------
189
- df : pd.DataFrame
190
- Dataframe with columns holding normalized intensities
191
- sensor: FUCCISensor
192
- FUCCI sensor with phase specifics
193
- channels: List[str]
194
- Names of channels
195
- phase_column: str
196
- Name of phase column
188
+ df : pandas.DataFrame
189
+ Dataframe with normalized intensity columns and a phase column.
190
+ channels : List[str]
191
+ Names of normalized intensity columns for each fluorophore.
192
+ sensor : FUCCISensor
193
+ FUCCI sensor used to map intensities and phase to cycle percentage.
194
+ phase_column : str
195
+ Name of the column storing discrete phase labels.
197
196
  """
198
197
  percentages = []
199
198
  # iterate through data frame
@@ -210,39 +209,39 @@ def estimate_cell_cycle_percentage(
210
209
 
211
210
  def estimate_cell_phase_from_max_intensity(
212
211
  df: pd.DataFrame,
213
- channels: List[str],
212
+ channels: list[str],
214
213
  sensor: FUCCISensor,
215
- background: List[float],
216
- thresholds: List[float],
214
+ background: list[float],
215
+ thresholds: list[float],
217
216
  ) -> None:
218
- """Add a column in place to the dataframe with the estimated phase of the cell
219
- cycle, where the phase is determined by thresholding the channel intensities.
217
+ """Estimate discrete cell-cycle phase by thresholding normalized intensities.
220
218
 
221
- The provided thresholds are used to decide if a channel is switched on (ON).
222
- For that, the background is subtracted from the mean intensity.
223
- The obtained values are normalized w.r.t. the maximum mean intensity in the
224
- respective channel available in the DataFrame.
225
- Hence, the threshold values should be between 0 and 1.
226
- This method will not work reliably if not enough cells from different phases
227
- are contained in the DataFrame.
219
+ For each channel, the background value is subtracted from the mean
220
+ intensity. The resulting intensities are normalized by the maximum
221
+ mean intensity observed in that channel. A channel is considered ON
222
+ if its normalized intensity exceeds the corresponding threshold.
223
+
224
+ The ON/OFF pattern across channels is then mapped to a discrete phase
225
+ using the sensor model.
228
226
 
229
227
  Parameters
230
228
  ----------
231
- df: pd.DataFrame
232
- Dataframe with a CELL_CYCLE_PERC column
233
- channels: List[str]
234
- Names of channels
235
- sensor: FUCCISensor
236
- FUCCI sensor with specific phase analysis information
237
- background: List[float]
238
- Single value per channel representing background
239
- thresholds: List[float]
240
- Thresholds to separate phases
229
+ df : pandas.DataFrame
230
+ Dataframe containing the normalized intensity columns.
231
+ channels : List[str]
232
+ Names of normalized intensity columns.
233
+ sensor : FUCCISensor
234
+ FUCCI sensor with phase analysis information.
235
+ background : List[float]
236
+ Single background value per channel.
237
+ thresholds : List[float]
238
+ Thresholds (0-1) used to separate phases.
241
239
 
242
240
  Raises
243
241
  ------
244
242
  ValueError
245
- If the dataframe does not contain the normalized channels.
243
+ If required channels are missing or if background/threshold lists
244
+ are inconsistent with the number of channels.
246
245
  """
247
246
  # sanity check: check that channels are present
248
247
  for channel in channels:
@@ -257,8 +256,10 @@ def estimate_cell_phase_from_max_intensity(
257
256
  check_channels(sensor.fluorophores, channels)
258
257
  check_thresholds(sensor.fluorophores, thresholds)
259
258
 
260
- phase_markers_list: List[pd.Series[bool]] = []
261
- for channel, bg_value, threshold in zip(channels, background, thresholds):
259
+ phase_markers_list: list[pd.Series[bool]] = []
260
+ for channel, bg_value, threshold in zip(
261
+ channels, background, thresholds, strict=True
262
+ ):
262
263
  # get intensities and subtract background
263
264
  intensity = df[channel] - bg_value
264
265
  # threshold channels to decide if ON / OFF (data is in list per spot)
@@ -275,10 +276,10 @@ def estimate_cell_phase_from_max_intensity(
275
276
 
276
277
  def estimate_cell_phase_from_background(
277
278
  df: pd.DataFrame,
278
- channels: List[str],
279
+ channels: list[str],
279
280
  sensor: FUCCISensor,
280
- background: List[float],
281
- thresholds: List[float],
281
+ background: list[float],
282
+ thresholds: list[float],
282
283
  ) -> None:
283
284
  """Add a column in place to the dataframe with the estimated phase of the cell
284
285
  cycle, where the phase is determined by comparing the channel intensities to
@@ -319,8 +320,10 @@ def estimate_cell_phase_from_background(
319
320
 
320
321
  check_channels(sensor.fluorophores, channels)
321
322
 
322
- phase_markers_list: List[pd.Series[bool]] = []
323
- for channel, bg_value, threshold in zip(channels, background, thresholds):
323
+ phase_markers_list: list[pd.Series[bool]] = []
324
+ for channel, bg_value, threshold in zip(
325
+ channels, background, thresholds, strict=True
326
+ ):
324
327
  intensity = df[channel]
325
328
  # threshold channels to decide if ON / OFF (data is in list per spot)
326
329
  phase_markers_list.append(intensity > threshold * bg_value)
@@ -337,7 +340,7 @@ def estimate_cell_phase_from_background(
337
340
  def estimate_percentage_by_subsequence_alignment(
338
341
  df: pd.DataFrame,
339
342
  dt: float,
340
- channels: List[str],
343
+ channels: list[str],
341
344
  reference_data: pd.DataFrame,
342
345
  smooth: float = 0.1,
343
346
  penalty: float = 0.05,