rapidtide 3.0.10__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 (141) hide show
  1. rapidtide/Colortables.py +492 -27
  2. rapidtide/OrthoImageItem.py +1053 -47
  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 +30 -1
  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/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin2009cAsym_2mm.nii.gz +0 -0
  23. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin2009cAsym_2mm_mask.nii.gz +0 -0
  24. rapidtide/decorators.py +91 -0
  25. rapidtide/dlfilter.py +2225 -108
  26. rapidtide/dlfiltertorch.py +4843 -0
  27. rapidtide/externaltools.py +327 -12
  28. rapidtide/fMRIData_class.py +79 -40
  29. rapidtide/filter.py +1899 -810
  30. rapidtide/fit.py +2004 -574
  31. rapidtide/genericmultiproc.py +93 -18
  32. rapidtide/happy_supportfuncs.py +2044 -171
  33. rapidtide/helper_classes.py +584 -43
  34. rapidtide/io.py +2363 -370
  35. rapidtide/linfitfiltpass.py +341 -75
  36. rapidtide/makelaggedtcs.py +211 -20
  37. rapidtide/maskutil.py +423 -53
  38. rapidtide/miscmath.py +827 -121
  39. rapidtide/multiproc.py +210 -22
  40. rapidtide/patchmatch.py +234 -33
  41. rapidtide/peakeval.py +32 -30
  42. rapidtide/ppgproc.py +2203 -0
  43. rapidtide/qualitycheck.py +352 -39
  44. rapidtide/refinedelay.py +422 -57
  45. rapidtide/refineregressor.py +498 -184
  46. rapidtide/resample.py +671 -185
  47. rapidtide/scripts/applyppgproc.py +28 -0
  48. rapidtide/simFuncClasses.py +1052 -77
  49. rapidtide/simfuncfit.py +260 -46
  50. rapidtide/stats.py +540 -238
  51. rapidtide/tests/happycomp +9 -0
  52. rapidtide/tests/test_dlfiltertorch.py +627 -0
  53. rapidtide/tests/test_findmaxlag.py +24 -8
  54. rapidtide/tests/test_fullrunhappy_v1.py +0 -2
  55. rapidtide/tests/test_fullrunhappy_v2.py +0 -2
  56. rapidtide/tests/test_fullrunhappy_v3.py +1 -0
  57. rapidtide/tests/test_fullrunhappy_v4.py +2 -2
  58. rapidtide/tests/test_fullrunrapidtide_v7.py +1 -1
  59. rapidtide/tests/test_simroundtrip.py +8 -8
  60. rapidtide/tests/utils.py +9 -8
  61. rapidtide/tidepoolTemplate.py +142 -38
  62. rapidtide/tidepoolTemplate_alt.py +165 -44
  63. rapidtide/tidepoolTemplate_big.py +189 -52
  64. rapidtide/util.py +1217 -118
  65. rapidtide/voxelData.py +684 -37
  66. rapidtide/wiener.py +19 -12
  67. rapidtide/wiener2.py +113 -7
  68. rapidtide/wiener_doc.py +255 -0
  69. rapidtide/workflows/adjustoffset.py +105 -3
  70. rapidtide/workflows/aligntcs.py +85 -2
  71. rapidtide/workflows/applydlfilter.py +87 -10
  72. rapidtide/workflows/applyppgproc.py +522 -0
  73. rapidtide/workflows/atlasaverage.py +210 -47
  74. rapidtide/workflows/atlastool.py +100 -3
  75. rapidtide/workflows/calcSimFuncMap.py +294 -64
  76. rapidtide/workflows/calctexticc.py +201 -9
  77. rapidtide/workflows/ccorrica.py +97 -4
  78. rapidtide/workflows/cleanregressor.py +168 -29
  79. rapidtide/workflows/delayvar.py +163 -10
  80. rapidtide/workflows/diffrois.py +81 -3
  81. rapidtide/workflows/endtidalproc.py +144 -4
  82. rapidtide/workflows/fdica.py +195 -15
  83. rapidtide/workflows/filtnifti.py +70 -3
  84. rapidtide/workflows/filttc.py +74 -3
  85. rapidtide/workflows/fitSimFuncMap.py +206 -48
  86. rapidtide/workflows/fixtr.py +73 -3
  87. rapidtide/workflows/gmscalc.py +113 -3
  88. rapidtide/workflows/happy.py +813 -201
  89. rapidtide/workflows/happy2std.py +144 -12
  90. rapidtide/workflows/happy_parser.py +149 -8
  91. rapidtide/workflows/histnifti.py +118 -2
  92. rapidtide/workflows/histtc.py +84 -3
  93. rapidtide/workflows/linfitfilt.py +117 -4
  94. rapidtide/workflows/localflow.py +328 -28
  95. rapidtide/workflows/mergequality.py +79 -3
  96. rapidtide/workflows/niftidecomp.py +322 -18
  97. rapidtide/workflows/niftistats.py +174 -4
  98. rapidtide/workflows/pairproc.py +88 -2
  99. rapidtide/workflows/pairwisemergenifti.py +85 -2
  100. rapidtide/workflows/parser_funcs.py +1421 -40
  101. rapidtide/workflows/physiofreq.py +137 -11
  102. rapidtide/workflows/pixelcomp.py +208 -5
  103. rapidtide/workflows/plethquality.py +103 -21
  104. rapidtide/workflows/polyfitim.py +151 -11
  105. rapidtide/workflows/proj2flow.py +75 -2
  106. rapidtide/workflows/rankimage.py +111 -4
  107. rapidtide/workflows/rapidtide.py +272 -15
  108. rapidtide/workflows/rapidtide2std.py +98 -2
  109. rapidtide/workflows/rapidtide_parser.py +109 -9
  110. rapidtide/workflows/refineDelayMap.py +143 -33
  111. rapidtide/workflows/refineRegressor.py +682 -93
  112. rapidtide/workflows/regressfrommaps.py +152 -31
  113. rapidtide/workflows/resamplenifti.py +85 -3
  114. rapidtide/workflows/resampletc.py +91 -3
  115. rapidtide/workflows/retrolagtcs.py +98 -6
  116. rapidtide/workflows/retroregress.py +165 -9
  117. rapidtide/workflows/roisummarize.py +173 -5
  118. rapidtide/workflows/runqualitycheck.py +71 -3
  119. rapidtide/workflows/showarbcorr.py +147 -4
  120. rapidtide/workflows/showhist.py +86 -2
  121. rapidtide/workflows/showstxcorr.py +160 -3
  122. rapidtide/workflows/showtc.py +159 -3
  123. rapidtide/workflows/showxcorrx.py +184 -4
  124. rapidtide/workflows/showxy.py +185 -15
  125. rapidtide/workflows/simdata.py +262 -36
  126. rapidtide/workflows/spatialfit.py +77 -2
  127. rapidtide/workflows/spatialmi.py +251 -27
  128. rapidtide/workflows/spectrogram.py +305 -32
  129. rapidtide/workflows/synthASL.py +154 -3
  130. rapidtide/workflows/tcfrom2col.py +76 -2
  131. rapidtide/workflows/tcfrom3col.py +74 -2
  132. rapidtide/workflows/tidepool.py +2972 -133
  133. rapidtide/workflows/utils.py +19 -14
  134. rapidtide/workflows/utils_doc.py +293 -0
  135. rapidtide/workflows/variabilityizer.py +116 -3
  136. {rapidtide-3.0.10.dist-info → rapidtide-3.1.dist-info}/METADATA +10 -9
  137. {rapidtide-3.0.10.dist-info → rapidtide-3.1.dist-info}/RECORD +141 -122
  138. {rapidtide-3.0.10.dist-info → rapidtide-3.1.dist-info}/entry_points.txt +1 -0
  139. {rapidtide-3.0.10.dist-info → rapidtide-3.1.dist-info}/WHEEL +0 -0
  140. {rapidtide-3.0.10.dist-info → rapidtide-3.1.dist-info}/licenses/LICENSE +0 -0
  141. {rapidtide-3.0.10.dist-info → rapidtide-3.1.dist-info}/top_level.txt +0 -0
