fucciphase 0.0.2__py3-none-any.whl → 0.0.4__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,5 @@
1
+ import logging
1
2
  from itertools import cycle
2
- from typing import List, Optional
3
3
 
4
4
  import numpy as np
5
5
  import pandas as pd
@@ -12,11 +12,33 @@ from scipy import interpolate
12
12
  from .phase import NewColumns
13
13
  from .utils import get_norm_channel_name
14
14
 
15
+ logger = logging.getLogger(__name__)
16
+
15
17
 
16
18
  def set_phase_colors(
17
19
  df: pd.DataFrame, colordict: dict, phase_column: str = "DISCRETE_PHASE_MAX"
18
20
  ) -> None:
19
- """Label each phase by fixed color."""
21
+ """Label each phase by fixed color.
22
+
23
+ This function adds a ``COLOR`` column to the dataframe by mapping each
24
+ entry in ``phase_column`` to a color specified in ``colordict``.
25
+
26
+ Parameters
27
+ ----------
28
+ df : pandas.DataFrame
29
+ Dataframe containing at least ``phase_column``.
30
+ colordict : dict
31
+ Mapping from phase label (str) to a valid matplotlib color.
32
+ phase_column : str, optional
33
+ Name of the column containing phase labels. Default is
34
+ ``"DISCRETE_PHASE_MAX"``.
35
+
36
+ Raises
37
+ ------
38
+ ValueError
39
+ If not all phase labels present in the dataframe have a color
40
+ entry in ``colordict``.
41
+ """
20
42
  phases = df[phase_column].unique()
21
43
  if not all(phase in colordict for phase in phases):
22
44
  raise ValueError(f"Provide a color for every phase in: {phases}")
@@ -32,10 +54,39 @@ def plot_feature(
32
54
  feature_name: str,
33
55
  interpolate_time: bool = False,
34
56
  track_id_name: str = "TRACK_ID",
35
- ylim: Optional[tuple] = None,
36
- yticks: Optional[list] = None,
57
+ ylim: tuple | None = None,
58
+ yticks: list | None = None,
37
59
  ) -> Figure:
38
- """Plot features of individual tracks in one plot."""
60
+ """Plot features of individual tracks in one plot.
61
+
62
+ Parameters
63
+ ----------
64
+ df : pandas.DataFrame
65
+ Dataframe containing the feature and time columns.
66
+ time_column : str
67
+ Name of the column containing time or frame indices.
68
+ feature_name : str
69
+ Name of the feature column to plot.
70
+ interpolate_time : bool, optional
71
+ Currently unused; kept for API compatibility. Default is False.
72
+ track_id_name : str, optional
73
+ Name of the column containing track IDs. Default is ``"TRACK_ID"``.
74
+ ylim : tuple, optional
75
+ y-axis limits as ``(ymin, ymax)``.
76
+ yticks : list, optional
77
+ Explicit y-tick locations.
78
+
79
+ Returns
80
+ -------
81
+ matplotlib.figure.Figure
82
+ The figure containing the plot.
83
+
84
+ Raises
85
+ ------
86
+ ValueError
87
+ If the feature or time columns are not found.
88
+
89
+ """
39
90
  if feature_name not in df:
40
91
  raise ValueError(f"(Feature {feature_name} not in provided DataFrame.")
41
92
  if time_column not in df:
@@ -63,20 +114,57 @@ def plot_feature_stacked(
63
114
  feature_name: str,
64
115
  interpolate_time: bool = False,
65
116
  track_id_name: str = "TRACK_ID",
66
- ylim: Optional[tuple] = None,
67
- yticks: Optional[list] = None,
117
+ ylim: tuple | None = None,
118
+ yticks: list | None = None,
68
119
  interpolation_steps: int = 1000,
69
- figsize: Optional[tuple] = None,
70
- selected_tracks: Optional[List[int]] = None,
120
+ figsize: tuple | None = None,
121
+ selected_tracks: list[int] | None = None,
71
122
  ) -> Figure:
72
123
  """Stack features of individual tracks.
73
124
 
125
+ Each selected track is plotted in its own horizontal panel. If
126
+ ``interpolate_time`` is True, an additional panel at the bottom shows
127
+ the mean interpolated feature across all tracks.
74
128
 
75
129
  Notes
76
130
  -----
77
- If `selected_tracks` are chosen, the averaging
78
- is still performed on all tracks.
79
- Few selected tracks are stacked to enhance visibility.
131
+ If ``selected_tracks`` are chosen, the averaging is still performed on
132
+ all tracks. The selected subset is only used for stacked visualization.
133
+
134
+ Parameters
135
+ ----------
136
+ df : pandas.DataFrame
137
+ Dataframe containing the feature, time and COLOR columns.
138
+ time_column : str
139
+ Name of the column containing time or frame indices.
140
+ feature_name : str
141
+ Name of the feature column to plot.
142
+ interpolate_time : bool, optional
143
+ If True, add an extra panel showing the average interpolated
144
+ feature over time. Default is False.
145
+ track_id_name : str, optional
146
+ Name of the column containing track IDs. Default is ``"TRACK_ID"``.
147
+ ylim : tuple, optional
148
+ y-axis limits as ``(ymin, ymax)``.
149
+ yticks : list, optional
150
+ Explicit y-tick locations.
151
+ interpolation_steps : int, optional
152
+ Number of time points used for interpolation. Default is 1000.
153
+ figsize : tuple, optional
154
+ Figure size passed to ``plt.subplots``.
155
+ selected_tracks : list of int, optional
156
+ Subset of track IDs to plot in stacked panels.
157
+
158
+ Returns
159
+ -------
160
+ matplotlib.figure.Figure
161
+ The figure containing the stacked plots.
162
+
163
+ Raises
164
+ ------
165
+ ValueError
166
+ If required columns are missing or ``selected_tracks`` contains
167
+ IDs not present in the dataframe.
80
168
  """
81
169
  if feature_name not in df:
82
170
  raise ValueError(f"(Feature {feature_name} not in provided DataFrame.")
@@ -156,7 +244,27 @@ def plot_raw_intensities(
156
244
  time_label: str = "Frame #",
157
245
  **plot_kwargs: bool,
158
246
  ) -> None:
159
- """Plot intensities of two-channel sensor."""
247
+ """Plot intensities of two-channel sensor over time.
248
+
249
+ Parameters
250
+ ----------
251
+ df : pandas.DataFrame
252
+ Dataframe containing intensity and time columns.
253
+ channel1 : str
254
+ Name of the first intensity column.
255
+ channel2 : str
256
+ Name of the second intensity column.
257
+ color1 : str, optional
258
+ Color used for ``channel1``. Default is ``"cyan"``.
259
+ color2 : str, optional
260
+ Color used for ``channel2``. Default is ``"magenta"``.
261
+ time_column : str, optional
262
+ Name of the time/frame column. Default is ``"FRAME"``.
263
+ time_label : str, optional
264
+ Label used for the x-axis. Default is ``"Frame #"``.
265
+ plot_kwargs : dict
266
+ Additional keyword arguments passed to ``matplotlib.pyplot.plot``.
267
+ """
160
268
  ch1_intensity = df[channel1]
161
269
  ch2_intensity = df[channel2]
162
270
 
@@ -188,7 +296,27 @@ def plot_normalized_intensities(
188
296
  time_label: str = "Frame #",
189
297
  **plot_kwargs: bool,
190
298
  ) -> None:
191
- """Plot normalised intensities of two-channel sensor."""
299
+ """Plot normalised intensities of two-channel sensor.
300
+
301
+ Parameters
302
+ ----------
303
+ df : pandas.DataFrame
304
+ Dataframe containing normalized intensity and time columns.
305
+ channel1 : str
306
+ Name of the first channel (pre-normalization).
307
+ channel2 : str
308
+ Name of the second channel (pre-normalization).
309
+ color1 : str, optional
310
+ Color used for ``channel1``. Default is ``"cyan"``.
311
+ color2 : str, optional
312
+ Color used for ``channel2``. Default is ``"magenta"``.
313
+ time_column : str, optional
314
+ Name of the time/frame column. Default is ``"FRAME"``.
315
+ time_label : str, optional
316
+ Label used for the x-axis. Default is ``"Frame #"``.
317
+ plot_kwargs : dict
318
+ Additional keyword arguments passed to ``matplotlib.pyplot.plot``.
319
+ """
192
320
  ch1_intensity = df[get_norm_channel_name(channel1)]
193
321
  ch2_intensity = df[get_norm_channel_name(channel2)]
194
322
 
