rapidtide 3.0.11__py3-none-any.whl → 3.1__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 (139) hide show
  1. rapidtide/Colortables.py +492 -27
  2. rapidtide/OrthoImageItem.py +1049 -46
  3. rapidtide/RapidtideDataset.py +1533 -86
  4. rapidtide/_version.py +3 -3
  5. rapidtide/calccoherence.py +196 -29
  6. rapidtide/calcnullsimfunc.py +191 -40
  7. rapidtide/calcsimfunc.py +245 -42
  8. rapidtide/correlate.py +1210 -393
  9. rapidtide/data/examples/src/testLD +56 -0
  10. rapidtide/data/examples/src/testalign +1 -1
  11. rapidtide/data/examples/src/testdelayvar +0 -1
  12. rapidtide/data/examples/src/testfmri +19 -1
  13. rapidtide/data/examples/src/testglmfilt +5 -5
  14. rapidtide/data/examples/src/testhappy +25 -3
  15. rapidtide/data/examples/src/testppgproc +17 -0
  16. rapidtide/data/examples/src/testrolloff +11 -0
  17. rapidtide/data/models/model_cnn_pytorch/best_model.pth +0 -0
  18. rapidtide/data/models/model_cnn_pytorch/loss.png +0 -0
  19. rapidtide/data/models/model_cnn_pytorch/loss.txt +1 -0
  20. rapidtide/data/models/model_cnn_pytorch/model.pth +0 -0
  21. rapidtide/data/models/model_cnn_pytorch/model_meta.json +68 -0
  22. rapidtide/decorators.py +91 -0
  23. rapidtide/dlfilter.py +2225 -108
  24. rapidtide/dlfiltertorch.py +4843 -0
  25. rapidtide/externaltools.py +327 -12
  26. rapidtide/fMRIData_class.py +79 -40
  27. rapidtide/filter.py +1899 -810
  28. rapidtide/fit.py +2004 -574
  29. rapidtide/genericmultiproc.py +93 -18
  30. rapidtide/happy_supportfuncs.py +2044 -171
  31. rapidtide/helper_classes.py +584 -43
  32. rapidtide/io.py +2363 -370
  33. rapidtide/linfitfiltpass.py +341 -75
  34. rapidtide/makelaggedtcs.py +211 -20
  35. rapidtide/maskutil.py +423 -53
  36. rapidtide/miscmath.py +827 -121
  37. rapidtide/multiproc.py +210 -22
  38. rapidtide/patchmatch.py +234 -33
  39. rapidtide/peakeval.py +32 -30
  40. rapidtide/ppgproc.py +2203 -0
  41. rapidtide/qualitycheck.py +352 -39
  42. rapidtide/refinedelay.py +422 -57
  43. rapidtide/refineregressor.py +498 -184
  44. rapidtide/resample.py +671 -185
  45. rapidtide/scripts/applyppgproc.py +28 -0
  46. rapidtide/simFuncClasses.py +1052 -77
  47. rapidtide/simfuncfit.py +260 -46
  48. rapidtide/stats.py +540 -238
  49. rapidtide/tests/happycomp +9 -0
  50. rapidtide/tests/test_dlfiltertorch.py +627 -0
  51. rapidtide/tests/test_findmaxlag.py +24 -8
  52. rapidtide/tests/test_fullrunhappy_v1.py +0 -2
  53. rapidtide/tests/test_fullrunhappy_v2.py +0 -2
  54. rapidtide/tests/test_fullrunhappy_v3.py +1 -0
  55. rapidtide/tests/test_fullrunhappy_v4.py +2 -2
  56. rapidtide/tests/test_fullrunrapidtide_v7.py +1 -1
  57. rapidtide/tests/test_simroundtrip.py +8 -8
  58. rapidtide/tests/utils.py +9 -8
  59. rapidtide/tidepoolTemplate.py +142 -38
  60. rapidtide/tidepoolTemplate_alt.py +165 -44
  61. rapidtide/tidepoolTemplate_big.py +189 -52
  62. rapidtide/util.py +1217 -118
  63. rapidtide/voxelData.py +684 -37
  64. rapidtide/wiener.py +19 -12
  65. rapidtide/wiener2.py +113 -7
  66. rapidtide/wiener_doc.py +255 -0
  67. rapidtide/workflows/adjustoffset.py +105 -3
  68. rapidtide/workflows/aligntcs.py +85 -2
  69. rapidtide/workflows/applydlfilter.py +87 -10
  70. rapidtide/workflows/applyppgproc.py +522 -0
  71. rapidtide/workflows/atlasaverage.py +210 -47
  72. rapidtide/workflows/atlastool.py +100 -3
  73. rapidtide/workflows/calcSimFuncMap.py +294 -64
  74. rapidtide/workflows/calctexticc.py +201 -9
  75. rapidtide/workflows/ccorrica.py +97 -4
  76. rapidtide/workflows/cleanregressor.py +168 -29
  77. rapidtide/workflows/delayvar.py +163 -10
  78. rapidtide/workflows/diffrois.py +81 -3
  79. rapidtide/workflows/endtidalproc.py +144 -4
  80. rapidtide/workflows/fdica.py +195 -15
  81. rapidtide/workflows/filtnifti.py +70 -3
  82. rapidtide/workflows/filttc.py +74 -3
  83. rapidtide/workflows/fitSimFuncMap.py +206 -48
  84. rapidtide/workflows/fixtr.py +73 -3
  85. rapidtide/workflows/gmscalc.py +113 -3
  86. rapidtide/workflows/happy.py +801 -199
  87. rapidtide/workflows/happy2std.py +144 -12
  88. rapidtide/workflows/happy_parser.py +138 -9
  89. rapidtide/workflows/histnifti.py +118 -2
  90. rapidtide/workflows/histtc.py +84 -3
  91. rapidtide/workflows/linfitfilt.py +117 -4
  92. rapidtide/workflows/localflow.py +328 -28
  93. rapidtide/workflows/mergequality.py +79 -3
  94. rapidtide/workflows/niftidecomp.py +322 -18
  95. rapidtide/workflows/niftistats.py +174 -4
  96. rapidtide/workflows/pairproc.py +88 -2
  97. rapidtide/workflows/pairwisemergenifti.py +85 -2
  98. rapidtide/workflows/parser_funcs.py +1421 -40
  99. rapidtide/workflows/physiofreq.py +137 -11
  100. rapidtide/workflows/pixelcomp.py +208 -5
  101. rapidtide/workflows/plethquality.py +103 -21
  102. rapidtide/workflows/polyfitim.py +151 -11
  103. rapidtide/workflows/proj2flow.py +75 -2
  104. rapidtide/workflows/rankimage.py +111 -4
  105. rapidtide/workflows/rapidtide.py +272 -15
  106. rapidtide/workflows/rapidtide2std.py +98 -2
  107. rapidtide/workflows/rapidtide_parser.py +109 -9
  108. rapidtide/workflows/refineDelayMap.py +143 -33
  109. rapidtide/workflows/refineRegressor.py +682 -93
  110. rapidtide/workflows/regressfrommaps.py +152 -31
  111. rapidtide/workflows/resamplenifti.py +85 -3
  112. rapidtide/workflows/resampletc.py +91 -3
  113. rapidtide/workflows/retrolagtcs.py +98 -6
  114. rapidtide/workflows/retroregress.py +165 -9
  115. rapidtide/workflows/roisummarize.py +173 -5
  116. rapidtide/workflows/runqualitycheck.py +71 -3
  117. rapidtide/workflows/showarbcorr.py +147 -4
  118. rapidtide/workflows/showhist.py +86 -2
  119. rapidtide/workflows/showstxcorr.py +160 -3
  120. rapidtide/workflows/showtc.py +159 -3
  121. rapidtide/workflows/showxcorrx.py +184 -4
  122. rapidtide/workflows/showxy.py +185 -15
  123. rapidtide/workflows/simdata.py +262 -36
  124. rapidtide/workflows/spatialfit.py +77 -2
  125. rapidtide/workflows/spatialmi.py +251 -27
  126. rapidtide/workflows/spectrogram.py +305 -32
  127. rapidtide/workflows/synthASL.py +154 -3
  128. rapidtide/workflows/tcfrom2col.py +76 -2
  129. rapidtide/workflows/tcfrom3col.py +74 -2
  130. rapidtide/workflows/tidepool.py +2969 -130
  131. rapidtide/workflows/utils.py +19 -14
  132. rapidtide/workflows/utils_doc.py +293 -0
  133. rapidtide/workflows/variabilityizer.py +116 -3
  134. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/METADATA +3 -2
  135. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/RECORD +139 -122
  136. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/entry_points.txt +1 -0
  137. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/WHEEL +0 -0
  138. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/licenses/LICENSE +0 -0
  139. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/top_level.txt +0 -0