rapidtide/stats.py CHANGED
@@ -17,9 +17,13 @@
17
17
  #
18
18
  #
19
19
  import warnings
20
+ from typing import Any, Callable, Optional, Tuple, Union
20
21
 
21
22
  import matplotlib.pyplot as plt
22
23
  import numpy as np
24
+ from numpy.typing import ArrayLike, NDArray
25
+
26
+ from rapidtide.decorators import conditionaljit, conditionaljit2
23
27
 
24
28
  with warnings.catch_warnings():
25
29
  warnings.simplefilter("ignore")
@@ -32,6 +36,7 @@ with warnings.catch_warnings():
32
36
 
33
37
  import scipy as sp
34
38
  from scipy.stats import johnsonsb, kurtosis, kurtosistest, skew, skewtest
39
+ from statsmodels.robust import mad
35
40
 
36
41
  import rapidtide.fit as tide_fit
37
42
  import rapidtide.io as tide_io
@@ -44,73 +49,55 @@ if pyfftwpresent:
44
49
  # ---------------------------------------- Global constants -------------------------------------------
45
50
  defaultbutterorder = 6
46
51
  MAXLINES = 10000000
47
- donotbeaggressive = True
48
-
49
- # ----------------------------------------- Conditional imports ---------------------------------------
50
- try:
51
- from numba import jit
52
- except ImportError:
53
- donotusenumba = True
54
- else:
55
- donotusenumba = False
56
-
57
-
58
- def disablenumba():
59
- global donotusenumba
60
- donotusenumba = True
61
-
62
-
63
- def conditionaljit():
64
- def resdec(f):
65
- if donotusenumba:
66
- return f
67
- return jit(f, nopython=True)
68
-
69
- return resdec
70
-
71
-
72
- def conditionaljit2():
73
- def resdec(f):
74
- if donotusenumba or donotbeaggressive:
75
- return f
76
- return jit(f, nopython=True)
77
-
78
- return resdec
79
52
 
80
53
 
81
54
  # --------------------------- probability functions -------------------------------------------------
82
- def printthresholds(pcts, thepercentiles, labeltext):
83
- """
55
+ def printthresholds(pcts: ArrayLike, thepercentiles: ArrayLike, labeltext: str) -> None:
56
+ """Print significance thresholds with formatted output.
84
57
 
85
58
  Parameters
86
59
  ----------
87
- pcts
88
- thepercentiles
89
- labeltext
90
-
91
- Returns
92
- -------
93
-
60
+ pcts : array-like
61
+ Percentile threshold values
62
+ thepercentiles : array-like
63
+ Percentile levels (0-1)
64
+ labeltext : str
65
+ Label to print before thresholds
94
66
  """
95
67
  print(labeltext)
96
68
  for i in range(0, len(pcts)):
97
69
  print(f"\tp <{1.0 - thepercentiles[i]:.3f}: {pcts[i]:.3f}")
98
70
 
99
71
 
100
- def fitgausspdf(thehist, histlen, thedata, displayplots=False, nozero=False, debug=False):
101
- """
72
+ def fitgausspdf(
73
+ thehist: Tuple,
74
+ histlen: int,
75
+ thedata: NDArray,
76
+ displayplots: bool = False,
77
+ nozero: bool = False,
78
+ debug: bool = False,
79
+ ) -> NDArray:
80
+ """Fit a Gaussian probability density function to histogram data.
102
81
 
103
82
  Parameters
104
83
  ----------
105
- thehist
106
- histlen
107
- thedata
108
- displayplots
109
- nozero
84
+ thehist : tuple
85
+ Histogram tuple from np.histogram containing (counts, bin_edges)
86
+ histlen : int
87
+ Length of histogram
88
+ thedata : array-like
89
+ Original data used to create histogram
90
+ displayplots : bool, optional
91
+ If True, display fit visualization. Default: False
92
+ nozero : bool, optional
93
+ If True, ignore zero values. Default: False
94
+ debug : bool, optional
95
+ Enable debug output. Default: False
110
96
 
111
97
  Returns
112
98
  -------
113
-
99
+ array-like
100
+ Array containing (peakheight, peakloc, peakwidth, zeroterm)
114
101
  """
