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/plot.py CHANGED
@@ -1,5 +1,4 @@
1
1
  from itertools import cycle
2
- from typing import List, Optional
3
2
 
4
3
  import numpy as np
5
4
  import pandas as pd
@@ -16,7 +15,27 @@ from .utils import get_norm_channel_name
16
15
  def set_phase_colors(
17
16
  df: pd.DataFrame, colordict: dict, phase_column: str = "DISCRETE_PHASE_MAX"
18
17
  ) -> None:
19
- """Label each phase by fixed color."""
18
+ """Label each phase by fixed color.
19
+
20
+ This function adds a ``COLOR`` column to the dataframe by mapping each
21
+ entry in ``phase_column`` to a color specified in ``colordict``.
22
+
23
+ Parameters
24
+ ----------
25
+ df : pandas.DataFrame
26
+ Dataframe containing at least ``phase_column``.
27
+ colordict : dict
28
+ Mapping from phase label (str) to a valid matplotlib color.
29
+ phase_column : str, optional
30
+ Name of the column containing phase labels. Default is
31
+ ``"DISCRETE_PHASE_MAX"``.
32
+
33
+ Raises
34
+ ------
35
+ ValueError
36
+ If not all phase labels present in the dataframe have a color
37
+ entry in ``colordict``.
38
+ """
20
39
  phases = df[phase_column].unique()
21
40
  if not all(phase in colordict for phase in phases):
22
41
  raise ValueError(f"Provide a color for every phase in: {phases}")
@@ -32,10 +51,39 @@ def plot_feature(
32
51
  feature_name: str,
33
52
  interpolate_time: bool = False,
34
53
  track_id_name: str = "TRACK_ID",
35
- ylim: Optional[tuple] = None,
36
- yticks: Optional[list] = None,
54
+ ylim: tuple | None = None,
55
+ yticks: list | None = None,
37
56
  ) -> Figure:
38
- """Plot features of individual tracks in one plot."""
57
+ """Plot features of individual tracks in one plot.
58
+
59
+ Parameters
60
+ ----------
61
+ df : pandas.DataFrame
62
+ Dataframe containing the feature and time columns.
63
+ time_column : str
64
+ Name of the column containing time or frame indices.
65
+ feature_name : str
66
+ Name of the feature column to plot.
67
+ interpolate_time : bool, optional
68
+ Currently unused; kept for API compatibility. Default is False.
69
+ track_id_name : str, optional
70
+ Name of the column containing track IDs. Default is ``"TRACK_ID"``.
71
+ ylim : tuple, optional
72
+ y-axis limits as ``(ymin, ymax)``.
73
+ yticks : list, optional
74
+ Explicit y-tick locations.
75
+
76
+ Returns
77
+ -------
78
+ matplotlib.figure.Figure
79
+ The figure containing the plot.
80
+
81
+ Raises
82
+ ------
83
+ ValueError
84
+ If the feature or time columns are not found.
85
+
86
+ """
39
87
  if feature_name not in df:
40
88
  raise ValueError(f"(Feature {feature_name} not in provided DataFrame.")
41
89
  if time_column not in df:
@@ -63,20 +111,57 @@ def plot_feature_stacked(
63
111
  feature_name: str,
64
112
  interpolate_time: bool = False,
65
113
  track_id_name: str = "TRACK_ID",
66
- ylim: Optional[tuple] = None,
67
- yticks: Optional[list] = None,
114
+ ylim: tuple | None = None,
115
+ yticks: list | None = None,
68
116
  interpolation_steps: int = 1000,
69
- figsize: Optional[tuple] = None,
70
- selected_tracks: Optional[List[int]] = None,
117
+ figsize: tuple | None = None,
118
+ selected_tracks: list[int] | None = None,
71
119
  ) -> Figure:
72
120
  """Stack features of individual tracks.
73
121
 
122
+ Each selected track is plotted in its own horizontal panel. If
123
+ ``interpolate_time`` is True, an additional panel at the bottom shows
124
+ the mean interpolated feature across all tracks.
74
125
 
75
126
  Notes
76
127
  -----
77
- If `selected_tracks` are chosen, the averaging
78
- is still performed on all tracks.
79
- Few selected tracks are stacked to enhance visibility.
128
+ If ``selected_tracks`` are chosen, the averaging is still performed on
129
+ all tracks. The selected subset is only used for stacked visualization.
130
+
131
+ Parameters
132
+ ----------
133
+ df : pandas.DataFrame
134
+ Dataframe containing the feature, time and COLOR columns.
135
+ time_column : str
136
+ Name of the column containing time or frame indices.
137
+ feature_name : str
138
+ Name of the feature column to plot.
139
+ interpolate_time : bool, optional
140
+ If True, add an extra panel showing the average interpolated
141
+ feature over time. Default is False.
142
+ track_id_name : str, optional
143
+ Name of the column containing track IDs. Default is ``"TRACK_ID"``.
144
+ ylim : tuple, optional
145
+ y-axis limits as ``(ymin, ymax)``.
146
+ yticks : list, optional
147
+ Explicit y-tick locations.
148
+ interpolation_steps : int, optional
149
+ Number of time points used for interpolation. Default is 1000.
150
+ figsize : tuple, optional
151
+ Figure size passed to ``plt.subplots``.
152
+ selected_tracks : list of int, optional
153
+ Subset of track IDs to plot in stacked panels.
154
+
155
+ Returns
156
+ -------
157
+ matplotlib.figure.Figure
158
+ The figure containing the stacked plots.
159
+
160
+ Raises
161
+ ------
162
+ ValueError
163
+ If required columns are missing or ``selected_tracks`` contains
164
+ IDs not present in the dataframe.
80
165
  """
81
166
  if feature_name not in df:
82
167
  raise ValueError(f"(Feature {feature_name} not in provided DataFrame.")
@@ -156,7 +241,27 @@ def plot_raw_intensities(
156
241
  time_label: str = "Frame #",
157
242
  **plot_kwargs: bool,
158
243
  ) -> None:
159
- """Plot intensities of two-channel sensor."""
244
+ """Plot intensities of two-channel sensor over time.
245
+
246
+ Parameters
247
+ ----------
248
+ df : pandas.DataFrame
249
+ Dataframe containing intensity and time columns.
250
+ channel1 : str
251
+ Name of the first intensity column.
252
+ channel2 : str
253
+ Name of the second intensity column.
254
+ color1 : str, optional
255
+ Color used for ``channel1``. Default is ``"cyan"``.
256
+ color2 : str, optional
257
+ Color used for ``channel2``. Default is ``"magenta"``.
258
+ time_column : str, optional
259
+ Name of the time/frame column. Default is ``"FRAME"``.
260
+ time_label : str, optional
261
+ Label used for the x-axis. Default is ``"Frame #"``.
262
+ plot_kwargs : dict
263
+ Additional keyword arguments passed to ``matplotlib.pyplot.plot``.
264
+ """
160
265
  ch1_intensity = df[channel1]
161
266
  ch2_intensity = df[channel2]
162
267
 
@@ -188,7 +293,27 @@ def plot_normalized_intensities(
188
293
  time_label: str = "Frame #",
189
294
  **plot_kwargs: bool,
190
295
  ) -> None:
191
- """Plot normalised intensities of two-channel sensor."""
296
+ """Plot normalised intensities of two-channel sensor.
297
+
298
+ Parameters
299
+ ----------
300
+ df : pandas.DataFrame
301
+ Dataframe containing normalized intensity and time columns.
302
+ channel1 : str
303
+ Name of the first channel (pre-normalization).
304
+ channel2 : str
305
+ Name of the second channel (pre-normalization).
306
+ color1 : str, optional
307
+ Color used for ``channel1``. Default is ``"cyan"``.
308
+ color2 : str, optional
309
+ Color used for ``channel2``. Default is ``"magenta"``.
310
+ time_column : str, optional
311
+ Name of the time/frame column. Default is ``"FRAME"``.
312
+ time_label : str, optional
313
+ Label used for the x-axis. Default is ``"Frame #"``.
314
+ plot_kwargs : dict
315
+ Additional keyword arguments passed to ``matplotlib.pyplot.plot``.
316
+ """
192
317
  ch1_intensity = df[get_norm_channel_name(channel1)]
