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/__init__.py +7 -1
- fucciphase/__main__.py +12 -0
- fucciphase/fucci_phase.py +123 -53
- fucciphase/io.py +18 -17
- fucciphase/main_cli.py +151 -34
- fucciphase/napari/tracks_to_napari.py +20 -21
- fucciphase/phase.py +350 -137
- fucciphase/plot.py +240 -88
- fucciphase/sensor.py +47 -33
- fucciphase/tracking_utilities.py +70 -9
- fucciphase/utils/__init__.py +14 -1
- fucciphase/utils/checks.py +2 -5
- fucciphase/utils/dtw.py +2 -4
- fucciphase/utils/normalize.py +46 -12
- fucciphase/utils/phase_fit.py +11 -7
- fucciphase/utils/simulator.py +1 -1
- fucciphase/utils/track_postprocessing.py +16 -11
- fucciphase/utils/trackmate.py +30 -13
- fucciphase-0.0.4.dist-info/METADATA +238 -0
- fucciphase-0.0.4.dist-info/RECORD +25 -0
- {fucciphase-0.0.2.dist-info → fucciphase-0.0.4.dist-info}/WHEEL +1 -1
- fucciphase-0.0.2.dist-info/METADATA +0 -137
- fucciphase-0.0.2.dist-info/RECORD +0 -24
- {fucciphase-0.0.2.dist-info → fucciphase-0.0.4.dist-info}/entry_points.txt +0 -0
- {fucciphase-0.0.2.dist-info → fucciphase-0.0.4.dist-info}/licenses/LICENSE +0 -0
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:
|
|
36
|
-
yticks:
|
|
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:
|
|
67
|
-
yticks:
|
|
117
|
+
ylim: tuple | None = None,
|
|
118
|
+
yticks: list | None = None,
|
|
68
119
|
interpolation_steps: int = 1000,
|
|
69
|
-
figsize:
|
|
70
|
-
selected_tracks:
|
|
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
|
|
78
|
-
is
|
|
79
|
-
|
|
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
|
|
204
|
-
corresponding to the change of phase.
|
|
331
|
+
"""Plot discrete cell-cycle phase-related signals over time.
|
|
205
332
|
|
|
206
|
-
|
|
207
|
-
|
|
333
|
+
Plot the two normalized channels and the unique intensity curve over
|
|
334
|
+
frame number. The dataframe must already contain:
|
|
208
335
|
|
|
209
|
-
-
|
|
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 :
|
|
216
|
-
|
|
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
|
|
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:
|
|
377
|
+
channels: list[str],
|
|
252
378
|
ref_percentage_column: str = "percentage",
|
|
253
379
|
est_percentage_column: str = "CELL_CYCLE_PERC_DTW",
|
|
254
|
-
ground_truth:
|
|
255
|
-
colors:
|
|
380
|
+
ground_truth: pd.DataFrame | None = None,
|
|
381
|
+
colors: list[str] | None = None,
|
|
256
382
|
**plot_kwargs: bool,
|
|
257
383
|
) -> None:
|
|
258
|
-
"""
|
|
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:
|
|
263
|
-
|
|
264
|
-
df:
|
|
265
|
-
|
|
266
|
-
channels:
|
|
267
|
-
|
|
268
|
-
ref_percentage_column: str
|
|
269
|
-
|
|
270
|
-
est_percentage_column: str
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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:
|
|
478
|
+
channels: list[str],
|
|
337
479
|
ref_time_column: str = "time",
|
|
338
480
|
query_time_column: str = "time",
|
|
339
|
-
colors:
|
|
340
|
-
channel_titles:
|
|
341
|
-
fig_title:
|
|
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
|
-
"""
|
|
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:
|
|
349
|
-
|
|
350
|
-
df:
|
|
351
|
-
|
|
352
|
-
channels:
|
|
353
|
-
|
|
354
|
-
ref_time_column: str
|
|
355
|
-
|
|
356
|
-
query_time_column: str
|
|
357
|
-
|
|
358
|
-
colors:
|
|
359
|
-
Colors for
|
|
360
|
-
plot_kwargs: dict
|
|
361
|
-
|
|
362
|
-
channel_titles:
|
|
363
|
-
titles
|
|
364
|
-
fig_title:
|
|
365
|
-
|
|
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
|
-
|
|
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:
|
|
442
|
-
percentage_column:
|
|
588
|
+
phase_column: str | None = None,
|
|
589
|
+
percentage_column: str | None = None,
|
|
443
590
|
coloring_mode: str = "phase",
|
|
444
|
-
line_cycle:
|
|
591
|
+
line_cycle: list | None = None,
|
|
445
592
|
**kwargs: int,
|
|
446
593
|
) -> None:
|
|
447
|
-
"""
|
|
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
|
|
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
|
|
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
|
|
609
|
+
Column name for y-coordinate of the cell centroid.
|
|
461
610
|
phase_column : str, optional
|
|
462
|
-
Column name containing cell
|
|
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
|
|
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
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
|
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
|