115
102
  thestore = np.zeros((2, histlen), dtype="float64")
116
103
  thestore[0, :] = thehist[1][:-1]
@@ -170,20 +157,35 @@ def fitgausspdf(thehist, histlen, thedata, displayplots=False, nozero=False, deb
170
157
  return np.append(params, np.array([zeroterm]))
171
158
 
172
159
 
173
- def fitjsbpdf(thehist, histlen, thedata, displayplots=False, nozero=False, debug=False):
174
- """
160
+ def fitjsbpdf(
161
+ thehist: Tuple,
162
+ histlen: int,
163
+ thedata: NDArray,
164
+ displayplots: bool = False,
165
+ nozero: bool = False,
166
+ debug: bool = False,
167
+ ) -> NDArray:
168
+ """Fit a Johnson SB probability density function to histogram data.
175
169
 
176
170
  Parameters
177
171
  ----------
178
- thehist
179
- histlen
180
- thedata
181
- displayplots
182
- nozero
172
+ thehist : tuple
173
+ Histogram tuple from np.histogram containing (counts, bin_edges)
174
+ histlen : int
175
+ Length of histogram
176
+ thedata : array-like
177
+ Original data used to create histogram
178
+ displayplots : bool, optional
179
+ If True, display fit visualization. Default: False
180
+ nozero : bool, optional
181
+ If True, ignore zero values. Default: False
182
+ debug : bool, optional
183
+ Enable debug output. Default: False
183
184
 
184
185
  Returns
185
186
  -------
186
-
187
+ array-like
188
+ Array containing (a, b, loc, scale, zeroterm) parameters of Johnson SB fit
187
189
  """
188
190
  thestore = np.zeros((2, histlen), dtype="float64")
189
191
  thestore[0, :] = thehist[1][:-1]
@@ -222,49 +224,65 @@ def fitjsbpdf(thehist, histlen, thedata, displayplots=False, nozero=False, debug
222
224
  return np.append(params, np.array([zeroterm]))
223
225
 
224
226
 
225
- def getjohnsonppf(percentile, params, zeroterm):
226
- """
227
+ def getjohnsonppf(percentile: float, params: ArrayLike, zeroterm: float) -> None:
228
+ """Get percent point function (inverse CDF) for Johnson SB distribution.
229
+
230
+ Note: This function is incomplete and only initializes variables.
227
231
 
228
232
  Parameters
229
233
  ----------
230
- percentile
231
- params
232
- zeroterm
233
-
234
- Returns
235
- -------
236
-
234
+ percentile : float
235
+ Percentile value (0-1)
236
+ params : array-like
237
+ Johnson SB distribution parameters (a, b, loc, scale)
238
+ zeroterm : float
239
+ Zero term correction factor
237
240
  """
238
241
  johnsonfunc = johnsonsb(params[0], params[1], params[2], params[3])
239
242
  corrfac = 1.0 - zeroterm
240
243
 
241
244
 
242
245
  def sigFromDistributionData(
243
- vallist,
244
- histlen,
245
- thepercentiles,
246
- similaritymetric="correlation",
247
- displayplots=False,
248
- twotail=False,
249
- nozero=False,
250
- dosighistfit=True,
251
- debug=False,
252
- ):
253
- """
246
+ vallist: NDArray,
247
+ histlen: int,
248
+ thepercentiles: ArrayLike,
249
+ similaritymetric: str = "correlation",
250
+ displayplots: bool = False,
251
+ twotail: bool = False,
252
+ nozero: bool = False,
253
+ dosighistfit: bool = True,
254
+ debug: bool = False,
255
+ ) -> Tuple[Optional[list], Optional[list], Optional[NDArray]]:
256
+ """Calculate significance thresholds from distribution data.
257
+
258
+ Fits a probability distribution to data and calculates percentile thresholds
259
+ for significance testing.
254
260
 
255
261
  Parameters
256
262
  ----------
257
- vallist
258
- histlen
259
- thepercentiles
260
- displayplots
261
- twotail
262
- nozero
263
- dosighistfit
263
+ vallist : array-like
264
+ List of similarity/correlation values
265
+ histlen : int
266
+ Length of histogram
267
+ thepercentiles : array-like
268
+ Percentile values to compute (0-1)
269
+ similaritymetric : str, optional
270
+ Type of similarity metric ("correlation" or "mutualinfo"). Default: "correlation"
271
+ displayplots : bool, optional
272
+ If True, display diagnostic plots. Default: False
273
+ twotail : bool, optional
274
+ If True, calculate two-tailed thresholds. Default: False
275
+ nozero : bool, optional
276
+ If True, exclude zero values. Default: False
277
+ dosighistfit : bool, optional
278
+ If True, fit distribution to data. Default: True
279
+ debug : bool, optional
280
+ Enable debug output. Default: False
264
281
 
265
282
  Returns
266
283
  -------
267
-
284
+ tuple
285
+ (pcts_data, pcts_fit, histfit) - percentiles from data, fitted distribution, and fit parameters
268
286
  """
269
287
  # check to make sure there are nonzero values first
270
288
  if len(np.where(vallist != 0.0)[0]) == 0:
@@ -313,8 +331,38 @@ maxrforneglogp = None
313
331
 
314
332
 
315
333
  def neglog10pfromr(
316
- rval, histfit, lutlen=3000, initialize=False, neglogpmin=0.0, neglogpmax=3.0, debug=False
317
- ):
334
+ rval: float,
335
+ histfit: ArrayLike,
336
+ lutlen: int = 3000,
337
+ initialize: bool = False,
338
+ neglogpmin: float = 0.0,
339
+ neglogpmax: float = 3.0,
340
+ debug: bool = False,
341
+ ) -> float:
342
+ """Convert correlation value to negative log10 p-value using histogram fit.
343
+
344
+ Parameters
345
+ ----------
346
+ rval : float
347
+ Correlation value to convert
348
+ histfit : array-like
349
+ Histogram fit parameters from fitjsbpdf
350
+ lutlen : int, optional
351
+ Length of lookup table. Default: 3000
352
+ initialize : bool, optional
353
+ Force reinitialization of interpolator. Default: False
354
+ neglogpmin : float, optional
355
+ Minimum negative log10 p-value. Default: 0.0
356
+ neglogpmax : float, optional
357
+ Maximum negative log10 p-value. Default: 3.0
358
+ debug : bool, optional
359
+ Enable debug output. Default: False
360
+
361
+ Returns
362
+ -------
363
+ float
364
+ Negative log10 p-value corresponding to the input correlation value
365
+ """
318
366
  global neglogpfromr_interpolator, minrforneglogp, maxrforneglogp
319
367
  if neglogpfromr_interpolator is None or initialize:
320
368
  neglogparray = np.linspace(neglogpmin, neglogpmax, lutlen, endpoint=True)
@@ -340,37 +388,52 @@ def neglog10pfromr(
340
388
  return np.float64(neglogpfromr_interpolator(np.asarray([rval], dtype=float))[0])
341
389
 
342
390
 
343
- def rfromp(fitfile, thepercentiles):
344
- """
391
+ def rfromp(fitfile: str, thepercentiles: ArrayLike) -> NDArray:
392
+ """Get correlation values from p-values using a saved distribution fit.
345
393
 
346
394
  Parameters
347
395
  ----------
348
- fitfile
349
- thepercentiles
396
+ fitfile : str
397
+ Path to file containing distribution fit parameters
398
+ thepercentiles : array-like
399
+ Percentile values to calculate (0-1)
350
400
 
351
401
  Returns
352
402
  -------
353
-
403
+ array-like
404
+ Correlation values corresponding to the percentiles
354
405
  """
355
406
  thefit = np.array(tide_io.readvecs(fitfile)[0]).astype("float64")
356
407
  print(f"thefit = {thefit}")
357
408
  return getfracvalsfromfit(thefit, thepercentiles)
358
409
 
359
410
 
360
- def tfromr(r, nsamps, dfcorrfac=1.0, oversampfactor=1.0, returnp=False):
361
- """
411
+ def tfromr(
412
+ r: float,
413
+ nsamps: int,
414
+ dfcorrfac: float = 1.0,
415
+ oversampfactor: float = 1.0,
416
+ returnp: bool = False,
417
+ ) -> Union[float, Tuple[float, float]]:
418
+ """Convert correlation to t-statistic.
362
419
 
363
420
  Parameters
364
421
  ----------
365
- r
366
- nsamps
367
- dfcorrfac
368
- oversampfactor
369
- returnp
422
+ r : float
423
+ Correlation coefficient
424
+ nsamps : int
425
+ Number of samples
426
+ dfcorrfac : float, optional
427
+ Degrees of freedom correction factor. Default: 1.0
428
+ oversampfactor : float, optional
429
+ Oversampling factor for DOF adjustment. Default: 1.0
430
+ returnp : bool, optional
431
+ If True, also return p-value. Default: False
370
432
 
371
433
  Returns
372
434
  -------
373
-
435
+ float or tuple
436
+ T-statistic, or (t-statistic, p-value) if returnp=True
374
437
  """
375
438
  if r >= 1.0:
376
439
  tval = float("inf")
@@ -385,7 +448,21 @@ def tfromr(r, nsamps, dfcorrfac=1.0, oversampfactor=1.0, returnp=False):
385
448
  return tval
386
449
 
387
450
 
388
- def pfromz(z, twotailed=True):
451
+ def pfromz(z: float, twotailed: bool = True) -> float:
452
+ """Calculate p-value from z-score.
453
+
454
+ Parameters
455
+ ----------
456
+ z : float
457
+ Z-score value
458
+ twotailed : bool, optional
459
+ If True, calculate two-tailed p-value. Default: True
460
+
461
+ Returns
462
+ -------
463
+ float
464
+ P-value corresponding to the z-score
465
+ """
389
466
  # importing packages
390
467
  import scipy.stats
391
468
 
@@ -396,20 +473,32 @@ def pfromz(z, twotailed=True):
396
473
  return scipy.stats.norm.sf(abs(z))
397
474
 
398
475
 
399
- def zfromr(r, nsamps, dfcorrfac=1.0, oversampfactor=1.0, returnp=False):
400
- """
476
+ def zfromr(
477
+ r: float,
478
+ nsamps: int,
479
+ dfcorrfac: float = 1.0,
480
+ oversampfactor: float = 1.0,
481
+ returnp: bool = False,
482
+ ) -> Union[float, Tuple[float, float]]:
483
+ """Convert correlation to z-statistic.
401
484
 
402
485
  Parameters
403
486
  ----------
404
- r
405
- nsamps
406
- dfcorrfac
407
- oversampfactor
408
- returnp
487
+ r : float
488
+ Correlation coefficient
489
+ nsamps : int
490
+ Number of samples
491
+ dfcorrfac : float, optional
492
+ Degrees of freedom correction factor. Default: 1.0
493
+ oversampfactor : float, optional
494
+ Oversampling factor for DOF adjustment. Default: 1.0
495
+ returnp : bool, optional
496
+ If True, also return p-value. Default: False
409
497
 
410
498
  Returns
411
499
  -------
412
-
500
+ float or tuple
501
+ Z-statistic, or (z-statistic, p-value) if returnp=True
413
502
  """
414
503
  if r >= 1.0:
415
504
  zval = float("inf")
@@ -424,29 +513,79 @@ def zfromr(r, nsamps, dfcorrfac=1.0, oversampfactor=1.0, returnp=False):
424
513
  return zval
425
514
 
426
515
 
427
- def zofcorrdiff(r1, r2, n1, n2):
516
+ def zofcorrdiff(r1: float, r2: float, n1: int, n2: int) -> float:
517
+ """Calculate z-statistic for the difference between two correlations.
518
+
519
+ Parameters
520
+ ----------
521
+ r1 : float
522
+ First correlation coefficient
523
+ r2 : float
524
+ Second correlation coefficient
525
+ n1 : int
526
+ Sample size for first correlation
527
+ n2 : int
528
+ Sample size for second correlation
529
+
530
+ Returns
531
+ -------
532
+ float
533
+ Z-statistic for the difference between the two correlations
534
+ """
428
535
  return (fisher(r1) - fisher(r2)) / stderrofdiff(n1, n2)
429
536
 
430
537
 
431
- def stderrofdiff(n1, n2):
432
- return np.sqrt(1.0 / (n1 - 3) + 1.0 / (n2 - 3))
538
+ def stderrofdiff(n1: int, n2: int) -> float:
539
+ """Calculate standard error of difference between two Fisher-transformed correlations.
433
540
 
541
+ Parameters
542
+ ----------
543
+ n1 : int
544
+ Sample size for first correlation
545
+ n2 : int
546
+ Sample size for second correlation
434
547
 
435
- def fisher(r):
548
+ Returns
549
+ -------
550
+ float
551
+ Standard error of the difference
436
552
  """
553
+ return np.sqrt(1.0 / (n1 - 3) + 1.0 / (n2 - 3))
554
+
555
+
556
+ def fisher(r: float) -> float:
557
+ """Apply Fisher's r-to-z transformation to correlation coefficient.
437
558
 
438
559
  Parameters
439
560
  ----------
440
- r
561
+ r : float
562
+ Correlation coefficient
441
563
 
442
564
  Returns
443
565
  -------
444
-
566
+ float
567
+ Fisher-transformed z-value
445
568
  """
446
569
  return 0.5 * np.log((1 + r) / (1 - r))
447
570
 
448
571
 
449
- def permute_phase(time_series):
572
+ def permute_phase(time_series: NDArray) -> NDArray:
573
+ """Generate phase-randomized surrogate time series.
574
+
575
+ Creates a surrogate time series with the same power spectrum as the input
576
+ but with randomized phases. Useful for generating null distributions in
577
+ time series analysis.
578
+
579
+ Parameters
580
+ ----------
581
+ time_series : array-like
582
+ Input time series
583
+
584
+ Returns
585
+ -------
586
+ array-like
587
+ Phase-randomized surrogate time series with same length as input
588
+ """
450
589
  # Compute the Fourier transform of the time series
451
590
  freq_domain = np.fft.rfft(time_series)
452
591
 
@@ -460,37 +599,71 @@ def permute_phase(time_series):
460
599
  return permuted_time_series
461
600
 
462
601
 
463
- def skewnessstats(timecourse):
464
- """
602
+ def skewnessstats(timecourse: NDArray) -> Tuple[float, float, float]:
603
+ """Calculate skewness and statistical test for timecourse.
465
604
 
466
605
  Parameters
467
606
  ----------
468
- timecourse: array
469
- The timecourse to test
470
-
471
- :return:
607
+ timecourse : array-like
608
+ Input time series
472
609
 
610
+ Returns
611
+ -------
612
+ tuple
613
+ (skewness, z-statistic, p-value) from skewness test
473
614
  """
474
615
  testres = skewtest(timecourse)
475
616
  return skew(timecourse), testres[0], testres[1]
476
617
 
477
618
 
478
- def kurtosisstats(timecourse):
479
- """
619
+ def kurtosisstats(timecourse: NDArray) -> Tuple[float, float, float]:
620
+ """Calculate kurtosis and statistical test for timecourse.
480
621
 
481
622
  Parameters
482
623
  ----------
483
- timecourse: array
484
- The timecourse to test
485
-
486
- :return:
624
+ timecourse : array-like
625
+ Input time series
487
626
 
627
+ Returns
628
+ -------
629
+ tuple
630
+ (kurtosis, z-statistic, p-value) from kurtosis test
488
631
  """
489
632
  testres = kurtosistest(timecourse)
490
633
  return kurtosis(timecourse), testres[0], testres[1]
491
634
 
492
635
 
493
- def fast_ICC_rep_anova(Y, nocache=False, debug=False):
636
+ def fmristats(
637
+ fmridata: NDArray,
638
+ ) -> Tuple[NDArray, NDArray, NDArray, NDArray, NDArray, NDArray, NDArray, NDArray]:
639
+ """Calculate comprehensive statistics for fMRI data along time axis.
640
+
641
+ Parameters
642
+ ----------
643
+ fmridata : ndarray
644
+ 2D array where rows are voxels and columns are timepoints
645
+
646
+ Returns
647
+ -------
648
+ tuple
649
+ (min, max, mean, std, median, mad, skew, kurtosis) - each as 1D array
650
+ with length equal to number of voxels, calculated along timepoints
651
+ """
652
+ return (
653
+ np.min(fmridata, axis=1),
654
+ np.max(fmridata, axis=1),
655
+ np.mean(fmridata, axis=1),
656
+ np.std(fmridata, axis=1),
657
+ np.median(fmridata, axis=1),
658
+ mad(fmridata, axis=1),
659
+ skew(fmridata, axis=1),
660
+ kurtosis(fmridata, axis=1),
661
+ )
662
+
663
+
664
+ def fast_ICC_rep_anova(
665
+ Y: NDArray, nocache: bool = False, debug: bool = False
666
+ ) -> Tuple[float, float, float, float, int, int]:
494
667
  """
495
668
  the data Y are entered as a 'table' ie subjects are in rows and repeated
496
669
  measures in columns
@@ -577,26 +750,34 @@ def fast_ICC_rep_anova(Y, nocache=False, debug=False):
577
750
 
578
751
  # --------------------------- histogram functions -------------------------------------------------
579
752
  def gethistprops(
580
- indata,
581
- histlen,
582
- refine=False,
583
- therange=None,
584
- pickleft=False,
585
- peakthresh=0.33,
586
- ):
587
- """
753
+ indata: NDArray,
754
+ histlen: int,
755
+ refine: bool = False,
756
+ therange: Optional[Tuple[float, float]] = None,
757
+ pickleft: bool = False,
758
+ peakthresh: float = 0.33,
759
+ ) -> Tuple[float, float, float]:
760
+ """Extract histogram peak properties from data.
588
761
 
589
762
  Parameters
590
763
  ----------
591
- indata
592
- histlen
593
- refine
594
- therange
595
- pickleftpeak
764
+ indata : array-like
765
+ Input data array
766
+ histlen : int
767
+ Number of histogram bins
768
+ refine : bool, optional
769
+ If True, refine peak estimates using Gaussian fit. Default: False
770
+ therange : tuple, optional
771
+ (min, max) range for histogram. If None, use data range. Default: None
772
+ pickleft : bool, optional
773
+ If True, pick leftmost peak above threshold. Default: False
774
+ peakthresh : float, optional
775
+ Threshold for peak detection (fraction of max). Default: 0.33
596
776
 
597
777
  Returns
598
778
  -------
599
-
779
+ tuple
780
+ (peakloc, peakheight, peakwidth) - peak location, height, and width
600
781
  """