193
318
  ch2_intensity = df[get_norm_channel_name(channel2)]
194
319
 
@@ -200,30 +325,28 @@ def plot_normalized_intensities(
200
325
 
201
326
 
202
327
  def plot_phase(df: pd.DataFrame, channel1: str, channel2: str) -> None:
203
- """Plot the two channels and vertical lines
204
- corresponding to the change of phase.
328
+ """Plot discrete cell-cycle phase-related signals over time.
205
329
 
206
- The dataframe must be preprocessed with one of the available phase
207
- computation function and must contain the following columns:
330
+ Plot the two normalized channels and the unique intensity curve over
331
+ frame number. The dataframe must already contain:
208
332
 
209
- - normalised channels (channel1 + "_NORM", etc)
210
- - cell cycle percentage
211
- - FRAME
333
+ - normalized channels (e.g. ``channel + "_NORM"``),
334
+ - the cell cycle percentage column,
335
+ - the ``FRAME`` column.
212
336
 
213
337
  Parameters
214
338
  ----------
215
- df : pd.DataFrame
216
- Dataframe
339
+ df : pandas.DataFrame
340
+ Input dataframe.
217
341
  channel1 : str
218
- First channel
342
+ First channel name (pre-normalization).
219
343
  channel2 : str
220
- Second channel
344
+ Second channel name (pre-normalization).
221
345
 
222
346
  Raises
223
347
  ------
224
348
  ValueError
225
- If the dataframe does not contain the FRAME, CELL_CYCLE_PERC and normalised
226
- columns.
349
+ If the dataframe does not contain the required columns.
227
350
  """
228
351
  # check if the FRAME column is present
229
352
  if "FRAME" not in df.columns:
@@ -248,33 +371,49 @@ def plot_phase(df: pd.DataFrame, channel1: str, channel2: str) -> None:
248
371
  def plot_dtw_query_vs_reference(
249
372
  reference_df: pd.DataFrame,
250
373
  df: pd.DataFrame,
251
- channels: List[str],
374
+ channels: list[str],
252
375
  ref_percentage_column: str = "percentage",
253
376
  est_percentage_column: str = "CELL_CYCLE_PERC_DTW",
254
- ground_truth: Optional[pd.DataFrame] = None,
255
- colors: Optional[List[str]] = None,
377
+ ground_truth: pd.DataFrame | None = None,
378
+ colors: list[str] | None = None,
256
379
  **plot_kwargs: bool,
257
380
  ) -> None:
258
- """Plot query and alignment to reference curve.
381
+ """
382
+ Plot query curves and their alignment to a reference cell-cycle curve.
383
+
384
+ For each channel, this function plots:
385
+
386
+ - the query intensity as a function of estimated percentage,
387
+ - the reference intensity as a function of reference percentage,
388
+ - the reference curve re-sampled at the query percentages (match).
389
+
390
+ Optionally, ground truth curves can be overlaid.
259
391
 
260
392
  Parameters
261
393
  ----------
262
- reference_df: pd.DataFrame
263
- DataFrame with reference curve data
264
- df: pd.DataFrame
265
- DataFrame used for query
266
- channels: List[str]
267
- Name of the channels
268
- ref_percentage_column: str
269
- Name of column with percentages of reference curve
270
- est_percentage_column: str
271
- Name of column with estimated percentages
272
- ground_truth: pd.DataFrame
273
- DataFrame with ground truth data, needs to be named as reference_df
274
- colors: List[str]
275
- Colors for plot
276
- plot_kwargs: dict
277
- Kwargs to be passed to matplotlib
394
+ reference_df : pandas.DataFrame
395
+ Dataframe with the reference curve (percentage vs intensity).
396
+ df : pandas.DataFrame
397
+ Dataframe used as query (estimated percentage vs intensity).
398
+ channels : list of str
399
+ Names of the channels to plot.
400
+ ref_percentage_column : str, optional
401
+ Column name for reference percentages. Default is ``"percentage"``.
402
+ est_percentage_column : str, optional
403
+ Column name for estimated percentages in the query dataframe.
404
+ Default is ``"CELL_CYCLE_PERC_DTW"``.
405
+ ground_truth : pandas.DataFrame, optional
406
+ Dataframe containing ground truth intensities, with the same
407
+ column names as ``reference_df``.
408
+ colors : list of str, optional
409
+ Colors to use for each channel in the reference plots.
410
+ plot_kwargs : dict
411
+ Additional keyword arguments passed to ``matplotlib.pyplot.plot``.
412
+
413
+ Raises
414
+ ------
415
+ ValueError
416
+ If required columns are missing in the reference or query dataframes.
278
417
  """
279
418
  for channel in channels:
280
419
  if channel not in reference_df.columns:
@@ -293,7 +432,7 @@ def plot_dtw_query_vs_reference(
293
432
  "Percentage column not found in reference DataFrame"
294
433
  f", available options {reference_df.columns}"
295
434
  )
296
- fig, ax = plt.subplots(1, len(channels))
435
+ _, ax = plt.subplots(1, len(channels))
297
436
  for idx, channel in enumerate(channels):
298
437
  ax[idx].plot(
299
438
  df[est_percentage_column], df[channel], label="Query", **plot_kwargs
@@ -333,36 +472,41 @@ def plot_dtw_query_vs_reference(
333
472
  def plot_query_vs_reference_in_time(
334
473
  reference_df: pd.DataFrame,
335
474
  df: pd.DataFrame,
336
- channels: List[str],
475
+ channels: list[str],
337
476
  ref_time_column: str = "time",
338
477
  query_time_column: str = "time",
339
- colors: Optional[List[str]] = None,
340
- channel_titles: Optional[List[str]] = None,
341
- fig_title: Optional[str] = None,
478
+ colors: list[str] | None = None,
479
+ channel_titles: list[str] | None = None,
480
+ fig_title: str | None = None,
342
481
  **plot_kwargs: bool,
343
482
  ) -> None:
344
- """Plot query and alignment to reference curve.
483
+ """
484
+ Plot query and reference curves as a function of time.
485
+
486
+ For each channel, this function overlays the query intensity and the
487
+ reference intensity over time. This is useful to visually compare
488
+ dynamics in the original time domain.
345
489
 
346
490
  Parameters
347
491
  ----------
348
- reference_df: pd.DataFrame
349
- DataFrame with reference curve data
350
- df: pd.DataFrame
351
- DataFrame used for query
352
- channels: List[str]
353
- Name of the channels
354
- ref_time_column: str
355
- Name of column with times of reference curve
356
- query_time_column: str
357
- Name of column with times in query
358
- colors: List[str]
359
- Colors for plot
360
- plot_kwargs: dict
361
- Kwargs to be passed to matplotlib
362
- channel_titles: Optional[List]
363
- titles for each channel
364
- fig_title: Optional[str]
365
- Figure title
492
+ reference_df : pandas.DataFrame
493
+ Dataframe with reference curve data (time vs intensity).
494
+ df : pandas.DataFrame
495
+ Dataframe with query data (time vs intensity).
496
+ channels : list of str
497
+ Names of the channels to plot.
498
+ ref_time_column : str, optional
499
+ Column name for reference time values. Default is ``"time"``.
500
+ query_time_column : str, optional
501
+ Column name for query time values. Default is ``"time"``.
502
+ colors : list of str, optional
503
+ Colors to use for the reference curves.
504
+ plot_kwargs : dict
505
+ Additional keyword arguments passed to ``matplotlib.pyplot.plot``.
506
+ channel_titles : list, optional
507
+ Per-channel titles to display above each subplot.
508
+ fig_title : str, optional
509
+ Overall figure title.
366
510
  """
367
511
  for channel in channels:
368
512
  if channel not in reference_df.columns:
@@ -438,13 +582,14 @@ def plot_cell_trajectory(
438
582
  min_track_length: int = 30,
439
583
  centroid0_name: str = "centroid-0",
440
584
  centroid1_name: str = "centroid-1",
441
- phase_column: Optional[str] = None,
442
- percentage_column: Optional[str] = None,
585
+ phase_column: str | None = None,
586
+ percentage_column: str | None = None,
443
587
  coloring_mode: str = "phase",
444
- line_cycle: Optional[list] = None,
588
+ line_cycle: list | None = None,
445
589
  **kwargs: int,
446
590
  ) -> None:
447
- """Plot cell migration trajectories with phase or percentage-based coloring.
591
+ """
592
+ Plot cell migration trajectories with phase- or percentage-based coloring.
448
593
 
449
594
  Parameters
450
595
  ----------
@@ -453,27 +598,31 @@ def plot_cell_trajectory(
453
598
  track_id_name : str
454
599
  Column name containing unique track identifiers.
455
600
  min_track_length : int, optional
456
- Minimum number of timepoints required to include a track, default is 30.
601
+ Minimum number of timepoints required to include a track.
602
+ Default is 30.
457
603
  centroid0_name : str, optional
458
- Column name for x-coordinate of cell centroid, default is "centroid-0".
604
+ Column name for x-coordinate of the cell centroid.
459
605
  centroid1_name : str, optional
460
- Column name for y-coordinate of cell centroid, default is "centroid-1".
606
+ Column name for y-coordinate of the cell centroid.
461
607
  phase_column : str, optional
462
- Column name containing cell cycle phase information, default is None.
608
+ Column name containing cell-cycle phase information. Required if
609
+ ``coloring_mode == "phase"``.
463
610
  percentage_column : str, optional
464
- Column name containing percentage values for coloring, default is None.
611
+ Column name containing percentage values for coloring. Required if
612
+ ``coloring_mode == "percentage"``.
465
613
  coloring_mode : str, optional
466
- Color tracks by cell cycle phase (`phase`) or by percentage (`percentage`)
467
- line_cycle: list
468
- Cycle through the list, can help with visualization
469
- kwargs: dict, optional
470
- Kwargs are directly passed to the LineCollection, use it to adjust
471
- the linestyle for example
614
+ Color tracks by cell-cycle phase (``"phase"``) or by percentage
615
+ (``"percentage"``). Default is ``"phase"``.
616
+ line_cycle : list, optional
617
+ List of linestyles to cycle through for successive tracks.
618
+ kwargs : dict, optional
619
+ Additional keyword arguments passed to
620
+ ``matplotlib.collections.LineCollection``.
472
621
 
473
622
  Notes
474
623
  -----
475
- Phase or percentage columns need to be provided for the respective coloring.
476
- If not, an error will be raised.
624
+ Phase or percentage columns need to be provided for the respective
625
+ coloring mode. If not, an error will be raised.
477
626
 
478
627
  """
479
628
  # inital checks
@@ -540,7 +689,7 @@ def plot_cell_trajectory(
540
689
  **kwargs,
541
690
  )
542
691
  )
543
- fig, ax = plt.subplots()
692
+ _, ax = plt.subplots()
544
693
  for line_collection in line_collections:
545
694
  ax.add_collection(line_collection)
546
695
  ax.margins(0.05)
fucciphase/sensor.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import List, Union
2
+ from typing import Union
3
3
 
4
4
  import numpy as np
5
5
  import pandas as pd
@@ -7,8 +7,8 @@ from scipy import optimize
7
7
 
8
8
 
9
9
  def logistic(
10
- x: Union[float, np.ndarray], center: float, sigma: float, sign: float = 1.0
11
- ) -> Union[float, np.ndarray]:
10
+ x: float | np.ndarray, center: float, sigma: float, sign: float = 1.0
11
+ ) -> float | np.ndarray:
12
12
  """Logistic function."""
13
13
  tiny = 1.0e-15
14
14
  arg = sign * (x - center) / max(tiny, sigma)
@@ -16,21 +16,21 @@ def logistic(
16
16
 
17
17
 
18
18
  def accumulation_function(
19
- x: Union[float, np.ndarray],
19
+ x: float | np.ndarray,
20
20
  center: float,
21
21
  sigma: float,
22
22
  offset_intensity: float = 0,
23
- ) -> Union[float, np.ndarray]:
23
+ ) -> float | np.ndarray:
24
24
  """Function to describe accumulation of sensor."""
25
25
  return 1.0 - logistic(x, center, sigma) - offset_intensity
26
26
 
27
27
 
28
28
  def degradation_function(
29
- x: Union[float, np.ndarray],
29
+ x: float | np.ndarray,
30
30
  center: float,
31
31
  sigma: float,
32
32
  offset_intensity: float = 0,
33
- ) -> Union[float, np.ndarray]:
33
+ ) -> float | np.ndarray:
34
34
  """Function to describe degradation of sensor."""