@@ -200,30 +328,28 @@ def plot_normalized_intensities(
200
328
 
201
329
 
202
330
  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.
331
+ """Plot discrete cell-cycle phase-related signals over time.
205
332
 
206
- The dataframe must be preprocessed with one of the available phase
207
- computation function and must contain the following columns:
333
+ Plot the two normalized channels and the unique intensity curve over
334
+ frame number. The dataframe must already contain:
208
335
 
209
- - normalised channels (channel1 + "_NORM", etc)
210
- - cell cycle percentage
211
- - FRAME
336
+ - normalized channels (e.g. ``channel + "_NORM"``),
337
+ - the cell cycle percentage column,
338
+ - the ``FRAME`` column.
212
339
 
213
340
  Parameters
214
341
  ----------
215
- df : pd.DataFrame
216
- Dataframe
342
+ df : pandas.DataFrame
343
+ Input dataframe.
217
344
  channel1 : str
218
- First channel
345
+ First channel name (pre-normalization).
219
346
  channel2 : str
220
- Second channel
347
+ Second channel name (pre-normalization).
221
348
 
222
349
  Raises
223
350
  ------
224
351
  ValueError
225
- If the dataframe does not contain the FRAME, CELL_CYCLE_PERC and normalised
226
- columns.
352
+ If the dataframe does not contain the required columns.
227
353
  """
228
354
  # check if the FRAME column is present
229
355
  if "FRAME" not in df.columns:
@@ -248,33 +374,49 @@ def plot_phase(df: pd.DataFrame, channel1: str, channel2: str) -> None:
248
374
  def plot_dtw_query_vs_reference(
249
375
  reference_df: pd.DataFrame,
250
376
  df: pd.DataFrame,
251
- channels: List[str],
377
+ channels: list[str],
252
378
  ref_percentage_column: str = "percentage",
253
379
  est_percentage_column: str = "CELL_CYCLE_PERC_DTW",
254
- ground_truth: Optional[pd.DataFrame] = None,
255
- colors: Optional[List[str]] = None,
380
+ ground_truth: pd.DataFrame | None = None,
381
+ colors: list[str] | None = None,
256
382
  **plot_kwargs: bool,
257
383
  ) -> None:
258
- """Plot query and alignment to reference curve.
384
+ """
385
+ Plot query curves and their alignment to a reference cell-cycle curve.
386
+
387
+ For each channel, this function plots:
388
+
389
+ - the query intensity as a function of estimated percentage,
390
+ - the reference intensity as a function of reference percentage,
391
+ - the reference curve re-sampled at the query percentages (match).
392
+
393
+ Optionally, ground truth curves can be overlaid.
259
394
 
260
395
  Parameters
261
396
  ----------
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
397
+ reference_df : pandas.DataFrame
398
+ Dataframe with the reference curve (percentage vs intensity).
399
+ df : pandas.DataFrame
400
+ Dataframe used as query (estimated percentage vs intensity).
401
+ channels : list of str
402
+ Names of the channels to plot.
403
+ ref_percentage_column : str, optional
404
+ Column name for reference percentages. Default is ``"percentage"``.
405
+ est_percentage_column : str, optional
406
+ Column name for estimated percentages in the query dataframe.
407
+ Default is ``"CELL_CYCLE_PERC_DTW"``.
408
+ ground_truth : pandas.DataFrame, optional
409
+ Dataframe containing ground truth intensities, with the same
410
+ column names as ``reference_df``.
411
+ colors : list of str, optional
412
+ Colors to use for each channel in the reference plots.
413
+ plot_kwargs : dict
414
+ Additional keyword arguments passed to ``matplotlib.pyplot.plot``.
415
+
416
+ Raises
417
+ ------
418
+ ValueError
419
+ If required columns are missing in the reference or query dataframes.
278
420
  """
279
421
  for channel in channels:
280
422
  if channel not in reference_df.columns:
@@ -333,36 +475,41 @@ def plot_dtw_query_vs_reference(
333
475
  def plot_query_vs_reference_in_time(
334
476
  reference_df: pd.DataFrame,
335
477
  df: pd.DataFrame,
336
- channels: List[str],
478
+ channels: list[str],
337
479
  ref_time_column: str = "time",
338
480
  query_time_column: str = "time",
339
- colors: Optional[List[str]] = None,
340
- channel_titles: Optional[List[str]] = None,
341
- fig_title: Optional[str] = None,
481
+ colors: list[str] | None = None,
482
+ channel_titles: list[str] | None = None,
483
+ fig_title: str | None = None,
342
484
  **plot_kwargs: bool,
343
485
  ) -> None:
344
- """Plot query and alignment to reference curve.
486
+ """
487
+ Plot query and reference curves as a function of time.
488
+
489
+ For each channel, this function overlays the query intensity and the
490
+ reference intensity over time. This is useful to visually compare
491
+ dynamics in the original time domain.
345
492
 
346
493
  Parameters
347
494
  ----------
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
495
+ reference_df : pandas.DataFrame
496
+ Dataframe with reference curve data (time vs intensity).
497
+ df : pandas.DataFrame
498
+ Dataframe with query data (time vs intensity).
499
+ channels : list of str
500
+ Names of the channels to plot.
501
+ ref_time_column : str, optional
502
+ Column name for reference time values. Default is ``"time"``.
503
+ query_time_column : str, optional
504
+ Column name for query time values. Default is ``"time"``.
505
+ colors : list of str, optional
506
+ Colors to use for the reference curves.
507
+ plot_kwargs : dict
508
+ Additional keyword arguments passed to ``matplotlib.pyplot.plot``.
509
+ channel_titles : list, optional
510
+ Per-channel titles to display above each subplot.
511
+ fig_title : str, optional
512
+ Overall figure title.
366
513
  """
367
514
  for channel in channels:
368
515
  if channel not in reference_df.columns:
@@ -425,7 +572,7 @@ def get_percentage_color(percentage: float) -> tuple:
425
572
  cmap_name = "cool"
426
573
  cmap = colormaps.get(cmap_name)
427
574
  if np.isnan(percentage):
428
- print("WARNING: NaN value detected, plot will be transparent")
575
+ logger.warning("NaN percentage value detected, plot will be transparent")
429
576
  rgba_value = (0, 0, 0, 0)
430
577
  else:
431
578
  rgba_value = cmap(percentage / 100.0)
@@ -438,13 +585,14 @@ def plot_cell_trajectory(
438
585
  min_track_length: int = 30,
439
586
  centroid0_name: str = "centroid-0",
440
587
  centroid1_name: str = "centroid-1",
441
- phase_column: Optional[str] = None,
442
- percentage_column: Optional[str] = None,
588
+ phase_column: str | None = None,
589
+ percentage_column: str | None = None,
443
590
  coloring_mode: str = "phase",
444
- line_cycle: Optional[list] = None,
591
+ line_cycle: list | None = None,
445
592
  **kwargs: int,
446
593
  ) -> None:
447
- """Plot cell migration trajectories with phase or percentage-based coloring.
594
+ """
595
+ Plot cell migration trajectories with phase- or percentage-based coloring.
448
596
 
449
597
  Parameters
450
598
  ----------
@@ -453,27 +601,31 @@ def plot_cell_trajectory(
453
601
  track_id_name : str
454
602
  Column name containing unique track identifiers.
455
603
  min_track_length : int, optional
456
- Minimum number of timepoints required to include a track, default is 30.
604
+ Minimum number of timepoints required to include a track.
605
+ Default is 30.
457
606
  centroid0_name : str, optional
458
- Column name for x-coordinate of cell centroid, default is "centroid-0".
607
+ Column name for x-coordinate of the cell centroid.
459
608
  centroid1_name : str, optional
460
- Column name for y-coordinate of cell centroid, default is "centroid-1".
609
+ Column name for y-coordinate of the cell centroid.
461
610
  phase_column : str, optional
462
- Column name containing cell cycle phase information, default is None.
611
+ Column name containing cell-cycle phase information. Required if
612
+ ``coloring_mode == "phase"``.
463
613
  percentage_column : str, optional
464
- Column name containing percentage values for coloring, default is None.
614
+ Column name containing percentage values for coloring. Required if
615
+ ``coloring_mode == "percentage"``.
465
616
  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
617
+ Color tracks by cell-cycle phase (``"phase"``) or by percentage
618
+ (``"percentage"``). Default is ``"phase"``.
619
+ line_cycle : list, optional
620
+ List of linestyles to cycle through for successive tracks.
621
+ kwargs : dict, optional
622
+ Additional keyword arguments passed to
623
+ ``matplotlib.collections.LineCollection``.
472
624
 
473
625
  Notes
474
626
  -----
475
- Phase or percentage columns need to be provided for the respective coloring.
476
- If not, an error will be raised.
627
+ Phase or percentage columns need to be provided for the respective
628
+ coloring mode. If not, an error will be raised.
477
629
 
478
630
  """
479
631
  # inital checks