@@ -18,9 +18,12 @@
18
18
  #
19
19
  import argparse
20
20
  import os
21
+ from argparse import Namespace
22
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
21
23
 
22
24
  import matplotlib.pyplot as plt
23
25
  import numpy as np
26
+ from numpy.typing import NDArray
24
27
  from scipy.signal import savgol_filter, welch
25
28
 
26
29
  import rapidtide.filter as tide_filt
@@ -29,7 +32,32 @@ import rapidtide.io as tide_io
29
32
  import rapidtide.miscmath as tide_math
30
33
 
31
34
 
32
- def _get_parser():
35
+ def _get_parser() -> Any:
36
+ """
37
+ Create and configure argument parser for physiofreq command line tool.
38
+
39
+ This function initializes an ArgumentParser object with all necessary
40
+ command line arguments for the physiofreq tool, which is designed to
41
+ find the dominant frequency in cardiac or respiratory waveforms.
42
+
43
+ Returns
44
+ -------
45
+ argparse.ArgumentParser
46
+ Configured argument parser object with all required arguments
47
+ for the physiofreq tool.
48
+
49
+ Notes
50
+ -----
51
+ The parser includes arguments for input file specification, display options,
52
+ sampling rate, frequency range constraints, and smoothing settings.
53
+
54
+ Examples
55
+ --------
56
+ >>> parser = _get_parser()
57
+ >>> args = parser.parse_args(['input.txt', '--display', '--samplerate', '2.0'])
58
+ >>> print(args.textfilename)
59
+ 'input.txt'
60
+ """
33
61
  parser = argparse.ArgumentParser(
34
62
  prog="physiofreq",
35
63
  description="Finds the dominant frequency in a cardiac or respiratory waveform.",
@@ -75,15 +103,65 @@ def _get_parser():
75
103
 
76
104
 
77
105
  def getwavefreq(
78
- thewaveform,
79
- thesamplerate,
80
- minpermin=40.0,
81
- maxpermin=140.0,
82
- smooth=True,
83
- smoothlen=101,
84
- debug=False,
85
- displayplots=False,
86
- ):
106
+ thewaveform: Any,
107
+ thesamplerate: Any,
108
+ minpermin: float = 40.0,
109
+ maxpermin: float = 140.0,
110
+ smooth: bool = True,
111
+ smoothlen: int = 101,
112
+ debug: bool = False,
113
+ displayplots: bool = False,
114
+ ) -> None:
115
+ """
116
+ Compute the fundamental frequency of a waveform using Welch's method and spectral analysis.
117
+
118
+ This function estimates the fundamental frequency of a given waveform by computing
119
+ its power spectrum using Welch's method, applying filtering and smoothing, and
120
+ identifying the peak within a specified frequency range. The result is returned in
121
+ beats per minute (BPM).
122
+
123
+ Parameters
124
+ ----------
125
+ thewaveform : array-like
126
+ Input waveform data to analyze.
127
+ thesamplerate : float
128
+ Sampling rate of the waveform in Hz.
129
+ minpermin : float, optional
130
+ Minimum allowed frequency in BPM. Default is 40.0.
131
+ maxpermin : float, optional
132
+ Maximum allowed frequency in BPM. Default is 140.0.
133
+ smooth : bool, optional
134
+ If True, apply Savitzky-Golay smoothing to the spectrum. Default is True.
135
+ smoothlen : int, optional
136
+ Length of the smoothing window for Savitzky-Golay filter. Default is 101.
137
+ debug : bool, optional
138
+ If True, print debug information during computation. Default is False.
139
+ displayplots : bool, optional
140
+ If True, display intermediate plots of the power spectrum. Default is False.
141
+
142
+ Returns
143
+ -------
144
+ float
145
+ Estimated fundamental frequency in BPM.
146
+
147
+ Notes
148
+ -----
149
+ - The function internally uses `scipy.signal.welch` for power spectral density estimation.
150
+ - A Hamming window and detrending are applied before spectral analysis.
151
+ - The frequency range is constrained to the interval [minpermin, maxpermin].
152
+ - If `displayplots` is True, two plots will be shown:
153
+ 1. Initial power spectrum with peak marked.
154
+ 2. Smoothed spectrum with final peak marked.
155
+
156
+ Examples
157
+ --------
158
+ >>> import numpy as np
159
+ >>> from scipy.signal import chirp
160
+ >>> t = np.linspace(0, 5, 1000, endpoint=False)
161
+ >>> signal = chirp(t, f0=60, f1=120, t1=5, method='linear')
162
+ >>> freq = getwavefreq(signal, thesamplerate=1000, debug=True)
163
+ >>> print(f"Estimated frequency: {freq} BPM")
164
+ """
87
165
  if len(thewaveform) % 2 == 1:
88
166
  thewaveform = thewaveform[:-1]
89
167
  if len(thewaveform) > 1024:
@@ -141,7 +219,55 @@ def getwavefreq(
141
219
  return peakfreq
142
220
 
143
221
 
144
- def physiofreq(args):
222
+ def physiofreq(args: Any) -> None:
223
+ """
224
+ Calculate and display the dominant frequency of physiological data.
225
+
226
+ This function reads physiological time series data from a file and determines
227
+ the peak frequency using wavelet analysis. It supports both JSON and standard
228
+ text file formats, and displays the results in Hz, BPM, and period in seconds.
229
+
230
+ Parameters
231
+ ----------
232
+ args : Any
233
+ An object containing command line arguments with the following attributes:
234
+ - textfilename : str
235
+ Path to the input file containing physiological data
236
+ - samplerate : float, optional
237
+ Sampling rate of the data (used when file is not in JSON format)
238
+ - lowestbpm : float, optional
239
+ Minimum allowed heart rate in beats per minute (default: 30)
240
+ - highestbpm : float, optional
241
+ Maximum allowed heart rate in beats per minute (default: 200)
242
+ - nosmooth : bool, optional
243
+ If True, disables smoothing of the frequency spectrum
244
+ - displayplots : bool, optional
245
+ If True, displays frequency plots
246
+
247
+ Returns
248
+ -------
249
+ None
250
+ This function prints the frequency analysis results to stdout and does not return a value.
251
+
252
+ Notes
253
+ -----
254
+ The function automatically detects the file format based on the extension:
255
+ - JSON files are processed using `tide_io.readbidstsv()`
256
+ - Other formats are processed using `tide_io.readvecs()`
257
+
258
+ Examples
259
+ --------
260
+ >>> args = type('Args', (), {
261
+ ... 'textfilename': 'data.txt',
262
+ ... 'samplerate': 100.0,
263
+ ... 'lowestbpm': 40,
264
+ ... 'highestbpm': 180,
265
+ ... 'nosmooth': False,
266
+ ... 'displayplots': False
267
+ ... })()
268
+ >>> physiofreq(args)
269
+ data.txt: 0.83 Hz, 49.80 per minute, period is 1.20 seconds
270
+ """
145
271
  textfileinfo, textfilecolspec = tide_io.parsefilespec(args.textfilename)
146
272
  filebase, extension = os.path.splitext(textfileinfo[0])
147
273
  if extension == ".json":
@@ -25,12 +25,62 @@ from numpy.polynomial import Polynomial
25
25
  import rapidtide.io as tide_io
26
26
 
27
27
  mpl.use("Agg")
28
+ from argparse import Namespace
29
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
30
+
28
31
  import matplotlib.pyplot as plt
32
+ from numpy.typing import NDArray
29
33
 
30
34
 
31
- def _get_parser():
35
+ def _get_parser() -> Any:
32
36
  """
33
- Argument parser for pixelcomp
37
+ Argument parser for pixelcomp.
38
+
39
+ This function creates and configures an argument parser for the pixelcomp tool,
40
+ which is used to compare two NIfTI files voxel by voxel and generate either
41
+ a contour plot or a scatter plot of the differences.
42
+
43
+ Returns
44
+ -------
45
+ argparse.ArgumentParser
46
+ Configured argument parser object with all required and optional arguments
47
+ for the pixelcomp tool.
48
+
49
+ Notes
50
+ -----
51
+ The parser supports the following positional arguments:
52
+
53
+ - ``inputfilename1`` : str
54
+ The name of the first input image NIfTI file.
55
+ - ``maskfilename1`` : str
56
+ The name of the first input mask NIfTI file.
57
+ - ``inputfilename2`` : str
58
+ The name of the second input image NIfTI file.
59
+ - ``maskfilename2`` : str
60
+ The name of the second input mask NIfTI file.
61
+ - ``outputroot`` : str
62
+ The root name of the output files.
63
+
64
+ And the following optional arguments:
65
+
66
+ - ``--scatter`` : bool, optional
67
+ Do a scatter plot instead of a contour plot. Default is False.
68
+ - ``--fitonly`` : bool, optional
69
+ Perform fit only - do not generate graph. Default is False.
70
+ - ``--nodisplay`` : bool, optional
71
+ Save graphs to file only - do not display. Default is True.
72
+ - ``--fitorder`` : int, optional
73
+ Order of line fit - default is 1 (linear). Default is 1.
74
+ - ``--usex`` : bool, optional
75
+ Use x instead of (y + x)/2 in Bland-Altman plot. Default is False.
76
+ - ``--histbins`` : int, optional
77
+ Number of bins per dimension for the contour plot - default is 51.
78
+ Default is 51.
79
+
80
+ Examples
81
+ --------
82
+ >>> parser = _get_parser()
83
+ >>> args = parser.parse_args()
34
84
  """
35
85
  parser = argparse.ArgumentParser(
36
86
  prog="pixelcomp",
@@ -99,7 +149,58 @@ def _get_parser():
99
149
  return parser
100
150
 
101
151
 
102
- def bland_altman_plot(data1, data2, usex=False, *args, **kwargs):
152
+ def bland_altman_plot(
153
+ data1: Any, data2: Any, usex: bool = False, *args: Any, **kwargs: Any
154
+ ) -> None:
155
+ """
156
+ Create a Bland-Altman plot for comparing two sets of measurements.
157
+
158
+ This function generates a scatter plot showing the difference between two
159
+ measurements against their mean. The plot includes horizontal lines indicating
160
+ the mean difference and ±2 standard deviations, which are commonly used to
161
+ assess agreement between two measurement methods.
162
+
163
+ Parameters
164
+ ----------
165
+ data1 : array-like
166
+ First set of measurements (X values in the plot).
167
+ data2 : array-like
168
+ Second set of measurements (Y values in the plot).
169
+ usex : bool, optional
170
+ If True, use data1 as the x-values for the plot. If False (default),
171
+ use the mean of data1 and data2 as x-values.
172
+ *args : tuple
173
+ Additional arguments to pass to matplotlib's scatter function.
174
+ **kwargs : dict
175
+ Additional keyword arguments to pass to matplotlib's scatter function.
176
+
177
+ Returns
178
+ -------
179
+ None
180
+ This function displays the plot but does not return any value.
181
+
182
+ Notes
183
+ -----
184
+ The Bland-Altman plot is used to assess the agreement between two different
185
+ measurement methods. The mean difference (MD) is plotted on the y-axis, and
186
+ the mean of the two measurements is plotted on the x-axis. The horizontal
187
+ lines represent:
188
+ - Mean difference (MD)
189
+ - Mean difference ± 2 standard deviations (±2SD)
190
+
191
+ Examples
192
+ --------
193
+ >>> import numpy as np
194
+ >>> import matplotlib.pyplot as plt
195
+ >>> data1 = np.array([1, 2, 3, 4, 5])
196
+ >>> data2 = np.array([1.1, 2.2, 2.8, 4.1, 4.9])
197
+ >>> bland_altman_plot(data1, data2)
198
+ >>> plt.show()
199
+
200
+ >>> # Using custom scatter plot properties
201
+ >>> bland_altman_plot(data1, data2, c='red', alpha=0.7)
202
+ >>> plt.show()
203
+ """
103
204
  # data1 is X, data2 is Y
104
205
  data1 = np.asarray(data1)
105
206
  data2 = np.asarray(data2)
@@ -117,7 +218,47 @@ def bland_altman_plot(data1, data2, usex=False, *args, **kwargs):
117
218
  plt.axhline(md - 2 * sd, color="gray", linestyle="--")
118
219
 
119
220
 
120
- def pairdata(input1_data, input2_data, totalmask):
221
+ def pairdata(input1_data: Any, input2_data: Any, totalmask: Any) -> None:
222
+ """
223
+ Pair corresponding elements from two 3D arrays based on a mask.
224
+
225
+ This function extracts elements from two 3D input arrays where the mask
226
+ has non-zero values, creating pairs of corresponding elements.
227
+
228
+ Parameters
229
+ ----------
230
+ input1_data : array-like
231
+ First 3D array from which elements will be extracted.
232
+ input2_data : array-like
233
+ Second 3D array from which elements will be extracted.
234
+ totalmask : array-like
235
+ 3D mask array where non-zero values indicate positions to pair.
236
+
237
+ Returns
238
+ -------
239
+ numpy.ndarray
240
+ 2D array where each row contains a pair of corresponding elements
241
+ from input1_data and input2_data at positions where totalmask > 0.
242
+
243
+ Notes
244
+ -----
245
+ - The function assumes all input arrays have the same shape
246
+ - Only positions where totalmask > 0 are considered
247
+ - The returned array has shape (n_pairs, 2) where n_pairs is the number
248
+ of non-zero mask positions
249
+
250
+ Examples
251
+ --------
252
+ >>> import numpy as np
253
+ >>> input1 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
254
+ >>> input2 = np.array([[[9, 10], [11, 12]], [[13, 14], [15, 16]]])
255
+ >>> mask = np.array([[[1, 0], [0, 1]], [[1, 1], [0, 0]]])
256
+ >>> pairdata(input1, input2, mask)
257
+ array([[ 1, 9],
258
+ [ 4, 12],
259
+ [ 5, 13],
260
+ [ 6, 14]])
261
+ """
121
262
  nonzeropoints = np.where(totalmask > 0)
122
263
  pairlist = []
123
264
  for i in range(0, len(nonzeropoints[0])):
@@ -131,7 +272,69 @@ def pairdata(input1_data, input2_data, totalmask):
131
272
  return np.asarray(pairlist)
132
273
 
133
274
 
134
- def pixelcomp(args):
275
+ def pixelcomp(args: Any) -> None:
276
+ """
277
+ Compare pixel values from two input images using masks and generate statistical plots.
278
+
279
+ This function reads two NIfTI images and their corresponding masks, performs a pixel-wise
280
+ comparison, and generates either a scatter plot or a 2D histogram of the paired data.
281
+ It also fits a polynomial to the data and optionally produces a Bland-Altman plot.
282
+
283
+ Parameters
284
+ ----------
285
+ args : Any
286
+ An object containing the following attributes:
287
+ - inputfilename1 : str
288
+ Path to the first input NIfTI image file.
289
+ - maskfilename1 : str
290
+ Path to the first mask NIfTI file.
291
+ - inputfilename2 : str
292
+ Path to the second input NIfTI image file.
293
+ - maskfilename2 : str
294
+ Path to the second mask NIfTI file.
295
+ - outputroot : str
296
+ Root name for output files.
297
+ - histbins : int
298
+ Number of bins for the 2D histogram.
299
+ - fitorder : int
300
+ Order of the polynomial to fit.
301
+ - display : bool
302
+ If True, display plots; otherwise, save them to files.
303
+ - scatter : bool
304
+ If True, generate a scatter plot; otherwise, generate a contour plot.
305
+ - fitonly : bool
306
+ If True, only perform the polynomial fit and save coefficients.
307
+
308
+ Returns
309
+ -------
310
+ None
311
+ This function does not return any value. It saves plots and data to files.
312
+
313
+ Notes
314
+ -----
315
+ - The function requires both input images and masks to have matching spatial dimensions.
316
+ - The output includes:
317
+ * A scatter or contour plot saved as PNG.
318
+ * A file with polynomial coefficients.
319
+ * Optionally, a Bland-Altman plot saved as PNG.
320
+ - If a RankWarning occurs during polynomial fitting, the coefficients are set to [0.0, 0.0].
321
+
322
+ Examples
323
+ --------
324
+ >>> class Args:
325
+ ... inputfilename1 = "image1.nii.gz"
326
+ ... maskfilename1 = "mask1.nii.gz"
327
+ ... inputfilename2 = "image2.nii.gz"
328
+ ... maskfilename2 = "mask2.nii.gz"
329
+ ... outputroot = "output"
330
+ ... histbins = 50
331
+ ... fitorder = 1
332
+ ... display = False
333
+ ... scatter = False
334
+ ... fitonly = False
335
+ >>> args = Args()
336
+ >>> pixelcomp(args)
337
+ """
135
338
  if args.display:
136
339
  mpl.use("TkAgg")
137
340
 
@@ -18,17 +18,39 @@
18
18
  #
19
19
  import argparse
20
20
  import sys
21
+ from argparse import Namespace
22
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
21
23
 
22
24
  import numpy as np
25
+ from numpy.typing import NDArray
23
26
  from scipy.stats import skew
24
27
 
25
28
  import rapidtide.io as tide_io
26
29
  import rapidtide.workflows.parser_funcs as pf
27
30
 
28
31
 
29
- def _get_parser():
32
+ def _get_parser() -> Any:
30
33
  """
31
- Argument parser for plethquality
34
+ Argument parser for plethquality.
35
+
36
+ This function creates and configures an argument parser for the plethquality
37
+ command-line tool that calculates quality metrics from cardiac text files.
38
+
39
+ Returns
40
+ -------
41
+ argparse.ArgumentParser
42
+ Configured argument parser object with all required and optional
43
+ arguments for plethquality functionality.
44
+
45
+ Notes
46
+ -----
47
+ The parser includes both required and optional arguments for processing
48
+ cardiac text files and generating quality metrics.
49
+
50
+ Examples
51
+ --------
52
+ >>> parser = _get_parser()
53
+ >>> args = parser.parse_args(['input.txt', 'output.txt'])
32
54
  """
33
55
  parser = argparse.ArgumentParser(
34
56
  prog="plethquality",
@@ -57,32 +79,50 @@ def _get_parser():
57
79
  return parser
58
80
 
59
81
 
60
- def plethquality(waveform, Fs, S_windowsecs=5.0, debug=False):
82
+ def plethquality(waveform: Any, Fs: Any, S_windowsecs: float = 5.0, debug: bool = False) -> None:
61
83
  """
84
+ Calculate the windowed skewness quality metrics for a photoplethysmogram (PPG) signal.
85
+
86
+ This function computes the signal quality index based on the skewness of the PPG waveform
87
+ over a sliding window, as described in Elgendi, M. "Optimal Signal Quality Index for
88
+ Photoplethysmogram Signals". Bioengineering 2016, Vol. 3, Page 21 (2016).
62
89
 
63
90
  Parameters
64
91
  ----------
65
- waveform: array-like
66
- The cardiac waveform to be assessed
67
- Fs: float
68
- The sample rate of the data
69
- S_windowsecs: float
70
- Window duration in seconds. Defaults to 2.0 (optimal according to Elgendi
71
- debug: boolean
72
- Turn on extended output
92
+ waveform : array-like
93
+ The cardiac waveform to be assessed.
94
+ Fs : float
95
+ The sample rate of the data in Hz.
96
+ S_windowsecs : float, optional
97
+ Window duration in seconds. Defaults to 5.0.
98
+ debug : bool, optional
99
+ Turn on extended output for debugging purposes. Defaults to False.
73
100
 
74
101
  Returns
75
102
  -------
76
- S_sqi_mean: float
77
- The mean value of the quality index over all time
78
- S_std_mean: float
79
- The standard deviation of the quality index over all time
80
- S_waveform: array
81
- The quality metric over all timepoints
82
-
103
+ S_sqi_mean : float
104
+ The mean value of the quality index over all time.
105
+ S_sqi_std : float
106
+ The standard deviation of the quality index over all time.
107
+ S_waveform : array
108
+ The quality metric computed over all timepoints.
109
+
110
+ Notes
111
+ -----
112
+ The window size is rounded to the nearest odd number of samples to ensure symmetric
113
+ sliding windows around each point. The skewness is calculated using `scipy.stats.skew`
114
+ with `nan_policy="omit"` to ignore NaN values.
115
+
116
+ Examples
117
+ --------
118
+ >>> import numpy as np
119
+ >>> from scipy.stats import skew
120
+ >>> waveform = np.random.randn(1000)
121
+ >>> Fs = 100.0
122
+ >>> mean_sqi, std_sqi, sqi_waveform = plethquality(waveform, Fs)
123
+ >>> print(f"Mean SQI: {mean_sqi:.3f}")
124
+ Mean SQI: 0.000
83
125
 
84
- Calculates the windowed skewness quality metrics described in Elgendi, M. "Optimal Signal Quality Index for
85
- Photoplethysmogram Signals". Bioengineering 2016, Vol. 3, Page 21 3, 21 (2016).
86
126
  """
87
127
  # calculate S_sqi over a sliding window. Window size should be an odd number of points.
88
128
  S_windowpts = int(np.round(S_windowsecs * Fs, 0))
@@ -103,7 +143,49 @@ def plethquality(waveform, Fs, S_windowsecs=5.0, debug=False):
103
143
  return S_sqi_mean, S_sqi_std, S_waveform
104
144
 
105
145
 
106
- def plethquality(args):
146
+ def plethquality(args: Any) -> None:
147
+ """
148
+ Calculate plethysmography quality score and optionally display results.
149
+
150
+ This function reads plethysmography data from a text file, calculates a quality
151
+ score based on the signal characteristics, and writes the quality scores to an
152
+ output file. Optionally displays the quality score plot.
153
+
154
+ Parameters
155
+ ----------
156
+ args : Any
157
+ An object containing command line arguments with the following attributes:
158
+ - infilename : str
159
+ Input filename containing plethysmography data
160
+ - outfilename : str
161
+ Output filename for quality scores
162
+ - samplerate : float, optional
163
+ Sampling rate of the data (if not specified, will be read from file)
164
+ - display : bool
165
+ Whether to display the quality score plot
166
+
167
+ Returns
168
+ -------
169
+ None
170
+ This function does not return a value but writes results to files and
171
+ optionally displays plots.
172
+
173
+ Notes
174
+ -----
175
+ The function uses `tide_io.readvectorsfromtextfile` to read data and
176
+ `tide_io.writevec` to write quality scores. Quality scores are calculated
177
+ using an internal `plethquality` function that analyzes signal characteristics.
178
+
179
+ Examples
180
+ --------
181
+ >>> args = argparse.Namespace(
182
+ ... infilename='pleth_data.txt',
183
+ ... outfilename='quality_scores.txt',
184
+ ... samplerate=100.0,
185
+ ... display=True
186
+ ... )
187
+ >>> plethquality(args)
188
+ """
107
189
  if args.display:
108
190
  import matplotlib as mpl
109
191