35
35
  return 1.0 - logistic(x, center, sigma, sign=-1.0) - offset_intensity
36
36
 
@@ -41,9 +41,9 @@ class FUCCISensor(ABC):
41
41
  @abstractmethod
42
42
  def __init__(
43
43
  self,
44
- phase_percentages: List[float],
45
- center: List[float],
46
- sigma: List[float],
44
+ phase_percentages: list[float],
45
+ center: list[float],
46
+ sigma: list[float],
47
47
  ) -> None:
48
48
  pass
49
49
 
@@ -55,17 +55,17 @@ class FUCCISensor(ABC):
55
55
 
56
56
  @property
57
57
  @abstractmethod
58
- def phases(self) -> List[str]:
58
+ def phases(self) -> list[str]:
59
59
  """Function to hard-code the supported phases of a sensor."""
60
60
  pass
61
61
 
62
62
  @property
63
- def phase_percentages(self) -> List[float]:
63
+ def phase_percentages(self) -> list[float]:
64
64
  """Percentage of individual phases."""
65
65
  return self._phase_percentages
66
66
 
67
67
  @phase_percentages.setter
68
- def phase_percentages(self, values: List[float]) -> None:
68
+ def phase_percentages(self, values: list[float]) -> None:
69
69
  if len(values) != len(self.phases):