601
782
  thestore = np.zeros((2, histlen), dtype="float64")
602
783
  if therange is None:
@@ -638,13 +819,35 @@ def gethistprops(
638
819
 
639
820
 
640
821
  def prochistogram(
641
- thehist,
642
- refine=False,
643
- pickleft=False,
644
- peakthresh=0.33,
645
- ignorefirstpoint=False,
646
- debug=False,
647
- ):
822
+ thehist: Tuple,
823
+ refine: bool = False,
824
+ pickleft: bool = False,
825
+ peakthresh: float = 0.33,
826
+ ignorefirstpoint: bool = False,
827
+ debug: bool = False,
828
+ ) -> Tuple[float, float, float, float]:
829
+ """Process histogram data to extract peak properties.
830
+
831
+ Parameters
832
+ ----------
833
+ thehist : tuple
834
+ Histogram tuple from np.histogram containing (counts, bin_edges)
835
+ refine : bool, optional
836
+ If True, refine peak estimates using Gaussian fit. Default: False
837
+ pickleft : bool, optional
838
+ If True, pick leftmost peak above threshold. Default: False
839
+ peakthresh : float, optional
840
+ Threshold for peak detection (fraction of max). Default: 0.33
841
+ ignorefirstpoint : bool, optional
842
+ If True, ignore first histogram bin. Default: False
843
+ debug : bool, optional
844
+ Enable debug output. Default: False
845
+
846
+ Returns
847
+ -------
848
+ tuple
849
+ (peakheight, peakloc, peakwidth, centerofmass) - peak properties
850
+ """
648
851
  thestore = np.zeros((2, len(thehist[0])), dtype="float64")