70
70
  raise ValueError("Pass percentage for each phase.")
71
71
 
@@ -76,25 +76,25 @@ class FUCCISensor(ABC):
76
76
  self._phase_percentages = values
77
77
 
78
78
  @abstractmethod
79
- def get_phase(self, phase_markers: Union[List[bool], "pd.Series[bool]"]) -> str:
79
+ def get_phase(self, phase_markers: Union[list[bool], "pd.Series[bool]"]) -> str:
80
80
  """Get the discrete phase based on phase markers.
81
81
 
82
82
  Notes
83
83
  -----
84
- Discrete phase refers to, for example, G1 or S phase.
84
+ The discrete phase refers to, for example, G1 or S phase.
85
85
  The phase_markers must match the number of used fluorophores.
86
86
  """
87
87
  pass
88
88
 
89
89
  @abstractmethod
90
90
  def get_estimated_cycle_percentage(
91
- self, phase: str, intensities: List[float]
91
+ self, phase: str, intensities: list[float]
92
92
  ) -> float:
93
93
  """Estimate percentage based on sensor intensities."""
94
94
  pass
95
95
 
96
96
  def set_accumulation_and_degradation_parameters(
97
- self, center: List[float], sigma: List[float]
97
+ self, center: list[float], sigma: list[float]
98
98
  ) -> None:
99
99
  """Pass list of functions for logistic functions.
100
100
 
@@ -114,8 +114,8 @@ class FUCCISensor(ABC):
114
114
 
115
115
  @abstractmethod
116
116
  def get_expected_intensities(
117
- self, percentage: Union[float, np.ndarray]
118
- ) -> List[Union[float, np.ndarray]]:
117
+ self, percentage: float | np.ndarray
118
+ ) -> list[float | np.ndarray]:
119
119
  """Return value of calibrated curves."""
120
120
  pass
121
121
 
@@ -124,7 +124,7 @@ class FUCCISASensor(FUCCISensor):
124
124
  """FUCCI(SA) sensor."""
125
125
 
126
126
  def __init__(
127
- self, phase_percentages: List[float], center: List[float], sigma: List[float]
127
+ self, phase_percentages: list[float], center: list[float], sigma: list[float]
128
128
  ) -> None:
129
129
  self.phase_percentages = phase_percentages
130
130
  self.set_accumulation_and_degradation_parameters(center, sigma)
@@ -135,11 +135,11 @@ class FUCCISASensor(FUCCISensor):
135
135
  return 2
136
136
 
137
137
  @property
138
- def phases(self) -> List[str]:
138
+ def phases(self) -> list[str]:
139
139
  """Function to hard-code the supported phases of a sensor."""
140
140
  return ["G1", "G1/S", "S/G2/M"]
141
141
 
142
- def get_phase(self, phase_markers: Union[List[bool], "pd.Series[bool]"]) -> str:
142
+ def get_phase(self, phase_markers: Union[list[bool], "pd.Series[bool]"]) -> str:
143
143
  """Return the discrete phase based channel ON / OFF data for the
144
144
  FUCCI(SA) sensor.
145
145
  """
@@ -279,7 +279,7 @@ class FUCCISASensor(FUCCISensor):
279
279
  return g1s_perc + 0.5 * (100.0 - g1s_perc - g1_perc)
280
280
 
281
281
  def get_estimated_cycle_percentage(
282
- self, phase: str, intensities: List[float]
282
+ self, phase: str, intensities: list[float]
283
283
  ) -> float:
284
284
  """Estimate a cell cycle percentage based on intensities.
285
285
 
@@ -300,8 +300,8 @@ class FUCCISASensor(FUCCISensor):
300
300
  return self._find_sg2m_percentage(intensities[1])
301
301
 
302
302
  def get_expected_intensities(
303
- self, percentage: Union[float, np.ndarray]
304
- ) -> List[Union[float, np.ndarray]]:
303
+ self, percentage: float | np.ndarray
304
+ ) -> list[float | np.ndarray]:
305
305
  """Return value of calibrated curves."""
306
306
  g1_acc = accumulation_function(
307
307
  percentage, self._center_values[0], self._sigma_values[0]
@@ -332,7 +332,7 @@ class PIPFUCCISensor(FUCCISensor):
332
332
  """PIP-FUCCI sensor."""
333
333
 
334
334
  def __init__(
335
- self, phase_percentages: List[float], center: List[float], sigma: List[float]
335
+ self, phase_percentages: list[float], center: list[float], sigma: list[float]
336
336
  ) -> None:
337
337
  self.phase_percentages = phase_percentages
338
338
  self.set_accumulation_and_degradation_parameters(center, sigma)
@@ -343,11 +343,11 @@ class PIPFUCCISensor(FUCCISensor):
343
343
  return 2
344
344
 
345
345
  @property
346
- def phases(self) -> List[str]:
346
+ def phases(self) -> list[str]:
347
347
  """Function to hard-code the supported phases of a sensor."""
348
348
  return ["G1", "S", "G2/M"]
349
349
 
350
- def get_phase(self, phase_markers: Union[List[bool], "pd.Series[bool]"]) -> str:
350
+ def get_phase(self, phase_markers: Union[list[bool], "pd.Series[bool]"]) -> str:
351
351
  """Return the discrete phase based channel ON / OFF data for the
352
352
  FUCCI(SA) sensor.
353
353
  """
@@ -414,7 +414,7 @@ class PIPFUCCISensor(FUCCISensor):
414
414
  raise NotImplementedError("Percentage estimate not yet implemented!")
415
415
 
416
416
  def get_estimated_cycle_percentage(
417
- self, phase: str, intensities: List[float]
417
+ self, phase: str, intensities: list[float]
418
418
  ) -> float:
419
419
  """Estimate a cell cycle percentage based on intensities.
420
420
 
@@ -437,8 +437,8 @@ class PIPFUCCISensor(FUCCISensor):
437
437
  return self._find_g2m_percentage(intensities[1])
438
438
 
439
439
  def get_expected_intensities(
440
- self, percentage: Union[float, np.ndarray]
441
- ) -> List[Union[float, np.ndarray]]:
440
+ self, percentage: float | np.ndarray
441
+ ) -> list[float | np.ndarray]:
442
442
  """Return value of calibrated curves."""
443
443
  raise NotImplementedError("Intensity estimate not yet implemented!")
444
444