649
852
  histlen = len(thehist[1])
650
853
  thestore[0, :] = (thehist[1][1:] + thehist[1][0:-1]) / 2.0
@@ -696,7 +899,23 @@ def prochistogram(
696
899
  return peakheight, peakloc, peakwidth, centerofmass
697
900
 
698
901
 
699
- def percentilefromloc(indata, peakloc, nozero=False):
902
+ def percentilefromloc(indata: NDArray, peakloc: float, nozero: bool = False) -> float:
903
+ """Calculate the percentile corresponding to a given value location.
904
+
905
+ Parameters
906
+ ----------
907
+ indata : array-like
908
+ Input data array
909
+ peakloc : float
910
+ Value location to find percentile for
911
+ nozero : bool, optional
912
+ If True, exclude zero values from calculation. Default: False
913
+
914
+ Returns
915
+ -------
916
+ float
917
+ Percentile (0-100) corresponding to the given value location
918
+ """
700
919
  order = indata.argsort()
701
920
  orderedvalues = indata[order]
702
921
  if nozero:
@@ -707,31 +926,46 @@ def percentilefromloc(indata, peakloc, nozero=False):
707
926
 
708
927
 
709
928
  def makehistogram(
710
- indata,
711
- histlen,
712
- binsize=None,
713
- therange=None,
714
- pickleft=False,
715
- peakthresh=0.33,
716
- refine=False,
717
- normalize=False,
718
- ignorefirstpoint=False,
719
- debug=False,
720
- ):
721
- """
929
+ indata: NDArray,
930
+ histlen: Optional[int],
931
+ binsize: Optional[float] = None,
932
+ therange: Optional[Tuple[float, float]] = None,
933
+ pickleft: bool = False,
934
+ peakthresh: float = 0.33,
935
+ refine: bool = False,
936
+ normalize: bool = False,
937
+ ignorefirstpoint: bool = False,
938
+ debug: bool = False,
939
+ ) -> Tuple[Tuple, float, float, float, float, float]:
940
+ """Create histogram and extract peak properties from data.
722
941
 
723
942
  Parameters
724
943
  ----------
725
- indata
726
- histlen
727
- binsize
728
- therange
729
- refine
730
- normalize
944
+ indata : array-like
945
+ Input data array
946
+ histlen : int or None
947
+ Number of histogram bins. If None, binsize must be specified
948
+ binsize : float, optional
949
+ Bin size for histogram. If specified, overrides histlen. Default: None
950
+ therange : tuple, optional
951
+ (min, max) range for histogram. If None, use data range. Default: None
952
+ pickleft : bool, optional
953
+ If True, pick leftmost peak above threshold. Default: False
954
+ peakthresh : float, optional
955
+ Threshold for peak detection (fraction of max). Default: 0.33
956
+ refine : bool, optional
957
+ If True, refine peak estimates using Gaussian fit. Default: False
958
+ normalize : bool, optional
959
+ If True, normalize histogram to unit area. Default: False
960
+ ignorefirstpoint : bool, optional
961
+ If True, ignore first histogram bin. Default: False
962
+ debug : bool, optional
963
+ Enable debug output. Default: False
731
964
 
732
965
  Returns
733
966
  -------
734
-
967
+ tuple
968
+ (histogram, peakheight, peakloc, peakwidth, centerofmass, peakpercentile)
735
969
  """
736
970
  if therange is None:
737
971
  therange = [indata.min(), indata.max()]
@@ -761,7 +995,27 @@ def makehistogram(
761
995
  return thehist, peakheight, peakloc, peakwidth, centerofmass, peakpercentile
762
996
 
763
997
 
764
- def echoloc(indata, histlen, startoffset=5.0):
998
+ def echoloc(indata: NDArray, histlen: int, startoffset: float = 5.0) -> Tuple[float, float]:
999
+ """Detect and analyze echo peak in histogram data.
1000
+
1001
+ Identifies a secondary (echo) peak in histogram data that occurs after
1002
+ the primary peak, useful for analyzing echo patterns in imaging data.
1003
+
1004
+ Parameters
1005
+ ----------
1006
+ indata : array-like
1007
+ Input data array
1008
+ histlen : int
1009
+ Number of histogram bins
1010
+ startoffset : float, optional
1011
+ Offset from primary peak to start echo search. Default: 5.0
1012
+
1013
+ Returns
1014
+ -------
1015
+ tuple
1016
+ (echo_lag, echo_ratio) where echo_lag is the distance between primary
1017
+ and echo peaks, and echo_ratio is the ratio of echo to primary peak areas
1018
+ """
765
1019
  thehist, peakheight, peakloc, peakwidth, centerofmass, peakpercentile = makehistogram(
766
1020
  indata, histlen, refine=True
767
1021
  )
@@ -790,43 +1044,56 @@ def echoloc(indata, histlen, startoffset=5.0):
790
1044
 
791
1045
 
792
1046
  def makeandsavehistogram(
793
- indata,
794
- histlen,
795
- endtrim,
796
- outname,
797
- binsize=None,
798
- saveimfile=False,
799
- displaytitle="histogram",
800
- displayplots=False,
801
- refine=False,
802
- therange=None,
803
- normalize=False,
804
- dictvarname=None,
805
- thedict=None,
806
- append=False,
807
- debug=False,
808
- ):
809
- """
1047
+ indata: NDArray,
1048
+ histlen: int,
1049
+ endtrim: int,
1050
+ outname: str,
1051
+ binsize: Optional[float] = None,
1052
+ saveimfile: bool = False,
1053
+ displaytitle: str = "histogram",
1054
+ displayplots: bool = False,
1055
+ refine: bool = False,
1056
+ therange: Optional[Tuple[float, float]] = None,
1057
+ normalize: bool = False,
1058
+ dictvarname: Optional[str] = None,
1059
+ thedict: Optional[dict] = None,
1060
+ append: bool = False,
1061
+ debug: bool = False,
1062
+ ) -> None:
1063
+ """Create histogram, extract properties, and save results to file.
810
1064
 
811
1065
  Parameters
812
1066
  ----------
813
- indata
814
- histlen
815
- endtrim
816
- outname
817
- displaytitle
818
- displayplots
819
- refine
820
- therange
821
- normalize
822
- dictvarname
823
- thedict
824
- append
825
- debug
826
-
827
- Returns
828
- -------
829
-
1067
+ indata : array-like
1068
+ Input data array
1069
+ histlen : int
1070
+ Number of histogram bins
1071
+ endtrim : int
1072
+ Number of bins to trim from end when plotting
1073
+ outname : str
1074
+ Output file path (without extension)
1075
+ binsize : float, optional
1076
+ Bin size for histogram. If specified, overrides histlen. Default: None
1077
+ saveimfile : bool, optional
1078
+ Unused parameter. Default: False
1079
+ displaytitle : str, optional
1080
+ Title for display plots. Default: "histogram"
1081
+ displayplots : bool, optional
1082
+ If True, display histogram plot. Default: False
1083
+ refine : bool, optional
1084
+ If True, refine peak estimates using Gaussian fit. Default: False
1085
+ therange : tuple, optional
1086
+ (min, max) range for histogram. If None, use data range. Default: None
1087
+ normalize : bool, optional
1088
+ If True, normalize histogram to unit area. Default: False
1089
+ dictvarname : str, optional
1090
+ Variable name for dictionary storage. If None, use outname. Default: None
1091
+ thedict : dict, optional
1092
+ Dictionary to store results in. If None, write to file. Default: None
1093
+ append : bool, optional
1094
+ If True, append to existing file. Default: False
1095
+ debug : bool, optional
1096
+ Enable debug output. Default: False
830
1097
  """
831
1098
  thehist, peakheight, peakloc, peakwidth, centerofmass, peakpercentile = makehistogram(
832
1099
  indata,
@@ -908,18 +1175,22 @@ def makeandsavehistogram(
908
1175
  plt.show()
909
1176
 
910
1177
 
911
- def symmetrize(a, antisymmetric=False, zerodiagonal=False):
912
- """
1178
+ def symmetrize(a: NDArray, antisymmetric: bool = False, zerodiagonal: bool = False) -> NDArray:
1179
+ """Symmetrize a matrix.
913
1180
 
914
1181
  Parameters
915
1182
  ----------
916
- a
917
- antisymmetric
918
- zerodiagonal
1183
+ a : ndarray
1184
+ Input matrix
1185
+ antisymmetric : bool, optional
1186
+ If True, create antisymmetric matrix (a - a.T) / 2. Default: False
1187
+ zerodiagonal : bool, optional
1188
+ If True, set diagonal elements to zero. Default: False
919
1189
 
920
1190
  Returns
921
1191
  -------
922
-
1192
+ ndarray
1193
+ Symmetrized matrix
923
1194
  """
924
1195
  if antisymmetric:
925
1196
  intermediate = (a - a.T) / 2.0
@@ -931,19 +1202,24 @@ def symmetrize(a, antisymmetric=False, zerodiagonal=False):
931
1202
  return intermediate
932
1203
 
933
1204
 
934
- def makepmask(rvals, pval, sighistfit, onesided=True):
935
- """
1205
+ def makepmask(rvals: NDArray, pval: float, sighistfit: NDArray, onesided: bool = True) -> NDArray:
1206
+ """Create significance mask from p-value threshold and distribution fit.
936
1207
 
937
1208
  Parameters
938
1209
  ----------
939
- rvals
940
- pval
941
- sighistfit
942
- onesided
1210
+ rvals : array-like
1211
+ Array of correlation or similarity values
1212
+ pval : float
1213
+ P-value threshold (0-1)
1214
+ sighistfit : array-like
1215
+ Distribution fit parameters from fitjsbpdf
1216
+ onesided : bool, optional
1217
+ If True, use one-sided test. If False, use two-sided test. Default: True
943
1218
 
944
1219
  Returns
945
1220
  -------
946
-
1221
+ ndarray
1222
+ Binary mask (int16) with 1 for significant values, 0 otherwise
947
1223
  """
948
1224
  if onesided:
949
1225
  return np.where(
@@ -958,35 +1234,49 @@ def makepmask(rvals, pval, sighistfit, onesided=True):
958
1234
 
959
1235
 
960
1236
  # Find the image intensity value which thefrac of the non-zero voxels in the image exceed
961
- def getfracval(datamat, thefrac, nozero=False):
962
- """
1237
+ def getfracval(datamat: NDArray, thefrac: float, nozero: bool = False) -> float:
1238
+ """Get data value at a specific fractional position in sorted data.
963
1239
 
964
1240
  Parameters
965
1241
  ----------
966
- datamat
967
- thefrac
1242
+ datamat : array-like
1243
+ Input data array
1244
+ thefrac : float
1245
+ Fractional position (0-1) to find value at
1246
+ nozero : bool, optional
1247
+ If True, exclude zero values. Default: False
968
1248
 
969
1249
  Returns
970
1250
  -------
971
-
1251
+ float
1252
+ Value at the specified fractional position
972
1253
  """
973
1254
  return getfracvals(datamat, [thefrac], nozero=nozero)[0]
974
1255
 
975
1256
 
976
- def getfracvals(datamat, thefracs, nozero=False, debug=False):
977
- """
1257
+ def getfracvals(
1258
+ datamat: NDArray, thefracs: ArrayLike, nozero: bool = False, debug: bool = False
1259
+ ) -> list:
1260
+ """Get data values at multiple fractional positions in sorted data.
1261
+
1262
+ Finds the intensity values that correspond to specified fractional positions
1263
+ when data is sorted in ascending order. Useful for percentile calculations.
978
1264
 
979
1265
  Parameters
980
1266
  ----------
981
- datamat
982
- thefracs
983
- displayplots
984
- nozero
985
- debug
1267
+ datamat : array-like
1268
+ Input data array
1269
+ thefracs : array-like
1270
+ List of fractional positions (0-1) to find values at
1271
+ nozero : bool, optional
1272
+ If True, exclude zero values. Default: False
1273
+ debug : bool, optional
1274
+ Enable debug output. Default: False
986
1275
 
987
1276
  Returns
988
1277
  -------
989
-
1278
+ list
1279
+ Values at the specified fractional positions
990
1280
  """
991
1281
  thevals = []
992
1282
 
@@ -1014,17 +1304,23 @@ def getfracvals(datamat, thefracs, nozero=False, debug=False):
1014
1304
  return thevals
1015
1305
 
1016
1306
 
1017
- def getfracvalsfromfit(histfit, thefracs):
1018
- """
1307
+ def getfracvalsfromfit(histfit: ArrayLike, thefracs: ArrayLike) -> NDArray:
1308
+ """Get data values at fractional positions from a Johnson SB distribution fit.
1309
+
1310
+ Uses the fitted Johnson SB distribution to calculate values corresponding
1311
+ to specified percentiles.
1019
1312
 
1020
1313
  Parameters
1021
1314
  ----------
1022
- histfit
1023
- thefracs
1315
+ histfit : array-like
1316
+ Johnson SB distribution fit parameters (a, b, loc, scale, zeroterm) from fitjsbpdf
1317
+ thefracs : array-like
1318
+ List of fractional positions/percentiles (0-1) to calculate values for
1024
1319
 
1025
1320
  Returns
1026
1321
  -------
1027
-
1322
+ array-like
1323
+ Values corresponding to the specified percentiles from the fitted distribution
1028
1324
  """
1029
1325
  # print('entering getfracvalsfromfit: histfit=',histfit, ' thefracs=', thefracs)
1030
1326
  thedist = johnsonsb(histfit[0], histfit[1], histfit[2], histfit[3])
@@ -1032,7 +1328,13 @@ def getfracvalsfromfit(histfit, thefracs):
1032
1328
  return thevals
1033
1329
 
1034
1330
 
1035
- def makemask(image, threshpct=25.0, verbose=False, nozero=False, noneg=False):
1331
+ def makemask(
1332
+ image: NDArray,
1333
+ threshpct: float = 25.0,
1334
+ verbose: bool = False,
1335
+ nozero: bool = False,
1336
+ noneg: bool = False,
1337
+ ) -> NDArray:
1036
1338
  """
1037
1339
 
1038
1340
  Parameters
@@ -1072,7 +1374,7 @@ def makemask(image, threshpct=25.0, verbose=False, nozero=False, noneg=False):
1072
1374
  return themask
1073
1375
 
1074
1376
 
1075
- def getmasksize(themask):
1377
+ def getmasksize(themask: NDArray) -> int:
1076
1378
  """
1077
1379
 
1078
1380
  Parameters